kde-workspace/kate/part/search/katesearchbar.cpp
Ivailo Monev fe52270c14 kate: explicitly convert to range and cursor objects
lots of opportunities for optimizations by caching the converted objects

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2022-05-28 11:08:27 +03:00

1627 lines
54 KiB
C++

/* This file is part of the KDE libraries
Copyright (C) 2009-2010 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
Copyright (C) 2007 Sebastian Pipping <webmaster@hartwork.org>
Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
Copyright (C) 2007 Thomas Friedrichsmeier <thomas.friedrichsmeier@ruhr-uni-bochum.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "katesearchbar.h"
#include "moc_katesearchbar.cpp"
#include "kateregexp.h"
#include "katematch.h"
#include "kateview.h"
#include "katedocument.h"
#include "kateundomanager.h"
#include "kateconfig.h"
#include "katerenderer.h"
#include <ktexteditor/movingcursor.h>
#include <ktexteditor/movingrange.h>
#include "ui_searchbarincremental.h"
#include "ui_searchbarpower.h"
#include <kcolorscheme.h>
#include <kmessagebox.h>
#include <kstandardaction.h>
#include <klocale.h>
#include <QtGui/QVBoxLayout>
#include <QtGui/QComboBox>
#include <QtGui/QCheckBox>
#include <QtGui/QShortcut>
// Turn debug messages on/off here
// #define FAST_DEBUG_ENABLE
#ifdef FAST_DEBUG_ENABLE
# define FAST_DEBUG(x) kDebug() << x
#else
# define FAST_DEBUG(x)
#endif
using namespace KTextEditor;
namespace {
class AddMenuManager {
private:
QVector<QString> m_insertBefore;
QVector<QString> m_insertAfter;
QSet<QAction *> m_actionPointers;
uint m_indexWalker;
QMenu * m_menu;
public:
AddMenuManager(QMenu * parent, int expectedItemCount)
: m_insertBefore(QVector<QString>(expectedItemCount)),
m_insertAfter(QVector<QString>(expectedItemCount)),
m_indexWalker(0),
m_menu(NULL) {
Q_ASSERT(parent != NULL);
m_menu = parent->addMenu(i18n("Add..."));
if (m_menu == NULL) {
return;
}
m_menu->setIcon(KIcon("list-add"));
}
void enableMenu(bool enabled) {
if (m_menu == NULL) {
return;
}
m_menu->setEnabled(enabled);
}
void addEntry(const QString & before, const QString after,
const QString description, const QString & realBefore = QString(),
const QString & realAfter = QString()) {
if (m_menu == NULL) {
return;
}
QAction * const action = m_menu->addAction(before + after + '\t' + description);
m_insertBefore[m_indexWalker] = QString(realBefore.isEmpty() ? before : realBefore);
m_insertAfter[m_indexWalker] = QString(realAfter.isEmpty() ? after : realAfter);
action->setData(QVariant(m_indexWalker++));
m_actionPointers.insert(action);
}
void addSeparator() {
if (m_menu == NULL) {
return;
}
m_menu->addSeparator();
}
void handle(QAction * action, QLineEdit * lineEdit) {
if (!m_actionPointers.contains(action)) {
return;
}
const int cursorPos = lineEdit->cursorPosition();
const int index = action->data().toUInt();
const QString & before = m_insertBefore[index];
const QString & after = m_insertAfter[index];
lineEdit->insert(before + after);
lineEdit->setCursorPosition(cursorPos + before.count());
lineEdit->setFocus();
}
};
} // anon namespace
KateSearchBar::KateSearchBar(bool initAsPower, KateView* view, KateViewConfig *config)
: KateViewBarWidget(true, view),
m_view(view),
m_config(config),
m_layout(new QVBoxLayout()),
m_widget(NULL),
m_incUi(NULL),
m_incInitCursor(view->cursorPosition()),
m_powerUi(NULL),
highlightMatchAttribute (new Attribute()),
highlightReplacementAttribute (new Attribute()),
m_incHighlightAll(false),
m_incFromCursor(true),
m_incMatchCase(false),
m_powerMatchCase(true),
m_powerFromCursor(false),
m_powerHighlightAll(false),
m_powerMode(0),
m_unitTestMode(false)
{
connect(view, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)),
this, SLOT(updateIncInitCursor()));
// init match attribute
Attribute::Ptr mouseInAttribute(new Attribute());
mouseInAttribute->setFontBold(true);
highlightMatchAttribute->setDynamicAttribute (Attribute::ActivateMouseIn, mouseInAttribute);
Attribute::Ptr caretInAttribute(new Attribute());
caretInAttribute->setFontItalic(true);
highlightMatchAttribute->setDynamicAttribute (Attribute::ActivateCaretIn, caretInAttribute);
updateHighlightColors();
// Modify parent
QWidget * const widget = centralWidget();
widget->setLayout(m_layout);
m_layout->setMargin(0);
// allow to have small size, for e.g. Kile
setMinimumWidth (100);
// Copy global to local config backup
const long searchFlags = m_config->searchFlags();
m_incHighlightAll = (searchFlags & KateViewConfig::IncHighlightAll) != 0;
m_incFromCursor = (searchFlags & KateViewConfig::IncFromCursor) != 0;
m_incMatchCase = (searchFlags & KateViewConfig::IncMatchCase) != 0;
m_powerMatchCase = (searchFlags & KateViewConfig::PowerMatchCase) != 0;
m_powerFromCursor = (searchFlags & KateViewConfig::PowerFromCursor) != 0;
m_powerHighlightAll = (searchFlags & KateViewConfig::PowerHighlightAll) != 0;
m_powerMode = ((searchFlags & KateViewConfig::PowerModeRegularExpression) != 0)
? MODE_REGEX
: (((searchFlags & KateViewConfig::PowerModeEscapeSequences) != 0)
? MODE_ESCAPE_SEQUENCES
: (((searchFlags & KateViewConfig::PowerModeWholeWords) != 0)
? MODE_WHOLE_WORDS
: MODE_PLAIN_TEXT));
// Load one of either dialogs
if (initAsPower) {
enterPowerMode();
} else {
enterIncrementalMode();
}
updateSelectionOnly();
connect(view, SIGNAL(selectionChanged(KTextEditor::View*)),
this, SLOT(updateSelectionOnly()));
}
KateSearchBar::~KateSearchBar() {
clearHighlights();
delete m_layout;
delete m_widget;
delete m_incUi;
delete m_powerUi;
}
void KateSearchBar::closed()
{
// remove search from the view bar, because it vertically bloats up the
// stacked layout in KateViewBar.
if (viewBar()) {
viewBar()->removeBarWidget(this);
}
clearHighlights();
}
void KateSearchBar::setReplacementPattern(const QString &replacementPattern) {
Q_ASSERT(isPower());
if (this->replacementPattern() == replacementPattern)
return;
m_powerUi->replacement->setEditText(replacementPattern);
}
QString KateSearchBar::replacementPattern() const
{
Q_ASSERT(isPower());
return m_powerUi->replacement->currentText();
}
void KateSearchBar::setSearchMode(KateSearchBar::SearchMode mode)
{
Q_ASSERT(isPower());
m_powerUi->searchMode->setCurrentIndex(mode);
}
void KateSearchBar::findNext()
{
const bool found = find();
if (found) {
QComboBox *combo = m_powerUi != 0 ? m_powerUi->pattern : m_incUi->pattern;
// Add to search history
addCurrentTextToHistory(combo);
}
}
void KateSearchBar::findPrevious() {
const bool found = find(SearchBackward);
if (found) {
QComboBox *combo = m_powerUi != 0 ? m_powerUi->pattern : m_incUi->pattern;
// Add to search history
addCurrentTextToHistory(combo);
}
}
void KateSearchBar::updateMessage(QPointer<KTextEditor::Message>& message, bool visible, const QString& text,
KTextEditor::Message::MessageType type, KTextEditor::Message::MessagePosition position,
KTextEditor::Message::AutoHideMode autoHideMode, int durationMs, bool blink)
{
// It the message is visible now and we want it to be visible and we don't want the message to blink,
// then just leave it.
if (message && visible && !blink)
return;
delete message;
if (!visible)
return;
message = new KTextEditor::Message(text, type);
message->setPosition(position);
message->setAutoHide(durationMs);
message->setAutoHideMode(autoHideMode);
m_view->doc()->postMessage(message);
}
void KateSearchBar::showInfoMessage(const QString& text)
{
typedef KTextEditor::Message KTEM;
updateMessage(m_infoMessage, true, text, KTEM::Positive, KTEM::BottomInView, KTEM::AfterUserInteraction, 3000, false);
}
void KateSearchBar::highlightMatch(const Range & range) {
KTextEditor::MovingRange* const highlight = m_view->doc()->newMovingRange(range, Kate::TextRange::DoNotExpand);
highlight->setView(m_view); // show only in this view
highlight->setAttributeOnlyForViews(true);
// use z depth defined in moving ranges interface
highlight->setZDepth (-10000.0);
highlight->setAttribute(highlightMatchAttribute);
m_hlRanges.append(highlight);
}
void KateSearchBar::highlightReplacement(const Range & range) {
KTextEditor::MovingRange* const highlight = m_view->doc()->newMovingRange(range, Kate::TextRange::DoNotExpand);
highlight->setView(m_view); // show only in this view
highlight->setAttributeOnlyForViews(true);
// use z depth defined in moving ranges interface
highlight->setZDepth (-10000.0);
highlight->setAttribute(highlightReplacementAttribute);
m_hlRanges.append(highlight);
}
void KateSearchBar::indicateMatch(MatchResult matchResult) {
QLineEdit * const lineEdit = isPower() ? m_powerUi->pattern->lineEdit()
: m_incUi->pattern->lineEdit();
QPalette background(lineEdit->palette());
switch (matchResult) {
case MatchFound: // FALLTHROUGH
case MatchWrappedForward:
case MatchWrappedBackward:
// Green background for line edit
KColorScheme::adjustBackground(background, KColorScheme::PositiveBackground);
break;
case MatchMismatch:
// Red background for line edit
KColorScheme::adjustBackground(background, KColorScheme::NegativeBackground);
break;
case MatchNothing:
// Reset background of line edit
background = QPalette();
break;
case MatchNeutral:
KColorScheme::adjustBackground(background, KColorScheme::NeutralBackground);
break;
}
typedef KTextEditor::Message KTEM;
const int messageDuration = 2000; // ms
updateMessage(m_wrappedTopMessage, matchResult == MatchWrappedForward, i18n("Continuing search from top"),
KTEM::Information, KTEM::TopInView, KTEM::Immediate, messageDuration, true);
updateMessage(m_wrappedBottomMessage, matchResult == MatchWrappedBackward, i18n("Continuing search from bottom"),
KTEM::Information, KTEM::BottomInView, KTEM::Immediate, messageDuration, true);
updateMessage(m_notFoundMessage, matchResult == MatchMismatch, i18n("Not found"),
KTEM::Warning, KTEM::BottomInView, KTEM::Immediate, messageDuration, false);
lineEdit->setPalette(background);
}
/*static*/ void KateSearchBar::selectRange(KateView * view, const KTextEditor::Range & range) {
view->setCursorPositionInternal(range.end());
view->setSelection(range);
}
void KateSearchBar::selectRange2(const KTextEditor::Range & range) {
disconnect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(updateSelectionOnly()));
selectRange(m_view, range);
connect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(updateSelectionOnly()));
}
void KateSearchBar::onIncPatternChanged(const QString & pattern)
{
if (!m_incUi)
return;
// clear prior highlightings (deletes info message if present)
clearHighlights();
m_incUi->next->setDisabled(pattern.isEmpty());
m_incUi->prev->setDisabled(pattern.isEmpty());
KateMatch match(m_view->doc(), searchOptions());
if (!pattern.isEmpty()) {
// Find, first try
const Range inputRange = KTextEditor::Range(m_incInitCursor, m_view->document()->documentEnd());
match.searchText(inputRange, pattern);
}
const bool wrap = !match.isValid() && !pattern.isEmpty();
if (wrap) {
// Find, second try
const KTextEditor::Range inputRange = m_view->document()->documentRange();
match.searchText(inputRange, pattern);
}
const MatchResult matchResult = match.isValid() ? (wrap ? MatchWrappedForward : MatchFound) :
pattern.isEmpty() ? MatchNothing :
MatchMismatch;
const Range selectionRange = pattern.isEmpty() ? Range(m_incInitCursor, m_incInitCursor) :
match.isValid() ? match.range() :
Range::invalid();
// don't update m_incInitCursor when we move the cursor
disconnect(m_view, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)),
this, SLOT(updateIncInitCursor()));
selectRange2(selectionRange);
connect(m_view, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)),
this, SLOT(updateIncInitCursor()));
indicateMatch(matchResult);
}
void KateSearchBar::setMatchCase(bool matchCase) {
if (this->matchCase() == matchCase)
return;
if (isPower())
m_powerUi->matchCase->setChecked(matchCase);
else
m_incUi->matchCase->setChecked(matchCase);
}
void KateSearchBar::onMatchCaseToggled(bool /*matchCase*/) {
sendConfig();
if (m_incUi != 0) {
// Re-search with new settings
const QString pattern = m_incUi->pattern->currentText();
onIncPatternChanged(pattern);
} else {
indicateMatch(MatchNothing);
}
}
bool KateSearchBar::matchCase() const
{
return isPower() ? m_powerUi->matchCase->isChecked()
: m_incUi->matchCase->isChecked();
}
void KateSearchBar::fixForSingleLine(Range & range, SearchDirection searchDirection) {
FAST_DEBUG("Single-line workaround checking BEFORE" << range);
if (searchDirection == SearchForward) {
const int line = range.start().line();
const int col = range.start().column();
const int maxColWithNewline = m_view->document()->lineLength(line) + 1;
if (col == maxColWithNewline) {
FAST_DEBUG("Starting on a newline" << range);
const int maxLine = m_view->document()->lines() - 1;
if (line < maxLine) {
range.setRange(Cursor(line + 1, 0), range.end());
FAST_DEBUG("Search range fixed to " << range);
} else {
FAST_DEBUG("Already at last line");
range = Range::invalid();
}
}
} else {
const int col = range.end().column();
if (col == 0) {
FAST_DEBUG("Ending after a newline" << range);
const int line = range.end().line();
if (line > 0) {
const int maxColWithNewline = m_view->document()->lineLength(line - 1);
range.setRange(range.start(), Cursor(line - 1, maxColWithNewline));
FAST_DEBUG("Search range fixed to " << range);
} else {
FAST_DEBUG("Already at first line");
range = Range::invalid();
}
}
}
FAST_DEBUG("Single-line workaround checking AFTER" << range);
}
void KateSearchBar::onReturnPressed() {
const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
const bool shiftDown = (modifiers & Qt::ShiftModifier) != 0;
const bool controlDown = (modifiers & Qt::ControlModifier) != 0;
if (shiftDown) {
// Shift down, search backwards
findPrevious();
} else {
// Shift up, search forwards
findNext();
}
if (controlDown) {
emit hideMe();
}
}
bool KateSearchBar::find(SearchDirection searchDirection, const QString * replacement) {
// What to find?
if (searchPattern().isEmpty()) {
return false; // == Pattern error
}
// don't let selectionChanged signal mess around in this routine
disconnect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(updateSelectionOnly()));
// clear previous highlights if there are any
clearHighlights();
const Search::SearchOptions enabledOptions = searchOptions(searchDirection);
// Where to find?
Range inputRange;
const Range selection = m_view->selection() ? m_view->selectionRange() : Range::invalid();
if (selection.isValid()) {
if (selectionOnly()) {
// First match in selection
inputRange = selection;
} else {
// Next match after/before selection if a match was selected before
if (searchDirection == SearchForward) {
inputRange.setRange(selection.start(), m_view->document()->documentEnd());
} else {
inputRange.setRange(Cursor(0, 0), selection.end());
}
}
} else {
// No selection
const Cursor cursorPos = m_view->cursorPosition();
if (searchDirection == SearchForward) {
inputRange.setRange(cursorPos, m_view->document()->documentEnd());
} else {
inputRange.setRange(Cursor(0, 0), cursorPos);
}
}
FAST_DEBUG("Search range is" << inputRange);
{
const bool regexMode = enabledOptions.testFlag(Search::Regex);
const bool multiLinePattern = regexMode ? KateRegExp(searchPattern()).isMultiLine() : false;
// Single-line pattern workaround
if (regexMode && !multiLinePattern) {
fixForSingleLine(inputRange, searchDirection);
}
}
KateMatch match(m_view->doc(), enabledOptions);
Range afterReplace = Range::invalid();
// Find, first try
match.searchText(inputRange, searchPattern());
if (match.isValid() && match.range() == selection) {
// Same match again
if (replacement != 0) {
// Selection is match -> replace
KTextEditor::MovingRange *smartInputRange = m_view->doc()->newMovingRange (inputRange, KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight);
afterReplace = match.replace(*replacement, m_view->blockSelection());
inputRange = smartInputRange->toRange();
delete smartInputRange;
}
if (!selectionOnly()) {
// Find, second try after old selection
if (searchDirection == SearchForward) {
const Cursor start = (replacement != 0) ? afterReplace.end() : selection.end();
inputRange.setRange(start, inputRange.end());
} else {
const Cursor end = (replacement != 0) ? afterReplace.start() : selection.start();
inputRange.setRange(inputRange.start(), end);
}
}
// Single-line pattern workaround
fixForSingleLine(inputRange, searchDirection);
match.searchText(inputRange, searchPattern());
}
bool askWrap = !match.isValid() && (!selection.isValid() || !selectionOnly());
bool wrap = false;
KateMatch matchAfterWarp(m_view->doc(), enabledOptions);
if (askWrap) {
matchAfterWarp.searchText(m_view->document()->documentRange(), searchPattern());
if (!matchAfterWarp.isValid())
askWrap = false;
}
if (askWrap) {
if (m_unitTestMode) {
wrap = true;
}
else {
QString question = searchDirection == SearchForward ? i18n("Bottom of file reached. Continue from top?")
: i18n("Top of file reached. Continue from bottom?");
wrap = (KMessageBox::questionYesNo( 0, question, i18n("Continue search?"), KStandardGuiItem::yes(), KStandardGuiItem::no(),
QString("DoNotShowAgainContinueSearchDialog")) == KMessageBox::Yes );
}
}
if (wrap) {
match = matchAfterWarp;
}
if (match.isValid()) {
selectRange2(match.range());
}
const MatchResult matchResult = !match.isValid() ? MatchMismatch :
!wrap ? MatchFound :
searchDirection == SearchForward ? MatchWrappedForward :
MatchWrappedBackward;
indicateMatch(matchResult);
// highlight replacements if applicable
if (afterReplace.isValid()) {
highlightReplacement(afterReplace);
}
// restore connection
connect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(updateSelectionOnly()));
return true; // == No pattern error
}
void KateSearchBar::findAll()
{
// clear highlightings of prior search&replace action
clearHighlights();
Range inputRange = (m_view->selection() && selectionOnly())
? m_view->selectionRange()
: m_view->document()->documentRange();
const int occurrences = findAll(inputRange, NULL);
// send passive notification to view
showInfoMessage(i18ncp("short translation", "1 match found", "%1 matches found", occurrences));
indicateMatch(occurrences > 0 ? MatchFound : MatchMismatch);
}
void KateSearchBar::onPowerPatternChanged(const QString & /*pattern*/) {
givePatternFeedback();
indicateMatch(MatchNothing);
}
bool KateSearchBar::isPatternValid() const {
if (searchPattern().isEmpty())
return false;
return searchOptions().testFlag(Search::WholeWords) ? searchPattern().trimmed() == searchPattern() :
searchOptions().testFlag(Search::Regex) ? QRegExp(searchPattern()).isValid() :
true;
}
void KateSearchBar::givePatternFeedback() {
// Enable/disable next/prev and replace next/all
m_powerUi->findNext->setEnabled(isPatternValid());
m_powerUi->findPrev->setEnabled(isPatternValid());
m_powerUi->replaceNext->setEnabled(isPatternValid());
m_powerUi->replaceAll->setEnabled(isPatternValid());
}
void KateSearchBar::addCurrentTextToHistory(QComboBox * combo) {
const QString text = combo->currentText();
const int index = combo->findText(text);
if (index > 0)
combo->removeItem(index);
if (index != 0) {
combo->insertItem(0, text);
combo->setCurrentIndex(0);
}
}
void KateSearchBar::backupConfig(bool ofPower) {
if (ofPower) {
m_powerMatchCase = m_powerUi->matchCase->isChecked();
m_powerMode = m_powerUi->searchMode->currentIndex();
} else {
m_incMatchCase = m_incUi->matchCase->isChecked();
}
}
void KateSearchBar::sendConfig() {
const long pastFlags = m_config->searchFlags();
long futureFlags = pastFlags;
if (m_powerUi != NULL) {
const bool OF_POWER = true;
backupConfig(OF_POWER);
// Update power search flags only
const long incFlagsOnly = pastFlags
& (KateViewConfig::IncHighlightAll
| KateViewConfig::IncFromCursor
| KateViewConfig::IncMatchCase);
futureFlags = incFlagsOnly
| (m_powerMatchCase ? KateViewConfig::PowerMatchCase : 0)
| (m_powerFromCursor ? KateViewConfig::PowerFromCursor : 0)
| (m_powerHighlightAll ? KateViewConfig::PowerHighlightAll : 0)
| ((m_powerMode == MODE_REGEX)
? KateViewConfig::PowerModeRegularExpression
: ((m_powerMode == MODE_ESCAPE_SEQUENCES)
? KateViewConfig::PowerModeEscapeSequences
: ((m_powerMode == MODE_WHOLE_WORDS)
? KateViewConfig::PowerModeWholeWords
: KateViewConfig::PowerModePlainText)));
} else if (m_incUi != NULL) {
const bool OF_INCREMENTAL = false;
backupConfig(OF_INCREMENTAL);
// Update incremental search flags only
const long powerFlagsOnly = pastFlags
& (KateViewConfig::PowerMatchCase
| KateViewConfig::PowerFromCursor
| KateViewConfig::PowerHighlightAll
| KateViewConfig::PowerModeRegularExpression
| KateViewConfig::PowerModeEscapeSequences
| KateViewConfig::PowerModeWholeWords
| KateViewConfig::PowerModePlainText);
futureFlags = powerFlagsOnly
| (m_incHighlightAll ? KateViewConfig::IncHighlightAll : 0)
| (m_incFromCursor ? KateViewConfig::IncFromCursor : 0)
| (m_incMatchCase ? KateViewConfig::IncMatchCase : 0);
}
// Adjust global config
m_config->setSearchFlags(futureFlags);
}
void KateSearchBar::replaceNext() {
const QString replacement = m_powerUi->replacement->currentText();
if (find(SearchForward, &replacement)) {
// Never merge replace actions with other replace actions/user actions
m_view->doc()->undoManager()->undoSafePoint();
// Add to search history
addCurrentTextToHistory(m_powerUi->pattern);
// Add to replace history
addCurrentTextToHistory(m_powerUi->replacement);
}
}
// replacement == NULL --> Highlight all matches
// replacement != NULL --> Replace and highlight all matches
int KateSearchBar::findAll(Range inputRange, const QString * replacement)
{
// don't let selectionChanged signal mess around in this routine
disconnect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(updateSelectionOnly()));
const Search::SearchOptions enabledOptions = searchOptions(SearchForward);
const bool regexMode = enabledOptions.testFlag(Search::Regex);
const bool multiLinePattern = regexMode ? KateRegExp(searchPattern()).isMultiLine() : false;
KTextEditor::MovingRange * workingRange = m_view->doc()->newMovingRange(inputRange);
QList<Range> highlightRanges;
int matchCounter = 0;
bool block = m_view->selection() && m_view->blockSelection();
int line = inputRange.start().line();
do {
if (block)
workingRange = m_view->doc()->newMovingRange(m_view->doc()->rangeOnLine(inputRange, line));
for (;;) {
KateMatch match(m_view->doc(), enabledOptions);
match.searchText(workingRange->toRange(), searchPattern());
if (!match.isValid()) {
break;
}
bool const originalMatchEmpty = match.isEmpty();
// Work with the match
if (replacement != NULL) {
if (matchCounter == 0) {
m_view->document()->startEditing();
}
// Replace
const Range afterReplace = match.replace(*replacement, false, ++matchCounter);
// Highlight and continue after adjusted match
//highlightReplacement(*afterReplace);
highlightRanges << afterReplace;
} else {
// Highlight and continue after original match
//highlightMatch(match);
highlightRanges << match.range();
matchCounter++;
}
// Continue after match
if (highlightRanges.last().end() >= workingRange->end().toCursor())
break;
KTextEditor::MovingCursor* workingStart =
static_cast<KateDocument*>(m_view->document())->newMovingCursor(highlightRanges.last().end());
if (originalMatchEmpty) {
// Can happen for regex patterns like "^".
// If we don't advance here we will loop forever...
workingStart->move(1);
} else if (regexMode && !multiLinePattern && workingStart->atEndOfLine()) {
// single-line regexps might match the naked line end
// therefore we better advance to the next line
workingStart->move(1);
}
workingRange->setRange(workingStart->toCursor(), workingRange->end().toCursor());
const bool atEndOfDocument = workingStart->atEndOfDocument();
delete workingStart;
// Are we done?
if (!workingRange->toRange().isValid() || atEndOfDocument) {
break;
}
}
} while (block && ++line <= inputRange.end().line());
// After last match
if (matchCounter > 0) {
if (replacement != NULL) {
m_view->document()->endEditing();
}
}
if (replacement == NULL)
foreach (const Range &r, highlightRanges) {
highlightMatch(r);
}
else
foreach (const Range &r, highlightRanges) {
highlightReplacement(r);
}
delete workingRange;
// restore connection
connect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(updateSelectionOnly()));
return matchCounter;
}
void KateSearchBar::replaceAll()
{
// clear prior highlightings (deletes info message if present)
clearHighlights();
// What to find/replace?
const QString replacement = m_powerUi->replacement->currentText();
// Where to replace?
Range selection;
const bool selected = m_view->selection();
Range inputRange = (selected && selectionOnly())
? m_view->selectionRange()
: m_view->document()->documentRange();
// Pass on the hard work
int replacementsDone=findAll(inputRange, &replacement);
// send passive notification to view
showInfoMessage(i18ncp("short translation", "1 replacement made", "%1 replacements made", replacementsDone));
// Never merge replace actions with other replace actions/user actions
m_view->doc()->undoManager()->undoSafePoint();
// Add to search history
addCurrentTextToHistory(m_powerUi->pattern);
// Add to replace history
addCurrentTextToHistory(m_powerUi->replacement);
}
void KateSearchBar::setSearchPattern(const QString &searchPattern)
{
if (searchPattern == this->searchPattern())
return;
if (isPower())
m_powerUi->pattern->setEditText(searchPattern);
else
m_incUi->pattern->setEditText(searchPattern);
}
QString KateSearchBar::searchPattern() const {
return (m_powerUi != 0) ? m_powerUi->pattern->currentText()
: m_incUi->pattern->currentText();
}
void KateSearchBar::setSelectionOnly(bool selectionOnly)
{
if (this->selectionOnly() == selectionOnly)
return;
if (isPower())
m_powerUi->selectionOnly->setChecked(selectionOnly);
}
bool KateSearchBar::selectionOnly() const {
return isPower() ? m_powerUi->selectionOnly->isChecked()
: false;
}
KTextEditor::Search::SearchOptions KateSearchBar::searchOptions(SearchDirection searchDirection) const {
Search::SearchOptions enabledOptions = KTextEditor::Search::Default;
if (!matchCase()) {
enabledOptions |= Search::CaseInsensitive;
}
if (searchDirection == SearchBackward) {
enabledOptions |= Search::Backwards;
}
if (m_powerUi != NULL) {
switch (m_powerUi->searchMode->currentIndex()) {
case MODE_WHOLE_WORDS:
enabledOptions |= Search::WholeWords;
break;
case MODE_ESCAPE_SEQUENCES:
enabledOptions |= Search::EscapeSequences;
break;
case MODE_REGEX:
enabledOptions |= Search::Regex;
break;
case MODE_PLAIN_TEXT: // FALLTHROUGH
default:
break;
}
}
return enabledOptions;
}
struct ParInfo {
int openIndex;
bool capturing;
int captureNumber; // 1..9
};
QVector<QString> KateSearchBar::getCapturePatterns(const QString & pattern) const {
QVector<QString> capturePatterns;
capturePatterns.reserve(9);
QStack<ParInfo> parInfos;
const int inputLen = pattern.length();
int input = 0; // walker index
bool insideClass = false;
int captureCount = 0;
while (input < inputLen) {
if (insideClass) {
// Wait for closing, unescaped ']'
if (pattern[input].unicode() == L']') {
insideClass = false;
}
input++;
}
else
{
switch (pattern[input].unicode())
{
case L'\\':
// Skip this and any next character
input += 2;
break;
case L'(':
ParInfo curInfo;
curInfo.openIndex = input;
curInfo.capturing = (input + 1 >= inputLen) || (pattern[input + 1].unicode() != '?');
if (curInfo.capturing) {
captureCount++;
}
curInfo.captureNumber = captureCount;
parInfos.push(curInfo);
input++;
break;
case L')':
if (!parInfos.empty()) {
ParInfo & top = parInfos.top();
if (top.capturing && (top.captureNumber <= 9)) {
const int start = top.openIndex + 1;
const int len = input - start;
if (capturePatterns.size() < top.captureNumber) {
capturePatterns.resize(top.captureNumber);
}
capturePatterns[top.captureNumber - 1] = pattern.mid(start, len);
}
parInfos.pop();
}
input++;
break;
case L'[':
input++;
insideClass = true;
break;
default:
input++;
break;
}
}
}
return capturePatterns;
}
void KateSearchBar::showExtendedContextMenu(bool forPattern, const QPoint& pos) {
// Make original menu
QComboBox* comboBox = forPattern ? m_powerUi->pattern : m_powerUi->replacement;
QMenu* const contextMenu = comboBox->lineEdit()->createStandardContextMenu();
if (contextMenu == NULL) {
return;
}
bool extendMenu = false;
bool regexMode = false;
switch (m_powerUi->searchMode->currentIndex()) {
case MODE_REGEX:
regexMode = true;
// FALLTHROUGH
case MODE_ESCAPE_SEQUENCES:
extendMenu = true;
break;
default:
break;
}
AddMenuManager addMenuManager(contextMenu, 37);
if (!extendMenu) {
addMenuManager.enableMenu(extendMenu);
} else {
// Build menu
if (forPattern) {
if (regexMode) {
addMenuManager.addEntry("^", "", i18n("Beginning of line"));
addMenuManager.addEntry("$", "", i18n("End of line"));
addMenuManager.addSeparator();
addMenuManager.addEntry(".", "", i18n("Any single character (excluding line breaks)"));
addMenuManager.addSeparator();
addMenuManager.addEntry("+", "", i18n("One or more occurrences"));
addMenuManager.addEntry("*", "", i18n("Zero or more occurrences"));
addMenuManager.addEntry("?", "", i18n("Zero or one occurrences"));
addMenuManager.addEntry("{a", ",b}", i18n("<a> through <b> occurrences"), "{", ",}");
addMenuManager.addSeparator();
addMenuManager.addEntry("(", ")", i18n("Group, capturing"));
addMenuManager.addEntry("|", "", i18n("Or"));
addMenuManager.addEntry("[", "]", i18n("Set of characters"));
addMenuManager.addEntry("[^", "]", i18n("Negative set of characters"));
addMenuManager.addSeparator();
}
} else {
addMenuManager.addEntry("\\0", "", i18n("Whole match reference"));
addMenuManager.addSeparator();
if (regexMode) {
const QString pattern = m_powerUi->pattern->currentText();
const QVector<QString> capturePatterns = getCapturePatterns(pattern);
const int captureCount = capturePatterns.count();
for (int i = 1; i <= 9; i++) {
const QString number = QString::number(i);
const QString & captureDetails = (i <= captureCount)
? (QString(" = (") + capturePatterns[i - 1].left(30)) + QString(")")
: QString();
addMenuManager.addEntry("\\" + number, "",
i18n("Reference") + ' ' + number + captureDetails);
}
addMenuManager.addSeparator();
}
}
addMenuManager.addEntry("\\n", "", i18n("Line break"));
addMenuManager.addEntry("\\t", "", i18n("Tab"));
if (forPattern && regexMode) {
addMenuManager.addEntry("\\b", "", i18n("Word boundary"));
addMenuManager.addEntry("\\B", "", i18n("Not word boundary"));
addMenuManager.addEntry("\\d", "", i18n("Digit"));
addMenuManager.addEntry("\\D", "", i18n("Non-digit"));
addMenuManager.addEntry("\\s", "", i18n("Whitespace (excluding line breaks)"));
addMenuManager.addEntry("\\S", "", i18n("Non-whitespace (excluding line breaks)"));
addMenuManager.addEntry("\\w", "", i18n("Word character (alphanumerics plus '_')"));
addMenuManager.addEntry("\\W", "", i18n("Non-word character"));
}
addMenuManager.addEntry("\\0???", "", i18n("Octal character 000 to 377 (2^8-1)"), "\\0");
addMenuManager.addEntry("\\x????", "", i18n("Hex character 0000 to FFFF (2^16-1)"), "\\x");
addMenuManager.addEntry("\\\\", "", i18n("Backslash"));
if (forPattern && regexMode) {
addMenuManager.addSeparator();
addMenuManager.addEntry("(?:E", ")", i18n("Group, non-capturing"), "(?:");
addMenuManager.addEntry("(?=E", ")", i18n("Lookahead"), "(?=");
addMenuManager.addEntry("(?!E", ")", i18n("Negative lookahead"), "(?!");
}
if (!forPattern) {
addMenuManager.addSeparator();
addMenuManager.addEntry("\\L", "", i18n("Begin lowercase conversion"));
addMenuManager.addEntry("\\U", "", i18n("Begin uppercase conversion"));
addMenuManager.addEntry("\\E", "", i18n("End case conversion"));
addMenuManager.addEntry("\\l", "", i18n("Lowercase first character conversion"));
addMenuManager.addEntry("\\u", "", i18n("Uppercase first character conversion"));
addMenuManager.addEntry("\\#[#..]", "", i18n("Replacement counter (for Replace All)"), "\\#");
}
}
// Show menu
QAction * const result = contextMenu->exec(comboBox->mapToGlobal(pos));
if (result != NULL) {
addMenuManager.handle(result, comboBox->lineEdit());
}
}
void KateSearchBar::onPowerModeChanged(int /*index*/) {
if (m_powerUi->searchMode->currentIndex() == MODE_REGEX) {
m_powerUi->matchCase->setChecked(true);
}
sendConfig();
indicateMatch(MatchNothing);
givePatternFeedback();
}
/*static*/ void KateSearchBar::nextMatchForSelection(KateView * view, SearchDirection searchDirection) {
const bool selected = view->selection();
if (selected) {
const QString pattern = view->selectionText();
// How to find?
Search::SearchOptions enabledOptions(KTextEditor::Search::Default);
if (searchDirection == SearchBackward) {
enabledOptions |= Search::Backwards;
}
// Where to find?
const Range selRange = view->selectionRange();
Range inputRange;
if (searchDirection == SearchForward) {
inputRange.setRange(selRange.end(), view->doc()->documentEnd());
} else {
inputRange.setRange(Cursor(0, 0), selRange.start());
}
// Find, first try
KateMatch match(view->doc(), enabledOptions);
match.searchText(inputRange, pattern);
if (match.isValid()) {
selectRange(view, match.range());
} else {
// Find, second try
if (searchDirection == SearchForward) {
inputRange.setRange(Cursor(0, 0), selRange.start());
} else {
inputRange.setRange(selRange.end(), view->doc()->documentEnd());
}
KateMatch match2(view->doc(), enabledOptions);
match2.searchText(inputRange, pattern);
if (match2.isValid()) {
selectRange(view, match2.range());
}
}
} else {
// Select current word so we can search for that the next time
const Cursor cursorPos = view->cursorPosition();
view->selectWord(cursorPos);
}
}
void KateSearchBar::enterPowerMode() {
QString initialPattern;
bool selectionOnly = false;
// Guess settings from context: init pattern with current selection
const bool selected = m_view->selection();
if (selected) {
const Range & selection = m_view->selectionRange();
if (selection.onSingleLine()) {
// ... with current selection
initialPattern = m_view->selectionText();
} else {
// Enable selection only
selectionOnly = true;
}
}
// If there's no new selection, we'll use the existing pattern
if (initialPattern.isNull()) {
// Coming from power search?
const bool fromReplace = (m_powerUi != NULL) && (m_widget->isVisible());
if (fromReplace) {
QLineEdit * const patternLineEdit = m_powerUi->pattern->lineEdit();
Q_ASSERT(patternLineEdit != NULL);
patternLineEdit->selectAll();
m_powerUi->pattern->setFocus(Qt::MouseFocusReason);
return;
}
// Coming from incremental search?
const bool fromIncremental = (m_incUi != NULL) && (m_widget->isVisible());
if (fromIncremental) {
initialPattern = m_incUi->pattern->currentText();
}
}
// Create dialog
const bool create = (m_powerUi == NULL);
if (create) {
// Kill incremental widget
if (m_incUi != NULL) {
// Backup current settings
const bool OF_INCREMENTAL = false;
backupConfig(OF_INCREMENTAL);
// Kill widget
delete m_incUi;
m_incUi = NULL;
m_layout->removeWidget(m_widget);
m_widget->deleteLater(); // I didn't get a crash here but for symmetrie to the other mutate slot^
}
// Add power widget
m_widget = new QWidget(this);
m_powerUi = new Ui::PowerSearchBar;
m_powerUi->setupUi(m_widget);
m_layout->addWidget(m_widget);
// Bind to shared history models
m_powerUi->pattern->setDuplicatesEnabled(false);
m_powerUi->pattern->setInsertPolicy(QComboBox::InsertAtTop);
m_powerUi->pattern->setMaxCount(m_config->maxHistorySize());
m_powerUi->pattern->setModel(m_config->patternHistoryModel());
m_powerUi->replacement->setDuplicatesEnabled(false);
m_powerUi->replacement->setInsertPolicy(QComboBox::InsertAtTop);
m_powerUi->replacement->setMaxCount(m_config->maxHistorySize());
m_powerUi->replacement->setModel(m_config->replacementHistoryModel());
// Icons
m_powerUi->mutate->setIcon(KIcon("arrow-down-double"));
m_powerUi->findNext->setIcon(KIcon("go-down-search"));
m_powerUi->findPrev->setIcon(KIcon("go-up-search"));
m_powerUi->findAll->setIcon(KIcon("edit-find"));
// Focus proxy
centralWidget()->setFocusProxy(m_powerUi->pattern);
// Make completers case-sensitive
m_powerUi->pattern->completionObject()->setIgnoreCase(false);
m_powerUi->replacement->completionObject()->setIgnoreCase(false);
}
m_powerUi->selectionOnly->setChecked(selectionOnly);
// Restore previous settings
if (create) {
m_powerUi->matchCase->setChecked(m_powerMatchCase);
m_powerUi->searchMode->setCurrentIndex(m_powerMode);
}
// force current index of -1 --> <cursor down> shows 1st completion entry instead of 2nd
m_powerUi->pattern->setCurrentIndex(-1);
m_powerUi->replacement->setCurrentIndex(-1);
// Set initial search pattern
QLineEdit * const patternLineEdit = m_powerUi->pattern->lineEdit();
Q_ASSERT(patternLineEdit != NULL);
patternLineEdit->setText(initialPattern);
patternLineEdit->selectAll();
// Set initial replacement text
QLineEdit * const replacementLineEdit = m_powerUi->replacement->lineEdit();
Q_ASSERT(replacementLineEdit != NULL);
replacementLineEdit->setText("");
// Propagate settings (slots are still inactive on purpose)
onPowerPatternChanged(initialPattern);
givePatternFeedback();
if (create) {
// Slots
connect(m_powerUi->mutate, SIGNAL(clicked()), this, SLOT(enterIncrementalMode()));
connect(patternLineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPowerPatternChanged(QString)));
connect(m_powerUi->findNext, SIGNAL(clicked()), this, SLOT(findNext()));
connect(m_powerUi->findPrev, SIGNAL(clicked()), this, SLOT(findPrevious()));
connect(m_powerUi->replaceNext, SIGNAL(clicked()), this, SLOT(replaceNext()));
connect(m_powerUi->replaceAll, SIGNAL(clicked()), this, SLOT(replaceAll()));
connect(m_powerUi->searchMode, SIGNAL(currentIndexChanged(int)), this, SLOT(onPowerModeChanged(int)));
connect(m_powerUi->matchCase, SIGNAL(toggled(bool)), this, SLOT(onMatchCaseToggled(bool)));
connect(m_powerUi->findAll, SIGNAL(clicked()), this, SLOT(findAll()));
// Make [return] in pattern line edit trigger <find next> action
connect(patternLineEdit, SIGNAL(returnPressed()), this, SLOT(onReturnPressed()));
connect(replacementLineEdit, SIGNAL(returnPressed()), this, SLOT(replaceNext()));
// Hook into line edit context menus
m_powerUi->pattern->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_powerUi->pattern, SIGNAL(customContextMenuRequested(QPoint)), this,
SLOT(onPowerPatternContextMenuRequest(QPoint)));
m_powerUi->replacement->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_powerUi->replacement, SIGNAL(customContextMenuRequested(QPoint)), this,
SLOT(onPowerReplacmentContextMenuRequest(QPoint)));
}
// Focus
if (m_widget->isVisible()) {
m_powerUi->pattern->setFocus(Qt::MouseFocusReason);
}
}
void KateSearchBar::enterIncrementalMode() {
QString initialPattern;
// Guess settings from context: init pattern with current selection
const bool selected = m_view->selection();
if (selected) {
const Range & selection = m_view->selectionRange();
if (selection.onSingleLine()) {
// ... with current selection
initialPattern = m_view->selectionText();
}
}
// If there's no new selection, we'll use the existing pattern
if (initialPattern.isNull()) {
// Coming from incremental search?
const bool fromIncremental = (m_incUi != NULL) && (m_widget->isVisible());
if (fromIncremental) {
m_incUi->pattern->lineEdit()->selectAll();
m_incUi->pattern->setFocus(Qt::MouseFocusReason);
return;
}
// Coming from power search?
const bool fromReplace = (m_powerUi != NULL) && (m_widget->isVisible());
if (fromReplace) {
initialPattern = m_powerUi->pattern->currentText();
}
}
// Still no search pattern? Use the word under the cursor
if (initialPattern.isNull()) {
const KTextEditor::Cursor cursorPosition = m_view->cursorPosition();
initialPattern = m_view->doc()->getWord( cursorPosition );
}
// Create dialog
const bool create = (m_incUi == NULL);
if (create) {
// Kill power widget
if (m_powerUi != NULL) {
// Backup current settings
const bool OF_POWER = true;
backupConfig(OF_POWER);
// Kill widget
delete m_powerUi;
m_powerUi = NULL;
m_layout->removeWidget(m_widget);
m_widget->deleteLater(); //deleteLater, because it's not a good idea too delete the widget and there for the button triggering this slot
}
// Add incremental widget
m_widget = new QWidget(this);
m_incUi = new Ui::IncrementalSearchBar;
m_incUi->setupUi(m_widget);
m_layout->addWidget(m_widget);
// new QShortcut(KStandardShortcut::paste().primary(), m_incUi->pattern, SLOT(paste()), 0, Qt::WidgetWithChildrenShortcut);
// if (!KStandardShortcut::paste().alternate().isEmpty())
// new QShortcut(KStandardShortcut::paste().alternate(), m_incUi->pattern, SLOT(paste()), 0, Qt::WidgetWithChildrenShortcut);
// Icons
m_incUi->mutate->setIcon(KIcon("arrow-up-double"));
m_incUi->next->setIcon(KIcon("go-down-search"));
m_incUi->prev->setIcon(KIcon("go-up-search"));
// Ensure minimum size
m_incUi->pattern->setMinimumWidth(12 * m_incUi->pattern->fontMetrics().height());
// Focus proxy
centralWidget()->setFocusProxy(m_incUi->pattern);
m_incUi->pattern->setDuplicatesEnabled(false);
m_incUi->pattern->setInsertPolicy(QComboBox::InsertAtTop);
m_incUi->pattern->setMaxCount(m_config->maxHistorySize());
m_incUi->pattern->setModel(m_config->patternHistoryModel());
m_incUi->pattern->setAutoCompletion(false);
}
// Restore previous settings
if (create) {
m_incUi->matchCase->setChecked(m_incMatchCase);
}
// force current index of -1 --> <cursor down> shows 1st completion entry instead of 2nd
m_incUi->pattern->setCurrentIndex(-1);
// Set initial search pattern
if (!create)
disconnect(m_incUi->pattern, SIGNAL(editTextChanged(QString)), this, SLOT(onIncPatternChanged(QString)));
m_incUi->pattern->setEditText(initialPattern);
connect(m_incUi->pattern, SIGNAL(editTextChanged(QString)), this, SLOT(onIncPatternChanged(QString)));
m_incUi->pattern->lineEdit()->selectAll();
// Propagate settings (slots are still inactive on purpose)
if (initialPattern.isEmpty()) {
// Reset edit color
indicateMatch(MatchNothing);
}
// Enable/disable next/prev
m_incUi->next->setDisabled(initialPattern.isEmpty());
m_incUi->prev->setDisabled(initialPattern.isEmpty());
if (create) {
// Slots
connect(m_incUi->mutate, SIGNAL(clicked()), this, SLOT(enterPowerMode()));
connect(m_incUi->pattern->lineEdit(), SIGNAL(returnPressed()), this, SLOT(onReturnPressed()));
connect(m_incUi->next, SIGNAL(clicked()), this, SLOT(findNext()));
connect(m_incUi->prev, SIGNAL(clicked()), this, SLOT(findPrevious()));
connect(m_incUi->matchCase, SIGNAL(toggled(bool)), this, SLOT(onMatchCaseToggled(bool)));
}
// Focus
if (m_widget->isVisible()) {
m_incUi->pattern->setFocus(Qt::MouseFocusReason);
}
}
bool KateSearchBar::clearHighlights()
{
if (m_infoMessage) {
delete m_infoMessage;
}
if (m_hlRanges.isEmpty()) {
return false;
}
qDeleteAll(m_hlRanges);
m_hlRanges.clear();
return true;
}
void KateSearchBar::updateHighlightColors()
{
const QColor& searchColor = m_view->renderer()->config()->searchHighlightColor();
const QColor& replaceColor = m_view->renderer()->config()->replaceHighlightColor();
// init match attribute
highlightMatchAttribute->setBackground(searchColor);
highlightMatchAttribute->dynamicAttribute (Attribute::ActivateMouseIn)->setBackground(searchColor);
highlightMatchAttribute->dynamicAttribute (Attribute::ActivateCaretIn)->setBackground(searchColor);
// init replacement attribute
highlightReplacementAttribute->setBackground(replaceColor);
}
void KateSearchBar::showEvent(QShowEvent * event) {
// Update init cursor
if (m_incUi != NULL) {
m_incInitCursor = m_view->cursorPosition();
}
updateSelectionOnly();
KateViewBarWidget::showEvent(event);
}
void KateSearchBar::updateSelectionOnly() {
if (m_powerUi == NULL) {
return;
}
// Re-init "Selection only" checkbox if power search bar open
const bool selected = m_view->selection();
bool selectionOnly = selected;
if (selected) {
Range const & selection = m_view->selectionRange();
selectionOnly = !selection.onSingleLine();
}
m_powerUi->selectionOnly->setChecked(selectionOnly);
}
void KateSearchBar::updateIncInitCursor() {
if (m_incUi == NULL) {
return;
}
// Update init cursor
m_incInitCursor = m_view->cursorPosition();
}
void KateSearchBar::onPowerPatternContextMenuRequest(const QPoint& pos) {
const bool FOR_PATTERN = true;
showExtendedContextMenu(FOR_PATTERN, pos);
}
void KateSearchBar::onPowerPatternContextMenuRequest() {
onPowerPatternContextMenuRequest(m_powerUi->pattern->mapFromGlobal(QCursor::pos()));
}
void KateSearchBar::onPowerReplacmentContextMenuRequest(const QPoint& pos) {
const bool FOR_REPLACEMENT = false;
showExtendedContextMenu(FOR_REPLACEMENT, pos);
}
void KateSearchBar::onPowerReplacmentContextMenuRequest() {
onPowerReplacmentContextMenuRequest(m_powerUi->replacement->mapFromGlobal(QCursor::pos()));
}
bool KateSearchBar::isPower() const {
return m_powerUi != 0;
}
void KateSearchBar::slotReadWriteChanged ()
{
if (!KateSearchBar::isPower())
return;
// perhaps disable/enable
m_powerUi->replaceNext->setEnabled (m_view->doc()->isReadWrite() && isPatternValid());
m_powerUi->replaceAll->setEnabled (m_view->doc()->isReadWrite() && isPatternValid());
}
// kate: space-indent on; indent-width 4; replace-tabs on;