basefilefind.cpp 22 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
hjk's avatar
hjk committed
25

con's avatar
con committed
26
#include "basefilefind.h"
27
#include "textdocument.h"
con's avatar
con committed
28

29
#include <aggregation/aggregate.h>
30
#include <coreplugin/icore.h>
31
#include <coreplugin/progressmanager/progressmanager.h>
32
#include <coreplugin/progressmanager/futureprogress.h>
33
#include <coreplugin/dialogs/readonlyfilesdialog.h>
34
#include <coreplugin/documentmanager.h>
35
#include <coreplugin/editormanager/editormanager.h>
36
#include <coreplugin/find/ifindsupport.h>
37
#include <texteditor/texteditor.h>
38
#include <texteditor/refactoringchanges.h>
39
#include <utils/algorithm.h>
40
#include <utils/fadingindicator.h>
41
#include <utils/filesearch.h>
42 43
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
con's avatar
con committed
44

45 46 47 48
#include <QDebug>
#include <QSettings>
#include <QHash>
#include <QPair>
49 50 51
#include <QStringListModel>
#include <QFutureWatcher>
#include <QPointer>
52
#include <QComboBox>
53
#include <QLabel>
con's avatar
con committed
54

55 56 57
using namespace Utils;
using namespace Core;

58 59
namespace TextEditor {
namespace Internal {
60

61 62 63 64 65 66 67 68 69 70
namespace {
class InternalEngine : public TextEditor::SearchEngine
{
public:
    InternalEngine() : m_widget(new QWidget) {}
    ~InternalEngine() override { delete m_widget;}
    QString title() const override { return tr("Internal"); }
    QString toolTip() const override { return QString(); }
    QWidget *widget() const override { return m_widget; }
    QVariant parameters() const override { return QVariant(); }
Tim Jenssen's avatar
Tim Jenssen committed
71 72
    void readSettings(QSettings * /*settings*/) override {}
    void writeSettings(QSettings * /*settings*/) const override {}
73 74 75 76 77 78 79 80 81
    QFuture<Utils::FileSearchResultList> executeSearch(
            const TextEditor::FileFindParameters &parameters,
            BaseFileFind *baseFileFind) override
    {
        auto func = parameters.flags & FindRegularExpression
                ? Utils::findInFilesRegExp
                : Utils::findInFiles;

        return func(parameters.text,
82 83
                    baseFileFind->files(parameters.nameFilters, parameters.exclusionFilters,
                                        parameters.additionalParameters),
84 85 86 87 88 89 90 91 92 93 94 95 96
                    textDocumentFlagsForFindFlags(parameters.flags),
                    TextDocument::openedTextDocumentContents());

    }
    Core::IEditor *openEditor(const Core::SearchResultItem &/*item*/,
                              const TextEditor::FileFindParameters &/*parameters*/) override
    {
        return nullptr;
    }

private:
    QWidget *m_widget;
};
97
} // namespace
98

99 100 101 102 103
class SearchEnginePrivate
{
public:
    bool isEnabled = true;
};
104

Orgad Shaneh's avatar
Orgad Shaneh committed
105 106 107 108 109 110 111
class CountingLabel : public QLabel
{
public:
    CountingLabel();
    void updateCount(int count);
};

112 113
class BaseFileFindPrivate
{
114
public:
115
    ~BaseFileFindPrivate() { delete m_internalSearchEngine; }
116
    QPointer<IFindSupport> m_currentFindSupport;
117

118
    QLabel *m_resultLabel = 0;
119
    // models in native path format
120
    QStringListModel m_filterStrings;
121 122
    QStringListModel m_exclusionStrings;
    // current filter in portable path format
123
    QString m_filterSetting;
124
    QString m_exclusionSetting;
125
    QPointer<QComboBox> m_filterCombo;
126
    QPointer<QComboBox> m_exclusionCombo;
127 128
    QVector<SearchEngine *> m_searchEngines;
    SearchEngine *m_internalSearchEngine;
129
    int m_currentSearchEngineIndex = -1;
130
};
131

132 133
} // namespace Internal

134 135 136 137
static void syncComboWithSettings(QComboBox *combo, const QString &setting)
{
    if (!combo)
        return;
138 139
    const QString &nativeSettings = QDir::toNativeSeparators(setting);
    int index = combo->findText(nativeSettings);
140
    if (index < 0)
141
        combo->setEditText(nativeSettings);
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
    else
        combo->setCurrentIndex(index);
}

static void updateComboEntries(QComboBox *combo, bool onTop)
{
    int index = combo->findText(combo->currentText());
    if (index < 0) {
        if (onTop)
            combo->insertItem(0, combo->currentText());
        else
            combo->addItem(combo->currentText());
        combo->setCurrentIndex(combo->findText(combo->currentText()));
    }
}

158
using namespace Internal;
159

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
SearchEngine::SearchEngine()
    : d(new SearchEnginePrivate)
{
}

SearchEngine::~SearchEngine()
{
    delete d;
}

bool SearchEngine::isEnabled() const
{
    return d->isEnabled;
}

void SearchEngine::setEnabled(bool enabled)
{
    if (enabled == d->isEnabled)
        return;
    d->isEnabled = enabled;
    emit enabledChanged(d->isEnabled);
}

183
BaseFileFind::BaseFileFind() : d(new BaseFileFindPrivate)
con's avatar
con committed
184
{
185 186
    d->m_internalSearchEngine = new InternalEngine;
    addSearchEngine(d->m_internalSearchEngine);
con's avatar
con committed
187 188
}

189 190
BaseFileFind::~BaseFileFind()
{
191
    delete d;
192 193
}

con's avatar
con committed
194 195
bool BaseFileFind::isEnabled() const
{
196
    return true;
con's avatar
con committed
197 198
}

199 200 201 202 203 204 205 206 207
static QStringList splitFilterUiText(const QString &text)
{
    const QStringList parts = text.split(',');
    const QStringList trimmedPortableParts = Utils::transform(parts, [](const QString &s) {
        return QDir::fromNativeSeparators(s.trimmed());
    });
    return Utils::filtered(trimmedPortableParts, [](const QString &s) { return !s.isEmpty(); });
}

con's avatar
con committed
208 209
QStringList BaseFileFind::fileNameFilters() const
{
210 211 212 213 214 215 216 217 218 219
    if (d->m_filterCombo)
        return splitFilterUiText(d->m_filterCombo->currentText());
    return QStringList();
}

QStringList BaseFileFind::fileExclusionFilters() const
{
    if (d->m_exclusionCombo)
        return splitFilterUiText(d->m_exclusionCombo->currentText());
    return QStringList();
con's avatar
con committed
220 221
}

222
SearchEngine *BaseFileFind::currentSearchEngine() const
223
{
224 225 226 227 228 229 230 231 232 233 234 235
    if (d->m_searchEngines.isEmpty() || d->m_currentSearchEngineIndex == -1)
        return nullptr;
    return d->m_searchEngines[d->m_currentSearchEngineIndex];
}

QVector<SearchEngine *> BaseFileFind::searchEngines() const
{
    return d->m_searchEngines;
}

void BaseFileFind::setCurrentSearchEngine(int index)
{
236 237
    if (d->m_currentSearchEngineIndex == index)
        return;
238
    d->m_currentSearchEngineIndex = index;
239
    emit currentSearchEngineChanged();
240 241
}

Eike Ziller's avatar
Eike Ziller committed
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
static void displayResult(QFutureWatcher<FileSearchResultList> *watcher,
                          SearchResult *search, int index)
{
    FileSearchResultList results = watcher->resultAt(index);
    QList<SearchResultItem> items;
    foreach (const FileSearchResult &result, results) {
        SearchResultItem item;
        item.path = QStringList() << QDir::toNativeSeparators(result.fileName);
        item.mainRange.begin.line = result.lineNumber;
        item.mainRange.begin.column = result.matchStart;
        item.mainRange.end = item.mainRange.begin;
        item.mainRange.end.column += result.matchLength;
        item.text = result.matchingLine;
        item.useTextEditorFont = true;
        item.userData = result.regexpCapturedTexts;
        items << item;
    }
    search->addResults(items, SearchResult::AddOrdered);
}

262
void BaseFileFind::runNewSearch(const QString &txt, FindFlags findFlags,
263
                                    SearchResultWindow::SearchMode searchMode)
con's avatar
con committed
264
{
265 266 267
    d->m_currentFindSupport = 0;
    if (d->m_filterCombo)
        updateComboEntries(d->m_filterCombo, true);
268 269
    if (d->m_exclusionCombo)
        updateComboEntries(d->m_exclusionCombo, true);
270
    QString tooltip = toolTip();
271 272 273 274 275 276

    SearchResult *search = SearchResultWindow::instance()->startNewSearch(
                label(),
                tooltip.arg(IFindFilter::descriptionForFindFlags(findFlags)),
                txt, searchMode, SearchResultWindow::PreserveCaseEnabled,
                QString::fromLatin1("TextEditor"));
277
    search->setTextToReplace(txt);
278 279 280 281 282
    search->setSearchAgainSupported(true);
    FileFindParameters parameters;
    parameters.text = txt;
    parameters.flags = findFlags;
    parameters.nameFilters = fileNameFilters();
283
    parameters.exclusionFilters = fileExclusionFilters();
284
    parameters.additionalParameters = additionalParameters();
285 286
    parameters.searchEngineParameters = currentSearchEngine()->parameters();
    parameters.searchEngineIndex = d->m_currentSearchEngineIndex;
287
    search->setUserData(qVariantFromValue(parameters));
288 289 290 291 292 293 294
    connect(search, &SearchResult::activated, this, &BaseFileFind::openEditor);
    if (searchMode == SearchResultWindow::SearchAndReplace)
        connect(search, &SearchResult::replaceButtonClicked, this, &BaseFileFind::doReplace);
    connect(search, &SearchResult::visibilityChanged, this, &BaseFileFind::hideHighlightAll);
    connect(search, &SearchResult::searchAgainRequested, this, &BaseFileFind::searchAgain);
    connect(this, &BaseFileFind::enabledChanged, search, &SearchResult::requestEnabledCheck);
    connect(search, &SearchResult::requestEnabledCheck, this, &BaseFileFind::recheckEnabled);
295

296 297 298
    runSearch(search);
}

299
void BaseFileFind::runSearch(SearchResult *search)
300 301
{
    FileFindParameters parameters = search->userData().value<FileFindParameters>();
302
    CountingLabel *label = new CountingLabel;
303
    connect(search, &SearchResult::countChanged, label, &CountingLabel::updateCount);
304
    CountingLabel *statusLabel = new CountingLabel;
305
    connect(search, &SearchResult::countChanged, statusLabel, &CountingLabel::updateCount);
306
    SearchResultWindow::instance()->popup(IOutputPane::Flags(IOutputPane::ModeSwitch|IOutputPane::WithFocus));
307 308
    QFutureWatcher<FileSearchResultList> *watcher = new QFutureWatcher<FileSearchResultList>();
    watcher->setPendingResultsLimit(1);
Eike Ziller's avatar
Eike Ziller committed
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
    // search is deleted if it is removed from search panel
    connect(search, &QObject::destroyed, watcher, &QFutureWatcherBase::cancel);
    connect(search, &SearchResult::cancelled, watcher, &QFutureWatcherBase::cancel);
    connect(search, &SearchResult::paused, watcher, [watcher](bool paused) {
        if (!paused || watcher->isRunning()) // guard against pausing when the search is finished
            watcher->setPaused(paused);
    });
    connect(watcher, &QFutureWatcherBase::resultReadyAt, search, [watcher, search](int index) {
        displayResult(watcher, search, index);
    });
    // auto-delete:
    connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater);
    connect(watcher, &QFutureWatcherBase::finished, search, [watcher, search]() {
        search->finishSearch(watcher->isCanceled());
    });
Orgad Shaneh's avatar
Orgad Shaneh committed
324
    watcher->setFuture(executeSearch(parameters));
325
    FutureProgress *progress =
326
        ProgressManager::addTask(watcher->future(), tr("Searching"), Constants::TASK_SEARCH);
327
    progress->setWidget(label);
328
    progress->setStatusBarWidget(statusLabel);
329
    connect(progress, &FutureProgress::clicked, search, &SearchResult::popup);
con's avatar
con committed
330 331
}

332
void BaseFileFind::findAll(const QString &txt, FindFlags findFlags)
333 334 335 336
{
    runNewSearch(txt, findFlags, SearchResultWindow::SearchOnly);
}

337
void BaseFileFind::replaceAll(const QString &txt, FindFlags findFlags)
338
{
339
    runNewSearch(txt, findFlags, SearchResultWindow::SearchAndReplace);
340 341
}

342
void BaseFileFind::addSearchEngine(SearchEngine *searchEngine)
343
{
344
    d->m_searchEngines.push_back(searchEngine);
345 346
    if (d->m_searchEngines.size() == 1) // empty before, make sure we have a current engine
        setCurrentSearchEngine(0);
347 348
}

349
void BaseFileFind::doReplace(const QString &text,
350
                             const QList<SearchResultItem> &items,
351
                             bool preserveCase)
352
{
353
    QStringList files = replaceAll(text, items, preserveCase);
354
    if (!files.isEmpty()) {
355
        Utils::FadingIndicator::showText(ICore::mainWindow(),
356
            tr("%n occurrences replaced.", 0, items.size()),
357
            Utils::FadingIndicator::SmallText);
358
        DocumentManager::notifyFilesChangedInternally(files);
359
        SearchResultWindow::instance()->hide();
360 361 362
    }
}

363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
static QComboBox *createCombo(QAbstractItemModel *model)
{
    auto combo = new QComboBox;
    combo->setEditable(true);
    combo->setModel(model);
    combo->setMaxCount(10);
    combo->setMinimumContentsLength(10);
    combo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
    combo->setInsertPolicy(QComboBox::InsertAtBottom);
    combo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
    return combo;
}

static QLabel *createLabel(const QString &text)
{
    auto filePatternLabel = new QLabel(text);
    filePatternLabel->setMinimumWidth(80);
    filePatternLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
    filePatternLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
    return filePatternLabel;
}

QList<QPair<QWidget *, QWidget *>> BaseFileFind::createPatternWidgets()
{
    static const QString filterToolTip = tr("List of comma separated wildcard filters. "
                                            "Files with file name or full file path matching any filter are included.");
    QLabel *filterLabel = createLabel(tr("Fi&le pattern:"));
    d->m_filterCombo = createCombo(&d->m_filterStrings);
391
    d->m_filterCombo->setToolTip(filterToolTip);
392
    filterLabel->setBuddy(d->m_filterCombo);
393
    syncComboWithSettings(d->m_filterCombo, d->m_filterSetting);
394 395 396 397 398 399 400
    QLabel *exclusionLabel = createLabel(tr("Exclusion pattern:"));
    d->m_exclusionCombo = createCombo(&d->m_exclusionStrings);
    d->m_exclusionCombo->setToolTip(filterToolTip);
    exclusionLabel->setBuddy(d->m_exclusionCombo);
    syncComboWithSettings(d->m_exclusionCombo, d->m_exclusionSetting);
    return { qMakePair(filterLabel,    d->m_filterCombo),
             qMakePair(exclusionLabel, d->m_exclusionCombo) };
con's avatar
con committed
401 402 403 404
}

void BaseFileFind::writeCommonSettings(QSettings *settings)
{
405 406 407 408 409
    std::function<QStringList(const QStringList &)> fromNativeSeparators = [](const QStringList &files) {
        return Utils::transform(files, &QDir::fromNativeSeparators);
    };

    settings->setValue("filters", fromNativeSeparators(d->m_filterStrings.stringList()));
410
    if (d->m_filterCombo)
411 412 413 414 415 416
        settings->setValue("currentFilter",
                           QDir::fromNativeSeparators(d->m_filterCombo->currentText()));
    settings->setValue("exclusionFilters", fromNativeSeparators(d->m_exclusionStrings.stringList()));
    if (d->m_exclusionCombo)
        settings->setValue("currentExclusionFilter",
                           QDir::fromNativeSeparators(d->m_exclusionCombo->currentText()));
417 418 419 420

    foreach (SearchEngine *searchEngine, d->m_searchEngines)
        searchEngine->writeSettings(settings);
    settings->setValue("currentSearchEngineIndex", d->m_currentSearchEngineIndex);
con's avatar
con committed
421 422
}

423 424
void BaseFileFind::readCommonSettings(QSettings *settings, const QString &defaultFilter,
                                      const QString &defaultExclusionFilter)
con's avatar
con committed
425
{
426 427 428 429
    std::function<QStringList(const QStringList &)> toNativeSeparators = [](const QStringList &files) {
        return Utils::transform(files, &QDir::toNativeSeparators);
    };

430
    QStringList filters = settings->value("filters").toStringList();
con's avatar
con committed
431 432
    if (filters.isEmpty())
        filters << defaultFilter;
433 434 435 436
    const QVariant currentFilter = settings->value("currentFilter");
    d->m_filterSetting = currentFilter.isValid() ? currentFilter.toString()
                                                 : filters.first();
    d->m_filterStrings.setStringList(toNativeSeparators(filters));
437 438
    if (d->m_filterCombo)
        syncComboWithSettings(d->m_filterCombo, d->m_filterSetting);
439

440 441 442 443 444 445 446 447 448 449
    QStringList exclusionFilters = settings->value("exclusionFilters").toStringList();
    if (exclusionFilters.isEmpty())
        exclusionFilters << defaultExclusionFilter;
    const QVariant currentExclusionFilter = settings->value("currentExclusionFilter");
    d->m_exclusionSetting = currentExclusionFilter.isValid() ? currentExclusionFilter.toString()
                                                          : exclusionFilters.first();
    d->m_exclusionStrings.setStringList(toNativeSeparators(exclusionFilters));
    if (d->m_exclusionCombo)
        syncComboWithSettings(d->m_exclusionCombo, d->m_exclusionSetting);

450 451 452 453
    foreach (SearchEngine* searchEngine, d->m_searchEngines)
        searchEngine->readSettings(settings);
    const int currentSearchEngineIndex = settings->value("currentSearchEngineIndex", 0).toInt();
    syncSearchEngineCombo(currentSearchEngineIndex);
con's avatar
con committed
454 455
}

456
void BaseFileFind::openEditor(const SearchResultItem &item)
con's avatar
con committed
457
{
458
    SearchResult *result = qobject_cast<SearchResult *>(sender());
459
    FileFindParameters parameters = result->userData().value<FileFindParameters>();
460 461
    IEditor *openedEditor =
            d->m_searchEngines[parameters.searchEngineIndex]->openEditor(item, parameters);
462 463 464
    if (!openedEditor) {
        if (item.path.size() > 0) {
            openedEditor = EditorManager::openEditorAt(QDir::fromNativeSeparators(item.path.first()),
465 466
                                                       item.mainRange.begin.line,
                                                       item.mainRange.begin.column, Id(),
467 468 469 470
                                                       EditorManager::DoNotSwitchToDesignMode);
        } else {
            openedEditor = EditorManager::openEditor(QDir::fromNativeSeparators(item.text));
        }
471
    }
472
    if (d->m_currentFindSupport)
473
        d->m_currentFindSupport->clearHighlights();
474
    d->m_currentFindSupport = 0;
475 476 477 478
    if (!openedEditor)
        return;
    // highlight results
    if (IFindSupport *findSupport = Aggregation::query<IFindSupport>(openedEditor->widget())) {
479 480
        d->m_currentFindSupport = findSupport;
        d->m_currentFindSupport->highlightAll(parameters.text, parameters.flags);
481
    }
con's avatar
con committed
482
}
483

484 485
void BaseFileFind::hideHighlightAll(bool visible)
{
486
    if (!visible && d->m_currentFindSupport)
487
        d->m_currentFindSupport->clearHighlights();
488 489
}

490 491 492
void BaseFileFind::searchAgain()
{
    SearchResult *search = qobject_cast<SearchResult *>(sender());
Eike Ziller's avatar
Eike Ziller committed
493
    search->restart();
494 495 496
    runSearch(search);
}

497 498 499 500 501 502 503 504
void BaseFileFind::recheckEnabled()
{
    SearchResult *search = qobject_cast<SearchResult *>(sender());
    if (!search)
        return;
    search->setSearchAgainEnabled(isEnabled());
}

505
QStringList BaseFileFind::replaceAll(const QString &text,
506
                                     const QList<SearchResultItem> &items,
507
                                     bool preserveCase)
508
{
509
    if (items.isEmpty())
510 511
        return QStringList();

512
    RefactoringChanges refactoring;
513

514 515
    QHash<QString, QList<SearchResultItem> > changes;
    foreach (const SearchResultItem &item, items)
516
        changes[QDir::fromNativeSeparators(item.path.first())].append(item);
517

518
    // Checking for files without write permissions
519
    QHashIterator<QString, QList<SearchResultItem> > it(changes);
520 521 522 523 524 525 526 527 528 529
    QSet<QString> roFiles;
    while (it.hasNext()) {
        it.next();
        const QFileInfo fileInfo(it.key());
        if (!fileInfo.isWritable())
            roFiles.insert(it.key());
    }

    // Query the user for permissions
    if (!roFiles.isEmpty()) {
hjk's avatar
hjk committed
530
        ReadOnlyFilesDialog roDialog(roFiles.toList(), ICore::mainWindow());
531
        roDialog.setShowFailWarning(true, tr("Aborting replace."));
hjk's avatar
hjk committed
532
        if (roDialog.exec() == ReadOnlyFilesDialog::RO_Cancel)
533 534 535 536
            return QStringList();
    }

    it.toFront();
537 538 539
    while (it.hasNext()) {
        it.next();
        const QString fileName = it.key();
540
        const QList<SearchResultItem> changeItems = it.value();
541

542 543 544
        ChangeSet changeSet;
        RefactoringFilePtr file = refactoring.file(fileName);
        QSet<QPair<int, int> > processed;
545
        foreach (const SearchResultItem &item, changeItems) {
546 547
            const QPair<int, int> &p = qMakePair(item.mainRange.begin.line,
                                                 item.mainRange.begin.column);
548 549 550 551 552
            if (processed.contains(p))
                continue;
            processed.insert(p);

            QString replacement;
553
            if (item.userData.canConvert<QStringList>() && !item.userData.toStringList().isEmpty()) {
554
                replacement = Utils::expandRegExpReplacement(text, item.userData.toStringList());
555
            } else if (preserveCase) {
556 557
                const QString originalText = (item.mainRange.length() == 0) ? item.text
                                                                            : item.mainRange.mid(text);
558 559
                replacement = Utils::matchCaseReplacement(originalText, text);
            } else {
560
                replacement = text;
561
            }
562

563 564 565 566
            const int start = file->position(item.mainRange.begin.line,
                                             item.mainRange.begin.column + 1);
            const int end = file->position(item.mainRange.end.line,
                                           item.mainRange.end.column + 1);
567
            changeSet.replace(start, end, replacement);
568
        }
569 570
        file->setChangeSet(changeSet);
        file->apply();
571 572 573 574
    }

    return changes.keys();
}
575

576 577 578 579 580
QVariant BaseFileFind::getAdditionalParameters(SearchResult *search)
{
    return search->userData().value<FileFindParameters>().additionalParameters;
}

Orgad Shaneh's avatar
Orgad Shaneh committed
581 582
QFuture<FileSearchResultList> BaseFileFind::executeSearch(const FileFindParameters &parameters)
{
583
    return d->m_searchEngines[parameters.searchEngineIndex]->executeSearch(parameters, this);
Orgad Shaneh's avatar
Orgad Shaneh committed
584 585 586 587
}

namespace Internal {

588 589 590 591 592 593 594 595 596
CountingLabel::CountingLabel()
{
    setAlignment(Qt::AlignCenter);
    // ### TODO this setup should be done by style
    QFont f = font();
    f.setBold(true);
    f.setPointSizeF(StyleHelper::sidebarFontSize());
    setFont(f);
    setPalette(StyleHelper::sidebarFontPalette(palette()));
597
    setProperty("_q_custom_style_disabled", QVariant(true));
598 599 600 601 602
    updateCount(0);
}

void CountingLabel::updateCount(int count)
{
Jaroslaw Kobus's avatar
Jaroslaw Kobus committed
603
    setText(BaseFileFind::tr("%n found", nullptr, count));
604
}
605

Orgad Shaneh's avatar
Orgad Shaneh committed
606
} // namespace Internal
607
} // namespace TextEditor