00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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 #include <ktexteditor/rangefeedback.h>
00031
00032 #include <QtGui/QVBoxLayout>
00033 #include <QtGui/QComboBox>
00034 #include <QtGui/QCheckBox>
00035 #include <QtGui/QKeySequence>
00036 #include <QtGui/QShortcut>
00037 #include <QtGui/QCursor>
00038 #include <QStringListModel>
00039 #include <QCompleter>
00040
00041 using namespace KTextEditor;
00042
00043
00044
00045
00046
00047
00048 #ifdef FAST_DEBUG_ENABLE
00049 # define FAST_DEBUG(x) (kDebug() << x)
00050 #else
00051 # define FAST_DEBUG(x)
00052 #endif
00053
00054
00055
00056 namespace {
00057
00058 class AddMenuManager {
00059
00060 private:
00061 QVector<QString> m_insertBefore;
00062 QVector<QString> m_insertAfter;
00063 QSet<QAction *> m_actionPointers;
00064 uint m_indexWalker;
00065 QMenu * m_menu;
00066
00067 public:
00068 AddMenuManager(QMenu * parent, int expectedItemCount)
00069 : m_insertBefore(QVector<QString>(expectedItemCount)),
00070 m_insertAfter(QVector<QString>(expectedItemCount)),
00071 m_indexWalker(0),
00072 m_menu(NULL) {
00073 Q_ASSERT(parent != NULL);
00074 m_menu = parent->addMenu(i18n("Add..."));
00075 if (m_menu == NULL) {
00076 return;
00077 }
00078 m_menu->setIcon(KIcon("list-add"));
00079 }
00080
00081 void enableMenu(bool enabled) {
00082 if (m_menu == NULL) {
00083 return;
00084 }
00085 m_menu->setEnabled(enabled);
00086 }
00087
00088 void addEntry(const QString & before, const QString after,
00089 const QString description, const QString & realBefore = QString(),
00090 const QString & realAfter = QString()) {
00091 if (m_menu == NULL) {
00092 return;
00093 }
00094 QAction * const action = m_menu->addAction(before + after + '\t' + description);
00095 m_insertBefore[m_indexWalker] = QString(realBefore.isEmpty() ? before : realBefore);
00096 m_insertAfter[m_indexWalker] = QString(realAfter.isEmpty() ? after : realAfter);
00097 action->setData(QVariant(m_indexWalker++));
00098 m_actionPointers.insert(action);
00099 }
00100
00101 void addSeparator() {
00102 if (m_menu == NULL) {
00103 return;
00104 }
00105 m_menu->addSeparator();
00106 }
00107
00108 void handle(QAction * action, QLineEdit * lineEdit) {
00109 if (!m_actionPointers.contains(action)) {
00110 return;
00111 }
00112
00113 const int cursorPos = lineEdit->cursorPosition();
00114 const int index = action->data().toUInt();
00115 const QString & before = m_insertBefore[index];
00116 const QString & after = m_insertAfter[index];
00117 lineEdit->insert(before + after);
00118 lineEdit->setCursorPosition(cursorPos + before.count());
00119 lineEdit->setFocus();
00120 }
00121 };
00122
00123 }
00124
00125
00126
00127 KateSearchBar::KateSearchBar(bool initAsPower, KateView* kateView, QWidget* parent)
00128 : KateViewBarWidget(true, kateView, parent),
00129 m_topRange(NULL),
00130 m_rangeNotifier(new KTextEditor::SmartRangeNotifier),
00131 m_layout(new QVBoxLayout()),
00132 m_widget(NULL),
00133 m_incUi(NULL),
00134 m_incMenu(NULL),
00135 m_incMenuMatchCase(NULL),
00136 m_incMenuFromCursor(NULL),
00137 m_incMenuHighlightAll(NULL),
00138 m_incInitCursor(0, 0),
00139 m_powerUi(NULL),
00140 m_powerMenu(NULL),
00141 m_powerMenuFromCursor(NULL),
00142 m_powerMenuHighlightAll(NULL),
00143 m_powerMenuSelectionOnly(NULL),
00144 m_incHighlightAll(false),
00145 m_incFromCursor(true),
00146 m_incMatchCase(false),
00147 m_powerMatchCase(true),
00148 m_powerFromCursor(false),
00149 m_powerHighlightAll(false),
00150 m_powerMode(0) {
00151
00152 connect(m_rangeNotifier,SIGNAL(rangeContentsChanged(KTextEditor::SmartRange*)),
00153 this,SLOT(rangeContentsChanged(KTextEditor::SmartRange*)));
00154
00155
00156 QWidget * const widget = centralWidget();
00157 widget->setLayout(m_layout);
00158 m_layout->setMargin(2);
00159
00160
00161 m_topRange = view()->doc()->newSmartRange(view()->doc()->documentRange());
00162 static_cast<KateSmartRange*>(m_topRange)->setInternal();
00163 m_topRange->setInsertBehavior(SmartRange::ExpandLeft | SmartRange::ExpandRight);
00164 enableHighlights(true);
00165
00166
00167
00168 KateViewConfig * const globalConfig = KateGlobal::self()->viewConfig();
00169 const long searchFlags = globalConfig->searchFlags();
00170 m_incHighlightAll = (searchFlags & KateViewConfig::IncHighlightAll) != 0;
00171 m_incFromCursor = (searchFlags & KateViewConfig::IncFromCursor) != 0;
00172 m_incMatchCase = (searchFlags & KateViewConfig::IncMatchCase) != 0;
00173 m_powerMatchCase = (searchFlags & KateViewConfig::PowerMatchCase) != 0;
00174 m_powerFromCursor = (searchFlags & KateViewConfig::PowerFromCursor) != 0;
00175 m_powerHighlightAll = (searchFlags & KateViewConfig::PowerHighlightAll) != 0;
00176 m_powerMode = ((searchFlags & KateViewConfig::PowerModeRegularExpression) != 0)
00177 ? MODE_REGEX
00178 : (((searchFlags & KateViewConfig::PowerModeEscapeSequences) != 0)
00179 ? MODE_ESCAPE_SEQUENCES
00180 : (((searchFlags & KateViewConfig::PowerModeWholeWords) != 0)
00181 ? MODE_WHOLE_WORDS
00182 : MODE_PLAIN_TEXT));
00183
00184
00185
00186 if (initAsPower) {
00187 onMutatePower();
00188 } else {
00189 onMutateIncremental();
00190 }
00191 }
00192
00193
00194
00195 KateSearchBar::~KateSearchBar() {
00196
00197 delete m_layout;
00198 delete m_widget;
00199
00200 delete m_incUi;
00201 delete m_incMenu;
00202
00203 delete m_powerUi;
00204 delete m_powerMenu;
00205 }
00206
00207
00208
00209 void KateSearchBar::findNext() {
00210 if (m_incUi != NULL) {
00211 onIncNext();
00212 } else {
00213 onPowerFindNext();
00214 }
00215 }
00216
00217
00218
00219 void KateSearchBar::findPrevious() {
00220 if (m_incUi != NULL) {
00221 onIncPrev();
00222 } else {
00223 onPowerFindPrev();
00224 }
00225 }
00226
00227
00228
00229 void KateSearchBar::highlight(const Range & range, const QColor & color) {
00230 SmartRange * const highlight = view()->doc()->newSmartRange(range, m_topRange);
00231 highlight->setInsertBehavior(SmartRange::DoNotExpand);
00232 Attribute::Ptr attribute(new Attribute());
00233 attribute->setBackground(color);
00234 highlight->setAttribute(attribute);
00235 highlight->addNotifier(m_rangeNotifier);
00236 }
00237
00238
00239
00240 void KateSearchBar::highlightMatch(const Range & range) {
00241 highlight(range, Qt::yellow);
00242 }
00243
00244
00245
00246 void KateSearchBar::highlightReplacement(const Range & range) {
00247 highlight(range, Qt::green);
00248 }
00249
00250
00251
00252 void KateSearchBar::highlightAllMatches(const QString & pattern,
00253 Search::SearchOptions searchOptions) {
00254 onForAll(pattern, view()->doc()->documentRange(),
00255 searchOptions, NULL);
00256 }
00257
00258 void KateSearchBar::rangeContentsChanged(KTextEditor::SmartRange* range) {
00259 neutralMatch();
00260 Attribute::Ptr attribute(new Attribute());
00261
00262 range->setAttribute(attribute);
00263
00264 }
00265
00266 void KateSearchBar::neutralMatch() {
00267 if (m_incUi != NULL) {
00268 QPalette background(m_incUi->pattern->palette());
00269 KColorScheme::adjustBackground(background, KColorScheme::NeutralBackground);
00270 m_incUi->pattern->setPalette(background);
00271 } else {
00272 QLineEdit * const lineEdit = m_powerUi->pattern->lineEdit();
00273 Q_ASSERT(lineEdit != NULL);
00274 QPalette background(lineEdit->palette());
00275 KColorScheme::adjustBackground(background, KColorScheme::NeutralBackground);
00276 lineEdit->setPalette(background);
00277 }
00278 }
00279
00280 void KateSearchBar::indicateMatch(bool wrapped) {
00281 if (m_incUi != NULL) {
00282
00283 QPalette background(m_incUi->pattern->palette());
00284 KColorScheme::adjustBackground(background, KColorScheme::PositiveBackground);
00285 m_incUi->pattern->setPalette(background);
00286
00287
00288 m_incUi->status->setText(wrapped
00289 ? i18n("Reached bottom, continued from top")
00290 : "");
00291 } else {
00292
00293 QLineEdit * const lineEdit = m_powerUi->pattern->lineEdit();
00294 Q_ASSERT(lineEdit != NULL);
00295 QPalette background(lineEdit->palette());
00296 KColorScheme::adjustBackground(background, KColorScheme::PositiveBackground);
00297 lineEdit->setPalette(background);
00298 }
00299 }
00300
00301
00302
00303 void KateSearchBar::indicateMismatch() {
00304 if (m_incUi != NULL) {
00305
00306 QPalette background(m_incUi->pattern->palette());
00307 KColorScheme::adjustBackground(background, KColorScheme::NegativeBackground);
00308 m_incUi->pattern->setPalette(background);
00309
00310
00311 m_incUi->status->setText(i18n("Not found"));
00312 } else {
00313
00314 QLineEdit * const lineEdit = m_powerUi->pattern->lineEdit();
00315 Q_ASSERT(lineEdit != NULL);
00316 QPalette background(lineEdit->palette());
00317 KColorScheme::adjustBackground(background, KColorScheme::NegativeBackground);
00318 lineEdit->setPalette(background);
00319 }
00320 }
00321
00322
00323
00324 void KateSearchBar::indicateNothing() {
00325 if (m_incUi != NULL) {
00326
00327 m_incUi->pattern->setPalette(QPalette());
00328
00329
00330 m_incUi->status->setText("");
00331 } else {
00332
00333 QLineEdit * const lineEdit = m_powerUi->pattern->lineEdit();
00334 Q_ASSERT(lineEdit != NULL);
00335
00336
00337 QColor color = QPalette().color(QPalette::Base);
00338 QPalette background(lineEdit->palette());
00339 background.setBrush(QPalette::Active, QPalette::Base, QPalette().brush(QPalette::Active, QPalette::Base));
00340 background.setBrush(QPalette::Inactive, QPalette::Base, QPalette().brush(QPalette::Inactive, QPalette::Base));
00341 background.setBrush(QPalette::Disabled, QPalette::Base, QPalette().brush(QPalette::Disabled, QPalette::Base));
00342 lineEdit->setPalette(background);
00343 }
00344 }
00345
00346
00347
00348 void KateSearchBar::selectRange(KateView * view, const KTextEditor::Range & range) {
00349 view->setCursorPositionInternal(range.start(), 1);
00350
00351
00352 if (!view->viInputMode())
00353 view->setSelection(range);
00354 }
00355
00356
00357
00358 void KateSearchBar::buildReplacement(QString & output, QList<ReplacementPart> & parts,
00359 const QVector<Range> & details, int replacementCounter) {
00360 const int MIN_REF_INDEX = 0;
00361 const int MAX_REF_INDEX = details.count() - 1;
00362
00363 output.clear();
00364 ReplacementPart::Type caseConversion = ReplacementPart::KeepCase;
00365 for (QList<ReplacementPart>::iterator iter = parts.begin(); iter != parts.end(); iter++) {
00366 ReplacementPart & curPart = *iter;
00367 switch (curPart.type) {
00368 case ReplacementPart::Reference:
00369 if ((curPart.index < MIN_REF_INDEX) || (curPart.index > MAX_REF_INDEX)) {
00370
00371 output.append(QString::number(curPart.index));
00372 } else {
00373 const Range & captureRange = details[curPart.index];
00374 if (captureRange.isValid()) {
00375
00376 const bool blockMode = view()->blockSelection();
00377 const QString content = view()->doc()->text(captureRange, blockMode);
00378 switch (caseConversion) {
00379 case ReplacementPart::UpperCase:
00380
00381 output.append(content.toUpper());
00382 break;
00383
00384 case ReplacementPart::LowerCase:
00385
00386 output.append(content.toLower());
00387 break;
00388
00389 case ReplacementPart::KeepCase:
00390 default:
00391
00392 output.append(content);
00393 break;
00394
00395 }
00396 }
00397 }
00398 break;
00399
00400 case ReplacementPart::UpperCase:
00401 case ReplacementPart::LowerCase:
00402 case ReplacementPart::KeepCase:
00403 caseConversion = curPart.type;
00404 break;
00405
00406 case ReplacementPart::Counter:
00407 {
00408
00409 const int minWidth = curPart.index;
00410 const int number = replacementCounter;
00411 output.append(QString("%1").arg(number, minWidth, 10, QLatin1Char('0')));
00412 }
00413 break;
00414
00415 case ReplacementPart::Text:
00416 default:
00417 switch (caseConversion) {
00418 case ReplacementPart::UpperCase:
00419
00420 output.append(curPart.text.toUpper());
00421 break;
00422
00423 case ReplacementPart::LowerCase:
00424
00425 output.append(curPart.text.toLower());
00426 break;
00427
00428 case ReplacementPart::KeepCase:
00429 default:
00430
00431 output.append(curPart.text);
00432 break;
00433
00434 }
00435 break;
00436
00437 }
00438 }
00439 }
00440
00441
00442
00443 void KateSearchBar::replaceMatch(const QVector<Range> & match, const QString & replacement,
00444 int replacementCounter) {
00445
00446 bool usePlaceholders = false;
00447 switch (m_powerUi->searchMode->currentIndex()) {
00448 case MODE_REGEX:
00449 case MODE_ESCAPE_SEQUENCES:
00450 usePlaceholders = true;
00451 break;
00452
00453 default:
00454 break;
00455
00456 }
00457
00458 const Range & targetRange = match[0];
00459 QString finalReplacement;
00460 if (usePlaceholders) {
00461
00462 QList<ReplacementPart> parts;
00463 QString writableHack(replacement);
00464 const bool REPLACEMENT_GOODIES = true;
00465 KateDocument::escapePlaintext(writableHack, &parts, REPLACEMENT_GOODIES);
00466 buildReplacement(finalReplacement, parts, match, replacementCounter);
00467 } else {
00468
00469 finalReplacement = replacement;
00470 }
00471
00472 const bool blockMode = (view()->blockSelection() && !targetRange.onSingleLine());
00473 view()->doc()->replaceText(targetRange, finalReplacement, blockMode);
00474 }
00475
00476
00477
00478 void KateSearchBar::onIncPatternChanged(const QString & pattern, bool invokedByUserAction) {
00479 if (pattern.isEmpty()) {
00480 if (invokedByUserAction) {
00481
00482 view()->setSelection(Range::invalid());
00483
00484
00485 resetHighlights();
00486 }
00487
00488
00489 indicateNothing();
00490
00491
00492 m_incUi->next->setDisabled(true);
00493 m_incUi->prev->setDisabled(true);
00494 return;
00495 }
00496
00497
00498 m_incUi->next->setDisabled(false);
00499 m_incUi->prev->setDisabled(false);
00500
00501 if (invokedByUserAction) {
00502
00503 Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
00504 const bool matchCase = isChecked(m_incMenuMatchCase);
00505 if (!matchCase) {
00506 enabledOptions |= Search::CaseInsensitive;
00507 }
00508
00509
00510
00511 Range inputRange;
00512 const bool fromCursor = isChecked(m_incMenuFromCursor);
00513 if (fromCursor) {
00514 inputRange.setRange(m_incInitCursor, view()->doc()->documentEnd());
00515 } else {
00516 inputRange = view()->doc()->documentRange();
00517 }
00518
00519
00520 const QVector<Range> resultRanges = view()->doc()->searchText(inputRange, pattern, enabledOptions);
00521 const Range & match = resultRanges[0];
00522
00523 bool found = false;
00524 if (match.isValid()) {
00525 selectRange(view(), match);
00526 const bool NOT_WRAPPED = false;
00527 indicateMatch(NOT_WRAPPED);
00528 found = true;
00529 } else {
00530
00531 if (fromCursor) {
00532
00533 inputRange = view()->doc()->documentRange();
00534 const QVector<Range> resultRanges2 = view()->doc()->searchText(inputRange, pattern, enabledOptions);
00535 const Range & match2 = resultRanges2[0];
00536 if (match2.isValid()) {
00537 selectRange(view(), match2);
00538 const bool WRAPPED = true;
00539 indicateMatch(WRAPPED);
00540 found = true;
00541 } else {
00542 indicateMismatch();
00543 }
00544 } else {
00545 indicateMismatch();
00546 }
00547 }
00548
00549
00550 if (isChecked(m_incMenuHighlightAll)) {
00551 if (found ) {
00552 highlightAllMatches(pattern, enabledOptions);
00553 } else {
00554 resetHighlights();
00555 }
00556 }
00557 if (!found) {
00558 view()->setSelection(Range::invalid());
00559 }
00560 }
00561 }
00562
00563
00564
00565 void KateSearchBar::onIncNext() {
00566 const bool FIND = false;
00567 onStep(FIND);
00568 }
00569
00570
00571
00572 void KateSearchBar::onIncPrev() {
00573 const bool FIND = false;
00574 const bool BACKWARDS = false;
00575 onStep(FIND, BACKWARDS);
00576 }
00577
00578
00579
00580 void KateSearchBar::onIncMatchCaseToggle(bool invokedByUserAction) {
00581 if (invokedByUserAction) {
00582 sendConfig();
00583
00584
00585 const QString pattern = m_incUi->pattern->displayText();
00586 onIncPatternChanged(pattern);
00587 }
00588 }
00589
00590
00591
00592 void KateSearchBar::onIncHighlightAllToggle(bool checked, bool invokedByUserAction) {
00593 if (invokedByUserAction) {
00594 sendConfig();
00595
00596 if (checked) {
00597 const QString pattern = m_incUi->pattern->displayText();
00598 if (!pattern.isEmpty()) {
00599
00600 Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
00601 const bool matchCase = isChecked(m_incMenuMatchCase);
00602 if (!matchCase) {
00603 enabledOptions |= Search::CaseInsensitive;
00604 }
00605
00606
00607 resetHighlights();
00608 highlightAllMatches(pattern, enabledOptions);
00609 }
00610 } else {
00611 resetHighlights();
00612 }
00613 }
00614 }
00615
00616
00617
00618 void KateSearchBar::onIncFromCursorToggle(bool invokedByUserAction) {
00619 if (invokedByUserAction) {
00620 sendConfig();
00621 }
00622 }
00623
00624
00625
00626 void KateSearchBar::fixForSingleLine(Range & range, bool forwards) {
00627 FAST_DEBUG("Single-line workaround checking BEFORE" << range);
00628 if (forwards) {
00629 const int line = range.start().line();
00630 const int col = range.start().column();
00631 const int maxColWithNewline = view()->doc()->lineLength(line) + 1;
00632 if (col == maxColWithNewline) {
00633 FAST_DEBUG("Starting on a newline" << range);
00634 const int maxLine = view()->doc()->lines() - 1;
00635 if (line < maxLine) {
00636 range.setRange(Cursor(line + 1, 0), range.end());
00637 FAST_DEBUG("Search range fixed to " << range);
00638 } else {
00639 FAST_DEBUG("Already at last line");
00640 range = Range::invalid();
00641 }
00642 }
00643 } else {
00644 const int col = range.end().column();
00645 if (col == 0) {
00646 FAST_DEBUG("Ending after a newline" << range);
00647 const int line = range.end().line();
00648 if (line > 0) {
00649 const int maxColWithNewline = view()->doc()->lineLength(line - 1);
00650 range.setRange(range.start(), Cursor(line - 1, maxColWithNewline));
00651 FAST_DEBUG("Search range fixed to " << range);
00652 } else {
00653 FAST_DEBUG("Already at first line");
00654 range = Range::invalid();
00655 }
00656 }
00657 }
00658 FAST_DEBUG("Single-line workaround checking AFTER" << range);
00659 }
00660
00661
00662
00663 void KateSearchBar::onReturnPressed() {
00664 const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
00665 const bool shiftDown = (modifiers & Qt::ShiftModifier) != 0;
00666 const bool controlDown = (modifiers & Qt::ControlModifier) != 0;
00667
00668 if (shiftDown) {
00669
00670 if (m_powerUi != NULL) {
00671 onPowerFindPrev();
00672 } else {
00673 onIncPrev();
00674 }
00675 } else {
00676
00677 if (m_powerUi != NULL) {
00678 onPowerFindNext();
00679 } else {
00680 onIncNext();
00681 }
00682 }
00683
00684 if (controlDown) {
00685 emit hideMe();
00686 }
00687 }
00688
00689
00690
00691 bool KateSearchBar::onStep(bool replace, bool forwards) {
00692
00693 const QString pattern = (m_powerUi != NULL)
00694 ? m_powerUi->pattern->currentText()
00695 : m_incUi->pattern->displayText();
00696 if (pattern.isEmpty()) {
00697 return false;
00698 }
00699
00700
00701 Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
00702 const bool matchCase = (m_powerUi != NULL)
00703 ? isChecked(m_powerUi->matchCase)
00704 : isChecked(m_incMenuMatchCase);
00705 if (!matchCase) {
00706 enabledOptions |= Search::CaseInsensitive;
00707 }
00708
00709 if (!forwards) {
00710 enabledOptions |= Search::Backwards;
00711 }
00712
00713 bool multiLinePattern = false;
00714 bool regexMode = false;
00715 if (m_powerUi != NULL) {
00716 switch (m_powerUi->searchMode->currentIndex()) {
00717 case MODE_WHOLE_WORDS:
00718 enabledOptions |= Search::WholeWords;
00719 break;
00720
00721 case MODE_ESCAPE_SEQUENCES:
00722 enabledOptions |= Search::EscapeSequences;
00723 break;
00724
00725 case MODE_REGEX:
00726 {
00727
00728 QString patternCopy(pattern);
00729 KateDocument::repairPattern(patternCopy, multiLinePattern);
00730 regexMode = true;
00731 }
00732 enabledOptions |= Search::Regex;
00733 break;
00734
00735 case MODE_PLAIN_TEXT:
00736 default:
00737 break;
00738
00739 }
00740 }
00741
00742
00743
00744 Range inputRange;
00745 Range selection;
00746 const bool selected = view()->selection();
00747 const bool selectionOnly = (m_powerUi != NULL)
00748 ? isChecked(m_powerMenuSelectionOnly)
00749 : false;
00750 if (selected) {
00751 selection = view()->selectionRange();
00752 if (selectionOnly) {
00753
00754 inputRange = selection;
00755 } else {
00756
00757 if (forwards) {
00758 inputRange.setRange(selection.start(), view()->doc()->documentEnd());
00759 } else {
00760 inputRange.setRange(Cursor(0, 0), selection.end());
00761 }
00762 }
00763 } else {
00764
00765 const bool fromCursor = (m_powerUi != NULL)
00766 ? isChecked(m_powerMenuFromCursor)
00767 : isChecked(m_incMenuFromCursor);
00768 if (fromCursor) {
00769 const Cursor cursorPos = view()->cursorPosition();
00770 if (forwards) {
00771
00772
00773
00774 if (!view()->viInputMode()) {
00775 inputRange.setRange(cursorPos, view()->doc()->documentEnd());
00776 } else {
00777 inputRange.setRange(Cursor(cursorPos.line(), cursorPos.column()+1), view()->doc()->documentEnd());
00778 }
00779 } else {
00780 inputRange.setRange(Cursor(0, 0), cursorPos);
00781 }
00782 } else {
00783 inputRange = view()->doc()->documentRange();
00784 }
00785 }
00786 FAST_DEBUG("Search range is" << inputRange);
00787
00788
00789 if (regexMode && !multiLinePattern) {
00790 fixForSingleLine(inputRange, forwards);
00791 }
00792
00793
00794
00795 const QVector<Range> resultRanges = view()->doc()->searchText(inputRange, pattern, enabledOptions);
00796 const Range & match = resultRanges[0];
00797 bool wrap = false;
00798 bool found = false;
00799 SmartRange * afterReplace = NULL;
00800 if (match.isValid()) {
00801
00802 if (selected && (match == selection) && (!selectionOnly || replace)) {
00803
00804 if (replace) {
00805
00806 const QString replacement = m_powerUi->replacement->currentText();
00807 afterReplace = view()->doc()->newSmartRange(match);
00808 afterReplace->setInsertBehavior(SmartRange::ExpandRight | SmartRange::ExpandLeft);
00809 replaceMatch(resultRanges, replacement);
00810
00811
00812 if (forwards) {
00813 inputRange.setRange(afterReplace->end(), inputRange.end());
00814 } else {
00815 inputRange.setRange(inputRange.start(), afterReplace->start());
00816 }
00817 } else {
00818
00819 if (forwards) {
00820 inputRange.setRange(selection.end(), inputRange.end());
00821 } else {
00822 inputRange.setRange(inputRange.start(), selection.start());
00823 }
00824 }
00825
00826
00827 fixForSingleLine(inputRange, forwards);
00828
00829 const QVector<Range> resultRanges2 = view()->doc()->searchText(inputRange, pattern, enabledOptions);
00830 const Range & match2 = resultRanges2[0];
00831 if (match2.isValid()) {
00832 selectRange(view(), match2);
00833 found = true;
00834 const bool NOT_WRAPPED = false;
00835 indicateMatch(NOT_WRAPPED);
00836 } else {
00837
00838 wrap = true;
00839 }
00840 } else {
00841 selectRange(view(), match);
00842 found = true;
00843 const bool NOT_WRAPPED = false;
00844 indicateMatch(NOT_WRAPPED);
00845 }
00846 } else if (!selected || !selectionOnly) {
00847
00848 wrap = true;
00849 }
00850
00851
00852 if (wrap) {
00853 inputRange = view()->doc()->documentRange();
00854 const QVector<Range> resultRanges3 = view()->doc()->searchText(inputRange, pattern, enabledOptions);
00855 const Range & match3 = resultRanges3[0];
00856 if (match3.isValid()) {
00857
00858 if (selected && !selectionOnly && (match3 == selection)) {
00859
00860 } else {
00861 selectRange(view(), match3);
00862 found = true;
00863 }
00864 const bool WRAPPED = true;
00865 indicateMatch(WRAPPED);
00866 } else {
00867 indicateMismatch();
00868 }
00869 }
00870
00871
00872 const bool highlightAll = (m_powerUi != NULL)
00873 ? isChecked(m_powerMenuHighlightAll)
00874 : isChecked(m_incMenuHighlightAll);
00875 if ((found && highlightAll) || (afterReplace != NULL)) {
00876
00877 if (found && highlightAll) {
00878 highlightAllMatches(pattern, enabledOptions);
00879 }
00880
00881
00882 if (found && (afterReplace != NULL)) {
00883
00884 if (!(found && highlightAll)) {
00885 resetHighlights();
00886 }
00887
00888 highlightReplacement(*afterReplace);
00889 }
00890
00891 }
00892
00893 delete afterReplace;
00894
00895 return true;
00896 }
00897
00898
00899
00900 void KateSearchBar::onPowerPatternChanged(const QString & pattern) {
00901 givePatternFeedback(pattern);
00902 indicateNothing();
00903 }
00904
00905
00906
00907 void KateSearchBar::givePatternFeedback(const QString & pattern) {
00908 bool enabled = true;
00909
00910 if (pattern.isEmpty()) {
00911 enabled = false;
00912 } else {
00913 switch (m_powerUi->searchMode->currentIndex()) {
00914 case MODE_WHOLE_WORDS:
00915 if (pattern.trimmed() != pattern) {
00916 enabled = false;
00917 }
00918 break;
00919
00920 case MODE_REGEX:
00921 m_patternTester.setPattern(pattern);
00922 enabled = m_patternTester.isValid();
00923 break;
00924
00925 case MODE_ESCAPE_SEQUENCES:
00926 case MODE_PLAIN_TEXT:
00927 default:
00928 ;
00929
00930 }
00931 }
00932
00933
00934 m_powerUi->findNext->setDisabled(!enabled);
00935 m_powerUi->findPrev->setDisabled(!enabled);
00936 m_powerUi->replaceNext->setDisabled(!enabled);
00937 m_powerUi->replaceAll->setDisabled(!enabled);
00938 }
00939
00940
00941
00942 void KateSearchBar::addCurrentTextToHistory(QComboBox * combo) {
00943 const QString text = combo->currentText();
00944 const int index = combo->findText(text);
00945 if (index != -1) {
00946 combo->removeItem(index);
00947 }
00948 combo->insertItem(0, text);
00949 combo->setCurrentIndex(0);
00950 }
00951
00952
00953
00954 void KateSearchBar::backupConfig(bool ofPower) {
00955 if (ofPower) {
00956 m_powerMatchCase = isChecked(m_powerUi->matchCase);
00957 m_powerFromCursor = isChecked(m_powerMenuFromCursor);
00958 m_powerHighlightAll = isChecked(m_powerMenuHighlightAll);
00959 m_powerMode = m_powerUi->searchMode->currentIndex();
00960 } else {
00961 m_incHighlightAll = isChecked(m_incMenuHighlightAll);
00962 m_incFromCursor = isChecked(m_incMenuFromCursor);
00963 m_incMatchCase = isChecked(m_incMenuMatchCase);
00964 }
00965 }
00966
00967
00968
00969 void KateSearchBar::sendConfig() {
00970 KateViewConfig * const globalConfig = KateGlobal::self()->viewConfig();
00971 const long pastFlags = globalConfig->searchFlags();
00972 long futureFlags = pastFlags;
00973
00974 if (m_powerUi != NULL) {
00975 const bool OF_POWER = true;
00976 backupConfig(OF_POWER);
00977
00978
00979 const long incFlagsOnly = pastFlags
00980 & (KateViewConfig::IncHighlightAll
00981 | KateViewConfig::IncFromCursor
00982 | KateViewConfig::IncMatchCase);
00983
00984 futureFlags = incFlagsOnly
00985 | (m_powerMatchCase ? KateViewConfig::PowerMatchCase : 0)
00986 | (m_powerFromCursor ? KateViewConfig::PowerFromCursor : 0)
00987 | (m_powerHighlightAll ? KateViewConfig::PowerHighlightAll : 0)
00988 | ((m_powerMode == MODE_REGEX)
00989 ? KateViewConfig::PowerModeRegularExpression
00990 : ((m_powerMode == MODE_ESCAPE_SEQUENCES)
00991 ? KateViewConfig::PowerModeEscapeSequences
00992 : ((m_powerMode == MODE_WHOLE_WORDS)
00993 ? KateViewConfig::PowerModeWholeWords
00994 : KateViewConfig::PowerModePlainText)));
00995
00996 } else if (m_incUi != NULL) {
00997 const bool OF_INCREMENTAL = false;
00998 backupConfig(OF_INCREMENTAL);
00999
01000
01001 const long powerFlagsOnly = pastFlags
01002 & (KateViewConfig::PowerMatchCase
01003 | KateViewConfig::PowerFromCursor
01004 | KateViewConfig::PowerHighlightAll
01005 | KateViewConfig::PowerModeRegularExpression
01006 | KateViewConfig::PowerModeEscapeSequences
01007 | KateViewConfig::PowerModeWholeWords
01008 | KateViewConfig::PowerModePlainText);
01009
01010 futureFlags = powerFlagsOnly
01011 | (m_incHighlightAll ? KateViewConfig::IncHighlightAll : 0)
01012 | (m_incFromCursor ? KateViewConfig::IncFromCursor : 0)
01013 | (m_incMatchCase ? KateViewConfig::IncMatchCase : 0);
01014 }
01015
01016
01017 globalConfig->setSearchFlags(futureFlags);
01018 }
01019
01020
01021
01022 void KateSearchBar::onPowerFindNext() {
01023 const bool FIND = false;
01024 if (onStep(FIND)) {
01025
01026 addCurrentTextToHistory(m_powerUi->pattern);
01027 }
01028 }
01029
01030
01031
01032 void KateSearchBar::onPowerFindPrev() {
01033 const bool FIND = false;
01034 const bool BACKWARDS = false;
01035 if (onStep(FIND, BACKWARDS)) {
01036
01037 addCurrentTextToHistory(m_powerUi->pattern);
01038 }
01039 }
01040
01041
01042
01043 void KateSearchBar::onPowerReplaceNext() {
01044 const bool REPLACE = true;
01045 if (onStep(REPLACE)) {
01046
01047 addCurrentTextToHistory(m_powerUi->pattern);
01048
01049
01050 addCurrentTextToHistory(m_powerUi->replacement);
01051 }
01052 }
01053
01054
01055
01056
01057
01058 void KateSearchBar::onForAll(const QString & pattern, Range inputRange,
01059 Search::SearchOptions enabledOptions,
01060 const QString * replacement) {
01061 bool multiLinePattern = false;
01062 const bool regexMode = enabledOptions.testFlag(Search::Regex);
01063 if (regexMode) {
01064
01065 QString patternCopy(pattern);
01066 KateDocument::repairPattern(patternCopy, multiLinePattern);
01067 }
01068
01069
01070 if (enabledOptions.testFlag(Search::Backwards)) {
01071 enabledOptions &= ~Search::SearchOptions(Search::Backwards);
01072 }
01073
01074
01075 resetHighlights();
01076
01077 SmartRange * const workingRange = view()->doc()->newSmartRange(inputRange);
01078 int matchCounter = 0;
01079 for (;;) {
01080 const QVector<Range> resultRanges = view()->doc()->searchText(*workingRange, pattern, enabledOptions);
01081 Range match = resultRanges[0];
01082 if (!match.isValid()) {
01083 break;
01084 }
01085 bool const originalMatchEmpty = match.isEmpty();
01086
01087
01088 if (replacement != NULL) {
01089 if (matchCounter == 0) {
01090 view()->doc()->editBegin();
01091 }
01092
01093
01094 SmartRange * const afterReplace = view()->doc()->newSmartRange(match);
01095 afterReplace->setInsertBehavior(SmartRange::ExpandRight | SmartRange::ExpandLeft);
01096
01097
01098 replaceMatch(resultRanges, *replacement, ++matchCounter);
01099
01100
01101 highlightReplacement(*afterReplace);
01102 match = *afterReplace;
01103 delete afterReplace;
01104 } else {
01105
01106 highlightMatch(match);
01107 matchCounter++;
01108 }
01109
01110
01111 SmartCursor & workingStart = workingRange->smartStart();
01112 workingStart.setPosition(match.end());
01113 if (originalMatchEmpty) {
01114
01115
01116 workingStart.advance(1);
01117 } else if (regexMode && !multiLinePattern && workingStart.atEndOfLine()) {
01118
01119
01120 workingStart.advance(1);
01121 }
01122
01123
01124 if (!workingRange->isValid() || workingStart.atEndOfDocument()) {
01125 break;
01126 }
01127 }
01128
01129
01130 if (matchCounter > 0) {
01131 if (replacement != NULL) {
01132 view()->doc()->editEnd();
01133 }
01134 }
01135
01136 delete workingRange;
01137 }
01138
01139
01140
01141 void KateSearchBar::onPowerReplaceAll() {
01142
01143 const QString pattern = m_powerUi->pattern->currentText();
01144 const QString replacement = m_powerUi->replacement->currentText();
01145
01146
01147
01148 Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
01149 const bool matchCase = isChecked(m_powerUi->matchCase);
01150 if (!matchCase) {
01151 enabledOptions |= Search::CaseInsensitive;
01152 }
01153
01154 if (m_powerUi != NULL) {
01155 switch (m_powerUi->searchMode->currentIndex()) {
01156 case MODE_WHOLE_WORDS:
01157 enabledOptions |= Search::WholeWords;
01158 break;
01159
01160 case MODE_ESCAPE_SEQUENCES:
01161 enabledOptions |= Search::EscapeSequences;
01162 break;
01163
01164 case MODE_REGEX:
01165 enabledOptions |= Search::Regex;
01166 break;
01167
01168 case MODE_PLAIN_TEXT:
01169 default:
01170 break;
01171
01172 }
01173 }
01174
01175
01176
01177 Range selection;
01178 const bool selected = view()->selection();
01179 const bool selectionOnly = isChecked(m_powerMenuSelectionOnly);
01180 Range inputRange = (selected && selectionOnly)
01181 ? view()->selectionRange()
01182 : view()->doc()->documentRange();
01183
01184
01185
01186 onForAll(pattern, inputRange, enabledOptions, &replacement);
01187
01188
01189
01190 addCurrentTextToHistory(m_powerUi->pattern);
01191
01192
01193 addCurrentTextToHistory(m_powerUi->replacement);
01194 }
01195
01196
01197
01198 struct ParInfo {
01199 int openIndex;
01200 bool capturing;
01201 int captureNumber;
01202 };
01203
01204
01205
01206 QVector<QString> KateSearchBar::getCapturePatterns(const QString & pattern) {
01207 QVector<QString> capturePatterns;
01208 capturePatterns.reserve(9);
01209 QStack<ParInfo> parInfos;
01210
01211 const int inputLen = pattern.length();
01212 int input = 0;
01213 bool insideClass = false;
01214 int captureCount = 0;
01215
01216 while (input < inputLen) {
01217 if (insideClass) {
01218
01219 if (pattern[input].unicode() == L']') {
01220 insideClass = false;
01221 }
01222 input++;
01223 }
01224 else
01225 {
01226 switch (pattern[input].unicode())
01227 {
01228 case L'\\':
01229
01230 input += 2;
01231 break;
01232
01233 case L'(':
01234 ParInfo curInfo;
01235 curInfo.openIndex = input;
01236 curInfo.capturing = (input + 1 >= inputLen) || (pattern[input + 1].unicode() != '?');
01237 if (curInfo.capturing) {
01238 captureCount++;
01239 }
01240 curInfo.captureNumber = captureCount;
01241 parInfos.push(curInfo);
01242
01243 input++;
01244 break;
01245
01246 case L')':
01247 if (!parInfos.empty()) {
01248 ParInfo & top = parInfos.top();
01249 if (top.capturing && (top.captureNumber <= 9)) {
01250 const int start = top.openIndex + 1;
01251 const int len = input - start;
01252 if (capturePatterns.size() < top.captureNumber) {
01253 capturePatterns.resize(top.captureNumber);
01254 }
01255 capturePatterns[top.captureNumber - 1] = pattern.mid(start, len);
01256 }
01257 parInfos.pop();
01258 }
01259
01260 input++;
01261 break;
01262
01263 case L'[':
01264 input++;
01265 insideClass = true;
01266 break;
01267
01268 default:
01269 input++;
01270 break;
01271
01272 }
01273 }
01274 }
01275
01276 return capturePatterns;
01277 }
01278
01279
01280
01281 void KateSearchBar::showExtendedContextMenu(bool forPattern) {
01282
01283 QMenu * const contextMenu = m_powerUi->pattern->lineEdit()->createStandardContextMenu();
01284 if (contextMenu == NULL) {
01285 return;
01286 }
01287
01288 bool extendMenu = false;
01289 bool regexMode = false;
01290 switch (m_powerUi->searchMode->currentIndex()) {
01291 case MODE_REGEX:
01292 regexMode = true;
01293
01294
01295 case MODE_ESCAPE_SEQUENCES:
01296 extendMenu = true;
01297 break;
01298
01299 default:
01300 break;
01301 }
01302
01303 AddMenuManager addMenuManager(contextMenu, 35);
01304 if (!extendMenu) {
01305 addMenuManager.enableMenu(extendMenu);
01306 } else {
01307
01308 if (forPattern) {
01309 if (regexMode) {
01310 addMenuManager.addEntry("^", "", i18n("Beginning of line"));
01311 addMenuManager.addEntry("$", "", i18n("End of line"));
01312 addMenuManager.addSeparator();
01313 addMenuManager.addEntry(".", "", i18n("Any single character (excluding line breaks)"));
01314 addMenuManager.addSeparator();
01315 addMenuManager.addEntry("+", "", i18n("One or more occurrences"));
01316 addMenuManager.addEntry("*", "", i18n("Zero or more occurrences"));
01317 addMenuManager.addEntry("?", "", i18n("Zero or one occurrences"));
01318 addMenuManager.addEntry("{a", ",b}", i18n("<a> through <b> occurrences"), "{", ",}");
01319 addMenuManager.addSeparator();
01320 addMenuManager.addEntry("(", ")", i18n("Group, capturing"));
01321 addMenuManager.addEntry("|", "", i18n("Or"));
01322 addMenuManager.addEntry("[", "]", i18n("Set of characters"));
01323 addMenuManager.addEntry("[^", "]", i18n("Negative set of characters"));
01324 addMenuManager.addSeparator();
01325 }
01326 } else {
01327 addMenuManager.addEntry("\\0", "", i18n("Whole match reference"));
01328 addMenuManager.addSeparator();
01329 if (regexMode) {
01330 const QString pattern = m_powerUi->pattern->currentText();
01331 const QVector<QString> capturePatterns = getCapturePatterns(pattern);
01332
01333 const int captureCount = capturePatterns.count();
01334 for (int i = 1; i <= 9; i++) {
01335 const QString number = QString::number(i);
01336 const QString & captureDetails = (i <= captureCount)
01337 ? (QString(" = (") + capturePatterns[i - 1].left(30)) + QString(")")
01338 : QString();
01339 addMenuManager.addEntry("\\" + number, "",
01340 i18n("Reference") + ' ' + number + captureDetails);
01341 }
01342
01343 addMenuManager.addSeparator();
01344 }
01345 }
01346
01347 addMenuManager.addEntry("\\n", "", i18n("Line break"));
01348 addMenuManager.addEntry("\\t", "", i18n("Tab"));
01349
01350 if (forPattern && regexMode) {
01351 addMenuManager.addEntry("\\b", "", i18n("Word boundary"));
01352 addMenuManager.addEntry("\\B", "", i18n("Not word boundary"));
01353 addMenuManager.addEntry("\\d", "", i18n("Digit"));
01354 addMenuManager.addEntry("\\D", "", i18n("Non-digit"));
01355 addMenuManager.addEntry("\\s", "", i18n("Whitespace (excluding line breaks)"));
01356 addMenuManager.addEntry("\\S", "", i18n("Non-whitespace (excluding line breaks)"));
01357 addMenuManager.addEntry("\\w", "", i18n("Word character (alphanumerics plus '_')"));
01358 addMenuManager.addEntry("\\W", "", i18n("Non-word character"));
01359 }
01360
01361 addMenuManager.addEntry("\\0???", "", i18n("Octal character 000 to 377 (2^8-1)"), "\\0");
01362 addMenuManager.addEntry("\\x????", "", i18n("Hex character 0000 to FFFF (2^16-1)"), "\\x");
01363 addMenuManager.addEntry("\\\\", "", i18n("Backslash"));
01364
01365 if (forPattern && regexMode) {
01366 addMenuManager.addSeparator();
01367 addMenuManager.addEntry("(?:E", ")", i18n("Group, non-capturing"), "(?:");
01368 addMenuManager.addEntry("(?=E", ")", i18n("Lookahead"), "(?=");
01369 addMenuManager.addEntry("(?!E", ")", i18n("Negative lookahead"), "(?!");
01370 }
01371
01372 if (!forPattern) {
01373 addMenuManager.addSeparator();
01374 addMenuManager.addEntry("\\L", "", i18n("Begin lowercase conversion"));
01375 addMenuManager.addEntry("\\U", "", i18n("Begin uppercase conversion"));
01376 addMenuManager.addEntry("\\E", "", i18n("End case conversion"));
01377 addMenuManager.addEntry("\\#[#..]", "", i18n("Replacement counter (for Replace All)"), "\\#");
01378 }
01379 }
01380
01381
01382 QAction * const result = contextMenu->exec(QCursor::pos());
01383 if (result != NULL) {
01384 QLineEdit * const lineEdit = forPattern
01385 ? m_powerUi->pattern->lineEdit()
01386 : m_powerUi->replacement->lineEdit();
01387 Q_ASSERT(lineEdit != NULL);
01388 addMenuManager.handle(result, lineEdit);
01389 }
01390 }
01391
01392
01393
01394 void KateSearchBar::onPowerMatchCaseToggle(bool invokedByUserAction) {
01395 if (invokedByUserAction) {
01396 sendConfig();
01397 indicateNothing();
01398 }
01399 }
01400
01401
01402
01403 void KateSearchBar::onPowerHighlightAllToggle(bool checked, bool invokedByUserAction) {
01404 if (invokedByUserAction) {
01405 sendConfig();
01406
01407 if (checked) {
01408 const QString pattern = m_powerUi->pattern->currentText();
01409 if (!pattern.isEmpty()) {
01410
01411 Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
01412 const bool matchCase = isChecked(m_powerUi->matchCase);
01413 if (!matchCase) {
01414 enabledOptions |= Search::CaseInsensitive;
01415 }
01416
01417 switch (m_powerUi->searchMode->currentIndex()) {
01418 case MODE_WHOLE_WORDS:
01419 enabledOptions |= Search::WholeWords;
01420 break;
01421
01422 case MODE_ESCAPE_SEQUENCES:
01423 enabledOptions |= Search::EscapeSequences;
01424 break;
01425
01426 case MODE_REGEX:
01427 enabledOptions |= Search::Regex;
01428 break;
01429
01430 case MODE_PLAIN_TEXT:
01431 default:
01432 break;
01433
01434 }
01435
01436
01437 resetHighlights();
01438 highlightAllMatches(pattern, enabledOptions);
01439 }
01440 } else {
01441 resetHighlights();
01442 }
01443 }
01444 }
01445
01446
01447
01448 void KateSearchBar::onPowerFromCursorToggle(bool invokedByUserAction) {
01449 if (invokedByUserAction) {
01450 sendConfig();
01451 }
01452 }
01453
01454
01455
01456 void KateSearchBar::onPowerModeChangedPlainText() {
01457 m_powerUi->searchMode->setCurrentIndex(MODE_PLAIN_TEXT);
01458 onPowerModeChanged();
01459 }
01460
01461
01462
01463 void KateSearchBar::onPowerModeChangedWholeWords() {
01464 m_powerUi->searchMode->setCurrentIndex(MODE_WHOLE_WORDS);
01465 onPowerModeChanged();
01466 }
01467
01468
01469
01470 void KateSearchBar::onPowerModeChangedEscapeSequences() {
01471 m_powerUi->searchMode->setCurrentIndex(MODE_ESCAPE_SEQUENCES);
01472 onPowerModeChanged();
01473 }
01474
01475
01476
01477 void KateSearchBar::onPowerModeChangedRegularExpression() {
01478 m_powerUi->searchMode->setCurrentIndex(MODE_REGEX);
01479 onPowerModeChanged();
01480 }
01481
01482
01483
01484 void KateSearchBar::onPowerModeChanged() {
01485 if (m_powerUi->searchMode->currentIndex() == MODE_REGEX) {
01486 setChecked(m_powerUi->matchCase, true);
01487 }
01488
01489 sendConfig();
01490 indicateNothing();
01491 }
01492
01493
01494
01495 void KateSearchBar::onPowerModeChanged(int , bool invokedByUserAction) {
01496 if (invokedByUserAction) {
01497 onPowerModeChanged();
01498 }
01499
01500 givePatternFeedback(m_powerUi->pattern->currentText());
01501 }
01502
01503
01504
01505 void KateSearchBar::nextMatchForSelection(KateView * view, bool forwards) {
01506 const bool selected = view->selection();
01507 if (selected) {
01508 const QString pattern = view->selectionText();
01509
01510
01511 Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
01512 if (!forwards) {
01513 enabledOptions |= Search::Backwards;
01514 }
01515
01516
01517 const Range selRange = view->selectionRange();
01518 Range inputRange;
01519 if (forwards) {
01520 inputRange.setRange(selRange.end(), view->doc()->documentEnd());
01521 } else {
01522 inputRange.setRange(Cursor(0, 0), selRange.start());
01523 }
01524
01525
01526 const QVector<Range> resultRanges = view->doc()->searchText(inputRange, pattern, enabledOptions);
01527 const Range & match = resultRanges[0];
01528
01529 if (match.isValid()) {
01530 selectRange(view, match);
01531 } else {
01532
01533 if (forwards) {
01534 inputRange.setRange(Cursor(0, 0), selRange.start());
01535 } else {
01536 inputRange.setRange(selRange.end(), view->doc()->documentEnd());
01537 }
01538 const QVector<Range> resultRanges2 = view->doc()->searchText(inputRange, pattern, enabledOptions);
01539 const Range & match2 = resultRanges2[0];
01540 if (match2.isValid()) {
01541 selectRange(view, match2);
01542 }
01543 }
01544 } else {
01545
01546 const Cursor cursorPos = view->cursorPosition();
01547 view->selectWord(cursorPos);
01548 }
01549 }
01550
01551
01552
01553 void KateSearchBar::onMutatePower() {
01554 QString initialPattern;
01555 bool selectionOnly = false;
01556
01557
01558 const bool selected = view()->selection();
01559 if (selected) {
01560 const Range & selection = view()->selectionRange();
01561 if (selection.onSingleLine()) {
01562
01563 initialPattern = view()->selectionText();
01564 } else {
01565
01566 selectionOnly = true;
01567 }
01568 }
01569
01570
01571 if (initialPattern.isNull()) {
01572
01573 const bool fromReplace = (m_powerUi != NULL) && (m_widget->isVisible());
01574 if (fromReplace) {
01575 QLineEdit * const patternLineEdit = m_powerUi->pattern->lineEdit();
01576 Q_ASSERT(patternLineEdit != NULL);
01577 patternLineEdit->selectAll();
01578 m_powerUi->pattern->setFocus(Qt::MouseFocusReason);
01579 return;
01580 }
01581
01582
01583 const bool fromIncremental = (m_incUi != NULL) && (m_widget->isVisible());
01584 if (fromIncremental) {
01585 initialPattern = m_incUi->pattern->displayText();
01586 }
01587 }
01588
01589
01590 const bool create = (m_powerUi == NULL);
01591 if (create) {
01592
01593 if (m_incUi != NULL) {
01594
01595 const bool OF_INCREMENTAL = false;
01596 backupConfig(OF_INCREMENTAL);
01597
01598
01599 delete m_incUi;
01600 delete m_incMenu;
01601 m_incUi = NULL;
01602 m_incMenu = NULL;
01603 m_incMenuMatchCase = NULL;
01604 m_incMenuFromCursor = NULL;
01605 m_incMenuHighlightAll = NULL;
01606 m_layout->removeWidget(m_widget);
01607 m_widget->deleteLater();
01608 }
01609
01610
01611 m_widget = new QWidget(this);
01612 m_powerUi = new Ui::PowerSearchBar;
01613 m_powerUi->setupUi(m_widget);
01614 m_layout->addWidget(m_widget);
01615
01616
01617 const int MAX_HISTORY_SIZE = 100;
01618 QStringListModel * const patternHistoryModel = KateHistoryModel::getPatternHistoryModel();
01619 QStringListModel * const replacementHistoryModel = KateHistoryModel::getReplacementHistoryModel();
01620 m_powerUi->pattern->setMaxCount(MAX_HISTORY_SIZE);
01621 m_powerUi->pattern->setModel(patternHistoryModel);
01622 m_powerUi->replacement->setMaxCount(MAX_HISTORY_SIZE);
01623 m_powerUi->replacement->setModel(replacementHistoryModel);
01624
01625
01626 m_powerMenu = new QMenu();
01627 m_powerUi->options->setMenu(m_powerMenu);
01628 m_powerMenuFromCursor = m_powerMenu->addAction(i18n("From &cursor"));
01629 m_powerMenuFromCursor->setCheckable(true);
01630 m_powerMenuHighlightAll = m_powerMenu->addAction(i18n("Hi&ghlight all"));
01631 m_powerMenuHighlightAll->setCheckable(true);
01632 m_powerMenuSelectionOnly = m_powerMenu->addAction(i18n("Selection &only"));
01633 m_powerMenuSelectionOnly->setCheckable(true);
01634
01635
01636 connect(new QShortcut(QKeySequence(Qt::ALT + Qt::Key_1), m_widget,
01637 0, 0, Qt::WidgetWithChildrenShortcut), SIGNAL(activated()),
01638 this, SLOT(onPowerModeChangedPlainText()));
01639 connect(new QShortcut(QKeySequence(Qt::ALT + Qt::Key_2), m_widget,
01640 0, 0, Qt::WidgetWithChildrenShortcut), SIGNAL(activated()),
01641 this, SLOT(onPowerModeChangedWholeWords()));
01642 connect(new QShortcut(QKeySequence(Qt::ALT + Qt::Key_3), m_widget,
01643 0, 0, Qt::WidgetWithChildrenShortcut), SIGNAL(activated()),
01644 this, SLOT(onPowerModeChangedEscapeSequences()));
01645 connect(new QShortcut(QKeySequence(Qt::ALT + Qt::Key_4), m_widget,
01646 0, 0, Qt::WidgetWithChildrenShortcut), SIGNAL(activated()),
01647 this, SLOT(onPowerModeChangedRegularExpression()));
01648
01649
01650 m_powerUi->mutate->setIcon(KIcon("arrow-down-double"));
01651 m_powerUi->findNext->setIcon(KIcon("go-down"));
01652 m_powerUi->findPrev->setIcon(KIcon("go-up"));
01653
01654
01655 centralWidget()->setFocusProxy(m_powerUi->pattern);
01656
01657
01658 QLineEdit * const patternLineEdit = m_powerUi->pattern->lineEdit();
01659 Q_ASSERT(patternLineEdit != NULL);
01660 patternLineEdit->completer()->setCaseSensitivity(Qt::CaseSensitive);
01661
01662 QLineEdit * const replacementLineEdit = m_powerUi->pattern->lineEdit();
01663 Q_ASSERT(replacementLineEdit != NULL);
01664 replacementLineEdit->completer()->setCaseSensitivity(Qt::CaseSensitive);
01665 }
01666
01667 setChecked(m_powerMenuSelectionOnly, selectionOnly);
01668
01669
01670 if (create) {
01671 setChecked(m_powerUi->matchCase, m_powerMatchCase);
01672 setChecked(m_powerMenuHighlightAll, m_powerHighlightAll);
01673 setChecked(m_powerMenuFromCursor, m_powerFromCursor);
01674 m_powerUi->searchMode->setCurrentIndex(m_powerMode);
01675 }
01676
01677
01678 QLineEdit * const patternLineEdit = m_powerUi->pattern->lineEdit();
01679 Q_ASSERT(patternLineEdit != NULL);
01680 patternLineEdit->setText(initialPattern);
01681 patternLineEdit->selectAll();
01682
01683
01684 QLineEdit * const replacementLineEdit = m_powerUi->replacement->lineEdit();
01685 Q_ASSERT(replacementLineEdit != NULL);
01686 replacementLineEdit->setText("");
01687
01688
01689 onPowerPatternChanged(initialPattern);
01690 const bool NOT_INVOKED_BY_USER_ACTION = false;
01691 onPowerModeChanged(m_powerUi->searchMode->currentIndex(), NOT_INVOKED_BY_USER_ACTION);
01692
01693 if (create) {
01694
01695 connect(m_powerUi->mutate, SIGNAL(clicked()), this, SLOT(onMutateIncremental()));
01696 connect(patternLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(onPowerPatternChanged(const QString &)));
01697 connect(m_powerUi->findNext, SIGNAL(clicked()), this, SLOT(onPowerFindNext()));
01698 connect(m_powerUi->findPrev, SIGNAL(clicked()), this, SLOT(onPowerFindPrev()));
01699 connect(m_powerUi->replaceNext, SIGNAL(clicked()), this, SLOT(onPowerReplaceNext()));
01700 connect(m_powerUi->replaceAll, SIGNAL(clicked()), this, SLOT(onPowerReplaceAll()));
01701 connect(m_powerUi->searchMode, SIGNAL(currentIndexChanged(int)), this, SLOT(onPowerModeChanged(int)));
01702 connect(m_powerUi->matchCase, SIGNAL(stateChanged(int)), this, SLOT(onPowerMatchCaseToggle()));
01703 connect(m_powerMenuHighlightAll, SIGNAL(toggled(bool)), this, SLOT(onPowerHighlightAllToggle(bool)));
01704 connect(m_powerMenuFromCursor, SIGNAL(changed()), this, SLOT(onPowerFromCursorToggle()));
01705
01706
01707
01708 connect(m_powerUi->options, SIGNAL(clicked()), m_powerUi->options, SLOT(showMenu()));
01709
01710
01711 connect(patternLineEdit, SIGNAL(returnPressed()), this, SLOT(onReturnPressed()));
01712 connect(replacementLineEdit, SIGNAL(returnPressed()), this, SLOT(onPowerReplaceNext()));
01713
01714
01715 patternLineEdit->setContextMenuPolicy(Qt::CustomContextMenu);
01716 connect(patternLineEdit, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onPowerPatternContextMenuRequest()));
01717 replacementLineEdit->setContextMenuPolicy(Qt::CustomContextMenu);
01718 connect(replacementLineEdit, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onPowerReplacmentContextMenuRequest()));
01719 }
01720
01721
01722 if (m_widget->isVisible()) {
01723 m_powerUi->pattern->setFocus(Qt::MouseFocusReason);
01724 }
01725 }
01726
01727
01728
01729 void KateSearchBar::onMutateIncremental() {
01730 QString initialPattern;
01731
01732
01733 const bool selected = view()->selection();
01734 if (selected) {
01735 const Range & selection = view()->selectionRange();
01736 if (selection.onSingleLine()) {
01737
01738 initialPattern = view()->selectionText();
01739 }
01740 }
01741
01742
01743 if (initialPattern.isNull()) {
01744
01745 const bool fromIncremental = (m_incUi != NULL) && (m_widget->isVisible());
01746 if (fromIncremental) {
01747 m_incUi->pattern->selectAll();
01748 m_incUi->pattern->setFocus(Qt::MouseFocusReason);
01749 return;
01750 }
01751
01752
01753 const bool fromReplace = (m_powerUi != NULL) && (m_widget->isVisible());
01754 if (fromReplace) {
01755 initialPattern = m_powerUi->pattern->currentText();
01756 }
01757 }
01758
01759
01760 const bool create = (m_incUi == NULL);
01761 if (create) {
01762
01763 if (m_powerUi != NULL) {
01764
01765 const bool OF_POWER = true;
01766 backupConfig(OF_POWER);
01767
01768
01769 delete m_powerUi;
01770 m_powerUi = NULL;
01771 m_layout->removeWidget(m_widget);
01772 m_widget->deleteLater();
01773 }
01774
01775
01776 m_widget = new QWidget(this);
01777 m_incUi = new Ui::IncrementalSearchBar;
01778 m_incUi->setupUi(m_widget);
01779 m_layout->addWidget(m_widget);
01780
01781 new QShortcut(KStandardShortcut::paste().primary(), m_incUi->pattern, SLOT(paste()), 0, Qt::WidgetWithChildrenShortcut);
01782 if (!KStandardShortcut::paste().alternate().isEmpty())
01783 new QShortcut(KStandardShortcut::paste().alternate(), m_incUi->pattern, SLOT(paste()), 0, Qt::WidgetWithChildrenShortcut);
01784
01785
01786
01787 m_incMenu = new QMenu();
01788 m_incUi->options->setMenu(m_incMenu);
01789 m_incMenuFromCursor = m_incMenu->addAction(i18n("From &cursor"));
01790 m_incMenuFromCursor->setCheckable(true);
01791 m_incMenuHighlightAll = m_incMenu->addAction(i18n("Hi&ghlight all"));
01792 m_incMenuHighlightAll->setCheckable(true);
01793 m_incMenuMatchCase = m_incMenu->addAction(i18n("&Match case"));
01794 m_incMenuMatchCase->setCheckable(true);
01795
01796
01797 m_incUi->mutate->setIcon(KIcon("arrow-up-double"));
01798 m_incUi->next->setIcon(KIcon("go-down"));
01799 m_incUi->prev->setIcon(KIcon("go-up"));
01800
01801
01802 m_incUi->status->setTextElideMode(Qt::ElideLeft);
01803
01804
01805 centralWidget()->setFocusProxy(m_incUi->pattern);
01806 }
01807
01808
01809 if (create) {
01810 setChecked(m_incMenuHighlightAll, m_incHighlightAll);
01811 setChecked(m_incMenuFromCursor, m_incFromCursor);
01812 setChecked(m_incMenuMatchCase, m_incMatchCase);
01813 }
01814
01815
01816 m_incUi->pattern->setText(initialPattern);
01817 m_incUi->pattern->selectAll();
01818
01819
01820 const bool NOT_INVOKED_BY_USER_ACTION = false;
01821 onIncPatternChanged(initialPattern, NOT_INVOKED_BY_USER_ACTION);
01822
01823 if (create) {
01824
01825 connect(m_incUi->mutate, SIGNAL(clicked()), this, SLOT(onMutatePower()));
01826 connect(m_incUi->pattern, SIGNAL(returnPressed()), this, SLOT(onReturnPressed()));
01827 connect(m_incUi->pattern, SIGNAL(textChanged(const QString &)), this, SLOT(onIncPatternChanged(const QString &)));
01828 connect(m_incUi->next, SIGNAL(clicked()), this, SLOT(onIncNext()));
01829 connect(m_incUi->prev, SIGNAL(clicked()), this, SLOT(onIncPrev()));
01830 connect(m_incMenuMatchCase, SIGNAL(changed()), this, SLOT(onIncMatchCaseToggle()));
01831 connect(m_incMenuFromCursor, SIGNAL(changed()), this, SLOT(onIncFromCursorToggle()));
01832 connect(m_incMenuHighlightAll, SIGNAL(toggled(bool)), this, SLOT(onIncHighlightAllToggle(bool)));
01833
01834
01835
01836 connect(m_incUi->options, SIGNAL(clicked()), m_incUi->options, SLOT(showMenu()));
01837 }
01838
01839
01840 if (m_widget->isVisible()) {
01841 m_incUi->pattern->setFocus(Qt::MouseFocusReason);
01842 }
01843 }
01844
01845
01846
01847 bool KateSearchBar::isChecked(QCheckBox * checkbox) {
01848 Q_ASSERT(checkbox != NULL);
01849 return checkbox->checkState() == Qt::Checked;
01850 }
01851
01852
01853
01854 bool KateSearchBar::isChecked(QAction * menuAction) {
01855 Q_ASSERT(menuAction != NULL);
01856 return menuAction->isChecked();
01857 }
01858
01859
01860
01861 void KateSearchBar::setChecked(QCheckBox * checkbox, bool checked) {
01862 Q_ASSERT(checkbox != NULL);
01863 checkbox->setCheckState(checked ? Qt::Checked : Qt::Unchecked);
01864 }
01865
01866
01867
01868 void KateSearchBar::setChecked(QAction * menuAction, bool checked) {
01869 Q_ASSERT(menuAction != NULL);
01870 menuAction->setChecked(checked);
01871 }
01872
01873
01874
01875 void KateSearchBar::enableHighlights(bool enable) {
01876 if (enable) {
01877 view()->addInternalHighlight(m_topRange);
01878 } else {
01879 view()->removeInternalHighlight(m_topRange);
01880 m_topRange->deleteChildRanges();
01881 }
01882 }
01883
01884
01885
01886 void KateSearchBar::resetHighlights() {
01887 enableHighlights(false);
01888 enableHighlights(true);
01889 }
01890
01891
01892
01893 void KateSearchBar::showEvent(QShowEvent * event) {
01894
01895 if (m_incUi != NULL) {
01896 m_incInitCursor = view()->cursorPosition();
01897 }
01898
01899 connect(view(), SIGNAL(selectionChanged(KTextEditor::View *)),
01900 this, SLOT(onSelectionChanged()));
01901 connect(view(), SIGNAL(cursorPositionChanged(KTextEditor::View *, KTextEditor::Cursor const &)),
01902 this, SLOT(onCursorPositionChanged()));
01903
01904 enableHighlights(true);
01905 KateViewBarWidget::showEvent(event);
01906 }
01907
01908
01909
01910 void KateSearchBar::closed() {
01911 disconnect(view(), SIGNAL(selectionChanged(KTextEditor::View *)),
01912 this, SLOT(onSelectionChanged()));
01913 disconnect(view(), SIGNAL(cursorPositionChanged(KTextEditor::View *, KTextEditor::Cursor const &)),
01914 this, SLOT(onCursorPositionChanged()));
01915
01916 enableHighlights(false);
01917 }
01918
01919
01920
01921 void KateSearchBar::onSelectionChanged() {
01922 if (m_powerUi == NULL) {
01923 return;
01924 }
01925
01926
01927 const bool selected = view()->selection();
01928 bool selectionOnly = selected;
01929 if (selected) {
01930 Range const & selection = view()->selectionRange();
01931 selectionOnly = !selection.onSingleLine();
01932 }
01933 setChecked(m_powerMenuSelectionOnly, selectionOnly);
01934 }
01935
01936
01937 void KateSearchBar::onCursorPositionChanged() {
01938 if (m_incUi == NULL) {
01939 return;
01940 }
01941
01942
01943 m_incInitCursor = view()->cursorPosition();
01944 }
01945
01946
01947 void KateSearchBar::onPowerPatternContextMenuRequest() {
01948 const bool FOR_PATTERN = true;
01949 showExtendedContextMenu(FOR_PATTERN);
01950 }
01951
01952
01953
01954 void KateSearchBar::onPowerReplacmentContextMenuRequest() {
01955 const bool FOR_REPLACEMENT = false;
01956 showExtendedContextMenu(FOR_REPLACEMENT);
01957 }
01958
01959
01960 #include "katesearchbar.moc"
01961
01962