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
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
00042
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
00074 QWidget * const widget = centralWidget();
00075 widget->setLayout(m_layout);
00076 m_layout->setMargin(2);
00077
00078
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
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
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);
00157 }
00158
00159
00160
00161 void KateSearchBar::highlightReplacement(const Range & range) {
00162 highlight(range, Qt::green);
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
00178 QPalette background(m_incUi->pattern->palette());
00179 KColorScheme::adjustBackground(background, KColorScheme::PositiveBackground);
00180 m_incUi->pattern->setPalette(background);
00181
00182
00183 m_incUi->status->setText(wrapped
00184 ? i18n("Reached bottom, continued from top")
00185 : "");
00186 } else {
00187
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
00201 QPalette background(m_incUi->pattern->palette());
00202 KColorScheme::adjustBackground(background, KColorScheme::NegativeBackground);
00203 m_incUi->pattern->setPalette(background);
00204
00205
00206 m_incUi->status->setText(i18n("Not found"));
00207 } else {
00208
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
00222 m_incUi->pattern->setPalette(QPalette());
00223
00224
00225 m_incUi->status->setText("");
00226 } else {
00227
00228 QLineEdit * const lineEdit = m_powerUi->pattern->lineEdit();
00229 Q_ASSERT(lineEdit != NULL);
00230
00231
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 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
00263 output.append(QString::number(curPart.index));
00264 } else {
00265 const Range & captureRange = details[curPart.index];
00266 if (captureRange.isValid()) {
00267
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
00273 output.append(content.toUpper());
00274 break;
00275
00276 case ReplacementPart::LowerCase:
00277
00278 output.append(content.toLower());
00279 break;
00280
00281 case ReplacementPart::KeepCase:
00282 default:
00283
00284 output.append(content);
00285 break;
00286
00287 }
00288 }
00289 }
00290 break;
00291
00292 case ReplacementPart::UpperCase:
00293 case ReplacementPart::LowerCase:
00294 case ReplacementPart::KeepCase:
00295 caseConversion = curPart.type;
00296 break;
00297
00298 case ReplacementPart::Counter:
00299 {
00300
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:
00308 default:
00309 switch (caseConversion) {
00310 case ReplacementPart::UpperCase:
00311
00312 output.append(curPart.text.toUpper());
00313 break;
00314
00315 case ReplacementPart::LowerCase:
00316
00317 output.append(curPart.text.toLower());
00318 break;
00319
00320 case ReplacementPart::KeepCase:
00321 default:
00322
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
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
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
00363 m_view->setSelection(Range::invalid());
00364
00365
00366 resetHighlights();
00367 }
00368
00369
00370 indicateNothing();
00371
00372
00373 m_incUi->next->setDisabled(true);
00374 m_incUi->prev->setDisabled(true);
00375 return;
00376 }
00377
00378
00379 m_incUi->next->setDisabled(false);
00380 m_incUi->prev->setDisabled(false);
00381
00382 if (invokedByUserAction) {
00383
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
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
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
00412 if (fromCursor) {
00413
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
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
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
00474 Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
00475 const bool matchCase = isChecked(m_incMenuMatchCase);
00476 if (!matchCase) {
00477 enabledOptions |= Search::CaseInsensitive;
00478 }
00479
00480
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
00544 if (m_powerUi != NULL) {
00545 onPowerFindPrev();
00546 } else {
00547 onIncPrev();
00548 }
00549 } else {
00550
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
00567 const QString pattern = (m_powerUi != NULL)
00568 ? m_powerUi->pattern->currentText()
00569 : m_incUi->pattern->displayText();
00570 if (pattern.isEmpty()) {
00571 return false;
00572 }
00573
00574
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
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:
00610 default:
00611 break;
00612
00613 }
00614 }
00615
00616
00617
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
00628 inputRange = selection;
00629 } else {
00630
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
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
00656 if (regexMode && !multiLinePattern) {
00657 fixForSingleLine(inputRange, forwards);
00658 }
00659
00660
00661
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
00669 if (selected && !selectionOnly && (match == selection)) {
00670
00671 if (replace) {
00672
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
00679 if (forwards) {
00680 inputRange.setRange(afterReplace->end(), inputRange.end());
00681 } else {
00682 inputRange.setRange(inputRange.start(), afterReplace->start());
00683 }
00684 } else {
00685
00686 if (forwards) {
00687 inputRange.setRange(selection.end(), inputRange.end());
00688 } else {
00689 inputRange.setRange(inputRange.start(), selection.start());
00690 }
00691 }
00692
00693
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
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
00715 wrap = true;
00716 }
00717
00718
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
00725 if (selected && !selectionOnly && (match3 == selection)) {
00726
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
00739 const bool highlightAll = (m_powerUi != NULL)
00740 ? isChecked(m_powerUi->highlightAll)
00741 : isChecked(m_incMenuHighlightAll);
00742 if ((found && highlightAll) || (afterReplace != NULL)) {
00743
00744 if (found && highlightAll) {
00745 highlightAllMatches(pattern, enabledOptions);
00746 }
00747
00748
00749 if (found && (afterReplace != NULL)) {
00750
00751 if (!(found && highlightAll)) {
00752 resetHighlights();
00753 }
00754
00755 highlightReplacement(*afterReplace);
00756 }
00757
00758 }
00759
00760 delete afterReplace;
00761
00762 return true;
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:
00793 case MODE_PLAIN_TEXT:
00794 default:
00795 ;
00796
00797 }
00798 }
00799
00800
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
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
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
00887 globalConfig->setSearchFlags(futureFlags);
00888 }
00889
00890
00891
00892 void KateSearchBar::onPowerFindNext() {
00893 const bool FIND = false;
00894 if (onStep(FIND)) {
00895
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
00907 addCurrentTextToHistory(m_powerUi->pattern);
00908 }
00909 }
00910
00911
00912
00913 void KateSearchBar::onPowerReplaceNext() {
00914 const bool REPLACE = true;
00915 if (onStep(REPLACE)) {
00916
00917 addCurrentTextToHistory(m_powerUi->pattern);
00918
00919
00920 addCurrentTextToHistory(m_powerUi->replacement);
00921 }
00922 }
00923
00924
00925
00926
00927
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
00935 QString patternCopy(pattern);
00936 KateDocument::repairPattern(patternCopy, multiLinePattern);
00937 }
00938
00939
00940 if (enabledOptions.testFlag(Search::Backwards)) {
00941 enabledOptions &= ~Search::SearchOptions(Search::Backwards);
00942 }
00943
00944
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 bool const originalMatchEmpty = match.isEmpty();
00956
00957
00958 if (replacement != NULL) {
00959 if (matchCounter == 0) {
00960 m_view->doc()->editBegin();
00961 }
00962
00963
00964 SmartRange * const afterReplace = m_view->doc()->newSmartRange(match);
00965 afterReplace->setInsertBehavior(SmartRange::ExpandRight | SmartRange::ExpandLeft);
00966
00967
00968 replaceMatch(resultRanges, *replacement, ++matchCounter);
00969
00970
00971 highlightReplacement(*afterReplace);
00972 match = *afterReplace;
00973 delete afterReplace;
00974 } else {
00975
00976 highlightMatch(match);
00977 matchCounter++;
00978 }
00979
00980
00981 SmartCursor & workingStart = workingRange->smartStart();
00982 workingStart.setPosition(match.end());
00983 if (originalMatchEmpty) {
00984
00985
00986 workingStart.advance(1);
00987 } else if (regexMode && !multiLinePattern && workingStart.atEndOfLine()) {
00988
00989
00990 workingStart.advance(1);
00991 }
00992
00993
00994 if (!workingRange->isValid() || workingStart.atEndOfDocument()) {
00995 break;
00996 }
00997 }
00998
00999
01000 if (matchCounter > 0) {
01001 if (replacement != NULL) {
01002 m_view->doc()->editEnd();
01003 }
01004 }
01005
01006 delete workingRange;
01007 }
01008
01009
01010
01011 void KateSearchBar::onPowerReplaceAll() {
01012
01013 const QString pattern = m_powerUi->pattern->currentText();
01014 const QString replacement = m_powerUi->replacement->currentText();
01015
01016
01017
01018 Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
01019 const bool matchCase = isChecked(m_powerUi->matchCase);
01020 if (!matchCase) {
01021 enabledOptions |= Search::CaseInsensitive;
01022 }
01023
01024 if (m_powerUi != NULL) {
01025 switch (m_powerUi->searchMode->currentIndex()) {
01026 case MODE_WHOLE_WORDS:
01027 enabledOptions |= Search::WholeWords;
01028 break;
01029
01030 case MODE_ESCAPE_SEQUENCES:
01031 enabledOptions |= Search::EscapeSequences;
01032 break;
01033
01034 case MODE_REGEX:
01035 enabledOptions |= Search::Regex;
01036 break;
01037
01038 case MODE_PLAIN_TEXT:
01039 default:
01040 break;
01041
01042 }
01043 }
01044
01045
01046
01047 Range selection;
01048 const bool selected = m_view->selection();
01049 const bool selectionOnly = isChecked(m_powerUi->selectionOnly);
01050 Range inputRange = (selected && selectionOnly)
01051 ? m_view->selectionRange()
01052 : m_view->doc()->documentRange();
01053
01054
01055
01056 onForAll(pattern, inputRange, enabledOptions, &replacement);
01057
01058
01059
01060 addCurrentTextToHistory(m_powerUi->pattern);
01061
01062
01063 addCurrentTextToHistory(m_powerUi->replacement);
01064 }
01065
01066
01067
01068 struct ParInfo {
01069 int openIndex;
01070 bool capturing;
01071 int captureNumber;
01072 };
01073
01074
01075
01076 QVector<QString> KateSearchBar::getCapturePatterns(const QString & pattern) {
01077 QVector<QString> capturePatterns;
01078 capturePatterns.reserve(9);
01079 QStack<ParInfo> parInfos;
01080
01081 const int inputLen = pattern.length();
01082 int input = 0;
01083 bool insideClass = false;
01084 int captureCount = 0;
01085
01086 while (input < inputLen) {
01087 if (insideClass) {
01088
01089 if (pattern[input].unicode() == L']') {
01090 insideClass = false;
01091 }
01092 input++;
01093 }
01094 else
01095 {
01096 switch (pattern[input].unicode())
01097 {
01098 case L'\\':
01099
01100 input += 2;
01101 break;
01102
01103 case L'(':
01104 ParInfo curInfo;
01105 curInfo.openIndex = input;
01106 curInfo.capturing = (input + 1 >= inputLen) || (pattern[input + 1].unicode() != '?');
01107 if (curInfo.capturing) {
01108 captureCount++;
01109 }
01110 curInfo.captureNumber = captureCount;
01111 parInfos.push(curInfo);
01112
01113 input++;
01114 break;
01115
01116 case L')':
01117 if (!parInfos.empty()) {
01118 ParInfo & top = parInfos.top();
01119 if (top.capturing && (top.captureNumber <= 9)) {
01120 const int start = top.openIndex + 1;
01121 const int len = input - start;
01122 if (capturePatterns.size() < top.captureNumber) {
01123 capturePatterns.resize(top.captureNumber);
01124 }
01125 capturePatterns[top.captureNumber - 1] = pattern.mid(start, len);
01126 }
01127 parInfos.pop();
01128 }
01129
01130 input++;
01131 break;
01132
01133 case L'[':
01134 input++;
01135 insideClass = true;
01136 break;
01137
01138 default:
01139 input++;
01140 break;
01141
01142 }
01143 }
01144 }
01145
01146 return capturePatterns;
01147 }
01148
01149
01150
01151 void KateSearchBar::addMenuEntry(QMenu * menu, QVector<QString> & insertBefore, QVector<QString> & insertAfter,
01152 uint & walker, const QString & before, const QString after, const QString description,
01153 const QString & realBefore, const QString & realAfter) {
01154 QAction * const action = menu->addAction(before + after + '\t' + description);
01155 insertBefore[walker] = QString(realBefore.isEmpty() ? before : realBefore);
01156 insertAfter[walker] = QString(realAfter.isEmpty() ? after : realAfter);
01157 action->setData(QVariant(walker++));
01158 }
01159
01160
01161
01162 void KateSearchBar::showAddMenu(bool forPattern) {
01163 QVector<QString> insertBefore(35);
01164 QVector<QString> insertAfter(35);
01165 uint walker = 0;
01166
01167
01168 QMenu * const popupMenu = new QMenu();
01169 const bool regexMode = (m_powerUi->searchMode->currentIndex() == MODE_REGEX);
01170
01171 if (forPattern) {
01172 if (regexMode) {
01173 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "^", "", i18n("Beginning of line"));
01174 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "$", "", i18n("End of line"));
01175 popupMenu->addSeparator();
01176 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, ".", "", i18n("Any single character (excluding line breaks)"));
01177 popupMenu->addSeparator();
01178 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "+", "", i18n("One or more occurrences"));
01179 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "*", "", i18n("Zero or more occurrences"));
01180 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "?", "", i18n("Zero or one occurrences"));
01181 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "{a", ",b}", i18n("<a> through <b> occurrences"), "{", ",}");
01182 popupMenu->addSeparator();
01183 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "(", ")", i18n("Group, capturing"));
01184 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "|", "", i18n("Or"));
01185 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "[", "]", i18n("Set of characters"));
01186 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "[^", "]", i18n("Negative set of characters"));
01187 popupMenu->addSeparator();
01188 }
01189 } else {
01190 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\0", "", i18n("Whole match reference"));
01191 popupMenu->addSeparator();
01192 if (regexMode) {
01193 const QString pattern = m_powerUi->pattern->currentText();
01194 const QVector<QString> capturePatterns = getCapturePatterns(pattern);
01195
01196 const int captureCount = capturePatterns.count();
01197 for (int i = 1; i <= 9; i++) {
01198 const QString number = QString::number(i);
01199 const QString & captureDetails = (i <= captureCount)
01200 ? (QString(" = (") + capturePatterns[i - 1].left(30)) + QString(")")
01201 : QString();
01202 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\" + number, "",
01203 i18n("Reference") + ' ' + number + captureDetails);
01204 }
01205
01206 popupMenu->addSeparator();
01207 }
01208 }
01209
01210 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\n", "", i18n("Line break"));
01211 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\t", "", i18n("Tab"));
01212
01213 if (forPattern && regexMode) {
01214 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\b", "", i18n("Word boundary"));
01215 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\B", "", i18n("Not word boundary"));
01216 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\d", "", i18n("Digit"));
01217 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\D", "", i18n("Non-digit"));
01218 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\s", "", i18n("Whitespace (excluding line breaks)"));
01219 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\S", "", i18n("Non-whitespace (excluding line breaks)"));
01220 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\w", "", i18n("Word character (alphanumerics plus '_')"));
01221 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\W", "", i18n("Non-word character"));
01222 }
01223
01224 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\0???", "", i18n("Octal character 000 to 377 (2^8-1)"), "\\0");
01225 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\x????", "", i18n("Hex character 0000 to FFFF (2^16-1)"), "\\x");
01226 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\\\", "", i18n("Backslash"));
01227
01228 if (forPattern && regexMode) {
01229 popupMenu->addSeparator();
01230 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "(?:E", ")", i18n("Group, non-capturing"), "(?:");
01231 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "(?=E", ")", i18n("Lookahead"), "(?=");
01232 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "(?!E", ")", i18n("Negative lookahead"), "(?!");
01233 }
01234
01235 if (!forPattern) {
01236 popupMenu->addSeparator();
01237 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\L", "", i18n("Begin lowercase conversion"));
01238 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\U", "", i18n("Begin uppercase conversion"));
01239 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\E", "", i18n("End case conversion"));
01240 addMenuEntry(popupMenu, insertBefore, insertAfter, walker, "\\#[#..]", "", i18n("Replacement counter (for Replace all)"), "\\#");
01241 }
01242
01243
01244
01245 const QPoint topLeftGlobal = m_powerUi->patternAdd->mapToGlobal(QPoint(0, 0));
01246 QAction * const result = popupMenu->exec(topLeftGlobal);
01247 if (result != NULL) {
01248 QLineEdit * const lineEdit = forPattern
01249 ? m_powerUi->pattern->lineEdit()
01250 : m_powerUi->replacement->lineEdit();
01251 Q_ASSERT(lineEdit != NULL);
01252 const int cursorPos = lineEdit->cursorPosition();
01253 const int index = result->data().toUInt();
01254 const QString & before = insertBefore[index];
01255 const QString & after = insertAfter[index];
01256 lineEdit->insert(before + after);
01257 lineEdit->setCursorPosition(cursorPos + before.count());
01258 lineEdit->setFocus();
01259 }
01260
01261
01262
01263 delete popupMenu;
01264 }
01265
01266
01267
01268 void KateSearchBar::onPowerAddToPatternClicked() {
01269 const bool FOR_PATTERN = true;
01270 showAddMenu(FOR_PATTERN);
01271 }
01272
01273
01274
01275 void KateSearchBar::onPowerAddToReplacementClicked() {
01276 const bool FOR_REPLACEMENT = false;
01277 showAddMenu(FOR_REPLACEMENT);
01278 }
01279
01280
01281
01282 void KateSearchBar::onPowerUsePlaceholdersToggle(int state, bool invokedByUserAction) {
01283 const bool disabled = (state != Qt::Checked);
01284 m_powerUi->replacementAdd->setDisabled(disabled);
01285
01286 if (invokedByUserAction) {
01287 sendConfig();
01288 }
01289 }
01290
01291
01292
01293 void KateSearchBar::onPowerMatchCaseToggle(bool invokedByUserAction) {
01294 if (invokedByUserAction) {
01295 sendConfig();
01296 indicateNothing();
01297 }
01298 }
01299
01300
01301
01302 void KateSearchBar::onPowerHighlightAllToggle(int state, bool invokedByUserAction) {
01303 if (invokedByUserAction) {
01304 sendConfig();
01305
01306 const bool wanted = (state == Qt::Checked);
01307 if (wanted) {
01308 const QString pattern = m_powerUi->pattern->currentText();
01309 if (!pattern.isEmpty()) {
01310
01311 Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
01312 const bool matchCase = isChecked(m_powerUi->matchCase);
01313 if (!matchCase) {
01314 enabledOptions |= Search::CaseInsensitive;
01315 }
01316
01317 switch (m_powerUi->searchMode->currentIndex()) {
01318 case MODE_WHOLE_WORDS:
01319 enabledOptions |= Search::WholeWords;
01320 break;
01321
01322 case MODE_ESCAPE_SEQUENCES:
01323 enabledOptions |= Search::EscapeSequences;
01324 break;
01325
01326 case MODE_REGEX:
01327 enabledOptions |= Search::Regex;
01328 break;
01329
01330 case MODE_PLAIN_TEXT:
01331 default:
01332 break;
01333
01334 }
01335
01336
01337 resetHighlights();
01338 highlightAllMatches(pattern, enabledOptions);
01339 }
01340 } else {
01341 resetHighlights();
01342 }
01343 }
01344 }
01345
01346
01347
01348 void KateSearchBar::onPowerFromCursorToggle(bool invokedByUserAction) {
01349 if (invokedByUserAction) {
01350 sendConfig();
01351 }
01352 }
01353
01354
01355
01356 void KateSearchBar::onPowerModeChanged(int index, bool invokedByUserAction) {
01357 const bool disabled = (index == MODE_PLAIN_TEXT)
01358 || (index == MODE_WHOLE_WORDS);
01359 m_powerUi->patternAdd->setDisabled(disabled);
01360
01361 if (invokedByUserAction) {
01362 switch (index) {
01363 case MODE_REGEX:
01364 setChecked(m_powerUi->matchCase, true);
01365
01366
01367 case MODE_ESCAPE_SEQUENCES:
01368 setChecked(m_powerUi->usePlaceholders, true);
01369 break;
01370
01371 case MODE_WHOLE_WORDS:
01372 case MODE_PLAIN_TEXT:
01373 default:
01374 ;
01375 }
01376
01377 sendConfig();
01378 indicateNothing();
01379 }
01380
01381 givePatternFeedback(m_powerUi->pattern->currentText());
01382 }
01383
01384
01385
01386 void KateSearchBar::nextMatchForSelection(KateView * view, bool forwards) {
01387 const bool selected = view->selection();
01388 if (selected) {
01389 const QString pattern = view->selectionText();
01390
01391
01392 Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
01393 if (!forwards) {
01394 enabledOptions |= Search::Backwards;
01395 }
01396
01397
01398 const Range selRange = view->selectionRange();
01399 Range inputRange;
01400 if (forwards) {
01401 inputRange.setRange(selRange.end(), view->doc()->documentEnd());
01402 } else {
01403 inputRange.setRange(Cursor(0, 0), selRange.start());
01404 }
01405
01406
01407 const QVector<Range> resultRanges = view->doc()->searchText(inputRange, pattern, enabledOptions);
01408 const Range & match = resultRanges[0];
01409
01410 if (match.isValid()) {
01411 selectRange(view, match);
01412 } else {
01413
01414 if (forwards) {
01415 inputRange.setRange(Cursor(0, 0), selRange.start());
01416 } else {
01417 inputRange.setRange(selRange.end(), view->doc()->documentEnd());
01418 }
01419 const QVector<Range> resultRanges2 = view->doc()->searchText(inputRange, pattern, enabledOptions);
01420 const Range & match2 = resultRanges2[0];
01421 if (match2.isValid()) {
01422 selectRange(view, match2);
01423 }
01424 }
01425 } else {
01426
01427 const Cursor cursorPos = view->cursorPosition();
01428 view->selectWord(cursorPos);
01429 }
01430 }
01431
01432
01433
01434 void KateSearchBar::onMutatePower() {
01435 QString initialPattern;
01436 bool selectionOnly = false;
01437
01438
01439 const bool selected = m_view->selection();
01440 if (selected) {
01441 const Range & selection = m_view->selectionRange();
01442 if (selection.onSingleLine()) {
01443
01444 initialPattern = m_view->selectionText();
01445 } else {
01446
01447 selectionOnly = true;
01448 }
01449 }
01450
01451
01452 if (initialPattern.isNull()) {
01453
01454 const bool fromReplace = (m_powerUi != NULL) && (m_widget->isVisible());
01455 if (fromReplace) {
01456 QLineEdit * const patternLineEdit = m_powerUi->pattern->lineEdit();
01457 Q_ASSERT(patternLineEdit != NULL);
01458 patternLineEdit->selectAll();
01459 m_powerUi->pattern->setFocus(Qt::MouseFocusReason);
01460 return;
01461 }
01462
01463
01464 const bool fromIncremental = (m_incUi != NULL) && (m_widget->isVisible());
01465 if (fromIncremental) {
01466 initialPattern = m_incUi->pattern->displayText();
01467 }
01468 }
01469
01470
01471 const bool create = (m_powerUi == NULL);
01472 if (create) {
01473
01474 if (m_incUi != NULL) {
01475
01476 const bool OF_INCREMENTAL = false;
01477 backupConfig(OF_INCREMENTAL);
01478
01479
01480 delete m_incUi;
01481 delete m_incMenu;
01482 m_incUi = NULL;
01483 m_incMenu = NULL;
01484 m_incMenuMatchCase = NULL;
01485 m_incMenuFromCursor = NULL;
01486 m_incMenuHighlightAll = NULL;
01487 m_layout->removeWidget(m_widget);
01488 m_widget->deleteLater();
01489 }
01490
01491
01492 m_widget = new QWidget(this);
01493 m_powerUi = new Ui::PowerSearchBar;
01494 m_powerUi->setupUi(m_widget);
01495 m_layout->addWidget(m_widget);
01496
01497
01498 const int MAX_HISTORY_SIZE = 100;
01499 QStringListModel * const patternHistoryModel = KateHistoryModel::getPatternHistoryModel();
01500 QStringListModel * const replacementHistoryModel = KateHistoryModel::getReplacementHistoryModel();
01501 m_powerUi->pattern->setMaxCount(MAX_HISTORY_SIZE);
01502 m_powerUi->pattern->setModel(patternHistoryModel);
01503 m_powerUi->replacement->setMaxCount(MAX_HISTORY_SIZE);
01504 m_powerUi->replacement->setModel(replacementHistoryModel);
01505
01506
01507 m_powerUi->mutate->setIcon(KIcon("arrow-down-double"));
01508 m_powerUi->findNext->setIcon(KIcon("go-down"));
01509 m_powerUi->findPrev->setIcon(KIcon("go-up"));
01510 m_powerUi->patternAdd->setIcon(KIcon("list-add"));
01511 m_powerUi->replacementAdd->setIcon(KIcon("list-add"));
01512
01513
01514 centralWidget()->setFocusProxy(m_powerUi->pattern);
01515
01516
01517 QLineEdit * const patternLineEdit = m_powerUi->pattern->lineEdit();
01518 Q_ASSERT(patternLineEdit != NULL);
01519 patternLineEdit->completer()->setCaseSensitivity(Qt::CaseSensitive);
01520
01521 QLineEdit * const replacementLineEdit = m_powerUi->pattern->lineEdit();
01522 Q_ASSERT(replacementLineEdit != NULL);
01523 replacementLineEdit->completer()->setCaseSensitivity(Qt::CaseSensitive);
01524 }
01525
01526 setChecked(m_powerUi->selectionOnly, selectionOnly);
01527
01528
01529 if (create) {
01530 setChecked(m_powerUi->matchCase, m_powerMatchCase);
01531 setChecked(m_powerUi->highlightAll, m_powerHighlightAll);
01532 setChecked(m_powerUi->usePlaceholders, m_powerUsePlaceholders);
01533 setChecked(m_powerUi->fromCursor, m_powerFromCursor);
01534 m_powerUi->searchMode->setCurrentIndex(m_powerMode);
01535 }
01536
01537
01538 QLineEdit * const patternLineEdit = m_powerUi->pattern->lineEdit();
01539 Q_ASSERT(patternLineEdit != NULL);
01540 patternLineEdit->setText(initialPattern);
01541 patternLineEdit->selectAll();
01542
01543
01544 QLineEdit * const replacementLineEdit = m_powerUi->replacement->lineEdit();
01545 Q_ASSERT(replacementLineEdit != NULL);
01546 replacementLineEdit->setText("");
01547
01548
01549 onPowerPatternChanged(initialPattern);
01550 const bool NOT_INVOKED_BY_USER_ACTION = false;
01551 onPowerUsePlaceholdersToggle(m_powerUi->usePlaceholders->checkState(), NOT_INVOKED_BY_USER_ACTION);
01552 onPowerModeChanged(m_powerUi->searchMode->currentIndex(), NOT_INVOKED_BY_USER_ACTION);
01553
01554 if (create) {
01555
01556 connect(m_powerUi->mutate, SIGNAL(clicked()), this, SLOT(onMutateIncremental()));
01557 connect(patternLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(onPowerPatternChanged(const QString &)));
01558 connect(m_powerUi->findNext, SIGNAL(clicked()), this, SLOT(onPowerFindNext()));
01559 connect(m_powerUi->findPrev, SIGNAL(clicked()), this, SLOT(onPowerFindPrev()));
01560 connect(m_powerUi->replaceNext, SIGNAL(clicked()), this, SLOT(onPowerReplaceNext()));
01561 connect(m_powerUi->replaceAll, SIGNAL(clicked()), this, SLOT(onPowerReplaceAll()));
01562 connect(m_powerUi->searchMode, SIGNAL(currentIndexChanged(int)), this, SLOT(onPowerModeChanged(int)));
01563 connect(m_powerUi->patternAdd, SIGNAL(clicked()), this, SLOT(onPowerAddToPatternClicked()));
01564 connect(m_powerUi->usePlaceholders, SIGNAL(stateChanged(int)), this, SLOT(onPowerUsePlaceholdersToggle(int)));
01565 connect(m_powerUi->matchCase, SIGNAL(stateChanged(int)), this, SLOT(onPowerMatchCaseToggle()));
01566 connect(m_powerUi->highlightAll, SIGNAL(stateChanged(int)), this, SLOT(onPowerHighlightAllToggle(int)));
01567 connect(m_powerUi->fromCursor, SIGNAL(stateChanged(int)), this, SLOT(onPowerFromCursorToggle()));
01568 connect(m_powerUi->replacementAdd, SIGNAL(clicked()), this, SLOT(onPowerAddToReplacementClicked()));
01569
01570
01571 connect(patternLineEdit, SIGNAL(returnPressed()), this, SLOT(onReturnPressed()));
01572 connect(replacementLineEdit, SIGNAL(returnPressed()), this, SLOT(onPowerReplaceNext()));
01573 }
01574
01575
01576 if (m_widget->isVisible()) {
01577 m_powerUi->pattern->setFocus(Qt::MouseFocusReason);
01578 }
01579 }
01580
01581
01582
01583 void KateSearchBar::onMutateIncremental() {
01584 QString initialPattern;
01585
01586
01587 const bool selected = m_view->selection();
01588 if (selected) {
01589 const Range & selection = m_view->selectionRange();
01590 if (selection.onSingleLine()) {
01591
01592 initialPattern = m_view->selectionText();
01593 }
01594 }
01595
01596
01597 if (initialPattern.isNull()) {
01598
01599 const bool fromIncremental = (m_incUi != NULL) && (m_widget->isVisible());
01600 if (fromIncremental) {
01601 m_incUi->pattern->selectAll();
01602 m_incUi->pattern->setFocus(Qt::MouseFocusReason);
01603 return;
01604 }
01605
01606
01607 const bool fromReplace = (m_powerUi != NULL) && (m_widget->isVisible());
01608 if (fromReplace) {
01609 initialPattern = m_powerUi->pattern->currentText();
01610 }
01611 }
01612
01613
01614 const bool create = (m_incUi == NULL);
01615 if (create) {
01616
01617 if (m_powerUi != NULL) {
01618
01619 const bool OF_POWER = true;
01620 backupConfig(OF_POWER);
01621
01622
01623 delete m_powerUi;
01624 m_powerUi = NULL;
01625 m_layout->removeWidget(m_widget);
01626 m_widget->deleteLater();
01627 }
01628
01629
01630 m_widget = new QWidget(this);
01631 m_incUi = new Ui::IncrementalSearchBar;
01632 m_incUi->setupUi(m_widget);
01633 m_layout->addWidget(m_widget);
01634
01635
01636 m_incMenu = new QMenu();
01637 m_incUi->options->setMenu(m_incMenu);
01638 m_incMenuHighlightAll = m_incMenu->addAction(i18n("Hi&ghlight all"));
01639 m_incMenuHighlightAll->setCheckable(true);
01640 m_incMenuMatchCase = m_incMenu->addAction(i18n("&Match case"));
01641 m_incMenuMatchCase->setCheckable(true);
01642 m_incMenuFromCursor = m_incMenu->addAction(i18n("From &cursor"));
01643 m_incMenuFromCursor->setCheckable(true);
01644
01645
01646 m_incUi->mutate->setIcon(KIcon("arrow-up-double"));
01647 m_incUi->next->setIcon(KIcon("go-down"));
01648 m_incUi->prev->setIcon(KIcon("go-up"));
01649
01650
01651 centralWidget()->setFocusProxy(m_incUi->pattern);
01652 }
01653
01654
01655 if (create) {
01656 setChecked(m_incMenuHighlightAll, m_incHighlightAll);
01657 setChecked(m_incMenuFromCursor, m_incFromCursor);
01658 setChecked(m_incMenuMatchCase, m_incMatchCase);
01659 }
01660
01661
01662 m_incUi->pattern->setText(initialPattern);
01663 m_incUi->pattern->selectAll();
01664
01665
01666 const bool NOT_INVOKED_BY_USER_ACTION = false;
01667 onIncPatternChanged(initialPattern, NOT_INVOKED_BY_USER_ACTION);
01668
01669 if (create) {
01670
01671 connect(m_incUi->mutate, SIGNAL(clicked()), this, SLOT(onMutatePower()));
01672 connect(m_incUi->pattern, SIGNAL(returnPressed()), this, SLOT(onReturnPressed()));
01673 connect(m_incUi->pattern, SIGNAL(textChanged(const QString &)), this, SLOT(onIncPatternChanged(const QString &)));
01674 connect(m_incUi->next, SIGNAL(clicked()), this, SLOT(onIncNext()));
01675 connect(m_incUi->prev, SIGNAL(clicked()), this, SLOT(onIncPrev()));
01676 connect(m_incMenuMatchCase, SIGNAL(changed()), this, SLOT(onIncMatchCaseToggle()));
01677 connect(m_incMenuFromCursor, SIGNAL(changed()), this, SLOT(onIncFromCursorToggle()));
01678 connect(m_incMenuHighlightAll, SIGNAL(toggled(bool)), this, SLOT(onIncHighlightAllToggle(bool)));
01679
01680
01681
01682 connect(m_incUi->options, SIGNAL(clicked()), m_incUi->options, SLOT(showMenu()));
01683 }
01684
01685
01686 if (m_widget->isVisible()) {
01687 m_incUi->pattern->setFocus(Qt::MouseFocusReason);
01688 }
01689 }
01690
01691
01692
01693 bool KateSearchBar::isChecked(QCheckBox * checkbox) {
01694 Q_ASSERT(checkbox != NULL);
01695 return checkbox->checkState() == Qt::Checked;
01696 }
01697
01698
01699
01700 bool KateSearchBar::isChecked(QAction * menuAction) {
01701 Q_ASSERT(menuAction != NULL);
01702 return menuAction->isChecked();
01703 }
01704
01705
01706
01707 void KateSearchBar::setChecked(QCheckBox * checkbox, bool checked) {
01708 Q_ASSERT(checkbox != NULL);
01709 checkbox->setCheckState(checked ? Qt::Checked : Qt::Unchecked);
01710 }
01711
01712
01713
01714 void KateSearchBar::setChecked(QAction * menuAction, bool checked) {
01715 Q_ASSERT(menuAction != NULL);
01716 menuAction->setChecked(checked);
01717 }
01718
01719
01720
01721 void KateSearchBar::enableHighlights(bool enable) {
01722 if (enable) {
01723 m_view->addInternalHighlight(m_topRange);
01724 } else {
01725 m_view->removeInternalHighlight(m_topRange);
01726 m_topRange->deleteChildRanges();
01727 }
01728 }
01729
01730
01731
01732 void KateSearchBar::resetHighlights() {
01733 enableHighlights(false);
01734 enableHighlights(true);
01735 }
01736
01737
01738
01739 void KateSearchBar::showEvent(QShowEvent * event) {
01740
01741 if (m_incUi != NULL) {
01742 m_incInitCursor = m_view->cursorPosition();
01743 }
01744
01745 enableHighlights(true);
01746 KateViewBarWidget::showEvent(event);
01747 }
01748
01749
01750
01751 void KateSearchBar::hideEvent(QHideEvent * event) {
01752 enableHighlights(false);
01753 KateViewBarWidget::hideEvent(event);
01754 }
01755
01756
01757
01758
01759 #ifdef FAST_DEBUG_ENABLE
01760 # undef FAST_DEBUG_ENABLE
01761 #endif
01762 #undef FAST_DEBUG
01763
01764
01765
01766 #include "katesearchbar.moc"
01767