• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Kate

katesearchbar.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2007 Sebastian Pipping <webmaster@hartwork.org>
00003    Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
00004    Copyright (C) 2007 Thomas Friedrichsmeier <thomas.friedrichsmeier@ruhr-uni-bochum.de>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License version 2 as published by the Free Software Foundation.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018    Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "katesearchbar.h"
00022 #include "kateview.h"
00023 #include "katedocument.h"
00024 #include "kateglobal.h"
00025 
00026 #include "ui_searchbarincremental.h"
00027 #include "ui_searchbarpower.h"
00028 
00029 #include <kactioncollection.h>
00030 
00031 #include <QtGui/QVBoxLayout>
00032 #include <QtGui/QComboBox>
00033 #include <QtGui/QCheckBox>
00034 #include <QStringListModel>
00035 #include <QCompleter>
00036 
00037 using namespace KTextEditor;
00038 
00039 
00040 
00041 // Turn debug messages on/off here
00042 // #define FAST_DEBUG_ENABLE
00043 
00044 #ifdef FAST_DEBUG_ENABLE
00045 # define FAST_DEBUG(x) (kDebug() << x)
00046 #else
00047 # define FAST_DEBUG(x)
00048 #endif
00049 
00050 
00051 
00052 KateSearchBar::KateSearchBar(KateViewBar * viewBar, bool initAsPower)
00053         : KateViewBarWidget(viewBar),
00054         m_view(viewBar->view()),
00055         m_topRange(NULL),
00056         m_layout(new QVBoxLayout()),
00057         m_widget(NULL),
00058         m_incUi(NULL),
00059         m_incMenu(NULL),
00060         m_incMenuMatchCase(NULL),
00061         m_incMenuFromCursor(NULL),
00062         m_incMenuHighlightAll(NULL),
00063         m_incInitCursor(0, 0),
00064         m_powerUi(NULL),
00065         m_incHighlightAll(false),
00066         m_incFromCursor(true),
00067         m_incMatchCase(false),
00068         m_powerMatchCase(true),
00069         m_powerFromCursor(false),
00070         m_powerHighlightAll(false),
00071         m_powerUsePlaceholders(false),
00072         m_powerMode(0) {
00073     // Modify parent
00074     QWidget * const widget = centralWidget();
00075     widget->setLayout(m_layout);
00076     m_layout->setMargin(2);
00077 
00078     // Init highlight
00079     m_topRange = m_view->doc()->newSmartRange(m_view->doc()->documentRange());
00080     static_cast<KateSmartRange*>(m_topRange)->setInternal();
00081     m_topRange->setInsertBehavior(SmartRange::ExpandLeft | SmartRange::ExpandRight);
00082     enableHighlights(true);
00083 
00084 
00085     // Copy global to local config backup
00086     KateViewConfig * const globalConfig = KateGlobal::self()->viewConfig();
00087     const long searchFlags = globalConfig->searchFlags();
00088     m_incHighlightAll = (searchFlags & KateViewConfig::IncHighlightAll) != 0;
00089     m_incFromCursor = (searchFlags & KateViewConfig::IncFromCursor) != 0;
00090     m_incMatchCase = (searchFlags & KateViewConfig::IncMatchCase) != 0;
00091     m_powerMatchCase = (searchFlags & KateViewConfig::PowerMatchCase) != 0;
00092     m_powerFromCursor = (searchFlags & KateViewConfig::PowerFromCursor) != 0;
00093     m_powerHighlightAll = (searchFlags & KateViewConfig::PowerHighlightAll) != 0;
00094     m_powerUsePlaceholders = (searchFlags & KateViewConfig::PowerUsePlaceholders) != 0;
00095     m_powerMode = ((searchFlags & KateViewConfig::PowerModeRegularExpression) != 0)
00096             ? MODE_REGEX
00097             : (((searchFlags & KateViewConfig::PowerModeEscapeSequences) != 0)
00098                 ? MODE_ESCAPE_SEQUENCES
00099                 : (((searchFlags & KateViewConfig::PowerModeWholeWords) != 0)
00100                     ? MODE_WHOLE_WORDS
00101                     : MODE_PLAIN_TEXT));
00102 
00103 
00104     // Load one of either dialogs
00105     if (initAsPower) {
00106         onMutatePower();
00107     } else {
00108         onMutateIncremental();
00109     }
00110 }
00111 
00112 
00113 
00114 KateSearchBar::~KateSearchBar() {
00115     delete m_topRange;
00116     delete m_layout;
00117     delete m_widget;
00118     delete m_incUi;
00119     delete m_incMenu;
00120     delete m_powerUi;
00121 }
00122 
00123 
00124 
00125 void KateSearchBar::findNext() {
00126     if (m_incUi != NULL) {
00127         onIncNext();
00128     } else {
00129         onPowerFindNext();
00130     }
00131 }
00132 
00133 
00134 
00135 void KateSearchBar::findPrevious() {
00136     if (m_incUi != NULL) {
00137         onIncPrev();
00138     } else {
00139         onPowerFindPrev();
00140     }
00141 }
00142 
00143 
00144 
00145 void KateSearchBar::highlight(const Range & range, const QColor & color) {
00146     SmartRange * const highlight = m_view->doc()->newSmartRange(range, m_topRange);
00147     highlight->setInsertBehavior(SmartRange::DoNotExpand);
00148     Attribute::Ptr attribute(new Attribute());
00149     attribute->setBackground(color);
00150     highlight->setAttribute(attribute);
00151 }
00152 
00153 
00154 
00155 void KateSearchBar::highlightMatch(const Range & range) {
00156     highlight(range, Qt::yellow); // TODO make this part of the color scheme
00157 }
00158 
00159 
00160 
00161 void KateSearchBar::highlightReplacement(const Range & range) {
00162     highlight(range, Qt::green); // TODO make this part of the color scheme
00163 }
00164 
00165 
00166 
00167 void KateSearchBar::highlightAllMatches(const QString & pattern,
00168         Search::SearchOptions searchOptions) {
00169     onForAll(pattern, m_view->doc()->documentRange(),
00170             searchOptions, NULL);
00171 }
00172 
00173 
00174 
00175 void KateSearchBar::indicateMatch(bool wrapped) {
00176     if (m_incUi != NULL) {
00177         // Green background for line edit
00178         QPalette background(m_incUi->pattern->palette());
00179         KColorScheme::adjustBackground(background, KColorScheme::PositiveBackground);
00180         m_incUi->pattern->setPalette(background);
00181 
00182         // Update status label
00183         m_incUi->status->setText(wrapped
00184                 ? i18n("Reached bottom, continued from top")
00185                 : "");
00186     } else {
00187         // Green background for line edit
00188         QLineEdit * const lineEdit = m_powerUi->pattern->lineEdit();
00189         Q_ASSERT(lineEdit != NULL);
00190         QPalette background(lineEdit->palette());
00191         KColorScheme::adjustBackground(background, KColorScheme::PositiveBackground);
00192         lineEdit->setPalette(background);
00193     }
00194 }
00195 
00196 
00197 
00198 void KateSearchBar::indicateMismatch() {
00199     if (m_incUi != NULL) {
00200         // Red background for line edit
00201         QPalette background(m_incUi->pattern->palette());
00202         KColorScheme::adjustBackground(background, KColorScheme::NegativeBackground);
00203         m_incUi->pattern->setPalette(background);
00204 
00205         // Update status label
00206         m_incUi->status->setText(i18n("Not found"));
00207     } else {
00208         // Red background for line edit
00209         QLineEdit * const lineEdit = m_powerUi->pattern->lineEdit();
00210         Q_ASSERT(lineEdit != NULL);
00211         QPalette background(lineEdit->palette());
00212         KColorScheme::adjustBackground(background, KColorScheme::NegativeBackground);
00213         lineEdit->setPalette(background);
00214     }
00215 }
00216 
00217 
00218 
00219 void KateSearchBar::indicateNothing() {
00220     if (m_incUi != NULL) {
00221         // Reset background of line edit
00222         m_incUi->pattern->setPalette(QPalette());
00223 
00224         // Update status label
00225         m_incUi->status->setText("");
00226     } else {
00227         // Reset background of line edit
00228         QLineEdit * const lineEdit = m_powerUi->pattern->lineEdit();
00229         Q_ASSERT(lineEdit != NULL);
00230         // ### this is fragile (depends on knowledge of QPalette::ColorGroup)
00231         // ...would it better to cache the original palette?
00232         QColor color = QPalette().color(QPalette::Base);
00233         QPalette background(lineEdit->palette());
00234         background.setBrush(QPalette::Active, QPalette::Base, QPalette().brush(QPalette::Active, QPalette::Base));
00235         background.setBrush(QPalette::Inactive, QPalette::Base, QPalette().brush(QPalette::Inactive, QPalette::Base));
00236         background.setBrush(QPalette::Disabled, QPalette::Base, QPalette().brush(QPalette::Disabled, QPalette::Base));
00237         lineEdit->setPalette(background);
00238     }
00239 }
00240 
00241 
00242 
00243 /*static*/ void KateSearchBar::selectRange(KateView * view, const KTextEditor::Range & range) {
00244     view->setCursorPositionInternal(range.start(), 1);
00245     view->setSelection(range);
00246 }
00247 
00248 
00249 
00250 void KateSearchBar::buildReplacement(QString & output, QList<ReplacementPart> & parts,
00251         const QVector<Range> & details, int replacementCounter) {
00252     const int MIN_REF_INDEX = 0;
00253     const int MAX_REF_INDEX = details.count() - 1;
00254 
00255     output.clear();
00256     ReplacementPart::Type caseConversion = ReplacementPart::KeepCase;
00257     for (QList<ReplacementPart>::iterator iter = parts.begin(); iter != parts.end(); iter++) {
00258         ReplacementPart & curPart = *iter;
00259         switch (curPart.type) {
00260         case ReplacementPart::Reference:
00261             if ((curPart.index < MIN_REF_INDEX) || (curPart.index > MAX_REF_INDEX)) {
00262                 // Insert just the number to be consistent with QRegExp ("\c" becomes "c")
00263                 output.append(QString::number(curPart.index));
00264             } else {
00265                 const Range & captureRange = details[curPart.index];
00266                 if (captureRange.isValid()) {
00267                     // Copy capture content
00268                     const bool blockMode = m_view->blockSelection();
00269                     const QString content = m_view->doc()->text(captureRange, blockMode);
00270                     switch (caseConversion) {
00271                     case ReplacementPart::UpperCase:
00272                         // Copy as uppercase
00273                         output.append(content.toUpper());
00274                         break;
00275 
00276                     case ReplacementPart::LowerCase:
00277                         // Copy as lowercase
00278                         output.append(content.toLower());
00279                         break;
00280 
00281                     case ReplacementPart::KeepCase: // FALLTHROUGH
00282                     default:
00283                         // Copy unmodified
00284                         output.append(content);
00285                         break;
00286 
00287                     }
00288                 }
00289             }
00290             break;
00291 
00292         case ReplacementPart::UpperCase: // FALLTHROUGH
00293         case ReplacementPart::LowerCase: // FALLTHROUGH
00294         case ReplacementPart::KeepCase:
00295             caseConversion = curPart.type;
00296             break;
00297 
00298         case ReplacementPart::Counter:
00299             {
00300                 // Zero padded counter value
00301                 const int minWidth = curPart.index;
00302                 const int number = replacementCounter;
00303                 output.append(QString("%1").arg(number, minWidth, 10, QLatin1Char('0')));
00304             }
00305             break;
00306 
00307         case ReplacementPart::Text: // FALLTHROUGH
00308         default:
00309             switch (caseConversion) {
00310             case ReplacementPart::UpperCase:
00311                 // Copy as uppercase
00312                 output.append(curPart.text.toUpper());
00313                 break;
00314 
00315             case ReplacementPart::LowerCase:
00316                 // Copy as lowercase
00317                 output.append(curPart.text.toLower());
00318                 break;
00319 
00320             case ReplacementPart::KeepCase: // FALLTHROUGH
00321             default:
00322                 // Copy unmodified
00323                 output.append(curPart.text);
00324                 break;
00325 
00326             }
00327             break;
00328 
00329         }
00330     }
00331 }
00332 
00333 
00334 
00335 void KateSearchBar::replaceMatch(const QVector<Range> & match, const QString & replacement,
00336         int replacementCounter) {
00337     const bool usePlaceholders = isChecked(m_powerUi->usePlaceholders);
00338     const Range & targetRange = match[0];
00339 
00340     QString finalReplacement;
00341     if (usePlaceholders) {
00342         // Resolve references and escape sequences
00343         QList<ReplacementPart> parts;
00344         QString writableHack(replacement);
00345         const bool REPLACEMENT_GOODIES = true;
00346         KateDocument::escapePlaintext(writableHack, &parts, REPLACEMENT_GOODIES);
00347         buildReplacement(finalReplacement, parts, match, replacementCounter);
00348     } else {
00349         // Plain text replacement
00350         finalReplacement = replacement;
00351     }
00352 
00353     const bool blockMode = (m_view->blockSelection() && !targetRange.onSingleLine());
00354     m_view->doc()->replaceText(targetRange, finalReplacement, blockMode);
00355 }
00356 
00357 
00358 
00359 void KateSearchBar::onIncPatternChanged(const QString & pattern, bool invokedByUserAction) {
00360     if (pattern.isEmpty()) {
00361         if (invokedByUserAction) {
00362             // Kill selection
00363             m_view->setSelection(Range::invalid());
00364 
00365             // Kill highlight
00366             resetHighlights();
00367         }
00368 
00369         // Reset edit color
00370         indicateNothing();
00371 
00372         // Disable next/prev
00373         m_incUi->next->setDisabled(true);
00374         m_incUi->prev->setDisabled(true);
00375         return;
00376     }
00377 
00378     // Enable next/prev
00379     m_incUi->next->setDisabled(false);
00380     m_incUi->prev->setDisabled(false);
00381 
00382     if (invokedByUserAction) {
00383         // How to find?
00384         Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
00385         const bool matchCase = isChecked(m_incMenuMatchCase);
00386         if (!matchCase) {
00387             enabledOptions |= Search::CaseInsensitive;
00388         }
00389 
00390 
00391         // Where to find?
00392         Range inputRange;
00393         const bool fromCursor = isChecked(m_incMenuFromCursor);
00394         if (fromCursor) {
00395             inputRange.setRange(m_incInitCursor, m_view->doc()->documentEnd());
00396         } else {
00397             inputRange = m_view->doc()->documentRange();
00398         }
00399 
00400         // Find, first try
00401         const QVector<Range> resultRanges = m_view->doc()->searchText(inputRange, pattern, enabledOptions);
00402         const Range & match = resultRanges[0];
00403 
00404         bool found = false;
00405         if (match.isValid()) {
00406             selectRange(m_view, match);
00407             const bool NOT_WRAPPED = false;
00408             indicateMatch(NOT_WRAPPED);
00409             found = true;
00410         } else {
00411             // Wrap if it makes sense
00412             if (fromCursor) {
00413                 // Find, second try
00414                 inputRange = m_view->doc()->documentRange();
00415                 const QVector<Range> resultRanges2 = m_view->doc()->searchText(inputRange, pattern, enabledOptions);
00416                 const Range & match2 = resultRanges2[0];
00417                 if (match2.isValid()) {
00418                     selectRange(m_view, match2);
00419                     const bool WRAPPED = true;
00420                     indicateMatch(WRAPPED);
00421                     found = true;
00422                 } else {
00423                     indicateMismatch();
00424                 }
00425             } else {
00426                 indicateMismatch();
00427             }
00428         }
00429 
00430         // Highlight all
00431         if (found && isChecked(m_incMenuHighlightAll)) {
00432             highlightAllMatches(pattern, enabledOptions);
00433         }
00434     }
00435 }
00436 
00437 
00438 
00439 void KateSearchBar::onIncNext() {
00440     const bool FIND = false;
00441     onStep(FIND);
00442 }
00443 
00444 
00445 
00446 void KateSearchBar::onIncPrev() {
00447     const bool FIND = false;
00448     const bool BACKWARDS = false;
00449     onStep(FIND, BACKWARDS);
00450 }
00451 
00452 
00453 
00454 void KateSearchBar::onIncMatchCaseToggle(bool invokedByUserAction) {
00455     if (invokedByUserAction) {
00456         sendConfig();
00457 
00458         // Re-search with new settings
00459         const QString pattern = m_incUi->pattern->displayText();
00460         onIncPatternChanged(pattern);
00461     }
00462 }
00463 
00464 
00465 
00466 void KateSearchBar::onIncHighlightAllToggle(bool checked, bool invokedByUserAction) {
00467     if (invokedByUserAction) {
00468         sendConfig();
00469 
00470         if (checked) {
00471             const QString pattern = m_incUi->pattern->displayText();
00472             if (!pattern.isEmpty()) {
00473                 // How to search while highlighting?
00474                 Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
00475                 const bool matchCase = isChecked(m_incMenuMatchCase);
00476                 if (!matchCase) {
00477                     enabledOptions |= Search::CaseInsensitive;
00478                 }
00479 
00480                 // Highlight them all
00481                 resetHighlights();
00482                 highlightAllMatches(pattern, enabledOptions);
00483             }
00484         } else {
00485             resetHighlights();
00486         }
00487     }
00488 }
00489 
00490 
00491 
00492 void KateSearchBar::onIncFromCursorToggle(bool invokedByUserAction) {
00493     if (invokedByUserAction) {
00494         sendConfig();
00495     }
00496 }
00497 
00498 
00499 
00500 void KateSearchBar::fixForSingleLine(Range & range, bool forwards) {
00501     FAST_DEBUG("Single-line workaround checking BEFORE" << range);
00502     if (forwards) {
00503         const int line = range.start().line();
00504         const int col = range.start().column();
00505         const int maxColWithNewline = m_view->doc()->lineLength(line) + 1;
00506         if (col == maxColWithNewline) {
00507             FAST_DEBUG("Starting on a newline" << range);
00508             const int maxLine = m_view->doc()->lines() - 1;
00509             if (line < maxLine) {
00510                 range.setRange(Cursor(line + 1, 0), range.end());
00511                 FAST_DEBUG("Search range fixed to " << range);
00512             } else {
00513                 FAST_DEBUG("Already at last line");
00514                 range = Range::invalid();
00515             }
00516         }
00517     } else {
00518         const int col = range.end().column();
00519         if (col == 0) {
00520             FAST_DEBUG("Ending after a newline" << range);
00521             const int line = range.end().line();
00522             if (line > 0) {
00523                 const int maxColWithNewline = m_view->doc()->lineLength(line - 1);
00524                 range.setRange(range.start(), Cursor(line - 1, maxColWithNewline));
00525                 FAST_DEBUG("Search range fixed to " << range);
00526             } else {
00527                 FAST_DEBUG("Already at first line");
00528                 range = Range::invalid();
00529             }
00530         }
00531     }
00532     FAST_DEBUG("Single-line workaround checking  AFTER" << range);
00533 }
00534 
00535 
00536 
00537 void KateSearchBar::onReturnPressed() {
00538     const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
00539     const bool shiftDown = (modifiers & Qt::ShiftModifier) != 0;
00540     const bool controlDown = (modifiers & Qt::ControlModifier) != 0;
00541 
00542     if (shiftDown) {
00543         // Shift down, search backwards
00544         if (m_powerUi != NULL) {
00545             onPowerFindPrev();
00546         } else {
00547             onIncPrev();
00548         }
00549     } else {
00550         // Shift up, search forwards
00551         if (m_powerUi != NULL) {
00552             onPowerFindNext();
00553         } else {
00554             onIncNext();
00555         }
00556     }
00557 
00558     if (controlDown) {
00559         hideBar();
00560     }
00561 }
00562 
00563 
00564 
00565 bool KateSearchBar::onStep(bool replace, bool forwards) {
00566     // What to find?
00567     const QString pattern = (m_powerUi != NULL)
00568             ? m_powerUi->pattern->currentText()
00569             : m_incUi->pattern->displayText();
00570     if (pattern.isEmpty()) {
00571         return false; // == Pattern error
00572     }
00573 
00574     // How to find?
00575     Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
00576     const bool matchCase = (m_powerUi != NULL)
00577             ? isChecked(m_powerUi->matchCase)
00578             : isChecked(m_incMenuMatchCase);
00579     if (!matchCase) {
00580         enabledOptions |= Search::CaseInsensitive;
00581     }
00582 
00583     if (!forwards) {
00584         enabledOptions |= Search::Backwards;
00585     }
00586 
00587     bool multiLinePattern = false;
00588     bool regexMode = false;
00589     if (m_powerUi != NULL) {
00590         switch (m_powerUi->searchMode->currentIndex()) {
00591         case MODE_WHOLE_WORDS:
00592             enabledOptions |= Search::WholeWords;
00593             break;
00594 
00595         case MODE_ESCAPE_SEQUENCES:
00596             enabledOptions |= Search::EscapeSequences;
00597             break;
00598 
00599         case MODE_REGEX:
00600             {
00601                 // Check if pattern multi-line
00602                 QString patternCopy(pattern);
00603                 KateDocument::repairPattern(patternCopy, multiLinePattern);
00604                 regexMode = true;
00605             }
00606             enabledOptions |= Search::Regex;
00607             break;
00608 
00609         case MODE_PLAIN_TEXT: // FALLTHROUGH
00610         default:
00611             break;
00612 
00613         }
00614     }
00615 
00616 
00617     // Where to find?
00618     Range inputRange;
00619     Range selection;
00620     const bool selected = m_view->selection();
00621     const bool selectionOnly = (m_powerUi != NULL)
00622             ? isChecked(m_powerUi->selectionOnly)
00623             : false;
00624     if (selected) {
00625         selection = m_view->selectionRange();
00626         if (selectionOnly) {
00627             // First match in selection
00628             inputRange = selection;
00629         } else {
00630             // Next match after/before selection if a match was selected before
00631             if (forwards) {
00632                 inputRange.setRange(selection.start(), m_view->doc()->documentEnd());
00633             } else {
00634                 inputRange.setRange(Cursor(0, 0), selection.end());
00635             }
00636         }
00637     } else {
00638         // No selection
00639         const bool fromCursor = (m_powerUi != NULL)
00640                 ? isChecked(m_powerUi->fromCursor)
00641                 : isChecked(m_incMenuFromCursor);
00642         if (fromCursor) {
00643             const Cursor cursorPos = m_view->cursorPosition();
00644             if (forwards) {
00645                 inputRange.setRange(cursorPos, m_view->doc()->documentEnd());
00646             } else {
00647                 inputRange.setRange(Cursor(0, 0), cursorPos);
00648             }
00649         } else {
00650             inputRange = m_view->doc()->documentRange();
00651         }
00652     }
00653     FAST_DEBUG("Search range is" << inputRange);
00654 
00655     // Single-line pattern workaround
00656     if (regexMode && !multiLinePattern) {
00657         fixForSingleLine(inputRange, forwards);
00658     }
00659 
00660 
00661     // Find, first try
00662     const QVector<Range> resultRanges = m_view->doc()->searchText(inputRange, pattern, enabledOptions);
00663     const Range & match = resultRanges[0];
00664     bool wrap = false;
00665     bool found = false;
00666     SmartRange * afterReplace = NULL;
00667     if (match.isValid()) {
00668         // Previously selected match again?
00669         if (selected && !selectionOnly && (match == selection)) {
00670             // Same match again
00671             if (replace) {
00672                 // Selection is match -> replace
00673                 const QString replacement = m_powerUi->replacement->currentText();
00674                 afterReplace = m_view->doc()->newSmartRange(match);
00675                 afterReplace->setInsertBehavior(SmartRange::ExpandRight | SmartRange::ExpandLeft);
00676                 replaceMatch(resultRanges, replacement);
00677 
00678                 // Find, second try after replaced text
00679                 if (forwards) {
00680                     inputRange.setRange(afterReplace->end(), inputRange.end());
00681                 } else {
00682                     inputRange.setRange(inputRange.start(), afterReplace->start());
00683                 }
00684             } else {
00685                 // Find, second try after old selection
00686                 if (forwards) {
00687                     inputRange.setRange(selection.end(), inputRange.end());
00688                 } else {
00689                     inputRange.setRange(inputRange.start(), selection.start());
00690                 }
00691             }
00692 
00693             // Single-line pattern workaround
00694             fixForSingleLine(inputRange, forwards);
00695 
00696             const QVector<Range> resultRanges2 = m_view->doc()->searchText(inputRange, pattern, enabledOptions);
00697             const Range & match2 = resultRanges2[0];
00698             if (match2.isValid()) {
00699                 selectRange(m_view, match2);
00700                 found = true;
00701                 const bool NOT_WRAPPED = false;
00702                 indicateMatch(NOT_WRAPPED);
00703             } else {
00704                 // Find, third try from doc start on
00705                 wrap = true;
00706             }
00707         } else {
00708             selectRange(m_view, match);
00709             found = true;
00710             const bool NOT_WRAPPED = false;
00711             indicateMatch(NOT_WRAPPED);
00712         }
00713     } else if (!selected || !selectionOnly) {
00714         // Find, second try from doc start on
00715         wrap = true;
00716     }
00717 
00718     // Wrap around
00719     if (wrap) {
00720         inputRange = m_view->doc()->documentRange();
00721         const QVector<Range> resultRanges3 = m_view->doc()->searchText(inputRange, pattern, enabledOptions);
00722         const Range & match3 = resultRanges3[0];
00723         if (match3.isValid()) {
00724             // Previously selected match again?
00725             if (selected && !selectionOnly && (match3 == selection)) {
00726                 // NOOP, same match again
00727             } else {
00728                 selectRange(m_view, match3);
00729                 found = true;
00730             }
00731             const bool WRAPPED = true;
00732             indicateMatch(WRAPPED);
00733         } else {
00734             indicateMismatch();
00735         }
00736     }
00737 
00738     // Highlight all matches and/or replacement
00739     const bool highlightAll = (m_powerUi != NULL)
00740             ? isChecked(m_powerUi->highlightAll)
00741             : isChecked(m_incMenuHighlightAll);
00742     if ((found && highlightAll) || (afterReplace != NULL)) {
00743         // Highlight all matches
00744         if (found && highlightAll) {
00745             highlightAllMatches(pattern, enabledOptions);
00746         }
00747 
00748         // Highlight replacement (on top if overlapping) if new match selected
00749         if (found && (afterReplace != NULL)) {
00750             // Note: highlightAllMatches already reset for us
00751             if (!(found && highlightAll)) {
00752                 resetHighlights();
00753             }
00754 
00755             highlightReplacement(*afterReplace);
00756         }
00757 
00758     }
00759 
00760     delete afterReplace;
00761 
00762     return true; // == No pattern error
00763 }
00764 
00765 
00766 
00767 void KateSearchBar::onPowerPatternChanged(const QString & pattern) {
00768     givePatternFeedback(pattern);
00769     indicateNothing();
00770 }
00771 
00772 
00773 
00774 void KateSearchBar::givePatternFeedback(const QString & pattern) {
00775     bool enabled = true;
00776 
00777     if (pattern.isEmpty()) {
00778         enabled = false;
00779     } else {
00780         switch (m_powerUi->searchMode->currentIndex()) {
00781         case MODE_WHOLE_WORDS:
00782             if (pattern.trimmed() != pattern) {
00783                 enabled = false;
00784             }
00785             break;
00786 
00787         case MODE_REGEX:
00788             m_patternTester.setPattern(pattern);
00789             enabled = m_patternTester.isValid();
00790             break;
00791 
00792         case MODE_ESCAPE_SEQUENCES: // FALLTHROUGH
00793         case MODE_PLAIN_TEXT: // FALLTHROUGH
00794         default:
00795             ; // NOOP
00796 
00797         }
00798     }
00799 
00800     // Enable/disable next/prev and replace next/all
00801     m_powerUi->findNext->setDisabled(!enabled);
00802     m_powerUi->findPrev->setDisabled(!enabled);
00803     m_powerUi->replaceNext->setDisabled(!enabled);
00804     m_powerUi->replaceAll->setDisabled(!enabled);
00805 }
00806 
00807 
00808 
00809 void KateSearchBar::addCurrentTextToHistory(QComboBox * combo) {
00810     const QString text = combo->currentText();
00811     const int index = combo->findText(text);
00812     if (index != -1) {
00813         combo->removeItem(index);
00814     }
00815     combo->insertItem(0, text);
00816     combo->setCurrentIndex(0);
00817 }
00818 
00819 
00820 
00821 void KateSearchBar::backupConfig(bool ofPower) {
00822     if (ofPower) {
00823         m_powerMatchCase = isChecked(m_powerUi->matchCase);
00824         m_powerFromCursor = isChecked(m_powerUi->fromCursor);
00825         m_powerHighlightAll = isChecked(m_powerUi->highlightAll);
00826         m_powerUsePlaceholders = isChecked(m_powerUi->usePlaceholders);
00827         m_powerMode = m_powerUi->searchMode->currentIndex();
00828     } else {
00829         m_incHighlightAll = isChecked(m_incMenuHighlightAll);
00830         m_incFromCursor = isChecked(m_incMenuFromCursor);
00831         m_incMatchCase = isChecked(m_incMenuMatchCase);
00832     }
00833 }
00834 
00835 
00836 
00837 void KateSearchBar::sendConfig() {
00838     KateViewConfig * const globalConfig = KateGlobal::self()->viewConfig();
00839     const long pastFlags = globalConfig->searchFlags();
00840     long futureFlags = pastFlags;
00841 
00842     if (m_powerUi != NULL) {
00843         const bool OF_POWER = true;
00844         backupConfig(OF_POWER);
00845 
00846         // Update power search flags only
00847         const long incFlagsOnly = pastFlags
00848                 & (KateViewConfig::IncHighlightAll
00849                     | KateViewConfig::IncFromCursor
00850                     | KateViewConfig::IncMatchCase);
00851 
00852         futureFlags = incFlagsOnly
00853             | (m_powerMatchCase ? KateViewConfig::PowerMatchCase : 0)
00854             | (m_powerFromCursor ? KateViewConfig::PowerFromCursor : 0)
00855             | (m_powerHighlightAll ? KateViewConfig::PowerHighlightAll : 0)
00856             | (m_powerUsePlaceholders ? KateViewConfig::PowerUsePlaceholders : 0)
00857             | ((m_powerMode == MODE_REGEX)
00858                 ? KateViewConfig::PowerModeRegularExpression
00859                 : ((m_powerMode == MODE_ESCAPE_SEQUENCES)
00860                     ? KateViewConfig::PowerModeEscapeSequences
00861                     : ((m_powerMode == MODE_WHOLE_WORDS)
00862                         ? KateViewConfig::PowerModeWholeWords
00863                         : KateViewConfig::PowerModePlainText)));
00864 
00865     } else if (m_incUi != NULL) {
00866         const bool OF_INCREMENTAL = false;
00867         backupConfig(OF_INCREMENTAL);
00868 
00869         // Update incremental search flags only
00870         const long powerFlagsOnly = pastFlags
00871                 & (KateViewConfig::PowerMatchCase
00872                     | KateViewConfig::PowerFromCursor
00873                     | KateViewConfig::PowerHighlightAll
00874                     | KateViewConfig::PowerUsePlaceholders
00875                     | KateViewConfig::PowerModeRegularExpression
00876                     | KateViewConfig::PowerModeEscapeSequences
00877                     | KateViewConfig::PowerModeWholeWords
00878                     | KateViewConfig::PowerModePlainText);
00879 
00880         futureFlags = powerFlagsOnly
00881                 | (m_incHighlightAll ? KateViewConfig::IncHighlightAll : 0)
00882                 | (m_incFromCursor ? KateViewConfig::IncFromCursor : 0)
00883                 | (m_incMatchCase ? KateViewConfig::IncMatchCase : 0);
00884     }
00885 
00886     // Adjust global config
00887     globalConfig->setSearchFlags(futureFlags);
00888 }
00889 
00890 
00891 
00892 void KateSearchBar::onPowerFindNext() {
00893     const bool FIND = false;
00894     if (onStep(FIND)) {
00895         // Add to search history
00896         addCurrentTextToHistory(m_powerUi->pattern);
00897     }
00898 }
00899 
00900 
00901 
00902 void KateSearchBar::onPowerFindPrev() {
00903     const bool FIND = false;
00904     const bool BACKWARDS = false;
00905     if (onStep(FIND, BACKWARDS)) {
00906         // Add to search history
00907         addCurrentTextToHistory(m_powerUi->pattern);
00908     }
00909 }
00910 
00911 
00912 
00913 void KateSearchBar::onPowerReplaceNext() {
00914     const bool REPLACE = true;
00915     if (onStep(REPLACE)) {
00916         // Add to search history
00917         addCurrentTextToHistory(m_powerUi->pattern);
00918 
00919         // Add to replace history
00920         addCurrentTextToHistory(m_powerUi->replacement);
00921     }
00922 }
00923 
00924 
00925 
00926 // replacement == NULL --> Highlight all matches
00927 // replacement != NULL --> Replace and highlight all matches
00928 void KateSearchBar::onForAll(const QString & pattern, Range inputRange,
00929         Search::SearchOptions enabledOptions,
00930         const QString * replacement) {
00931     bool multiLinePattern = false;
00932     const bool regexMode = enabledOptions.testFlag(Search::Regex);
00933     if (regexMode) {
00934         // Check if pattern multi-line
00935         QString patternCopy(pattern);
00936         KateDocument::repairPattern(patternCopy, multiLinePattern);
00937     }
00938 
00939     // Clear backwards flag, this algorithm is for forward mode
00940     if (enabledOptions.testFlag(Search::Backwards)) {
00941         enabledOptions &= ~Search::SearchOptions(Search::Backwards);
00942     }
00943 
00944     // Before first match
00945     resetHighlights();
00946 
00947     SmartRange * const workingRange = m_view->doc()->newSmartRange(inputRange);
00948     int matchCounter = 0;
00949     for (;;) {
00950         const QVector<Range> resultRanges = m_view->doc()->searchText(*workingRange, pattern, enabledOptions);
00951         Range match = resultRanges[0];
00952         if (!match.isValid()) {
00953             break;
00954         }
00955 
00956         // Work with the match
00957         if (replacement != NULL) {
00958             if (matchCounter == 0) {
00959                 m_view->doc()->editBegin();
00960             }
00961 
00962             // Track replacement operation
00963             SmartRange * const afterReplace = m_view->doc()->newSmartRange(match);
00964             afterReplace->setInsertBehavior(SmartRange::ExpandRight | SmartRange::ExpandLeft);
00965 
00966             // Replace
00967             replaceMatch(resultRanges, *replacement, ++matchCounter);
00968 
00969             // Highlight and continue after adjusted match
00970             highlightReplacement(match = *afterReplace);
00971             delete afterReplace;
00972         } else {
00973             // Highlight and continue after original match
00974             highlightMatch(match);
00975             matchCounter++;
00976         }
00977 
00978         // Continue after match
00979         SmartCursor & workingStart = workingRange->smartStart();
00980         workingStart.setPosition(match.end());
00981         if (workingStart == match.start()) {
00982             // Can happen for regex patterns like "^".
00983             // If we don't advance here we will loop forever...
00984             workingStart.advance(1);
00985         } else if (regexMode && !multiLinePattern && workingStart.atEndOfLine()) {
00986             // single-line regexps might match the naked line end
00987             // therefore we better advance to the next line
00988             workingStart.advance(1);
00989         }
00990 
00991         if (!workingRange->isValid() || workingStart.atEndOfDocument()) {
00992             break;
00993         }
00994     }
00995 
00996     // After last match
00997     if (matchCounter > 0) {
00998         if (replacement != NULL) {
00999             m_view->doc()->editEnd();
01000         }
01001     }
01002 
01003     delete workingRange;
01004 }
01005 
01006 
01007 
01008 void KateSearchBar::onPowerReplaceAll() {
01009     // What to find/replace?
01010     const QString pattern = m_powerUi->pattern->currentText();
01011     const QString replacement = m_powerUi->replacement->currentText();
01012 
01013 
01014     // How to find?
01015     Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
01016     const bool matchCase = isChecked(m_powerUi->matchCase);
01017     if (!matchCase) {
01018         enabledOptions |= Search::CaseInsensitive;
01019     }
01020 
01021     if (m_powerUi != NULL) {
01022         switch (m_powerUi->searchMode->currentIndex()) {
01023         case MODE_WHOLE_WORDS:
01024             enabledOptions |= Search::WholeWords;
01025             break;
01026 
01027         case MODE_ESCAPE_SEQUENCES:
01028             enabledOptions |= Search::EscapeSequences;
01029             break;
01030 
01031         case MODE_REGEX:
01032             enabledOptions |= Search::Regex;
01033             break;
01034 
01035         case MODE_PLAIN_TEXT: // FALLTHROUGH
01036         default:
01037             break;
01038 
01039         }
01040     }
01041 
01042 
01043     // Where to replace?
01044     Range selection;
01045     const bool selected = m_view->selection();
01046     const bool selectionOnly = isChecked(m_powerUi->selectionOnly);
01047     Range inputRange = (selected && selectionOnly)
01048             ? m_view->selectionRange()
01049             : m_view->doc()->documentRange();
01050 
01051 
01052     // Pass on the hard work
01053     onForAll(pattern, inputRange, enabledOptions, &replacement);
01054 
01055 
01056     // Add to search history
01057     addCurrentTextToHistory(m_powerUi->pattern);
01058 
01059     // Add to replace history
01060     addCurrentTextToHistory(m_powerUi->replacement);
01061 }
01062 
01063 
01064 
01065 struct ParInfo {
01066     int openIndex;
01067     bool capturing;
01068     int captureNumber; // 1..9
01069 };
01070 
01071 
01072 
01073 QVector<QString> KateSearchBar::getCapturePatterns(const QString & pattern) {
01074     QVector<QString> capturePatterns;
01075     capturePatterns.reserve(9);
01076     QStack<ParInfo> parInfos;
01077 
01078     const int inputLen = pattern.length();
01079     int input = 0; // walker index
01080     bool insideClass = false;
01081     int captureCount = 0;
01082 
01083     while (input < inputLen) {
01084         if (insideClass) {
01085             // Wait for closing, unescaped ']'
01086             if (pattern[input].unicode() == L']') {
01087                 insideClass = false;
01088             }
01089             input++;
01090         }
01091         else
01092         {
01093             switch (pattern[input].unicode())
01094             {
01095             case L'\\':
01096                 // Skip this and any next character
01097                 input += 2;
01098                 break;
01099 
01100             case L'(':
01101                 ParInfo curInfo;
01102                 curInfo.openIndex = input;
01103                 curInfo.capturing = (input + 1 >= inputLen) || (pattern[input + 1].unicode() != '?');
01104                 if (curInfo.capturing) {
01105                     captureCount++;
01106                 }
01107                 curInfo.captureNumber = captureCount;
01108                 parInfos.push(curInfo);
01109 
01110                 input++;
01111                 break;
01112 
01113             case L')':
01114                 if (!parInfos.empty()) {
01115                     ParInfo & top = parInfos.top();
01116                     if (top.capturing && (top.captureNumber <= 9)) {
01117                         const int start = top.openIndex + 1;
01118                         const int len = input - start;
01119                         if (capturePatterns.size() < top.captureNumber) {
01120                             capturePatterns.resize(top.captureNumber);
01121                         }
01122                         capturePatterns[top.captureNumber - 1] = pattern.mid(start, len);
01123                     }
01124                     parInfos.pop();
01125                 }
01126 
01127                 input++;
01128                 break;
01129 
01130             case L'[':
01131                 input++;
01132                 insideClass = true;
01133                 break;
01134 
01135             default:
01136                 input++;
01137                 break;
01138 
01139             }
01140         }
01141     }
01142 
01143     return capturePatterns;
01144 }
01145 
01146 
01147 
01148 void KateSearchBar::addMenuEntry(QMenu * menu, QVector<QString> & insertBefore, QVector<QString> & insertAfter,
01149         uint & walker, const QString & before, const QString after, const QString description,
01150         const QString & realBefore, const QString & realAfter) {
01151     QAction * const action = menu->addAction(before + after + '\t' + description);
01152     insertBefore[walker] = QString(realBefore.isEmpty() ? before : realBefore);
01153     insertAfter[walker] = QString(realAfter.isEmpty() ? after : realAfter);
01154     action->setData(QVariant(walker++));
01155 }
01156 
01157 
01158 
01159 void KateSearchBar::showAddMenu(bool forPattern) {
01160     QVector<QString> insertBefore(35);
01161     QVector<QString> insertAfter(35);
01162     uint walker = 0;
01163 
01164     // Build menu
01165     QMenu * const popupMenu = new QMenu();
01166     const bool regexMode = (m_powerUi->searchMode->currentIndex() == MODE_REGEX);
01167 
01168     if (forPattern) {
01169         if (regexMode) {
01170             addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "^", "", i18n("Beginning of line"));
01171             addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "$", "", i18n("End of line"));
01172             popupMenu->addSeparator();
01173             addMenuEntry(popupMenu, insertBefore, insertAfter, walker, ".", "", i18n("Any single character (excluding line breaks)"));
01174             popupMenu->addSeparator();
01175             addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "+", "", i18n("One or more occurrences"));
01176             addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "*", "", i18n("Zero or more occurrences"));
01177             addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "?", "", i18n("Zero or one occurrences"));
01178             addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "{a", ",b}", i18n("<a> through <b> occurrences"), "{", ",}");
01179             popupMenu->addSeparator();
01180             addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "(", ")", i18n("Group, capturing"));
01181             addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "|", "", i18n("Or"));
01182             addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "[", "]", i18n("Set of characters"));
01183             addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "[^", "]", i18n("Negative set of characters"));
01184             popupMenu->addSeparator();
01185         }
01186     } else {
01187         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\0", "", i18n("Whole match reference"));
01188         popupMenu->addSeparator();
01189         if (regexMode) {
01190             const QString pattern = m_powerUi->pattern->currentText();
01191             const QVector<QString> capturePatterns = getCapturePatterns(pattern);
01192 
01193             const int captureCount = capturePatterns.count();
01194             for (int i = 1; i <= 9; i++) {
01195                 const QString number = QString::number(i);
01196                 const QString & captureDetails = (i <= captureCount)
01197                         ? (QString(" = (") + capturePatterns[i - 1].left(30)) + QString(")")
01198                         : QString();
01199                 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\" + number, "",
01200                         i18n("Reference") + ' ' + number + captureDetails);
01201             }
01202 
01203             popupMenu->addSeparator();
01204         }
01205     }
01206 
01207     addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\n", "", i18n("Line break"));
01208     addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\t", "", i18n("Tab"));
01209 
01210     if (forPattern && regexMode) {
01211         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\b", "", i18n("Word boundary"));
01212         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\B", "", i18n("Not word boundary"));
01213         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\d", "", i18n("Digit"));
01214         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\D", "", i18n("Non-digit"));
01215         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\s", "", i18n("Whitespace (excluding line breaks)"));
01216         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\S", "", i18n("Non-whitespace (excluding line breaks)"));
01217         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\w", "", i18n("Word character (alphanumerics plus '_')"));
01218         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\W", "", i18n("Non-word character"));
01219     }
01220 
01221     addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\0???", "", i18n("Octal character 000 to 377 (2^8-1)"), "\\0");
01222     addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\x????", "", i18n("Hex character 0000 to FFFF (2^16-1)"), "\\x");
01223     addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\\\", "", i18n("Backslash"));
01224 
01225     if (forPattern && regexMode) {
01226         popupMenu->addSeparator();
01227         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "(?:E", ")", i18n("Group, non-capturing"), "(?:");
01228         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "(?=E", ")", i18n("Lookahead"), "(?=");
01229         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "(?!E", ")", i18n("Negative lookahead"), "(?!");
01230     }
01231 
01232     if (!forPattern) {
01233         popupMenu->addSeparator();
01234         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\L", "", i18n("Begin lowercase conversion"));
01235         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\U", "", i18n("Begin uppercase conversion"));
01236         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\E", "", i18n("End case conversion"));
01237         addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\#[#..]", "", i18n("Replacement counter (for Replace all)"), "\\#");
01238     }
01239 
01240 
01241     // Show menu
01242     const QPoint topLeftGlobal = m_powerUi->patternAdd->mapToGlobal(QPoint(0, 0));
01243     QAction * const result = popupMenu->exec(topLeftGlobal);
01244     if (result != NULL) {
01245         QLineEdit * const lineEdit = forPattern
01246                 ? m_powerUi->pattern->lineEdit()
01247                 : m_powerUi->replacement->lineEdit();
01248         Q_ASSERT(lineEdit != NULL);
01249         const int cursorPos = lineEdit->cursorPosition();
01250         const int index = result->data().toUInt();
01251         const QString & before = insertBefore[index];
01252         const QString & after = insertAfter[index];
01253         lineEdit->insert(before + after);
01254         lineEdit->setCursorPosition(cursorPos + before.count());
01255         lineEdit->setFocus();
01256     }
01257 
01258 
01259     // Kill menu
01260     delete popupMenu;
01261 }
01262 
01263 
01264 
01265 void KateSearchBar::onPowerAddToPatternClicked() {
01266     const bool FOR_PATTERN = true;
01267     showAddMenu(FOR_PATTERN);
01268 }
01269 
01270 
01271 
01272 void KateSearchBar::onPowerAddToReplacementClicked() {
01273     const bool FOR_REPLACEMENT = false;
01274     showAddMenu(FOR_REPLACEMENT);
01275 }
01276 
01277 
01278 
01279 void KateSearchBar::onPowerUsePlaceholdersToggle(int state, bool invokedByUserAction) {
01280     const bool disabled = (state != Qt::Checked);
01281     m_powerUi->replacementAdd->setDisabled(disabled);
01282 
01283     if (invokedByUserAction) {
01284         sendConfig();
01285     }
01286 }
01287 
01288 
01289 
01290 void KateSearchBar::onPowerMatchCaseToggle(bool invokedByUserAction) {
01291     if (invokedByUserAction) {
01292         sendConfig();
01293         indicateNothing();
01294     }
01295 }
01296 
01297 
01298 
01299 void KateSearchBar::onPowerHighlightAllToggle(int state, bool invokedByUserAction) {
01300     if (invokedByUserAction) {
01301         sendConfig();
01302 
01303         const bool wanted = (state == Qt::Checked);
01304         if (wanted) {
01305             const QString pattern = m_powerUi->pattern->currentText();
01306             if (!pattern.isEmpty()) {
01307                 // How to search while highlighting?
01308                 Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
01309                 const bool matchCase = isChecked(m_powerUi->matchCase);
01310                 if (!matchCase) {
01311                     enabledOptions |= Search::CaseInsensitive;
01312                 }
01313 
01314                 switch (m_powerUi->searchMode->currentIndex()) {
01315                 case MODE_WHOLE_WORDS:
01316                     enabledOptions |= Search::WholeWords;
01317                     break;
01318 
01319                 case MODE_ESCAPE_SEQUENCES:
01320                     enabledOptions |= Search::EscapeSequences;
01321                     break;
01322 
01323                 case MODE_REGEX:
01324                     enabledOptions |= Search::Regex;
01325                     break;
01326 
01327                 case MODE_PLAIN_TEXT: // FALLTHROUGH
01328                 default:
01329                     break;
01330 
01331                 }
01332 
01333                 // Highlight them all
01334                 resetHighlights();
01335                 highlightAllMatches(pattern, enabledOptions);
01336             }
01337         } else {
01338             resetHighlights();
01339         }
01340     }
01341 }
01342 
01343 
01344 
01345 void KateSearchBar::onPowerFromCursorToggle(bool invokedByUserAction) {
01346     if (invokedByUserAction) {
01347         sendConfig();
01348     }
01349 }
01350 
01351 
01352 
01353 void KateSearchBar::onPowerModeChanged(int index, bool invokedByUserAction) {
01354     const bool disabled = (index == MODE_PLAIN_TEXT)
01355             || (index == MODE_WHOLE_WORDS);
01356     m_powerUi->patternAdd->setDisabled(disabled);
01357 
01358     if (invokedByUserAction) {
01359         switch (index) {
01360         case MODE_REGEX:
01361             setChecked(m_powerUi->matchCase, true);
01362             // FALLTROUGH
01363 
01364         case MODE_ESCAPE_SEQUENCES:
01365             setChecked(m_powerUi->usePlaceholders, true);
01366             break;
01367 
01368         case MODE_WHOLE_WORDS: // FALLTROUGH
01369         case MODE_PLAIN_TEXT: // FALLTROUGH
01370         default:
01371             ; // NOOP
01372         }
01373 
01374         sendConfig();
01375         indicateNothing();
01376     }
01377 
01378     givePatternFeedback(m_powerUi->pattern->currentText());
01379 }
01380 
01381 
01382 
01383 /*static*/ void KateSearchBar::nextMatchForSelection(KateView * view, bool forwards) {
01384     const bool selected = view->selection();
01385     if (selected) {
01386         const QString pattern = view->selectionText();
01387 
01388         // How to find?
01389         Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
01390         if (!forwards) {
01391             enabledOptions |= Search::Backwards;
01392         }
01393 
01394         // Where to find?
01395         const Range selRange = view->selectionRange();
01396         Range inputRange;
01397         if (forwards) {
01398             inputRange.setRange(selRange.end(), view->doc()->documentEnd());
01399         } else {
01400             inputRange.setRange(Cursor(0, 0), selRange.start());
01401         }
01402 
01403         // Find, first try
01404         const QVector<Range> resultRanges = view->doc()->searchText(inputRange, pattern, enabledOptions);
01405         const Range & match = resultRanges[0];
01406 
01407         if (match.isValid()) {
01408             selectRange(view, match);
01409         } else {
01410             // Find, second try
01411             if (forwards) {
01412                 inputRange.setRange(Cursor(0, 0), selRange.start());
01413             } else {
01414                 inputRange.setRange(selRange.end(), view->doc()->documentEnd());
01415             }
01416             const QVector<Range> resultRanges2 = view->doc()->searchText(inputRange, pattern, enabledOptions);
01417             const Range & match2 = resultRanges2[0];
01418             if (match2.isValid()) {
01419                 selectRange(view, match2);
01420             }
01421         }
01422     } else {
01423         // Select current word so we can search for that the next time
01424         const Cursor cursorPos = view->cursorPosition();
01425         view->selectWord(cursorPos);
01426     }
01427 }
01428 
01429 
01430 
01431 void KateSearchBar::onMutatePower() {
01432     QString initialPattern;
01433     bool selectionOnly = false;
01434 
01435     // Guess settings from context: init pattern with current selection
01436     const bool selected = m_view->selection();
01437     if (selected) {
01438         const Range & selection = m_view->selectionRange();
01439         if (selection.onSingleLine()) {
01440             // ... with current selection
01441             initialPattern = m_view->selectionText();
01442         } else {
01443             // Enable selection only
01444             selectionOnly = true;
01445         }
01446     }
01447 
01448     // If there's no new selection, we'll use the existing pattern
01449     if (initialPattern.isNull()) {
01450         // Coming from power search?
01451         const bool fromReplace = (m_powerUi != NULL) && (m_widget->isVisible());
01452         if (fromReplace) {
01453             QLineEdit * const patternLineEdit = m_powerUi->pattern->lineEdit();
01454             Q_ASSERT(patternLineEdit != NULL);
01455             patternLineEdit->selectAll();
01456             m_powerUi->pattern->setFocus(Qt::MouseFocusReason);
01457             return;
01458         }
01459 
01460         // Coming from incremental search?
01461         const bool fromIncremental = (m_incUi != NULL) && (m_widget->isVisible());
01462         if (fromIncremental) {
01463             initialPattern = m_incUi->pattern->displayText();
01464         }
01465     }
01466 
01467     // Create dialog
01468     const bool create = (m_powerUi == NULL);
01469     if (create) {
01470         // Kill incremental widget
01471         if (m_incUi != NULL) {
01472             // Backup current settings
01473             const bool OF_INCREMENTAL = false;
01474             backupConfig(OF_INCREMENTAL);
01475 
01476             // Kill widget
01477             delete m_incUi;
01478             delete m_incMenu;
01479             m_incUi = NULL;
01480             m_incMenu = NULL;
01481             m_incMenuMatchCase = NULL;
01482             m_incMenuFromCursor = NULL;
01483             m_incMenuHighlightAll = NULL;
01484             m_layout->removeWidget(m_widget);
01485             m_widget->deleteLater(); // I didn't get a crash here but for symmetrie to the other mutate slot^
01486         }
01487 
01488         // Add power widget
01489         m_widget = new QWidget(this);
01490         m_powerUi = new Ui::PowerSearchBar;
01491         m_powerUi->setupUi(m_widget);
01492         m_layout->addWidget(m_widget);
01493 
01494         // Bind to shared history models
01495         const int MAX_HISTORY_SIZE = 100; // Please don't lower this value! Thanks, Sebastian
01496         QStringListModel * const patternHistoryModel = KateHistoryModel::getPatternHistoryModel();
01497         QStringListModel * const replacementHistoryModel = KateHistoryModel::getReplacementHistoryModel();
01498         m_powerUi->pattern->setMaxCount(MAX_HISTORY_SIZE);
01499         m_powerUi->pattern->setModel(patternHistoryModel);
01500         m_powerUi->replacement->setMaxCount(MAX_HISTORY_SIZE);
01501         m_powerUi->replacement->setModel(replacementHistoryModel);
01502 
01503         // Icons
01504         m_powerUi->mutate->setIcon(KIcon("arrow-down-double"));
01505         m_powerUi->findNext->setIcon(KIcon("go-down"));
01506         m_powerUi->findPrev->setIcon(KIcon("go-up"));
01507         m_powerUi->patternAdd->setIcon(KIcon("list-add"));
01508         m_powerUi->replacementAdd->setIcon(KIcon("list-add"));
01509 
01510         // Focus proxy
01511         centralWidget()->setFocusProxy(m_powerUi->pattern);
01512 
01513         // Make completers case-sensitive
01514         QLineEdit * const patternLineEdit = m_powerUi->pattern->lineEdit();
01515         Q_ASSERT(patternLineEdit != NULL);
01516         patternLineEdit->completer()->setCaseSensitivity(Qt::CaseSensitive);
01517 
01518         QLineEdit * const replacementLineEdit = m_powerUi->pattern->lineEdit();
01519         Q_ASSERT(replacementLineEdit != NULL);
01520         replacementLineEdit->completer()->setCaseSensitivity(Qt::CaseSensitive);
01521     }
01522 
01523     setChecked(m_powerUi->selectionOnly, selectionOnly);
01524 
01525     // Restore previous settings
01526     if (create) {
01527         setChecked(m_powerUi->matchCase, m_powerMatchCase);
01528         setChecked(m_powerUi->highlightAll, m_powerHighlightAll);
01529         setChecked(m_powerUi->usePlaceholders, m_powerUsePlaceholders);
01530         setChecked(m_powerUi->fromCursor, m_powerFromCursor);
01531         m_powerUi->searchMode->setCurrentIndex(m_powerMode);
01532     }
01533 
01534     // Set initial search pattern
01535     QLineEdit * const patternLineEdit = m_powerUi->pattern->lineEdit();
01536     Q_ASSERT(patternLineEdit != NULL);
01537     patternLineEdit->setText(initialPattern);
01538     patternLineEdit->selectAll();
01539 
01540     // Set initial replacement text
01541     QLineEdit * const replacementLineEdit = m_powerUi->replacement->lineEdit();
01542     Q_ASSERT(replacementLineEdit != NULL);
01543     replacementLineEdit->setText("");
01544 
01545     // Propagate settings (slots are still inactive on purpose)
01546     onPowerPatternChanged(initialPattern);
01547     const bool NOT_INVOKED_BY_USER_ACTION = false;
01548     onPowerUsePlaceholdersToggle(m_powerUi->usePlaceholders->checkState(), NOT_INVOKED_BY_USER_ACTION);
01549     onPowerModeChanged(m_powerUi->searchMode->currentIndex(), NOT_INVOKED_BY_USER_ACTION);
01550 
01551     if (create) {
01552         // Slots
01553         connect(m_powerUi->mutate, SIGNAL(clicked()), this, SLOT(onMutateIncremental()));
01554         connect(patternLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(onPowerPatternChanged(const QString &)));
01555         connect(m_powerUi->findNext, SIGNAL(clicked()), this, SLOT(onPowerFindNext()));
01556         connect(m_powerUi->findPrev, SIGNAL(clicked()), this, SLOT(onPowerFindPrev()));
01557         connect(m_powerUi->replaceNext, SIGNAL(clicked()), this, SLOT(onPowerReplaceNext()));
01558         connect(m_powerUi->replaceAll, SIGNAL(clicked()), this, SLOT(onPowerReplaceAll()));
01559         connect(m_powerUi->searchMode, SIGNAL(currentIndexChanged(int)), this, SLOT(onPowerModeChanged(int)));
01560         connect(m_powerUi->patternAdd, SIGNAL(clicked()), this, SLOT(onPowerAddToPatternClicked()));
01561         connect(m_powerUi->usePlaceholders, SIGNAL(stateChanged(int)), this, SLOT(onPowerUsePlaceholdersToggle(int)));
01562         connect(m_powerUi->matchCase, SIGNAL(stateChanged(int)), this, SLOT(onPowerMatchCaseToggle()));
01563         connect(m_powerUi->highlightAll, SIGNAL(stateChanged(int)), this, SLOT(onPowerHighlightAllToggle(int)));
01564         connect(m_powerUi->fromCursor, SIGNAL(stateChanged(int)), this, SLOT(onPowerFromCursorToggle()));
01565         connect(m_powerUi->replacementAdd, SIGNAL(clicked()), this, SLOT(onPowerAddToReplacementClicked()));
01566 
01567         // Make [return] in pattern line edit trigger <find next> action
01568         connect(patternLineEdit, SIGNAL(returnPressed()), this, SLOT(onReturnPressed()));
01569         connect(replacementLineEdit, SIGNAL(returnPressed()), this, SLOT(onPowerReplaceNext()));
01570     }
01571 
01572     // Focus
01573     if (m_widget->isVisible()) {
01574         m_powerUi->pattern->setFocus(Qt::MouseFocusReason);
01575     }
01576 }
01577 
01578 
01579 
01580 void KateSearchBar::onMutateIncremental() {
01581     QString initialPattern;
01582 
01583     // Guess settings from context: init pattern with current selection
01584     const bool selected = m_view->selection();
01585     if (selected) {
01586         const Range & selection = m_view->selectionRange();
01587         if (selection.onSingleLine()) {
01588             // ... with current selection
01589             initialPattern = m_view->selectionText();
01590         }
01591     }
01592 
01593     // If there's no new selection, we'll use the existing pattern
01594     if (initialPattern.isNull()) {
01595         // Coming from incremental search?
01596         const bool fromIncremental = (m_incUi != NULL) && (m_widget->isVisible());
01597         if (fromIncremental) {
01598             m_incUi->pattern->selectAll();
01599             m_incUi->pattern->setFocus(Qt::MouseFocusReason);
01600             return;
01601         }
01602 
01603         // Coming from power search?
01604         const bool fromReplace = (m_powerUi != NULL) && (m_widget->isVisible());
01605         if (fromReplace) {
01606             initialPattern = m_powerUi->pattern->currentText();
01607         }
01608     }
01609 
01610     // Create dialog
01611     const bool create = (m_incUi == NULL);
01612     if (create) {
01613         // Kill power widget
01614         if (m_powerUi != NULL) {
01615             // Backup current settings
01616             const bool OF_POWER = true;
01617             backupConfig(OF_POWER);
01618 
01619             // Kill widget
01620             delete m_powerUi;
01621             m_powerUi = NULL;
01622             m_layout->removeWidget(m_widget);
01623             m_widget->deleteLater(); //deleteLater, because it's not a good idea too delete the widget and there for the button triggering this slot
01624         }
01625 
01626         // Add incremental widget
01627         m_widget = new QWidget(this);
01628         m_incUi = new Ui::IncrementalSearchBar;
01629         m_incUi->setupUi(m_widget);
01630         m_layout->addWidget(m_widget);
01631 
01632         // Fill options menu
01633         m_incMenu = new QMenu();
01634         m_incUi->options->setMenu(m_incMenu);
01635         m_incMenuHighlightAll = m_incMenu->addAction(i18n("Hi&ghlight all"));
01636         m_incMenuHighlightAll->setCheckable(true);
01637         m_incMenuMatchCase = m_incMenu->addAction(i18n("&Match case"));
01638         m_incMenuMatchCase->setCheckable(true);
01639         m_incMenuFromCursor = m_incMenu->addAction(i18n("From &cursor"));
01640         m_incMenuFromCursor->setCheckable(true);
01641 
01642         // Icons
01643         m_incUi->mutate->setIcon(KIcon("arrow-up-double"));
01644         m_incUi->next->setIcon(KIcon("go-down"));
01645         m_incUi->prev->setIcon(KIcon("go-up"));
01646 
01647         // Focus proxy
01648         centralWidget()->setFocusProxy(m_incUi->pattern);
01649     }
01650 
01651     // Restore previous settings
01652     if (create) {
01653         setChecked(m_incMenuHighlightAll, m_incHighlightAll);
01654         setChecked(m_incMenuFromCursor, m_incFromCursor);
01655         setChecked(m_incMenuMatchCase, m_incMatchCase);
01656     }
01657 
01658     // Set initial search pattern
01659     m_incUi->pattern->setText(initialPattern);
01660     m_incUi->pattern->selectAll();
01661 
01662     // Propagate settings (slots are still inactive on purpose)
01663     const bool NOT_INVOKED_BY_USER_ACTION = false;
01664     onIncPatternChanged(initialPattern, NOT_INVOKED_BY_USER_ACTION);
01665 
01666     if (create) {
01667         // Slots
01668         connect(m_incUi->mutate, SIGNAL(clicked()), this, SLOT(onMutatePower()));
01669         connect(m_incUi->pattern, SIGNAL(returnPressed()), this, SLOT(onReturnPressed()));
01670         connect(m_incUi->pattern, SIGNAL(textChanged(const QString &)), this, SLOT(onIncPatternChanged(const QString &)));
01671         connect(m_incUi->next, SIGNAL(clicked()), this, SLOT(onIncNext()));
01672         connect(m_incUi->prev, SIGNAL(clicked()), this, SLOT(onIncPrev()));
01673         connect(m_incMenuMatchCase, SIGNAL(changed()), this, SLOT(onIncMatchCaseToggle()));
01674         connect(m_incMenuFromCursor, SIGNAL(changed()), this, SLOT(onIncFromCursorToggle()));
01675         connect(m_incMenuHighlightAll, SIGNAL(toggled(bool)), this, SLOT(onIncHighlightAllToggle(bool)));
01676 
01677         // Make button click open the menu as well. IMHO with the dropdown arrow present the button
01678         // better shows his nature than in instant popup mode.
01679         connect(m_incUi->options, SIGNAL(clicked()), m_incUi->options, SLOT(showMenu()));
01680     }
01681 
01682     // Focus
01683     if (m_widget->isVisible()) {
01684         m_incUi->pattern->setFocus(Qt::MouseFocusReason);
01685     }
01686 }
01687 
01688 
01689 
01690 bool KateSearchBar::isChecked(QCheckBox * checkbox) {
01691     Q_ASSERT(checkbox != NULL);
01692     return checkbox->checkState() == Qt::Checked;
01693 }
01694 
01695 
01696 
01697 bool KateSearchBar::isChecked(QAction * menuAction) {
01698     Q_ASSERT(menuAction != NULL);
01699     return menuAction->isChecked();
01700 }
01701 
01702 
01703 
01704 void KateSearchBar::setChecked(QCheckBox * checkbox, bool checked) {
01705     Q_ASSERT(checkbox != NULL);
01706     checkbox->setCheckState(checked ? Qt::Checked : Qt::Unchecked);
01707 }
01708 
01709 
01710 
01711 void KateSearchBar::setChecked(QAction * menuAction, bool checked) {
01712     Q_ASSERT(menuAction != NULL);
01713     menuAction->setChecked(checked);
01714 }
01715 
01716 
01717 
01718 void KateSearchBar::enableHighlights(bool enable) {
01719     if (enable) {
01720         m_view->addInternalHighlight(m_topRange);
01721     } else {
01722         m_view->removeInternalHighlight(m_topRange);
01723         m_topRange->deleteChildRanges();
01724     }
01725 }
01726 
01727 
01728 
01729 void KateSearchBar::resetHighlights() {
01730     enableHighlights(false);
01731     enableHighlights(true);
01732 }
01733 
01734 
01735 
01736 void KateSearchBar::showEvent(QShowEvent * event) {
01737     // Update init cursor
01738     if (m_incUi != NULL) {
01739         m_incInitCursor = m_view->cursorPosition();
01740     }
01741 
01742     enableHighlights(true);
01743     KateViewBarWidget::showEvent(event);
01744 }
01745 
01746 
01747 
01748 void KateSearchBar::hideEvent(QHideEvent * event) {
01749     enableHighlights(false);
01750     KateViewBarWidget::hideEvent(event);
01751 }
01752 
01753 
01754 
01755 // Kill our helpers again
01756 #ifdef FAST_DEBUG_ENABLE
01757 # undef FAST_DEBUG_ENABLE
01758 #endif
01759 #undef FAST_DEBUG
01760 
01761 
01762 
01763 #include "katesearchbar.moc"
01764 

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal