QPlainTextEdit - searches the document to the end and again from the beginning - c++

I want to search in QPlainTextEdit for a string from the current cursor to the end. If nothing is found I want to continue searching from the start. Only in this stage, if nothing is found, a message will appear.
This is the code:
void BasicEdit::findString(QString s, bool reverse, bool casesens, bool words) {
QTextDocument::FindFlags flag;
if (reverse) flag |= QTextDocument::FindBackward;
if (casesens) flag |= QTextDocument::FindCaseSensitively;
if (words) flag |= QTextDocument::FindWholeWords;
QTextCursor cursor = this->textCursor();
if (!find(s, flag)) {
//nothing is found | jump to start/end
cursor.movePosition(reverse?QTextCursor::End:QTextCursor::Start);
setTextCursor(cursor); //!!!!!!
if (!find(s, flag)) {
//no match in whole document
QMessageBox msgBox;
msgBox.setText(tr("String not found."));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
}
}
}
The problem is line setTextCursor(cursor);
without this line, the search does not continue from the beginning/end
with this line everything is fine except that when string is not found the cursor is positioned at the beginning / end of the document, and the current position in document for user is lost.
How to search for a string in a document and do not change current position if none is found?
Update
Thanks to IAmInPLS the code looks like below.
I added value preservation for verticalScrollBar.
Even so, there is a short flickering when nothing is found generated by: cursor.movePosition(reverse?QTextCursor::End:QTextCursor::Start);
How can we get rid of it?
How can look like a professional editor?
It is an idea to create another invisible QPlainTextEdit element to search in it?
void BasicEdit::findString(QString s, bool reverse, bool casesens, bool words)
{
QTextDocument::FindFlags flag;
if (reverse) flag |= QTextDocument::FindBackward;
if (casesens) flag |= QTextDocument::FindCaseSensitively;
if (words) flag |= QTextDocument::FindWholeWords;
QTextCursor cursor = this->textCursor();
// here we save the cursor position and the verticalScrollBar value
QTextCursor cursorSaved = cursor;
int scroll = verticalScrollBar()->value();
if (!find(s, flag))
{
//nothing is found | jump to start/end
cursor.movePosition(reverse?QTextCursor::End:QTextCursor::Start);
setTextCursor(cursor);
if (!find(s, flag))
{
// word not found : we set the cursor back to its initial position and restore verticalScrollBar value
setTextCursor(cursorSaved);
verticalScrollBar()->setValue(scroll);
QMessageBox msgBox(this);
msgBox.setText(tr("String not found."));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
}
}
}

The idea is to keep the cursor position you have before starting to search for the word. Then, after the research, you will set the cursor back to the position you saved.
void BasicEdit::findString(QString s, bool reverse, bool casesens, bool words)
{
QTextDocument::FindFlags flag;
if (reverse) flag |= QTextDocument::FindBackward;
if (casesens) flag |= QTextDocument::FindCaseSensitively;
if (words) flag |= QTextDocument::FindWholeWords;
QTextCursor cursor = this->textCursor();
// here , you save the cursor position
QTextCursor cursorSaved = cursor;
if (!find(s, flag))
{
//nothing is found | jump to start/end
cursor.movePosition(reverse?QTextCursor::End:QTextCursor::Start);
/* following line :
- the cursor is set at the beginning/end of the document (if search is reverse or not)
- in the next "find", if the word is found, now you will change the cursor position
*/
setTextCursor(cursor);
if (!find(s, flag))
{
//no match in whole document
QMessageBox msgBox;
msgBox.setText(tr("String not found."));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
// word not found : we set the cursor back to its initial position
setTextCursor(cursorSaved);
}
}
}

Related

Reset text formatting in QTextEdit after space

Are there good solution to reset text formatting (e.g. bold) after press Space button for QTextEdit?
Example of case:
boldText notBoldText // After press Space button text is not bold anymore
For example, I can use QWidget::eventFilter(...) and check inside
if (key == Qt::Key_Space) { ... }
But it looks bad.
It's how I set formatting now:
QTextCursor cursor = textCursor();
if (!cursor.hasSelection())
return;
QTextCharFormat fmt;
fmt.setFontWeight(cursor.charFormat().font().bold() ? QFont::Normal : QFont::DemiBold);
cursor.mergeCharFormat(format);
mergeCurrentCharFormat(format);

How to do a wrapped text finder?

I am working on a text finder in QtCreator(c++). I used a "QtextEdit::find" function and it finds every occurrence of the searched word until the document reaches the end, when a NEXT button is pushed. Now I want to add a "QCheckBox" that when checked restart the "finder" at the beginning of the document and continues the search.
Do you have any suggestions how to do that, considering that the "QTextEdit::find" returns a bool?
Here is the my find function:
void textFinder(const QString& textToFind, bool rev, bool wrappedSearch)
{
QTextDocument *document = this->document();
QTextCursor cursor (document);
QTextDocument::FindFlags flag;
if (rev == flag)
flag |= QTextDocument::FindBackward;
bool found = find(textToFind, flag);
if (wrappedSearch == true){
cursor.movePosition(QTextCursor::Start);
}
}
The tool bar that I did contains a QLineEdit, QPushButton, and a QCheckBox.
Thank you for your answears ! Those helped me a lot. For my code, it worked like this:
if (searchWrapped == true && !found){
if (rev == false && cursor.atStart() == true){
cursor.movePosition(QTextCursor::End);
found = find (text, flag);
}
if (rev && cursor.atEnd() == true){
cursor.movePosition(QTextCursor::Start);
found = find (text, flag);
}
}
setTextCursor(cursor);
}
If I have understood the question you want help with implementing wrapped search. Then I think it will suffice to perform another search after having moved the cursor to either start or end of document depending on whether reversed search or not:
void textFinder(const QString& textToFind, bool rev, bool wrappedSearch)
{
QTextDocument *document = this->document();
QTextCursor cursor (document);
QTextDocument::FindFlags flag;
if (rev == flag)
flag |= QTextDocument::FindBackward;
bool found = find(textToFind, flag);
if (!found&&wrappedSearch == true)
{
cursor.movePosition(rev?QTextCursor::End:QTextCursor::Start);
found = find(textToFind, flag);
}
}
Theres a few problems with your code and the logic inside it.
First of all, I don't understand why you are comparing your rev bool to the flag variable. Shouldn't you compare it against true? I'd also set the initial value of the flag variable to 0.
Second of all, if the wrapped search is set to true, you should only wrap it if QTextEdit::find returned false. Otherwise you'd be stuck at the beginning of the document.
Then you should also check if the value rev is true before setting the cursor to the beginning. If you are searching backwards, you should set the cursor to the end of the document when wrapping.
And you also have to set the text cursor back to your text edit after modifying it.
Here is my working search function, I didn't subclass QTextEdit because I was lazy, but the logic is the same.
void MainWindow::findText(const QString &text, bool rev, bool wrapped)
{
QTextDocument::FindFlags flag = 0;
if(rev)
flag = flag | QTextDocument::FindBackward;
bool found = ui->textEdit->find(text, flag);
if(!found && wrapped)
{
QTextCursor cursor = ui->textEdit->textCursor();
if(!rev)
cursor.movePosition(QTextCursor::Start);
else
cursor.movePosition(QTextCursor::End);
ui->textEdit->setTextCursor(cursor);
ui->textEdit->find(text, flag);
}
}

QTextEdit. How to select text manually?

There are functions like textEdit->textCursor()->selectionStart() and textEdit->textCursor()->selectionEnd(), but there are no functions setSelectionStart, setSelectionEnd.
Is there any way to select some part of text manually?
QTextCursor c = textEdit->textCursor();
c.setPosition(startPos);
c.setPosition(endPos, QTextCursor::KeepAnchor);
textEdit->setTextCursor(c);
This piece of code moves the cursor to the start position of the selection using setPosition, then moves it to the end of the selection, but leaves the selection anchor at the old position by specifying a MoveMode as the second parameter.
The last line sets the selection to be visible inside the edit control, so you should skip it if you just want to do some manipulations with the selected text.
Also, if you don't have the exact positions, movePosition is helpful: you can move the cursor in various ways, such as one word to the right or down one line.
I encountered a similar problem.
In Windows 10, there might be a bug of 'drag/move'. We use QT_NO_DRAGANDDROP as a compiler option, which makes text selection in QTextEdit not work anymore.
Solution:
void QTextEditEx::mouseMoveEvent(QMouseEvent *event)
{
QTextEdit::mouseMoveEvent(event);
if (event->buttons() & Qt::LeftButton)
{
QTextCursor cursor = textCursor();
QTextCursor endCursor = cursorForPosition(event->pos()); // key point
cursor.setPosition(pos, QTextCursor::MoveAnchor);
cursor.setPosition(endCursor.position(), QTextCursor::KeepAnchor);
setTextCursor(cursor);
}
}
void QTextEditEx::mousePressEvent(QMouseEvent *event)
{
QTextEdit::mousePressEvent(event);
if (event->buttons() & Qt::LeftButton)
{
QTextCursor cursor = cursorForPosition(event->pos());
// int pos; member variable
pos = cursor.position();
cursor.clearSelection();
setTextCursor(cursor);
}
}
reference:
Two existing answers
QTextEdit: get word under the mouse pointer?
Try to use:
QTextCursor cur = tw->textCursor();
cur.clearSelection();
tw->setTextCursor(cur);

Why does this code not highlight the search term once found?

The code below does not highlight the search term when it is found. In fact the cursor disappears from the QPlainTextEdit (called ui->Editor) after pressing the 'next' button. What's causing it?
void TextEditor::findNextInstanceOfSearchTerm()
{
QString searchTerm = this->edtFind->text();
if(this->TextDocument == NULL)
{
this->TextDocument = ui->Editor->document();
}
QTextCursor documentCursor(this->TextDocument);
documentCursor = this->TextDocument->find(searchTerm,documentCursor);
if(!documentCursor.isNull())
{
documentCursor.select(QTextCursor::WordUnderCursor);
}else
{
ui->statusbar->showMessage("\""+searchTerm+"\" could not be found",MESSAGE_DURATION);
}
}
Firstly, your code creates a new cursor at the beginning of the document each time you press the next button, so you will always search from the beginning. Secondly, you must understand that the cursor you manipulate has nothing to do with the one in your QPlainTextEdit: you manipulate a copy. If you want to impact the text edit, you must modify its cursor using setTextCursor. Here is a working solution:
void TextEditor::findNextInstanceOfSearchTerm()
{
QString searchTerm = this->edtFind->text();
if(this->TextDocument == NULL)
{
this->TextDocument = ui->Editor->document();
}
// get the current cursor
QTextCursor documentCursor = ui->Editor->textCursor();
documentCursor = this->TextDocument->find(searchTerm,documentCursor);
if(!documentCursor.isNull())
{
// needed only if you want the entire word to be selected
documentCursor.select(QTextCursor::WordUnderCursor);
// modify the text edit cursor
ui->Editor->setTextCursor(documentCursor);
}
else
{
ui->statusbar->showMessage(
"\""+searchTerm+"\" could not be found",MESSAGE_DURATION);
}
}
As a side note, you might want to know that QPlainTextEdit provides a find method, so this might be an easier way to achieve what you want:
void TextEditor::findNextInstanceOfSearchTerm()
{
QString searchTerm = this->edtFind->text();
bool found = ui->Editor->find(searchTerm);
if (found)
{
QTextCursor cursor = ui->Editor->textCursor();
cursor.select(QTextCursor::WordUnderCursor);
ui->Editor->setTextCursor(cursor);
}
else
{
// set message in status bar
}
}
Use QTextCursor::EndOfWord
Use QPlainTextEdit::setExtraSelections to select/highlight something in QPlainTextEdit
Simply you already have cursor that would highlight word, but you didn't apply it to text edit

Gotoline in Qt Creator

Well, I'm doing a Goto Line System. But it seems it doesn't work. It did before but I think I broke it.
void ScriptWindow::gotoLine()
{
int line = QInputDialog::getInteger(myEdit, "Line Number","To what line do you want to go?", 1, 1, myEdit->document()->lineCount());
QTextCursor cursor = myEdit->textCursor();
myEdit->setTextCursor(cursor);
cursor.setPosition(QTextCursor::Start, QTextCursor::MoveAnchor);
while(cursor.position() == QTextCursor::Start) {
cursor.setPosition(line - 1, QTextCursor::MoveAnchor);
}
}
Could you please tell me what am I doing wrong?
Set the cursor position to zero, move down by number of lines, and set myEdit's text cursor.
QTextCursor cursor = myEdit->textCursor();
cursor.setPosition(0);
cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, line-1);
myEdit->setTextCursor(cursor);
Alternatively, find the position via the QTextDocument and then just set the position.
int pos = myEdit->document()->findBlockByLineNumber(line-1).position();
QTextCursor cursor = myEdit->textCursor();
cursor.setPosition(pos);
myEdit->setTextCursor(cursor);