#include "findwin.h" #include "scintillaeditview.h" #include "ccnotepad.h" #include "progresswin.h" #include "doctypelistview.h" #include "filemanager.h" #include "ccnotepad.h" #include #include #include #include #include #include #include enum TAB_TYPES { FIND_TYPE=0, RELPACE_TYPE, DIR_FIND_TYPE, MARK_TYPE, }; const int MAX_RECORD_KEY_LENGTH = 20; FindWin::FindWin(QWidget *parent):QMainWindow(parent), m_editTabWidget(nullptr), m_isFindFirst(true), m_findHistory(nullptr), \ pEditTemp(nullptr), m_curEditWin(nullptr), m_isStatic(false), m_isReverseFind(false), m_pMainPad(parent) { ui.setupUi(this); //默认值要与界面初始值一样。 m_BackwardDir = false; m_matchWhole = false; m_matchCase = false; m_matchWrap = true; m_extend = false; m_searchMode = 1; m_re = false; m_cs = false; m_wo = false; m_wrap = true; m_forward = true;;//是否向前查找。注意如果向后,要为false m_extend = false; m_isFound = false; connect(ui.findModeRegularBt, &QRadioButton::toggled, this, &FindWin::slot_findModeRegularBtChange); connect(ui.replaceModeRegularBt, &QRadioButton::toggled, this, &FindWin::slot_replaceModeRegularBtChange); connect(ui.dealFileType, &QCheckBox::stateChanged, this,&FindWin::slot_dealFileTypeChange); connect(ui.skipDir, &QCheckBox::stateChanged, this, &FindWin::slot_skipDirChange); connect(ui.clearBt, &QAbstractButton::clicked, this, &FindWin::sign_clearResult); connect(ui.findClearBt, &QAbstractButton::clicked, this, &FindWin::sign_clearResult); connect(ui.findinfilesTab, &QTabWidget::currentChanged, this, &FindWin::slot_tabIndexChange); #if 0 //这样是无效的,记住一下,不删除,避免后面再做无用功 Qt::WindowFlags m_flags = windowFlags(); //这样使得窗口在上面,不至于总是到后面被挡住。 #if defined (Q_MAC_OS) setWindowFlags(m_flags | Qt::WindowStaysOnTopHint); #endif #endif raise(); setFocusPolicy(Qt::StrongFocus); setFocus(); ui.findinfilesTab->setAttribute(Qt::WA_StyledBackground); ui.findComboBox->installEventFilter(this); } FindWin::~FindWin() { m_findHistory = nullptr; if (pEditTemp != nullptr) { delete pEditTemp; pEditTemp = nullptr; } } void FindWin::slot_tabIndexChange(int index) { TAB_TYPES type = (TAB_TYPES)index; if (RELPACE_TYPE == type) { ui.replaceTextBox->setFocus(); if (ui.replaceTextBox->currentText().isEmpty() && !ui.findComboBox->currentText().isEmpty()) { if (ui.findComboBox->currentText().size() < MAX_RECORD_KEY_LENGTH) { ui.replaceTextBox->setCurrentText(ui.findComboBox->currentText()); } } } else if(FIND_TYPE == type) { ui.findComboBox->setFocus(); if (ui.findComboBox->currentText().isEmpty() && !ui.replaceTextBox->currentText().isEmpty()) { if (ui.replaceTextBox->currentText().size() < MAX_RECORD_KEY_LENGTH) { ui.findComboBox->setCurrentText(ui.replaceTextBox->currentText()); } } } else if (DIR_FIND_TYPE == type) { ui.dirFindWhat->setFocus(); } else if (MARK_TYPE == type) { ui.markTextBox->setFocus(); } m_isFindFirst = true; if (m_findHistory->isEmpty()) { return; } } void FindWin::slot_dealFileTypeChange(int state) { if (state == Qt::Checked) { ui.fileType->setEnabled(true); } else { ui.fileType->setEnabled(false); } } void FindWin::slot_skipDirChange(int state) { if (state == Qt::Checked) { ui.skipDirNames->setEnabled(true); } else { ui.skipDirNames->setEnabled(false); } } //自动调整当前窗口的状态,如果发生了变化,则需要认定为是首次查找 QWidget* FindWin::autoAdjustCurrentEditWin() { QWidget* pw = m_editTabWidget->currentWidget(); if (m_curEditWin != pw) { m_curEditWin = pw; m_isFindFirst = true; } return pw; } void FindWin::setCurrentTab(FindTabIndex index) { ui.findinfilesTab->setCurrentIndex(index); if (FIND_TAB == index) { ui.findComboBox->setFocus(); } else if(REPLACE_TAB == index) { ui.replaceTextBox->setFocus(); } raise(); } void FindWin::setTabWidget(QTabWidget *editTabWidget) { m_editTabWidget = editTabWidget; } void FindWin::setFindText(QString &text) { ui.findComboBox->setEditText(text); addFindHistory(text); } void FindWin::setReplaceFindText(QString& text) { ui.replaceTextBox->setEditText(text); addFindHistory(text); } void FindWin::setDirFindText(QString& text) { ui.dirFindWhat->setEditText(text); addFindHistory(text); } void FindWin::disableReplace() { ui.tab_replace->setEnabled(false); } void FindWin::setFindHistory(QList* findHistory) { m_findHistory = findHistory; if ((m_findHistory != nullptr) && !m_findHistory->isEmpty()) { ui.findComboBox->addItems(*m_findHistory); ui.replaceTextBox->addItems(*m_findHistory); ui.dirFindWhat->addItems(*m_findHistory); ui.markTextBox->addItems(*m_findHistory); ui.findComboBox->clearEditText(); ui.replaceTextBox->clearEditText(); ui.dirFindWhat->clearEditText(); ui.markTextBox->clearEditText(); } } //标记高亮所有word单词 int FindWin::markAllWord(QString & word) { ui.markTextBox->setCurrentText(word); ui.findinfilesTab->setCurrentIndex(3); //发现中文在全词匹配下面不能匹配到单词。最好是英文全词匹配,中文不需要 //但是好像没有一个现成的方法来判断word中的字符。暂时不做全词匹配 ui.markMatchWholeBox->setChecked(false); ui.markMatchCaseBox->setChecked(true); return markAll(); } //删除行首尾的空白字符 void FindWin::removeLineHeadEndBlank(int mode) { QWidget* pw = autoAdjustCurrentEditWin(); ScintillaEditView* pEdit = dynamic_cast(pw); if (pEdit != nullptr) { if (pEdit->isReadOnly()) { ui.statusbar->showMessage(tr("The ReadOnly document does not allow this operation."), 8000); QApplication::beep(); return; } ui.findinfilesTab->setCurrentIndex(1); if (mode == 1) { ui.replaceTextBox->setCurrentText("^[ ]+"); } else if (mode == 2) { ui.replaceTextBox->setCurrentText("[ ]+$"); } ui.replaceWithBox->setText(""); ui.replaceModeRegularBt->setChecked(true); m_isStatic = true; slot_replaceAll(); m_isStatic = false; } } void FindWin::focusInEvent(QFocusEvent * ev) { QWidget::focusInEvent(ev); setWindowOpacity(1.0); } void FindWin::focusOutEvent(QFocusEvent * ev) { QWidget::focusOutEvent(ev); if (this->isActiveWindow()) { //当前还是活动窗口,不设置透明 return; } else { setWindowOpacity(0.6); //static int i = 0; //qDebug() << ++i; } } bool FindWin::eventFilter(QObject* watched, QEvent *event) { if (watched == ui.findComboBox) { if (event->type() == QEvent::KeyPress) { QKeyEvent *ke = static_cast(event); if (ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return) { emit ui.findTextNext->click(); return true; //该事件已经被处理 } return false; } else { return false; } } return QWidget::eventFilter(watched, event); // 最后将事件交给上层对话框 } //从ui读取参数配置到成员变量 void FindWin::updateParameterFromUI() { bool extend = false; if (ui.findinfilesTab->currentIndex() == 0) { int searchMode = 0; if (ui.findModeNormalBt->isChecked()) { searchMode = 1; extend = false; } else if (ui.findModeRegularBt->isChecked()) { searchMode = 2; extend = false; } else if (ui.findModeExtendBt->isChecked()) { searchMode = 1; extend = true; } if (m_searchMode != searchMode) { m_searchMode = searchMode; m_isFindFirst = true; } if (m_expr != ui.findComboBox->currentText()) { m_expr = ui.findComboBox->currentText(); m_isFindFirst = true; } if (m_BackwardDir != ui.findBackwardBox->isChecked()) { m_BackwardDir = ui.findBackwardBox->isChecked(); m_isFindFirst = true; } if (m_matchWhole != ui.findMatchWholeBox->isChecked()) { m_matchWhole = ui.findMatchWholeBox->isChecked(); m_isFindFirst = true; } if (m_matchCase != ui.findMatchCaseBox->isChecked()) { m_matchCase = ui.findMatchCaseBox->isChecked(); m_isFindFirst = true; } if (m_matchWrap != ui.findWrapBox->isChecked()) { m_matchWrap = ui.findWrapBox->isChecked(); m_isFindFirst = true; } } else if (ui.findinfilesTab->currentIndex() == 1) { int searchMode = 0; if (ui.replaceModeNormalBox->isChecked()) { searchMode = 1; extend = false; } else if (ui.replaceModeRegularBt->isChecked()) { searchMode = 2; extend = false; } else if (ui.replaceModeExtendBox->isChecked()) { searchMode = 1; extend = true; } if (m_searchMode != searchMode) { m_searchMode = searchMode; m_isFindFirst = true; } if (m_expr != ui.replaceTextBox->currentText()) { m_expr = ui.replaceTextBox->currentText(); m_isFindFirst = true; } if (m_replaceWithText != ui.replaceWithBox->text()) { m_replaceWithText = ui.replaceWithBox->text(); m_isFindFirst = true; } if (m_BackwardDir != ui.replaceBackwardBox->isChecked()) { m_BackwardDir = ui.replaceBackwardBox->isChecked(); m_isFindFirst = true; } if (m_matchWhole != ui.replaceMatchWholeBox->isChecked()) { m_matchWhole = ui.replaceMatchWholeBox->isChecked(); m_isFindFirst = true; } if (m_matchCase != ui.replaceMatchCaseBox->isChecked()) { m_matchCase = ui.replaceMatchCaseBox->isChecked(); m_isFindFirst = true; } if (m_matchWrap != ui.replaceWrapBox->isChecked()) { m_matchWrap = ui.replaceWrapBox->isChecked(); m_isFindFirst = true; } } else if (ui.findinfilesTab->currentIndex() == 2) { int searchMode = 0; if (ui.dirFindModeNormalBox->isChecked()) { searchMode = 1; extend = false; } else if (ui.dirFindModeRegularBt->isChecked()) { searchMode = 2; extend = false; } else if (ui.dirFindModeExtendBox->isChecked()) { searchMode = 1; extend = true; } if (m_searchMode != searchMode) { m_searchMode = searchMode; m_isFindFirst = true; } if (m_expr != ui.dirFindWhat->currentText()) { m_expr = ui.dirFindWhat->currentText(); m_isFindFirst = true; } if (m_replaceWithText != ui.dirReplaceWhat->text()) { m_replaceWithText = ui.dirReplaceWhat->text(); m_isFindFirst = true; } m_BackwardDir = false; if (m_matchWhole != ui.dirFindMatchWholeBox->isChecked()) { m_matchWhole = ui.dirFindMatchWholeBox->isChecked(); m_isFindFirst = true; } if (m_matchCase != ui.dirFindMatchCaseBox->isChecked()) { m_matchCase = ui.dirFindMatchCaseBox->isChecked(); m_isFindFirst = true; } m_matchWrap = true; } else if (ui.findinfilesTab->currentIndex() == 3) { int searchMode = 0; if (ui.markModeNormalBox->isChecked()) { searchMode = 1; extend = false; } else if (ui.markModeRegularBt->isChecked()) { searchMode = 2; extend = false; } else if (ui.markModeExtendBox->isChecked()) { searchMode = 1; extend = true; } if (m_searchMode != searchMode) { m_searchMode = searchMode; m_isFindFirst = true; } if (m_expr != ui.markTextBox->currentText()) { m_expr = ui.markTextBox->currentText(); m_isFindFirst = true; } m_BackwardDir = false; if (m_matchWhole != ui.markMatchWholeBox->isChecked()) { m_matchWhole = ui.markMatchWholeBox->isChecked(); m_isFindFirst = true; } if (m_matchCase != ui.markMatchCaseBox->isChecked()) { m_matchCase = ui.markMatchCaseBox->isChecked(); m_isFindFirst = true; } m_matchWrap = true; } m_re = ((m_searchMode == 2) ? true : false); if (m_cs != m_matchCase) { m_cs = m_matchCase; } if (m_wo != m_matchWhole) { m_wo = m_matchWhole; } if (m_wrap != m_matchWrap) { m_wrap = m_matchWrap; } if (m_extend != extend) { m_extend = extend; m_isFindFirst = true; } //本来的m_BackwardDir只控制是否勾选反向 m_forward = !m_BackwardDir; //m_isReverseFind 控制是否还需要反向一直,只在查找前一个生效,只影响查找界面 if (ui.findinfilesTab->currentIndex() == 0) { m_forward = (m_isReverseFind ? !m_forward : m_forward); } } void FindWin::addFindHistory(QString &text) { //太长会导致看起来很杂乱,也不记录 if (text.isEmpty() || text.size() >= MAX_RECORD_KEY_LENGTH) { return; } if ((m_findHistory != nullptr) && (-1 == m_findHistory->indexOf(text))) { m_findHistory->push_front(text); if (m_findHistory->size() >= 15) { m_findHistory->takeLast(); } ui.findComboBox->insertItem(0, text); ui.replaceTextBox->insertItem(0, text); ui.dirFindWhat->insertItem(0,text); ui.markTextBox->insertItem(0, text); } } //检查是否是第一次查找,凡是参数变化了,则认定为是第一次查找。 //因为查找分firstFirst和firstNext,则是qscint特性决定的。所以正确识别第一次查找是必要的 bool FindWin::isFirstFind() { return m_isFindFirst; } //const QChar *constData() const bool readBase(const QChar * str, int * value, int base, int size) { int i = 0, temp = 0; *value = 0; QChar max = QChar(QChar('0').digitValue() + base - 1); QChar current; while (i < size) { current = str[i]; if (current >= 'A') { current = current.digitValue() & 0xdf; current = current.digitValue() - ('A' - '0' - 10); } else if (current > '9') return false; if (current >= '0' && current <= max) { temp *= base; temp += (current.digitValue() - '0'); } else { return false; } ++i; } *value = temp; return true; } int convertExtendedToString(QString& query, QString &result) { //query may equal to result, since it always gets smaller int i = 0, j = 0; int length = query.length(); int charLeft = length; QChar current; while (i < length) { //because the backslash escape quences always reduce the size of the generic_string, no overflow checks have to be made for target, assuming parameters are correct current = query.at(i); --charLeft; if (current == '\\' && charLeft) { //possible escape sequence ++i; --charLeft; current = query.at(i); switch (current.toLatin1()) { case 'r': result[j] = '\r'; break; case 'n': result[j] = '\n'; break; case '0': result[j] = '\0'; break; case 't': result[j] = '\t'; break; case '\\': result[j] = '\\'; break; case 'b': case 'd': case 'o': case 'x': case 'u': { int size = 0, base = 0; if (current == 'b') { //11111111 size = 8, base = 2; } else if (current == 'o') { //377 size = 3, base = 8; } else if (current == 'd') { //255 size = 3, base = 10; } else if (current == 'x') { //0xFF size = 2, base = 16; } else if (current == 'u') { //0xCDCD size = 4, base = 16; } if (charLeft >= size) { int res = 0; if (readBase(query.constData() + (i + 1), &res, base, size)) { result[j] = QChar(res); i += size; break; } } //not enough chars to make parameter, use default method as fallback } default: { //unknown sequence, treat as regular text result[j] = '\\'; ++j; result[j] = current; break; } } } else { result[j] = query[i]; } ++i; ++j; } return j; } void FindWin::showCallTip(QsciScintilla* pEdit, int pos) { QString zeroString(tr("find-regex-zero-length-match")); QByteArray bytes = zeroString.toUtf8(); bytes.append('\0'); /*int size = bytes.size(); char* newStr = new char[size + 1]; memcpy(newStr, bytes.data(), size); newStr[bytes.size()] = '\0';*/ pEdit->SendScintilla(SCI_CALLTIPSHOW, pos, (void*)(bytes.data())); /*delete[]newStr;*/ } //删除空白行 void FindWin::removeEmptyLine(bool isBlankContained) { QWidget* pw = autoAdjustCurrentEditWin(); ScintillaEditView* pEdit = dynamic_cast(pw); if (pEdit != nullptr) { if (pEdit->isReadOnly()) { ui.statusbar->showMessage(tr("The ReadOnly document does not allow this operation."), 8000); QApplication::beep(); return; } ui.findinfilesTab->setCurrentIndex(1); if (isBlankContained) { ui.replaceTextBox->setCurrentText("^[\\t ]*$(\\r\\n|\\r|\\n)"); } else { ui.replaceTextBox->setCurrentText("^$(\\r\\n|\\r|\\n)"); } ui.replaceWithBox->setText(""); ui.replaceModeRegularBt->setChecked(true); m_isStatic = true; slot_replaceAll(); m_isStatic = false; } } void FindWin::findNext() { slot_findNext(); } void FindWin::findPrev() { slot_findPrev(); } /*处理查找时零长的问题。一定要处理,否则会死循环,因为每次都在原地查找。 * 就是把下次查找的startpos往前一个,否则每次都从这个startpos找到自己 */ void FindWin::dealWithZeroFound(QsciScintilla* pEdit) { FindState& state = pEdit->getLastFindState(); if (state.targstart == state.targend) { //强行加1,否则每次查找总是在同一个地方。 state.startpos++; } } //调整光标变化后,查找位置需要调整的情况 void FindWin::adjustSearchStartPosChange(QsciScintilla* pEdit) { int caretPos = pEdit->SendScintilla(SCI_GETCURRENTPOS); FindState& state = pEdit->getLastFindState(); if (state.targend != caretPos) { state.startpos = caretPos; } } /*处理查找时零长的问题。一定要处理,否则会死循环,因为每次都在原地查找。 * 就是把下次查找的startpos往前一个,否则每次都从这个startpos找到自己 * 和dealWithZeroFound是一样的,就是要显示消息而已 */ void FindWin::dealWithZeroFoundShowTip(QsciScintilla* pEdit, bool isShowTip) { if (!isShowTip) { return dealWithZeroFound(pEdit); } FindState& state = pEdit->getLastFindState(); //int linpos = pEdit->SendScintilla(SCI_POSITIONFROMLINE, state.linenum); int line = 0; int indexStart = 0; int indexEnd = 0; pEdit->lineIndexFromPosition(state.targstart, &line, &indexStart); pEdit->lineIndexFromPosition(state.targend, &line, &indexEnd); ui.statusbar->showMessage(tr("target info linenum %1 pos is %2 - %3").arg(state.linenum + 1).arg(indexStart).arg(indexEnd), 8000); if (state.targstart == state.targend) { //强行加1,否则每次查找总是在同一个地方。 //这里有个问题,如果是反向查找,则应该是pos减去1才对。 //不过只有在正则查找才会出现零长,而正则查找时我们不允许反向查找。反向是回调的。 state.startpos++; showCallTip(pEdit, state.targstart); } } void FindWin::dofindNext() { if (ui.findComboBox->currentText().isEmpty()) { ui.statusbar->showMessage(tr("what find is null !"), 8000); QApplication::beep(); return; } updateParameterFromUI(); QWidget* pw = autoAdjustCurrentEditWin(); QsciScintilla* pEdit = dynamic_cast(pw); //第一次查找 if (isFirstFind()) { if (pEdit != nullptr) { QString whatFind = ui.findComboBox->currentText(); addFindHistory(whatFind); if (m_extend) { QString extendFind; convertExtendedToString(whatFind, extendFind); whatFind = extendFind; } if (!pEdit->findFirst(whatFind, m_re, m_cs, m_wo, m_wrap, m_forward, FINDNEXTTYPE_FINDNEXT, -1, -1, true, false, false)) { ui.statusbar->showMessage(tr("cant't find text \'%1\'").arg(m_expr), 8000); QApplication::beep(); m_isFindFirst = true; } else { m_isFindFirst = false; dealWithZeroFoundShowTip(pEdit); } } } else { //查找下一个 if (pEdit != nullptr) { adjustSearchStartPosChange(pEdit); if (!pEdit->findNext()) { ui.statusbar->showMessage(tr("no more find text \'%1\'").arg(m_expr), 8000); m_isFindFirst = true; QApplication::beep(); } else { dealWithZeroFoundShowTip(pEdit); } } } } //一旦修改条件发生变化,则认定为第一次查找 void FindWin::slot_findNext() { if (m_isReverseFind) { m_isReverseFind = false; m_isFindFirst = true; } dofindNext(); } void FindWin::setFindBackward(bool isBackward) { if (ui.findBackwardBox->isChecked() != isBackward) { ui.findBackwardBox->setChecked(isBackward); } } void FindWin::slot_findPrev() { if (!m_isReverseFind) { m_isReverseFind = true; m_isFindFirst = true; } dofindNext(); } //查找计数 void FindWin::slot_findCount() { if (ui.findComboBox->currentText().isEmpty()) { ui.statusbar->showMessage(tr("what find is null !"), 8000); QApplication::beep(); return; } QWidget* pw = autoAdjustCurrentEditWin(); ScintillaEditView* pEdit = dynamic_cast(pw); if (pEdit != nullptr) { if (pEdit->isReadOnly()) { ui.statusbar->showMessage(tr("The ReadOnly document does not allow this operation."), 8000); QApplication::beep(); return; } updateParameterFromUI(); int srcPostion = pEdit->execute(SCI_GETCURRENTPOS); int firstDisLineNum = pEdit->execute(SCI_GETFIRSTVISIBLELINE); int countNums = 0; //无条件进行第一次查找,从0行0列开始查找,而且不回环。如果没有找到,则替换完毕 QString whatFind = ui.findComboBox->currentText(); //这里不能直接修改results.findText的值,该值在外部显示还需要。如果修改则会显示紊乱 if (m_extend) { QString extendFind; convertExtendedToString(whatFind, extendFind); whatFind = extendFind; } //这里的forward一定要是true。回环一定是false if (!pEdit->findFirst(whatFind, m_re, m_cs, m_wo, false, true, FINDNEXTTYPE_FINDNEXT, 0, 0,false)) { ui.statusbar->showMessage(tr("count %1 times with \'%2\'").arg(countNums).arg(m_expr), 8000); QApplication::beep(); m_isFindFirst = true; return; } else { dealWithZeroFound(pEdit); } ++countNums; //找到了,增加计数 while (pEdit->findNext()) { ++countNums; dealWithZeroFound(pEdit); } pEdit->execute(SCI_GOTOPOS, srcPostion); pEdit->execute(SCI_SETFIRSTVISIBLELINE, firstDisLineNum); pEdit->execute(SCI_SETXOFFSET, 0); //全部替换后,下次查找,必须算第一次查找 m_isFindFirst = true; ui.statusbar->showMessage(tr("count %1 times with \'%2\'").arg(countNums).arg(m_expr), 8000); } else { ui.statusbar->showMessage(tr("The mode of the current document does not allow this operation."), 8000); QApplication::beep(); } } //去掉行尾的\n\r符号 static QString trimmedEnd(QString lineText) { if (lineText.endsWith("\r\n")) { return lineText.mid(0, lineText.length()-2); } if (lineText.endsWith("\r")) { return lineText.mid(0, lineText.length()-1); } if (lineText.endsWith("\n")) { return lineText.mid(0, lineText.length()-1); } return lineText; } void FindWin::addCurFindRecord(ScintillaEditView* pEdit, FindRecords& recordRet,bool isMark) { FindRecord aRecord; //看了源码,当前查找到的结果,是会被选中的。所以可通过选中范围,来记录当前被查找中的结果 //光标在选择词的尾部下一个位置 #if 0 aRecord.pos = pEdit->execute(SCI_GETSELECTIONSTART); aRecord.end = pEdit->execute(SCI_GETSELECTIONEND); #endif //加速 FindState& state = pEdit->getLastFindState(); aRecord.pos = state.targstart; aRecord.end = state.targend; //mark模式不需要这么多信息,可直接返回 if (!isMark) { aRecord.lineNum = pEdit->execute(SCI_LINEFROMPOSITION, aRecord.pos); aRecord.lineStartPos = pEdit->execute(SCI_POSITIONFROMLINE, aRecord.lineNum); int lineLens = pEdit->execute(SCI_LINELENGTH, aRecord.lineNum); if (lineLens <= 0) { return; } char* lineText = new char[lineLens + 1]; memset(lineText, 0, lineLens + 1); //这里有个bug,是qscint的,查找最后一行,会漏掉最后一个字符 pEdit->execute(SCI_GETLINE, aRecord.lineNum, reinterpret_cast(lineText)); //务必要去掉行位的换行,否则显示结果列表会显示换行 aRecord.lineContents = trimmedEnd(QString(lineText)); delete[]lineText; } recordRet.records.append(aRecord); } //在后台查找 int FindWin::findAtBack(QString keyword) { this->setCurrentTab(FIND_TAB); ui.findComboBox->setCurrentText(keyword); ui.findBackwardBox->setChecked(false); ui.findMatchCaseBox->setChecked(true); ui.findWrapBox->setChecked(false); ui.findMatchWholeBox->setChecked(false); ui.findModeNormalBt->setChecked(true); m_isStatic = true; int times = findAllInCurDoc(); m_isStatic = false; return times; } //在后台替换 int FindWin::replaceAtBack(QStringList& keyword, QStringList& replace) { assert(keyword.size() == replace.size()); this->setCurrentTab(REPLACE_TAB); QWidget* pw = autoAdjustCurrentEditWin(); ScintillaEditView* pEdit = dynamic_cast(pw); if (pEdit != nullptr) { if (pEdit->isReadOnly()) { ui.statusbar->showMessage(tr("The ReadOnly document does not allow replacement."), 8000); QApplication::beep(); return 0; } } ui.replaceBackwardBox->setChecked(false); ui.replaceMatchWholeBox->setChecked(false); ui.replaceMatchCaseBox->setChecked(true); ui.replaceWrapBox->setChecked(false); ui.replaceModeNormalBox->setChecked(true); m_isStatic = true; int times = 0; pEdit->execute(SCI_BEGINUNDOACTION); ProgressWin* loadFileProcessWin = new ProgressWin(this); loadFileProcessWin->setWindowModality(Qt::WindowModal); loadFileProcessWin->info(tr("total %1 keyword, please wait ...").arg(keyword.size())); loadFileProcessWin->setTotalSteps(keyword.size()); loadFileProcessWin->show(); for (int i = 0; i < keyword.size(); ++i) { if (loadFileProcessWin->isCancel()) { break; } ui.replaceTextBox->setCurrentText(keyword.at(i)); ui.replaceWithBox->setText(replace.at(i)); updateParameterFromUI(); QString whatFind = ui.replaceTextBox->currentText(); QString replaceText = ui.replaceWithBox->text(); times += doReplaceAll(pEdit, whatFind, replaceText, false); loadFileProcessWin->moveStep(); QCoreApplication::processEvents(); } delete loadFileProcessWin; pEdit->execute(SCI_ENDUNDOACTION); m_isStatic = false; return times; } int FindWin::findAllInCurDoc() { if (ui.findComboBox->currentText().isEmpty()) { ui.statusbar->showMessage(tr("what find is null !"), 8000); QApplication::beep(); return 0; } QWidget* pw = autoAdjustCurrentEditWin(); ScintillaEditView* pEdit = dynamic_cast(pw); if (pEdit != nullptr) { if (pEdit->isReadOnly()) { if (!m_isStatic) { ui.statusbar->showMessage(tr("The ReadOnly document does not allow this operation."), 8000); } QApplication::beep(); return 0; } FindRecords results; results.pEdit = pEdit; results.findFilePath = pw->property("filePath").toString(); updateParameterFromUI(); int srcPostion = pEdit->execute(SCI_GETCURRENTPOS); int firstDisLineNum = pEdit->execute(SCI_GETFIRSTVISIBLELINE); int replaceNums = 0; //无条件进行第一次查找,从0行0列开始查找,而且不回环。如果没有找到,则替换完毕 QString whatFind = ui.findComboBox->currentText(); results.findText = whatFind; //这里不能直接修改results.findText的值,该值在外部显示还需要。如果修改则会显示紊乱 if (m_extend) { QString extendFind; convertExtendedToString(whatFind, extendFind); whatFind = extendFind; } //这里的forward一定要是true。回环一定是false if (!pEdit->findFirst(whatFind, m_re, m_cs, m_wo, false, true, FINDNEXTTYPE_FINDNEXT, 0, 0)) { ui.statusbar->showMessage(tr("cant't find text \'%1\'").arg(m_expr), 8000); if (!m_isStatic) { QApplication::beep(); emit sign_findAllInCurDoc(&results); } m_isFindFirst = true; return 0; } else { dealWithZeroFound(pEdit); } addCurFindRecord(pEdit, results); ++replaceNums; //找到了,把结果收集起来 while (pEdit->findNext()) { addCurFindRecord(pEdit, results); ++replaceNums; dealWithZeroFound(pEdit); } pEdit->execute(SCI_GOTOPOS, srcPostion); pEdit->execute(SCI_SETFIRSTVISIBLELINE, firstDisLineNum); pEdit->execute(SCI_SETXOFFSET, 0); //全部替换后,下次查找,必须算第一次查找 m_isFindFirst = true; ui.statusbar->showMessage(tr("find finished, total %1 found!").arg(replaceNums), 10000); emit sign_findAllInCurDoc(&results); return replaceNums; } else { if (!m_isStatic) { ui.statusbar->showMessage(tr("The mode of the current document does not allow this operation."), 8000); QApplication::beep(); } } return 0; } void FindWin::slot_findAllInCurDoc() { findAllInCurDoc(); } void FindWin::slot_findAllInOpenDoc() { if (ui.findComboBox->currentText().isEmpty()) { ui.statusbar->showMessage(tr("what find is null !"), 8000); QApplication::beep(); return; } QString whatFind = ui.findComboBox->currentText(); QString originWhatFine = whatFind; if (m_extend) { QString extendFind; convertExtendedToString(whatFind, extendFind); whatFind = extendFind; } int replaceNums = 0; QVector* allOpenFileRecord = new QVector(); for (int i = 0; i < m_editTabWidget->count(); ++i) { QWidget* pw = m_editTabWidget->widget(i); ScintillaEditView* pEdit = dynamic_cast(pw); if (pEdit != nullptr) { if (pEdit->isReadOnly()) { continue; } FindRecords* results = new FindRecords(); results->pEdit = pEdit; results->findFilePath = pw->property("filePath").toString(); updateParameterFromUI(); //无条件进行第一次查找,从0行0列开始查找,而且不回环。如果没有找到,则替换完毕 //results->findText要是有原来的值,因为扩展模式下\r\n不会转义,直接输出会换行显示 results->findText = originWhatFine; if (!pEdit->findFirst(whatFind, m_re, m_cs, m_wo, false, true, FINDNEXTTYPE_FINDNEXT, 0, 0)) { delete results; continue; } else { dealWithZeroFound(pEdit); } addCurFindRecord(pEdit, *results); ++replaceNums; //找到了,把结果收集起来 while (pEdit->findNext()) { addCurFindRecord(pEdit, *results); ++replaceNums; dealWithZeroFound(pEdit); } allOpenFileRecord->append(results); } } //全部替换后,下次查找,必须算第一次查找 m_isFindFirst = true; ui.statusbar->showMessage(tr("find finished, total %1 found!").arg(replaceNums), 10000); emit sign_findAllInOpenDoc(allOpenFileRecord, replaceNums, whatFind); //释放元素 for (int i = 0; i < allOpenFileRecord->size(); ++i) { delete allOpenFileRecord->at(i); } delete allOpenFileRecord; } //返回是否查找得到内容 bool FindWin::replaceFindNext(QsciScintilla* pEdit, bool showZeroFindTip) { m_isFound = false; //第一次查找 if (m_isFindFirst) { if (pEdit != nullptr) { QString whatFind = ui.replaceTextBox->currentText(); addFindHistory(whatFind); if (m_extend) { QString extendFind; convertExtendedToString(whatFind, extendFind); whatFind = extendFind; } if (!pEdit->findFirst(whatFind, m_re, m_cs, m_wo, m_wrap, m_forward, FINDNEXTTYPE_REPLACENEXT)) { ui.statusbar->showMessage(tr("cant't find text \'%1\'").arg(m_expr), 8000); QApplication::beep(); m_isFindFirst = true; } else { m_isFound = true; m_isFindFirst = false; dealWithZeroFoundShowTip(pEdit, showZeroFindTip); } } } else { //查找下一个 if (pEdit != nullptr) { adjustSearchStartPosChange(pEdit); if (!pEdit->findNext()) { ui.statusbar->showMessage(tr("no more find text \'%1\'").arg(m_expr), 8000); m_isFindFirst = true; QApplication::beep(); } else { m_isFound = true; dealWithZeroFoundShowTip(pEdit, showZeroFindTip); } } } return m_isFound; } void FindWin::slot_replaceFindNext() { if (ui.replaceTextBox->currentText().isEmpty()) { ui.statusbar->showMessage(tr("what find is null !"), 8000); QApplication::beep(); return; } updateParameterFromUI(); QWidget* pw = autoAdjustCurrentEditWin(); QsciScintilla* pEdit = dynamic_cast(pw); replaceFindNext(pEdit, true); } //返回值:是否还可以继续替换 bool FindWin::replace(ScintillaEditView* pEdit) { if (isFirstFind()) { replaceFindNext(pEdit, false); //如果没有找到,则不替换 if (!m_isFound) { return false; } } QString findText = ui.replaceTextBox->currentText(); QString replaceText = ui.replaceWithBox->text(); if (m_extend) { QString extendFind; convertExtendedToString(findText, extendFind); findText = extendFind; QString extendReplace; convertExtendedToString(replaceText, extendReplace); replaceText = extendReplace; } //当前有选中内容,而且与待替换内容一样,则直接替换 if (m_isFound && pEdit->hasSelectedText()) { //如果选中内容,与待查找替换的内容一致,进行替换。前提是在normal模式下 if ((m_searchMode == 1) && (pEdit->selectedText().compare(findText, (m_cs ? Qt::CaseSensitive : Qt::CaseInsensitive)) == 0)) { pEdit->replace(replaceText); return replaceFindNext(pEdit,false); } else if (m_searchMode == 2) { //如果是正则表达式模式,则不能使用全部匹配才替换,要使用正则匹配,那样会比较麻烦 //只有上次查找成功,才替换? if (m_isFound) { pEdit->replace(replaceText); return replaceFindNext(pEdit,false); } else { ui.statusbar->showMessage(tr("no more replace text \'%1\'").arg(m_expr), 8000); QApplication::beep(); return false; } } else { ui.statusbar->showMessage(tr("no more replace text \'%1\'").arg(m_expr), 8000); QApplication::beep(); return false; } } else if (m_isFound && m_searchMode == 2) { //找到了内容,但是因为是0长,而无法选中。这种情况就是0长的情况。只在正则表达式情况出现 pEdit->replace(replaceText); //每次替换后,因为是0长替换,再把下次查找位置加1,否则会一直在原地查找 dealWithZeroFound(pEdit); return replaceFindNext(pEdit,false); } //当前没有查找到 return replaceFindNext(pEdit,false); } //把当前选中的内容,使用文本替换掉 void FindWin::slot_replace() { if (ui.replaceTextBox->currentText().isEmpty()) { ui.statusbar->showMessage(tr("what find is null !"), 8000); QApplication::beep(); return; } QWidget* pw = autoAdjustCurrentEditWin(); ScintillaEditView* pEdit = dynamic_cast(pw); if (pEdit != nullptr) { if (pEdit->isReadOnly()) { ui.statusbar->showMessage(tr("The ReadOnly document does not allow replacement."), 8000); QApplication::beep(); return; } //切换查询条件后,则是第一次查找。防止前后查找条件发生了变化, //导致选中的内容不是需要替换的,所以检查是第一次查找,则查找一下 updateParameterFromUI(); replace(pEdit); } else { ui.statusbar->showMessage(tr("The mode of the current document does not allow replacement."), 8000); QApplication::beep(); } } void FindWin::slot_findModeRegularBtChange(bool checked) { if (checked) { ui.findBackwardBox->setEnabled(false); ui.findBackwardBox->setChecked(false); ui.findMatchWholeBox->setEnabled(false); ui.findMatchWholeBox->setChecked(false); } else { ui.findBackwardBox->setEnabled(true); ui.findMatchWholeBox->setEnabled(true); } m_isFindFirst = true; } void FindWin::slot_replaceModeRegularBtChange(bool checked) { if (checked) { ui.replaceBackwardBox->setEnabled(false); ui.replaceBackwardBox->setChecked(false); ui.replaceMatchWholeBox->setEnabled(false); ui.replaceMatchWholeBox->setChecked(false); } else { ui.replaceBackwardBox->setEnabled(true); ui.replaceMatchWholeBox->setEnabled(true); } m_isFindFirst = true; } #if 0 //替换当前文档里面的所有 void FindWin::slot_replaceAll() { if (ui.replaceTextBox->currentText().isEmpty()) { ui.statusbar->showMessage(tr("what find is null !"), 8000); return; } if (!m_isStatic && QMessageBox::Yes != QMessageBox::question(this, tr("Replace All current Doc"), tr("Are you sure replace all occurrences in current documents?"))) { return; } QWidget* pw = autoAdjustCurrentEditWin(); ScintillaEditView* pEdit = dynamic_cast(pw); if (pEdit != nullptr) { if (pEdit->isReadOnly()) { ui.statusbar->showMessage(tr("The ReadOnly document does not allow replacement."), 8000); QApplication::beep(); return; } updateParameterFromUI(); int srcPostion = pEdit->execute(SCI_GETCURRENTPOS); int firstDisLineNum = pEdit->execute(SCI_GETFIRSTVISIBLELINE); int replaceNums = 0; //无条件进行第一次查找,从0行0列开始查找,而且不回环。如果没有找到,则替换完毕 QString whatFind = ui.replaceTextBox->currentText(); QString replaceText = ui.replaceWithBox->currentText(); if (m_extend) { QString extendFind; convertExtendedToString(whatFind, extendFind); whatFind = extendFind; QString extendReplace; convertExtendedToString(replaceText, extendReplace); replaceText = extendReplace; } if (!pEdit->findFirst(whatFind, m_re, m_cs, m_wo, false, true, FINDNEXTTYPE_REPLACENEXT, 0,0)) { ui.statusbar->showMessage(tr("cant't find text \'%1\'").arg(m_expr), 8000); QApplication::beep(); m_isFindFirst = true; return; } pEdit->execute(SCI_BEGINUNDOACTION); pEdit->replace(replaceText); dealWithZeroFound(pEdit); ++replaceNums; //找到了,则自动进行全部替换 while(pEdit->findNext()) { pEdit->replace(replaceText); ++replaceNums; dealWithZeroFound(pEdit); } pEdit->execute(SCI_ENDUNDOACTION); pEdit->execute(SCI_GOTOPOS, srcPostion); pEdit->execute(SCI_SETFIRSTVISIBLELINE, firstDisLineNum); pEdit->execute(SCI_SETXOFFSET, 0); //全部替换后,下次查找,必须算第一次查找 m_isFindFirst = true; ui.statusbar->showMessage(tr("replace finished, total %1 replaced!").arg(replaceNums), 10000); } else { ui.statusbar->showMessage(tr("The mode of the current document does not allow replacement."), 8000); QApplication::beep(); } } #endif // Find the first occurrence of a string. int buildSearchFlags(bool re, bool cs, bool wo, bool wrap, bool forward, FindNextType findNextType, bool posix, bool cxx11) { int flags = 0; flags = (cs ? SCFIND_MATCHCASE : 0) | (wo ? SCFIND_WHOLEWORD : 0) | (re ? SCFIND_REGEXP : 0) | (posix ? SCFIND_POSIX : 0) | (cxx11 ? SCFIND_CXX11REGEX : 0); switch (findNextType) { case FINDNEXTTYPE_FINDNEXT: flags |= SCFIND_REGEXP_EMPTYMATCH_ALL | SCFIND_REGEXP_SKIPCRLFASONE; break; case FINDNEXTTYPE_REPLACENEXT: flags |= SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH | SCFIND_REGEXP_SKIPCRLFASONE; break; case FINDNEXTTYPE_FINDNEXTFORREPLACE: flags |= SCFIND_REGEXP_EMPTYMATCH_ALL | SCFIND_REGEXP_EMPTYMATCH_ALLOWATSTART | SCFIND_REGEXP_SKIPCRLFASONE; break; } return flags; } struct FindReplaceInfo { intptr_t _startRange = -1; intptr_t _endRange = -1; }; //返回值替换数量 int FindWin::doReplaceAll(ScintillaEditView* pEdit, QString &whatFind, QString& replaceText, bool isCombineUndo) { int replaceNums = 0; int srcPostion = pEdit->execute(SCI_GETCURRENTPOS); int firstDisLineNum = pEdit->execute(SCI_GETFIRSTVISIBLELINE); if (isCombineUndo) { pEdit->execute(SCI_BEGINUNDOACTION); } int flags = buildSearchFlags(m_re, m_cs, m_wo, false, true, FINDNEXTTYPE_REPLACENEXT, 0, 0); intptr_t targetStart = 0; intptr_t targetEnd = 0; //Initial range for searching pEdit->execute(SCI_SETSEARCHFLAGS, flags); FindReplaceInfo findReplaceInfo; findReplaceInfo._startRange = 0; findReplaceInfo._endRange = pEdit->execute(SCI_GETLENGTH); QByteArray pTextFind = whatFind.toUtf8(); QByteArray pTextReplace = replaceText.toUtf8(); while (targetStart >= 0) { targetStart = pEdit->searchInTarget(pTextFind, findReplaceInfo._startRange, findReplaceInfo._endRange); // If we've not found anything, just break out of the loop if (targetStart == -1 || targetStart == -2) break; targetEnd = pEdit->execute(SCI_GETTARGETEND); if (targetEnd > findReplaceInfo._endRange) { //we found a result but outside our range, therefore do not process it break; } intptr_t foundTextLen = targetEnd - targetStart; intptr_t replaceDelta = 0; intptr_t replacedLength; if (m_re) { replacedLength = pEdit->replaceTargetRegExMode(pTextReplace); } else { replacedLength = pEdit->replaceTarget(pTextReplace); } replaceDelta = replacedLength - foundTextLen; ++replaceNums; // After the processing of the last string occurrence the search loop should be stopped // This helps to avoid the endless replacement during the EOL ("$") searching if (targetStart + foundTextLen == findReplaceInfo._endRange) break; findReplaceInfo._startRange = targetStart + foundTextLen + replaceDelta; //search from result onwards findReplaceInfo._endRange += replaceDelta; //adjust end of range in case of replace } if (isCombineUndo) { pEdit->execute(SCI_ENDUNDOACTION); } pEdit->execute(SCI_GOTOPOS, srcPostion); pEdit->execute(SCI_SETFIRSTVISIBLELINE, firstDisLineNum); pEdit->execute(SCI_SETXOFFSET, 0); return replaceNums; } int FindWin::replaceAll() { if (ui.replaceTextBox->currentText().isEmpty()) { ui.statusbar->showMessage(tr("what find is null !"), 8000); return 0; } if (!m_isStatic && QMessageBox::Yes != QMessageBox::question(this, tr("Replace All current Doc"), tr("Are you sure replace all occurrences in current documents?"))) { return 0; } QWidget* pw = autoAdjustCurrentEditWin(); ScintillaEditView* pEdit = dynamic_cast(pw); if (pEdit != nullptr) { if (pEdit->isReadOnly()) { ui.statusbar->showMessage(tr("The ReadOnly document does not allow replacement."), 8000); QApplication::beep(); return 0; } } updateParameterFromUI(); QString whatFind = ui.replaceTextBox->currentText(); QString replaceText = ui.replaceWithBox->text(); if (m_extend) { QString extendFind; convertExtendedToString(whatFind, extendFind); whatFind = extendFind; QString extendReplace; convertExtendedToString(replaceText, extendReplace); replaceText = extendReplace; } int replaceNums = doReplaceAll(pEdit, whatFind, replaceText); //全部替换后,下次查找,必须算第一次查找 m_isFindFirst = true; ui.statusbar->showMessage(tr("replace finished, total %1 replaced!").arg(replaceNums), 10000); return replaceNums; } //替换当前文档里面的所有。之前的要慢,是因为qscintilla中实时计算了行在屏幕需要的长度。 //大量的这种计算一行实时长度的操作,非常耗时。查找、标记均不耗时,只有替换修改了文本才耗时。 void FindWin::slot_replaceAll() { replaceAll(); } void FindWin::slot_replaceAllInOpenDoc() { if (ui.replaceTextBox->currentText().isEmpty()) { ui.statusbar->showMessage(tr("what find is null !"), 8000); QApplication::beep(); return; } if (QMessageBox::Yes != QMessageBox::question(this, tr("Replace All Open Doc"), tr("Are you sure replace all occurrences in all open documents?"))) { return; } updateParameterFromUI(); int replaceNums = 0; QString whatFind = ui.replaceTextBox->currentText(); QString whatReplace = m_replaceWithText; if (m_extend) { QString extendFind; convertExtendedToString(whatFind, extendFind); whatFind = extendFind; QString extendReplace; convertExtendedToString(whatReplace, extendReplace); whatReplace = extendReplace; } for (int i = 0; i < m_editTabWidget->count(); ++i) { QWidget* pw = m_editTabWidget->widget(i); ScintillaEditView* pEdit = dynamic_cast(pw); if (pEdit != nullptr) { //只读的文档不能替换 if (pEdit->isReadOnly()) { continue; } replaceNums += doReplaceAll(pEdit, whatFind, whatReplace); #if 0 //无条件进行第一次查找,从0行0列开始查找,而且不回环。如果没有找到,则替换完毕 if (!pEdit->findFirst(whatFind, m_re, m_cs, m_wo, false, true, FINDNEXTTYPE_REPLACENEXT,0, 0)) { continue; } pEdit->replace(whatReplace); dealWithZeroFound(pEdit); ++replaceNums; //找到了,则自动进行全部替换 while (pEdit->findNext()) { pEdit->replace(whatReplace); ++replaceNums; dealWithZeroFound(pEdit); } #endif } } //全部替换后,下次查找,必须算第一次查找 m_isFindFirst = true; ui.statusbar->showMessage(tr("Replace in Opened Files: %1 occurrences were replaced.").arg(replaceNums), 10000); } int FindWin::markAll() { if (ui.markTextBox->currentText().isEmpty()) { ui.statusbar->showMessage(tr("what mark is null !"), 8000); QApplication::beep(); return 0; } QWidget* pw = autoAdjustCurrentEditWin(); ScintillaEditView* pEdit = dynamic_cast(pw); if (pEdit != nullptr) { FindRecords* results = new FindRecords; results->pEdit = pEdit; results->hightLightColor = CCNotePad::s_curMarkColorId; results->findFilePath = pw->property("filePath").toString(); updateParameterFromUI(); int replaceNums = 0; //无条件进行第一次查找,从0行0列开始查找,而且不回环。如果没有找到,则替换完毕 QString whatMark = ui.markTextBox->currentText(); results->findText = whatMark; if (m_extend) { QString extendFind; convertExtendedToString(whatMark, extendFind); whatMark = extendFind; } int srcPostion = pEdit->execute(SCI_GETCURRENTPOS); int firstDisLineNum = pEdit->execute(SCI_GETFIRSTVISIBLELINE); if (!pEdit->findFirst(whatMark, m_re, m_cs, m_wo, false, true, FINDNEXTTYPE_FINDNEXT, 0, 0)) { ui.statusbar->showMessage(tr("cant't find text \'%1\'").arg(m_expr), 8000); //QApplication::beep(); return 0; } else { //不支持零长的高亮。0长不高亮 FindState& state = pEdit->getLastFindState(); if (state.targstart == state.targend) { ui.statusbar->showMessage(tr("cant't mark text \'%1\'").arg(m_expr), 8000); QApplication::beep(); return 0; } } addCurFindRecord(pEdit, *results, true); ++replaceNums; //找到了,把结果收集起来 while (pEdit->findNext()) { addCurFindRecord(pEdit, *results, true); ++replaceNums; } //把结果高亮起来。 int foundTextLen = 0; for (int i = 0, s = results->records.size(); i < s; ++i) { const FindRecord& rs = results->records.at(i); foundTextLen = rs.end - rs.pos; if (foundTextLen > 0) { pEdit->execute(SCI_SETINDICATORCURRENT, CCNotePad::s_curMarkColorId); pEdit->execute(SCI_INDICATORFILLRANGE, rs.pos, foundTextLen); } } if (!results->records.isEmpty()) { pEdit->appendMarkRecord(results); } pEdit->execute(SCI_SETFIRSTVISIBLELINE, firstDisLineNum); pEdit->execute(SCI_GOTOPOS, srcPostion); pEdit->execute(SCI_SETXOFFSET, 0); //全部替换后,下次查找,必须算第一次查找 m_isFindFirst = true; ui.statusbar->showMessage(tr("mark finished, total %1 found!").arg(replaceNums), 10000); return replaceNums; } else { ui.statusbar->showMessage(tr("The mode of the current document does not allow mark."), 8000); QApplication::beep(); } return 0; } //标记高亮单词 void FindWin::slot_markAll() { markAll(); } //取消高亮当前关键字 void FindWin::slot_clearMark() { if (ui.markTextBox->currentText().isEmpty()) { ui.statusbar->showMessage(tr("what mark is null !"), 8000); QApplication::beep(); return; } CCNotePad* pMainPad = dynamic_cast(m_pMainPad); if (pMainPad != nullptr) { pMainPad->clearHighlightWord(ui.markTextBox->currentText()); } } //取消所有高亮 void FindWin::slot_clearAllMark() { CCNotePad* pMainPad = dynamic_cast(m_pMainPad); if (pMainPad != nullptr) { pMainPad->slot_clearMark(); } } //选择查找目录 void FindWin::slot_dirSelectDest() { QString curDirPath = ui.destFindDir->text(); if (curDirPath.isEmpty()) { curDirPath = CCNotePad::s_lastOpenDirPath; } QString destDir = QFileDialog::getExistingDirectory(this, tr("Open Directory"), curDirPath, QFileDialog::DontResolveSymlinks); if (!destDir.isEmpty()) { ui.destFindDir->setText(destDir); } } //在walkDirfile中用作回调函数处理。命中则返回true bool FindWin::findTextInFile(QString &filePath, int &findNums, QVector* allfileInDirRecord) { pEditTemp->clear(); if (0 != FileManager::getInstance().loadFileForSearch(pEditTemp, filePath)) { return false; } FindRecords* results = new FindRecords(); //返回结果的edit无条件写无。注意不要忘记 results->pEdit = nullptr; results->findFilePath = filePath; //无条件进行第一次查找,从0行0列开始查找,而且不回环。如果没有找到,则替换完毕 results->findText = m_expr; QString whatFind = m_expr; if (m_extend) { QString extendFind; convertExtendedToString(whatFind, extendFind); whatFind = extendFind; } if (!pEditTemp->findFirst(whatFind, m_re, m_cs, m_wo, false, m_forward, FINDNEXTTYPE_FINDNEXT, 0, 0,false)) { delete results; return false; } else { dealWithZeroFound(pEditTemp); } addCurFindRecord(pEditTemp, *results); ++findNums; //找到了,把结果收集起来 while (pEditTemp->findNext()) { addCurFindRecord(pEditTemp, *results); ++findNums; dealWithZeroFound(pEditTemp); } allfileInDirRecord->append(results); return true; } //在walkDirfile中用作回调函数处理。命中则返回true 第三个参数不需要,为了复用walkdir,暂时保留和findTextInFile一致 bool FindWin::replaceTextInFile(QString &filePath, int &replaceNums, QVector*) { pEditTemp->clear(); if (0 != FileManager::getInstance().loadFileForSearch(pEditTemp, filePath)) { return false; } QString find = m_expr; QString replace = m_replaceWithText; if (m_extend) { QString extendFind; convertExtendedToString(find, extendFind); find = extendFind; QString extendReplace; convertExtendedToString(replace, extendReplace); replace = extendReplace; } replaceNums += doReplaceAll(pEditTemp, find, replace); #if 0 if (!pEditTemp->findFirst(find, m_re, m_cs, m_wo, false, m_forward, FINDNEXTTYPE_REPLACENEXT,0, 0,false)) { return false; } pEditTemp->replace(replace); dealWithZeroFound(pEditTemp); ++replaceNums; //找到了,则自动进行全部替换 while (pEditTemp->findNext()) { pEditTemp->replace(replace); ++replaceNums; dealWithZeroFound(pEditTemp); } #endif //必须要保存一下 emit sign_replaceSaveFile(filePath, pEditTemp); return true; } //非递归版本的递归文件,从CompareDirs中修改而来 //isSkipBinary:是否跳过二进制 //isSkipHide:是否处理隐藏文件 //skipMaxSize::处理文件的最大大小,超过则不处理。如果是0,则表示不跳过任何文件 //isfilterFileType:过滤类型,只处理这类类型文件 为true时 fileExtType 不能为空 //fileExtType的格式为:.cpp .h 类似,但是不需要前面的.,传递的时候不传递下来 int FindWin::walkDirfile(QString path, int &foundTimes, bool isSkipBinary, bool isSkipHide, int skipMaxSize, bool isfilterFileType, QStringList& fileExtType, bool isSkipDir, QStringList & skipDirNames, bool isSkipChildDirs, std::function* allfileInDirRecord)> foundCallBack, bool isAskAbort) { QList dirsList; QString oneDir(path); dirsList.append(oneDir); int fileNums = 0; int hitFileNums = 0; //再获取文件夹到列表 QDir::Filters dirfilter; if (!isSkipHide) { dirfilter = QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::NoSymLinks; } else { dirfilter = QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks; } //过滤文件类型。true 合格,需要处理, false 不合格,跳过 auto fileTypeFilter = [&fileExtType](QFileInfo& fileInfo)->bool { QString suffix = fileInfo.suffix(); if (!suffix.isEmpty()) { return (-1 != fileExtType.indexOf(suffix)); } //对于没有后缀的文件,一律跳过 return false; }; //是否二进制文件 auto binaryFiltre = [](QFileInfo& fi)->bool { return DocTypeListView::isHexExt(fi.suffix()); }; ProgressWin* loadFileProcessWin = new ProgressWin(this); loadFileProcessWin->setWindowModality(Qt::WindowModal); loadFileProcessWin->info(tr("load dir file in progress\n, please wait ...")); loadFileProcessWin->show(); int dirNums = 0; bool firstChildDirs = true; int totalStep = 0; bool canAbort = true; bool canAbortSecond = true; if (!isAskAbort) { canAbort = false; canAbortSecond = false; } while (!dirsList.isEmpty()) { QString curDir = dirsList.takeFirst(); if (!isSkipChildDirs) { /*添加path路径文件*/ QDir dir(curDir); //遍历各级子目录 QFileInfoList folder_list = dir.entryInfoList(dirfilter); //获取当前所有目录 for (int i = 0; i != folder_list.size(); ++i) //自动递归添加各目录到上一级目录 { QString namepath = folder_list.at(i).absoluteFilePath(); //获取路径 QFileInfo folderinfo = folder_list.at(i); if (folderinfo.baseName().isEmpty()) { loadFileProcessWin->info(tr("skip dir %1").arg(namepath)); continue; } QString name = folderinfo.fileName(); //获取目录名 if (isSkipDir && (-1 != skipDirNames.indexOf(name))) { loadFileProcessWin->info(tr("skip dir %1").arg(namepath)); continue; } dirsList.push_front(namepath); dirNums++; loadFileProcessWin->info(tr("found %1 dir %2").arg(dirNums).arg(namepath)); QCoreApplication::processEvents(/*QEventLoop::ExcludeUserInputEvents*/); } if (firstChildDirs) { totalStep = dirNums; loadFileProcessWin->setTotalSteps(dirNums); firstChildDirs = false; } if (dirsList.size() < totalStep) { totalStep = dirsList.size(); loadFileProcessWin->moveStep(); } } QDir dir_file(curDir); if (!isSkipHide) { dir_file.setFilter(QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden | QDir::NoSymLinks);//获取当前所有文件 } else { dir_file.setFilter(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks);//获取当前所有文件 } QFileInfoList list_file = dir_file.entryInfoList(); for (int i = 0; i < list_file.size(); ++i) { //将当前目录中所有文件添加到treewidget中 QFileInfo fileInfo = list_file.at(i); QString namepath = list_file.at(i).absoluteFilePath(); //获取路径 //不支持所有文件,仅仅支持指定类型文件。没有通过 if (isfilterFileType && !fileTypeFilter(fileInfo)) { loadFileProcessWin->info(tr("ext type skip file %1").arg(namepath)); continue; } //大小过滤 if ((skipMaxSize != 0) && fileInfo.size() > skipMaxSize) { continue; } //二进制过滤//对于二进制文件该如何处理,我觉得必须要过滤,暂时不处理 if (isSkipBinary &&binaryFiltre(fileInfo)) { continue; } //回调处理该函数 if (foundCallBack(namepath, foundTimes, nullptr)) { ++hitFileNums; } if (i % 2 == 0) { if (loadFileProcessWin->isCancel()) { loadFileProcessWin->info(tr("found in dir canceled ...")); break; } QCoreApplication::processEvents(); } if ((canAbort && (hitFileNums > 100 || foundTimes > 1000)) || (canAbortSecond && foundTimes > 8000)) { int ret = QMessageBox::question(this, tr("Continue Find ?"), tr("The search results have been greater than %1 times in %2 files, and more may be slow. Continue to search?").arg(foundTimes).arg(hitFileNums), tr("Yes"), tr("Abort")); if(ret == 1) { loadFileProcessWin->setCancel(); break; } else { if (canAbort && (hitFileNums > 100 || foundTimes > 1000)) { canAbort = false; } if ((canAbortSecond && foundTimes > 8000)) { canAbortSecond = false; } } } } fileNums += list_file.size(); if (loadFileProcessWin->isCancel()) { break; } } if (loadFileProcessWin != nullptr) { delete loadFileProcessWin; loadFileProcessWin = nullptr; } return fileNums; } //在目标文件夹中查找 void FindWin::slot_dirFindAll() { QString dirPath = ui.destFindDir->text().trimmed(); QString whatFind = ui.dirFindWhat->currentText(); if (dirPath.isEmpty()) { ui.statusbar->showMessage(tr("please select find dest dir !"), 8000); QApplication::beep(); return; } if(whatFind.isEmpty()) { ui.statusbar->showMessage(tr("what find is null !"), 8000); QApplication::beep(); return; } QDir dir(dirPath); if (!dir.exists()) { ui.statusbar->showMessage(tr("dest dir %1 not exist !").arg(dirPath), 8000); QApplication::beep(); return; } bool isfilterFileType = ui.dealFileType->isChecked(); bool isSkipDirs = ui.skipDir->isChecked(); QStringList fileExtTypeList; QStringList skipDirNameList; if (isfilterFileType) { QString fileExtType = ui.fileType->text().trimmed(); if (fileExtType.isEmpty() || (fileExtType == "*.*")) { isfilterFileType = false; } else { //格式是*.h:*.c:*.cpp类似的 QStringList typeList = fileExtType.split(":"); foreach (QString var, typeList) { if (var.size() >= 3) { //只取后面的h或或cpp后缀 fileExtTypeList.append(var.mid(2)); } } if (fileExtTypeList.isEmpty()) { isfilterFileType = false; } } } if (isSkipDirs) { QString dirNames = ui.skipDirNames->text().trimmed(); if (dirNames.isEmpty()) { isSkipDirs = false; } else { QStringList nameList = dirNames.split(":"); foreach(QString var, nameList) { if (var.size() > 0) { //只取后面的h或或cpp后缀 skipDirNameList.append(var); } } if (skipDirNameList.isEmpty()) { isSkipDirs = false; } } } bool isSkipBinary = ui.skipBinary->isChecked(); bool isSkipHide = ui.skipHideFile->isChecked(); int skipMaxSize = (ui.skipFileMaxSize->isChecked()) ? ui.maxFileSizeSpinBox->value()*1024*1024:0; bool isSkipChildDir = ui.skipChildDirs->isChecked(); updateParameterFromUI(); if (pEditTemp == nullptr) { pEditTemp = new ScintillaEditView(nullptr); } int foundNums = 0; QVector* allfileInDirRecord = new QVector(); std::function* allfileInDirRecord)> foundCallBack = std::bind(&FindWin::findTextInFile, this, std::placeholders::_1, std::placeholders::_2, allfileInDirRecord); int filesNum = walkDirfile(dirPath, foundNums, isSkipBinary, isSkipHide, skipMaxSize, isfilterFileType, fileExtTypeList, isSkipDirs, skipDirNameList, isSkipChildDir, foundCallBack); //全部替换后,下次查找,必须算第一次查找 m_isFindFirst = true; ui.statusbar->showMessage(tr("find finished, total %1 found in %2 file!").arg(foundNums).arg(filesNum), 10000); //复用了这个信号函数,没有新做消息,要注意 emit sign_findAllInOpenDoc(allfileInDirRecord, foundNums, whatFind); addFindHistory(whatFind); //释放元素 for (int i = 0; i < allfileInDirRecord->size(); ++i) { delete allfileInDirRecord->at(i); } delete allfileInDirRecord; } //目录中直接替换 void FindWin::slot_dirReplaceAll() { QString dirPath = ui.destFindDir->text(); QString whatFind = ui.dirFindWhat->currentText(); QString dirReplaceWhat = ui.dirReplaceWhat->text(); if (dirPath.isEmpty()) { ui.statusbar->showMessage(tr("please select find dest dir !"), 8000); QApplication::beep(); return; } if (whatFind.isEmpty()) { ui.statusbar->showMessage(tr("what find is null !"), 8000); QApplication::beep(); return; } if (QMessageBox::Yes != QMessageBox::question(this, tr("Replace All Dirs"), tr("Are you sure replace all \"%1\" to \"%2\" occurrences in selected dirs ?").arg(whatFind).arg(dirReplaceWhat))) { return; } bool isfilterFileType = ui.dealFileType->isChecked(); QStringList fileExtTypeList; bool isSkipDirs = ui.skipDir->isChecked(); QStringList skipDirNameList; if (isfilterFileType) { QString fileExtType = ui.fileType->text().trimmed(); if (fileExtType.isEmpty() || (fileExtType == "*.*")) { isfilterFileType = false; } else { //格式是*.h:*.c:*.cpp类似的 QStringList typeList = fileExtType.split(":"); foreach(QString var, typeList) { if (var.size() >= 3) { //只取后面的h或或cpp后缀 fileExtTypeList.append(var.mid(2)); } } if (fileExtTypeList.isEmpty()) { isfilterFileType = false; } } } if (isSkipDirs) { QString dirNames = ui.skipDirNames->text().trimmed(); if (dirNames.isEmpty()) { isSkipDirs = false; } else { QStringList nameList = dirNames.split(":"); foreach(QString var, nameList) { if (var.size() > 0) { //只取后面的h或或cpp后缀 skipDirNameList.append(var); } } if (skipDirNameList.isEmpty()) { isSkipDirs = false; } } } bool isSkipBinary = ui.skipBinary->isChecked(); bool isSkipHide = ui.skipHideFile->isChecked(); int skipMaxSize = (ui.skipFileMaxSize->isChecked()) ? ui.maxFileSizeSpinBox->value() * 1024 * 1024 : 0; bool isSkipChildDir = ui.skipChildDirs->isChecked(); updateParameterFromUI(); if (pEditTemp == nullptr) { pEditTemp = new ScintillaEditView(nullptr); } int replaceNums = 0; std::function* allfileInDirRecord)> foundCallBack = std::bind(&FindWin::replaceTextInFile, this, std::placeholders::_1, std::placeholders::_2, nullptr); int filesNum = walkDirfile(dirPath, replaceNums, isSkipBinary, isSkipHide, skipMaxSize, isfilterFileType, fileExtTypeList, isSkipDirs, skipDirNameList,isSkipChildDir, foundCallBack,false); //全部替换后,下次查找,必须算第一次查找 m_isFindFirst = true; ui.statusbar->showMessage(tr("replace finished, total %1 replace in %2 file!").arg(replaceNums).arg(filesNum), 10000); }