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