From 2bda8675e509c5547a0057e50d9024b49054cf78 Mon Sep 17 00:00:00 2001 From: con Date: Mon, 19 Jul 2010 14:46:53 +0200 Subject: [PATCH] C++ symbols find filter for advanced find. Required refactoring of the search result window to show real trees of search results. The backend is the backend from the Locator filter, which is a bit outdated now. --- src/plugins/cpptools/CppTools.pluginspec | 1 + src/plugins/cpptools/cppclassesfilter.cpp | 4 +- src/plugins/cpptools/cppclassesfilter.h | 2 +- src/plugins/cpptools/cppfindreferences.cpp | 6 +- src/plugins/cpptools/cppfunctionsfilter.cpp | 4 +- src/plugins/cpptools/cppfunctionsfilter.h | 2 +- src/plugins/cpptools/cpplocatorfilter.cpp | 5 +- src/plugins/cpptools/cpplocatorfilter.h | 7 +- src/plugins/cpptools/cpptools.pro | 6 +- .../cpptools/cpptools_dependencies.pri | 1 + src/plugins/cpptools/cpptoolsplugin.cpp | 10 +- src/plugins/cpptools/searchsymbols.cpp | 13 +- src/plugins/cpptools/searchsymbols.h | 22 +- src/plugins/cpptools/symbolsfindfilter.cpp | 317 ++++++++++++++ src/plugins/cpptools/symbolsfindfilter.h | 124 ++++++ src/plugins/find/ifindfilter.h | 4 +- .../find/searchresulttreeitemdelegate.cpp | 88 ++-- .../find/searchresulttreeitemdelegate.h | 3 +- src/plugins/find/searchresulttreeitemroles.h | 11 +- src/plugins/find/searchresulttreeitems.cpp | 110 ++--- src/plugins/find/searchresulttreeitems.h | 62 +-- src/plugins/find/searchresulttreemodel.cpp | 387 +++++++++--------- src/plugins/find/searchresulttreemodel.h | 25 +- src/plugins/find/searchresulttreeview.cpp | 19 +- src/plugins/find/searchresulttreeview.h | 4 +- src/plugins/find/searchresultwindow.cpp | 62 ++- src/plugins/find/searchresultwindow.h | 53 ++- src/plugins/plugins.pro | 1 + .../projectexplorer/allprojectsfind.cpp | 5 - src/plugins/projectexplorer/allprojectsfind.h | 1 - .../projectexplorer/currentprojectfind.cpp | 5 - .../projectexplorer/currentprojectfind.h | 1 - src/plugins/texteditor/basefilefind.cpp | 23 +- src/plugins/texteditor/findincurrentfile.cpp | 5 - src/plugins/texteditor/findincurrentfile.h | 1 - src/plugins/texteditor/findinfiles.cpp | 5 - src/plugins/texteditor/findinfiles.h | 1 - 37 files changed, 926 insertions(+), 474 deletions(-) create mode 100644 src/plugins/cpptools/symbolsfindfilter.cpp create mode 100644 src/plugins/cpptools/symbolsfindfilter.h diff --git a/src/plugins/cpptools/CppTools.pluginspec b/src/plugins/cpptools/CppTools.pluginspec index bc76520f15d..35492513f36 100644 --- a/src/plugins/cpptools/CppTools.pluginspec +++ b/src/plugins/cpptools/CppTools.pluginspec @@ -17,5 +17,6 @@ Alternatively, this plugin may be used under the terms of the GNU Lesser General + diff --git a/src/plugins/cpptools/cppclassesfilter.cpp b/src/plugins/cpptools/cppclassesfilter.cpp index d15901c1207..7609949a740 100644 --- a/src/plugins/cpptools/cppclassesfilter.cpp +++ b/src/plugins/cpptools/cppclassesfilter.cpp @@ -31,8 +31,8 @@ using namespace CppTools::Internal; -CppClassesFilter::CppClassesFilter(CppModelManager *manager, Core::EditorManager *editorManager) - : CppLocatorFilter(manager, editorManager) +CppClassesFilter::CppClassesFilter(CppModelManager *manager) + : CppLocatorFilter(manager) { setShortcutString("c"); setIncludedByDefault(false); diff --git a/src/plugins/cpptools/cppclassesfilter.h b/src/plugins/cpptools/cppclassesfilter.h index 7fbbe475b07..417997ada02 100644 --- a/src/plugins/cpptools/cppclassesfilter.h +++ b/src/plugins/cpptools/cppclassesfilter.h @@ -40,7 +40,7 @@ class CppClassesFilter : public CppLocatorFilter Q_OBJECT public: - CppClassesFilter(CppModelManager *manager, Core::EditorManager *editorManager); + CppClassesFilter(CppModelManager *manager); ~CppClassesFilter(); QString displayName() const { return tr("Classes"); } diff --git a/src/plugins/cpptools/cppfindreferences.cpp b/src/plugins/cpptools/cppfindreferences.cpp index c3c36dcb2ec..451aef42613 100644 --- a/src/plugins/cpptools/cppfindreferences.cpp +++ b/src/plugins/cpptools/cppfindreferences.cpp @@ -313,7 +313,11 @@ void CppFindReferences::searchFinished() void CppFindReferences::openEditor(const Find::SearchResultItem &item) { - TextEditor::BaseTextEditor::openEditorAt(item.fileName, item.lineNumber, item.searchTermStart); + if (item.path.size() > 0) { + TextEditor::BaseTextEditor::openEditorAt(item.path.first(), item.lineNumber, item.textMarkPos); + } else { + Core::EditorManager::instance()->openEditor(item.text); + } } diff --git a/src/plugins/cpptools/cppfunctionsfilter.cpp b/src/plugins/cpptools/cppfunctionsfilter.cpp index 300a9f065c8..bc8f46e6b5c 100644 --- a/src/plugins/cpptools/cppfunctionsfilter.cpp +++ b/src/plugins/cpptools/cppfunctionsfilter.cpp @@ -31,8 +31,8 @@ using namespace CppTools::Internal; -CppFunctionsFilter::CppFunctionsFilter(CppModelManager *manager, Core::EditorManager *editorManager) - : CppLocatorFilter(manager, editorManager) +CppFunctionsFilter::CppFunctionsFilter(CppModelManager *manager) + : CppLocatorFilter(manager) { setShortcutString(QString(QLatin1Char('m'))); setIncludedByDefault(false); diff --git a/src/plugins/cpptools/cppfunctionsfilter.h b/src/plugins/cpptools/cppfunctionsfilter.h index 35bf55e39c0..becae07b033 100644 --- a/src/plugins/cpptools/cppfunctionsfilter.h +++ b/src/plugins/cpptools/cppfunctionsfilter.h @@ -40,7 +40,7 @@ class CppFunctionsFilter : public CppLocatorFilter Q_OBJECT public: - CppFunctionsFilter(CppModelManager *manager, Core::EditorManager *editorManager); + CppFunctionsFilter(CppModelManager *manager); ~CppFunctionsFilter(); QString displayName() const { return tr("Methods"); } diff --git a/src/plugins/cpptools/cpplocatorfilter.cpp b/src/plugins/cpptools/cpplocatorfilter.cpp index 913a411a7ff..34c696a15d8 100644 --- a/src/plugins/cpptools/cpplocatorfilter.cpp +++ b/src/plugins/cpptools/cpplocatorfilter.cpp @@ -30,8 +30,6 @@ #include "cpplocatorfilter.h" #include "cppmodelmanager.h" -#include -#include #include #include @@ -39,9 +37,8 @@ using namespace CppTools::Internal; -CppLocatorFilter::CppLocatorFilter(CppModelManager *manager, Core::EditorManager *editorManager) +CppLocatorFilter::CppLocatorFilter(CppModelManager *manager) : m_manager(manager), - m_editorManager(editorManager), m_forceNewSearchList(true) { setShortcutString(QString(QLatin1Char(':'))); diff --git a/src/plugins/cpptools/cpplocatorfilter.h b/src/plugins/cpptools/cpplocatorfilter.h index 7176f7a0545..4c4df83a236 100644 --- a/src/plugins/cpptools/cpplocatorfilter.h +++ b/src/plugins/cpptools/cpplocatorfilter.h @@ -34,10 +34,6 @@ #include -namespace Core { -class EditorManager; -} - namespace CppTools { namespace Internal { @@ -47,7 +43,7 @@ class CppLocatorFilter : public Locator::ILocatorFilter { Q_OBJECT public: - CppLocatorFilter(CppModelManager *manager, Core::EditorManager *editorManager); + CppLocatorFilter(CppModelManager *manager); ~CppLocatorFilter(); QString displayName() const { return tr("Classes and Methods"); } @@ -66,7 +62,6 @@ private slots: private: CppModelManager *m_manager; - Core::EditorManager *m_editorManager; struct Info { Info(): dirty(true) {} diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index 9382bcb5cf7..346d10f86c8 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -24,7 +24,8 @@ HEADERS += completionsettingspage.h \ cppdoxygen.h \ cppfilesettingspage.h \ cppfindreferences.h \ - cppcodeformatter.h + cppcodeformatter.h \ + symbolsfindfilter.h SOURCES += completionsettingspage.cpp \ cppclassesfilter.cpp \ @@ -40,7 +41,8 @@ SOURCES += completionsettingspage.cpp \ cppfilesettingspage.cpp \ abstracteditorsupport.cpp \ cppfindreferences.cpp \ - cppcodeformatter.cpp + cppcodeformatter.cpp \ + symbolsfindfilter.cpp FORMS += completionsettingspage.ui \ cppfilesettingspage.ui diff --git a/src/plugins/cpptools/cpptools_dependencies.pri b/src/plugins/cpptools/cpptools_dependencies.pri index 7ac540da28b..84e655797d4 100644 --- a/src/plugins/cpptools/cpptools_dependencies.pri +++ b/src/plugins/cpptools/cpptools_dependencies.pri @@ -1,3 +1,4 @@ include($$IDE_SOURCE_TREE/src/libs/cplusplus/cplusplus.pri) include($$IDE_SOURCE_TREE/src/plugins/projectexplorer/projectexplorer.pri) include($$IDE_SOURCE_TREE/src/plugins/texteditor/texteditor.pri) +include($$IDE_SOURCE_TREE/src/plugins/find/find.pri) diff --git a/src/plugins/cpptools/cpptoolsplugin.cpp b/src/plugins/cpptools/cpptoolsplugin.cpp index 9b84e8012ba..1595fd231cd 100644 --- a/src/plugins/cpptools/cpptoolsplugin.cpp +++ b/src/plugins/cpptools/cpptoolsplugin.cpp @@ -37,6 +37,7 @@ #include "cppmodelmanager.h" #include "cpptoolsconstants.h" #include "cpplocatorfilter.h" +#include "symbolsfindfilter.h" #include @@ -111,14 +112,13 @@ bool CppToolsPlugin::initialize(const QStringList &arguments, QString *error) CppCodeCompletion *completion = new CppCodeCompletion(m_modelManager); addAutoReleasedObject(completion); - CppLocatorFilter *locatorFilter = new CppLocatorFilter(m_modelManager, - core->editorManager()); - addAutoReleasedObject(locatorFilter); - addAutoReleasedObject(new CppClassesFilter(m_modelManager, core->editorManager())); - addAutoReleasedObject(new CppFunctionsFilter(m_modelManager, core->editorManager())); + addAutoReleasedObject(new CppLocatorFilter(m_modelManager)); + addAutoReleasedObject(new CppClassesFilter(m_modelManager)); + addAutoReleasedObject(new CppFunctionsFilter(m_modelManager)); addAutoReleasedObject(new CppCurrentDocumentFilter(m_modelManager, core->editorManager())); addAutoReleasedObject(new CompletionSettingsPage); addAutoReleasedObject(new CppFileSettingsPage(m_fileSettings)); + addAutoReleasedObject(new SymbolsFindFilter(m_modelManager)); // Menus Core::ActionContainer *mtools = am->actionContainer(Core::Constants::M_TOOLS); diff --git a/src/plugins/cpptools/searchsymbols.cpp b/src/plugins/cpptools/searchsymbols.cpp index ac13d4fd818..70cd1452bd0 100644 --- a/src/plugins/cpptools/searchsymbols.cpp +++ b/src/plugins/cpptools/searchsymbols.cpp @@ -32,10 +32,17 @@ #include #include #include +#include using namespace CPlusPlus; using namespace CppTools::Internal; +SearchSymbols::SymbolTypes SearchSymbols::AllTypes = + SearchSymbols::Classes + | SearchSymbols::Functions + | SearchSymbols::Enums + | SearchSymbols::Declarations; + SearchSymbols::SearchSymbols(): symbolsToSearchFor(Classes | Functions | Enums), separateScope(false) @@ -204,13 +211,17 @@ QString SearchSymbols::symbolName(const Symbol *symbol) const void SearchSymbols::appendItem(const QString &name, const QString &info, ModelItemInfo::ItemType type, - const Symbol *symbol) + Symbol *symbol) { if (!symbol->name()) return; + QStringList fullyQualifiedName; + foreach (const Name *name, LookupContext::fullyQualifiedName(symbol)) + fullyQualifiedName.append(overview.prettyName(name)); const QIcon icon = icons.iconForSymbol(symbol); items.append(ModelItemInfo(name, info, type, + fullyQualifiedName, QString::fromUtf8(symbol->fileName(), symbol->fileNameLength()), symbol->line(), symbol->column() - 1, // 1-based vs 0-based column diff --git a/src/plugins/cpptools/searchsymbols.h b/src/plugins/cpptools/searchsymbols.h index 7c3c379e688..9a08b4dced2 100644 --- a/src/plugins/cpptools/searchsymbols.h +++ b/src/plugins/cpptools/searchsymbols.h @@ -52,12 +52,14 @@ struct ModelItemInfo ModelItemInfo() : type(Declaration), - line(0) + line(0), + column(0) { } ModelItemInfo(const QString &symbolName, const QString &symbolType, ItemType type, + QStringList fullyQualifiedName, const QString &fileName, int line, int column, @@ -65,15 +67,28 @@ struct ModelItemInfo : symbolName(symbolName), symbolType(symbolType), type(type), + fullyQualifiedName(fullyQualifiedName), fileName(fileName), line(line), column(column), icon(icon) { } + ModelItemInfo(const ModelItemInfo &otherInfo) + : symbolName(otherInfo.symbolName), + symbolType(otherInfo.symbolType), + type(otherInfo.type), + fullyQualifiedName(otherInfo.fullyQualifiedName), + fileName(otherInfo.fileName), + line(otherInfo.line), + column(otherInfo.column), + icon(otherInfo.icon) + { } + QString symbolName; QString symbolType; ItemType type; + QStringList fullyQualifiedName; QString fileName; int line; int column; @@ -90,8 +105,11 @@ public: Enums = 0x4, Declarations = 0x8 }; + Q_DECLARE_FLAGS(SymbolTypes, SymbolType) + static SymbolTypes AllTypes; + SearchSymbols(); void setSymbolsToSearchFor(SymbolTypes types); @@ -121,7 +139,7 @@ protected: void appendItem(const QString &name, const QString &info, ModelItemInfo::ItemType type, - const CPlusPlus::Symbol *symbol); + CPlusPlus::Symbol *symbol); private: QString findOrInsert(const QString &s) diff --git a/src/plugins/cpptools/symbolsfindfilter.cpp b/src/plugins/cpptools/symbolsfindfilter.cpp new file mode 100644 index 00000000000..f133831a256 --- /dev/null +++ b/src/plugins/cpptools/symbolsfindfilter.cpp @@ -0,0 +1,317 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "symbolsfindfilter.h" + +#include "cppmodelmanager.h" +#include "cpptoolsconstants.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace CppTools; +using namespace CppTools::Internal; + +namespace { + const char * const SETTINGS_GROUP = "CppSymbols"; + const char * const SETTINGS_SYMBOLTYPES = "SymbolsToSearchFor"; + const char * const SETTINGS_SEARCHSCOPE = "SearchScope"; + + void runSearch(QFutureInterface &future, + QString txt, Find::FindFlags findFlags, CPlusPlus::Snapshot snapshot, + SearchSymbols *search, QSet fileNames) + { + future.setProgressRange(0, snapshot.size()); + future.setProgressValue(0); + int progress = 0; + + CPlusPlus::Snapshot::const_iterator it = snapshot.begin(); + + QString findString = (findFlags & Find::FindRegularExpression ? txt : QRegExp::escape(txt)); + if (findFlags & Find::FindWholeWords) + findString = QString::fromLatin1("\\b%1\\b").arg(findString); + QRegExp matcher(findString, (findFlags & Find::FindCaseSensitively ? Qt::CaseSensitive : Qt::CaseInsensitive)); + while (it != snapshot.end() && !future.isCanceled()) { + if (fileNames.isEmpty() || fileNames.contains(it.value()->fileName())) { + QVector resultItems; + QList modelInfos = (*search)(it.value()); + foreach (const ModelItemInfo &info, modelInfos) { + int index = matcher.indexIn(info.symbolName); + if (index != -1) { + QStringList path = info.fullyQualifiedName.mid(0, info.fullyQualifiedName.size() - 1); + Find::SearchResultItem item; + item.path = path; + item.text = info.symbolName; + item.textMarkPos = -1; + item.textMarkLength = 0; + item.icon = info.icon; + item.lineNumber = -1; + item.userData = qVariantFromValue(info); + resultItems << item; + } + } + if (!resultItems.isEmpty()) + future.reportResults(resultItems); + } + ++it; + ++progress; + future.setProgressValue(progress); + } + } +} //namespace + +SymbolsFindFilter::SymbolsFindFilter(CppModelManager *manager) + : m_manager(manager), + m_isRunning(false), + m_enabled(true), + m_symbolsToSearch(SearchSymbols::AllTypes), + m_scope(SearchProjectsOnly) +{ + // for disabling while parser is running + connect(Core::ICore::instance()->progressManager(), SIGNAL(taskStarted(QString)), + this, SLOT(onTaskStarted(QString))); + connect(Core::ICore::instance()->progressManager(), SIGNAL(allTasksFinished(QString)), + this, SLOT(onAllTasksFinished(QString))); + + connect(&m_watcher, SIGNAL(finished()), + this, SLOT(finish())); + connect(&m_watcher, SIGNAL(resultsReadyAt(int,int)), + this, SLOT(addResults(int, int))); +} + +QString SymbolsFindFilter::id() const +{ + return QLatin1String("CppSymbols"); +} + +QString SymbolsFindFilter::displayName() const +{ + return tr("C++ Symbols"); +} + +bool SymbolsFindFilter::isEnabled() const +{ + return !m_isRunning && m_enabled; +} + +Find::FindFlags SymbolsFindFilter::supportedFindFlags() const +{ + return Find::FindCaseSensitively | Find::FindRegularExpression | Find::FindWholeWords; +} + +void SymbolsFindFilter::findAll(const QString &txt, Find::FindFlags findFlags) +{ + m_isRunning = true; + emit changed(); + Find::SearchResultWindow *window = Find::SearchResultWindow::instance(); + Find::SearchResult *result = window->startNewSearch(); + connect(result, SIGNAL(activated(Find::SearchResultItem)), this, SLOT(openEditor(Find::SearchResultItem))); + window->popup(true); + + m_search.setSymbolsToSearchFor(m_symbolsToSearch); + m_search.setSeparateScope(true); + QSet projectFileNames; + if (m_scope == SymbolsFindFilter::SearchProjectsOnly) { + foreach (ProjectExplorer::Project *project, + ProjectExplorer::ProjectExplorerPlugin::instance()->session()->projects()) { + projectFileNames += project->files(ProjectExplorer::Project::AllFiles).toSet(); + } + } + + m_watcher.setFuture(QtConcurrent::run >(runSearch, txt, findFlags, m_manager->snapshot(), + &m_search, projectFileNames)); + Core::ICore::instance()->progressManager()->addTask(m_watcher.future(), + tr("Searching"), + Find::Constants::TASK_SEARCH); +} + +void SymbolsFindFilter::addResults(int begin, int end) +{ + Find::SearchResultWindow *window = Find::SearchResultWindow::instance(); + QList items; + for (int i = begin; i < end; ++i) + items << m_watcher.resultAt(i); + window->addResults(items, Find::SearchResultWindow::AddSorted); +} + +void SymbolsFindFilter::finish() +{ + Find::SearchResultWindow *window = Find::SearchResultWindow::instance(); + window->finishSearch(); + m_isRunning = false; + emit changed(); +} + +void SymbolsFindFilter::openEditor(const Find::SearchResultItem &item) +{ + if (!item.userData.canConvert()) + return; + ModelItemInfo info = item.userData.value(); + TextEditor::BaseTextEditor::openEditorAt(info.fileName, + info.line, + info.column); +} + +QWidget *SymbolsFindFilter::createConfigWidget() +{ + return new SymbolsFindFilterConfigWidget(this); +} + +void SymbolsFindFilter::writeSettings(QSettings *settings) +{ + settings->beginGroup(QLatin1String(SETTINGS_GROUP)); + settings->setValue(SETTINGS_SYMBOLTYPES, (int)m_symbolsToSearch); + settings->setValue(SETTINGS_SEARCHSCOPE, (int)m_scope); + settings->endGroup(); +} + +void SymbolsFindFilter::readSettings(QSettings *settings) +{ + settings->beginGroup(QLatin1String(SETTINGS_GROUP)); + m_symbolsToSearch = (SearchSymbols::SymbolTypes)settings->value(SETTINGS_SYMBOLTYPES, + (int)SearchSymbols::AllTypes).toInt(); + m_scope = (SearchScope)settings->value(SETTINGS_SEARCHSCOPE, + (int)SearchProjectsOnly).toInt(); + settings->endGroup(); + emit symbolsToSearchChanged(); +} + +void SymbolsFindFilter::onTaskStarted(const QString &type) +{ + if (type == CppTools::Constants::TASK_INDEX) { + m_enabled = false; + emit changed(); + } +} + +void SymbolsFindFilter::onAllTasksFinished(const QString &type) +{ + if (type == CppTools::Constants::TASK_INDEX) { + m_enabled = true; + emit changed(); + } +} + +// #pragma mark -- SymbolsFindFilterConfigWidget + +SymbolsFindFilterConfigWidget::SymbolsFindFilterConfigWidget(SymbolsFindFilter *filter) + : m_filter(filter) +{ + connect(m_filter, SIGNAL(symbolsToSearchChanged()), this, SLOT(getState())); + + QGridLayout *layout = new QGridLayout(this); + setLayout(layout); + layout->setMargin(0); + + QLabel *typeLabel = new QLabel(tr("Types:")); + layout->addWidget(typeLabel, 0, 0); + + m_typeClasses = new QCheckBox(tr("Classes")); + layout->addWidget(m_typeClasses, 0, 1); + + m_typeMethods = new QCheckBox(tr("Methods")); + layout->addWidget(m_typeMethods, 0, 2); + + m_typeEnums = new QCheckBox(tr("Enums")); + layout->addWidget(m_typeEnums, 1, 1); + + m_typeDeclarations = new QCheckBox(tr("Declarations")); + layout->addWidget(m_typeDeclarations, 1, 2); + + // hacks to fix layouting: + typeLabel->setMinimumWidth(80); + typeLabel->setAlignment(Qt::AlignRight); + m_typeClasses->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + m_typeMethods->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + connect(m_typeClasses, SIGNAL(clicked(bool)), this, SLOT(setState())); + connect(m_typeMethods, SIGNAL(clicked(bool)), this, SLOT(setState())); + connect(m_typeEnums, SIGNAL(clicked(bool)), this, SLOT(setState())); + connect(m_typeDeclarations, SIGNAL(clicked(bool)), this, SLOT(setState())); + + m_searchProjectsOnly = new QRadioButton(tr("Projects Only")); + layout->addWidget(m_searchProjectsOnly, 2, 1); + + m_searchGlobal = new QRadioButton(tr("Global")); + layout->addWidget(m_searchGlobal, 2, 2); + + m_searchGroup = new QButtonGroup(this); + m_searchGroup->addButton(m_searchProjectsOnly); + m_searchGroup->addButton(m_searchGlobal); + + connect(m_searchProjectsOnly, SIGNAL(clicked(bool)), + this, SLOT(setState())); + connect(m_searchGlobal, SIGNAL(clicked(bool)), + this, SLOT(setState())); +} + +void SymbolsFindFilterConfigWidget::getState() +{ + SearchSymbols::SymbolTypes symbols = m_filter->symbolsToSearch(); + m_typeClasses->setChecked(symbols & SearchSymbols::Classes); + m_typeMethods->setChecked(symbols & SearchSymbols::Functions); + m_typeEnums->setChecked(symbols & SearchSymbols::Enums); + m_typeDeclarations->setChecked(symbols & SearchSymbols::Declarations); + + SymbolsFindFilter::SearchScope scope = m_filter->searchScope(); + m_searchProjectsOnly->setChecked(scope == SymbolsFindFilter::SearchProjectsOnly); + m_searchGlobal->setChecked(scope == SymbolsFindFilter::SearchGlobal); +} + +void SymbolsFindFilterConfigWidget::setState() const +{ + SearchSymbols::SymbolTypes symbols; + if (m_typeClasses->isChecked()) + symbols |= SearchSymbols::Classes; + if (m_typeMethods->isChecked()) + symbols |= SearchSymbols::Functions; + if (m_typeEnums->isChecked()) + symbols |= SearchSymbols::Enums; + if (m_typeDeclarations->isChecked()) + symbols |= SearchSymbols::Declarations; + m_filter->setSymbolsToSearch(symbols); + + if (m_searchProjectsOnly->isChecked()) + m_filter->setSearchScope(SymbolsFindFilter::SearchProjectsOnly); + else + m_filter->setSearchScope(SymbolsFindFilter::SearchGlobal); +} diff --git a/src/plugins/cpptools/symbolsfindfilter.h b/src/plugins/cpptools/symbolsfindfilter.h new file mode 100644 index 00000000000..420420dc4af --- /dev/null +++ b/src/plugins/cpptools/symbolsfindfilter.h @@ -0,0 +1,124 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef SYMBOLSFINDFILTER_H +#define SYMBOLSFINDFILTER_H + +#include "searchsymbols.h" + +#include +#include + +#include +#include +#include +#include +#include + +namespace CppTools { +namespace Internal { + +class CppModelManager; + +class SymbolsFindFilter : public Find::IFindFilter +{ + Q_OBJECT +public: + enum SearchScope { + SearchProjectsOnly, + SearchGlobal + }; + + explicit SymbolsFindFilter(CppModelManager *manager); + + QString id() const; + QString displayName() const; + bool isEnabled() const; + Find::FindFlags supportedFindFlags() const; + + void findAll(const QString &txt, Find::FindFlags findFlags); + + QWidget *createConfigWidget(); + void writeSettings(QSettings *settings); + void readSettings(QSettings *settings); + + void setSymbolsToSearch(SearchSymbols::SymbolTypes types) { m_symbolsToSearch = types; } + SearchSymbols::SymbolTypes symbolsToSearch() const { return m_symbolsToSearch; } + + void setSearchScope(SearchScope scope) { m_scope = scope; } + SearchScope searchScope() const { return m_scope; } + +signals: + void symbolsToSearchChanged(); + +private slots: + void openEditor(const Find::SearchResultItem &item); + + void addResults(int begin, int end); + void finish(); + void onTaskStarted(const QString &type); + void onAllTasksFinished(const QString &type); + +private: + CppModelManager *m_manager; + bool m_isRunning; + bool m_enabled; + QFutureWatcher m_watcher; + SearchSymbols::SymbolTypes m_symbolsToSearch; + SearchSymbols m_search; + SearchScope m_scope; +}; + +class SymbolsFindFilterConfigWidget : public QWidget +{ + Q_OBJECT +public: + SymbolsFindFilterConfigWidget(SymbolsFindFilter *filter); + +private slots: + void setState() const; + void getState(); + +private: + SymbolsFindFilter *m_filter; + + QCheckBox *m_typeClasses; + QCheckBox *m_typeMethods; + QCheckBox *m_typeEnums; + QCheckBox *m_typeDeclarations; + + QRadioButton *m_searchGlobal; + QRadioButton *m_searchProjectsOnly; + QButtonGroup *m_searchGroup; +}; + +} // Internal +} // CppTools + +#endif // SYMBOLSFINDFILTER_H diff --git a/src/plugins/find/ifindfilter.h b/src/plugins/find/ifindfilter.h index 72d8cacdec8..d45dc83a7c2 100644 --- a/src/plugins/find/ifindfilter.h +++ b/src/plugins/find/ifindfilter.h @@ -34,9 +34,9 @@ #include "textfindconstants.h" #include +#include QT_BEGIN_NAMESPACE -class QKeySequence; class QWidget; class QSettings; QT_END_NAMESPACE @@ -53,7 +53,7 @@ public: virtual QString id() const = 0; virtual QString displayName() const = 0; virtual bool isEnabled() const = 0; - virtual QKeySequence defaultShortcut() const = 0; + virtual QKeySequence defaultShortcut() const { return QKeySequence(); } virtual bool isReplaceSupported() const { return false; } virtual FindFlags supportedFindFlags() const; diff --git a/src/plugins/find/searchresulttreeitemdelegate.cpp b/src/plugins/find/searchresulttreeitemdelegate.cpp index d7b1972bc93..379db290b12 100644 --- a/src/plugins/find/searchresulttreeitemdelegate.cpp +++ b/src/plugins/find/searchresulttreeitemdelegate.cpp @@ -48,53 +48,77 @@ SearchResultTreeItemDelegate::SearchResultTreeItemDelegate(QObject *parent) void SearchResultTreeItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { - if (index.model()->data(index, ItemDataRoles::TypeRole).toString().compare(QLatin1String("file")) == 0) { - QItemDelegate::paint(painter, option, index); - } else { - painter->save(); + painter->save(); - QStyleOptionViewItemV3 opt = setOptions(index, option); - painter->setFont(opt.font); + QStyleOptionViewItemV3 opt = setOptions(index, option); + painter->setFont(opt.font); - QItemDelegate::drawBackground(painter, opt, index); + QItemDelegate::drawBackground(painter, opt, index); - int lineNumberAreaWidth = drawLineNumber(painter, opt, index); + int iconAreaWidth = drawIcon(painter, opt, opt.rect, index); + QRect resultRowRect(opt.rect.adjusted(iconAreaWidth, 0, 0, 0)); - QRect resultRowRect(opt.rect.adjusted(lineNumberAreaWidth, 0, 0, 0)); - QString displayString = index.model()->data(index, Qt::DisplayRole).toString(); - drawMarker(painter, index, displayString, resultRowRect); + int lineNumberAreaWidth = drawLineNumber(painter, opt, resultRowRect, index); + resultRowRect.adjust(lineNumberAreaWidth, 0, 0, 0); - // Draw the text and focus/selection - QItemDelegate::drawDisplay(painter, opt, resultRowRect, displayString); - QItemDelegate::drawFocus(painter, opt, opt.rect); + QString displayString = index.model()->data(index, Qt::DisplayRole).toString(); + drawMarker(painter, index, displayString, resultRowRect); - QVariant value = index.data(Qt::CheckStateRole); - if (value.isValid()) { - Qt::CheckState checkState = Qt::Unchecked; - checkState = static_cast(value.toInt()); - QRect checkRect = check(opt, opt.rect, value); - - QRect emptyRect; - doLayout(opt, &checkRect, &emptyRect, &emptyRect, false); - - QItemDelegate::drawCheck(painter, opt, checkRect, checkState); - } - - painter->restore(); + // Show number of subresults in displayString + if (index.model()->hasChildren(index)) { + displayString += QString::fromLatin1(" (") + + QString::number(index.model()->rowCount(index)) + + QLatin1Char(')'); } + + // Draw the text and focus/selection + QItemDelegate::drawDisplay(painter, opt, resultRowRect, displayString); + QItemDelegate::drawFocus(painter, opt, opt.rect); + + QVariant value = index.data(Qt::CheckStateRole); + if (value.isValid()) { + Qt::CheckState checkState = Qt::Unchecked; + checkState = static_cast(value.toInt()); + QRect checkRect = check(opt, opt.rect, value); + + QRect emptyRect; + doLayout(opt, &checkRect, &emptyRect, &emptyRect, false); + + QItemDelegate::drawCheck(painter, opt, checkRect, checkState); + } + + painter->restore(); +} + +int SearchResultTreeItemDelegate::drawIcon(QPainter *painter, const QStyleOptionViewItemV3 &option, + const QRect &rect, + const QModelIndex &index) const +{ + static const int iconWidth = 16; + static const int iconPadding = 4; + QIcon icon = index.model()->data(index, ItemDataRoles::ResultIconRole).value(); + if (icon.isNull()) + return 0; + QRect iconRect = rect.adjusted(iconPadding, 0, /*is set below anyhow*/0, 0); + iconRect.setWidth(iconWidth); + QItemDelegate::drawDecoration(painter, option, iconRect, icon.pixmap(iconWidth)); + return iconWidth + iconPadding; } int SearchResultTreeItemDelegate::drawLineNumber(QPainter *painter, const QStyleOptionViewItemV3 &option, + const QRect &rect, const QModelIndex &index) const { static const int lineNumberAreaHorizontalPadding = 4; - const bool isSelected = option.state & QStyle::State_Selected; int lineNumber = index.model()->data(index, ItemDataRoles::ResultLineNumberRole).toInt(); + if (lineNumber < 1) + return 0; + const bool isSelected = option.state & QStyle::State_Selected; int lineNumberDigits = (int)floor(log10((double)lineNumber)) + 1; int minimumLineNumberDigits = qMax((int)m_minimumLineNumberDigits, lineNumberDigits); int fontWidth = painter->fontMetrics().width(QString(minimumLineNumberDigits, QLatin1Char('0'))); int lineNumberAreaWidth = lineNumberAreaHorizontalPadding + fontWidth + lineNumberAreaHorizontalPadding; - QRect lineNumberAreaRect(option.rect); + QRect lineNumberAreaRect(rect); lineNumberAreaRect.setWidth(lineNumberAreaWidth); QPalette::ColorGroup cg = QPalette::Normal; @@ -123,10 +147,12 @@ int SearchResultTreeItemDelegate::drawLineNumber(QPainter *painter, const QStyle void SearchResultTreeItemDelegate::drawMarker(QPainter *painter, const QModelIndex &index, const QString text, const QRect &rect) const { - const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; int searchTermStart = index.model()->data(index, ItemDataRoles::SearchTermStartRole).toInt(); - int searchTermStartPixels = painter->fontMetrics().width(text.left(searchTermStart)); int searchTermLength = index.model()->data(index, ItemDataRoles::SearchTermLengthRole).toInt(); + if (searchTermStart < 0 || searchTermLength < 1) + return; + const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; + int searchTermStartPixels = painter->fontMetrics().width(text.left(searchTermStart)); int searchTermLengthPixels = painter->fontMetrics().width(text.mid(searchTermStart, searchTermLength)); QRect resultHighlightRect(rect); resultHighlightRect.setLeft(resultHighlightRect.left() + searchTermStartPixels + textMargin - 1); // -1: Cosmetics diff --git a/src/plugins/find/searchresulttreeitemdelegate.h b/src/plugins/find/searchresulttreeitemdelegate.h index f751d336328..d9fe62cc7f0 100644 --- a/src/plugins/find/searchresulttreeitemdelegate.h +++ b/src/plugins/find/searchresulttreeitemdelegate.h @@ -42,7 +42,8 @@ public: void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; private: - int drawLineNumber(QPainter *painter, const QStyleOptionViewItemV3 &option, const QModelIndex &index) const; + int drawIcon(QPainter *painter, const QStyleOptionViewItemV3 &option, const QRect &rect, const QModelIndex &index) const; + int drawLineNumber(QPainter *painter, const QStyleOptionViewItemV3 &option, const QRect &rect, const QModelIndex &index) const; void drawMarker(QPainter *painter, const QModelIndex &index, const QString text, const QRect &rect) const; static const int m_minimumLineNumberDigits = 6; diff --git a/src/plugins/find/searchresulttreeitemroles.h b/src/plugins/find/searchresulttreeitemroles.h index eae775192b3..d79877e8de5 100644 --- a/src/plugins/find/searchresulttreeitemroles.h +++ b/src/plugins/find/searchresulttreeitemroles.h @@ -30,22 +30,21 @@ #ifndef SEARCHRESULTTREEITEMROLES_H #define SEARCHRESULTTREEITEMROLES_H +#include + namespace Find { namespace Internal { namespace ItemDataRoles { enum Roles { - TypeRole = Qt::UserRole, - FileNameRole, - ResultLinesCountRole, - ResultIndexRole, + ResultItemRole = Qt::UserRole, ResultLineRole, ResultLineNumberRole, + ResultIconRole, SearchTermStartRole, SearchTermLengthRole, - RowOfItem, // The ?-th child of its parent is this this item - TextRole // for files == FileNameRole, for results == ResultLineRole + RowOfItem // The ?-th child of its parent is this this item }; } // namespace Internal diff --git a/src/plugins/find/searchresulttreeitems.cpp b/src/plugins/find/searchresulttreeitems.cpp index e31a83a9827..0738e370825 100644 --- a/src/plugins/find/searchresulttreeitems.cpp +++ b/src/plugins/find/searchresulttreeitems.cpp @@ -31,8 +31,13 @@ using namespace Find::Internal; -SearchResultTreeItem::SearchResultTreeItem(SearchResultTreeItem::ItemType type, const SearchResultTreeItem *parent) - : m_type(type), m_parent(parent), m_isUserCheckable(false), m_checkState(Qt::Unchecked) +SearchResultTreeItem::SearchResultTreeItem(const SearchResultItem &item, + const SearchResultTreeItem *parent) + : item(item), + m_parent(parent), + m_isUserCheckable(false), + m_checkState(Qt::Unchecked), + m_isGenerated(false) { } @@ -41,6 +46,11 @@ SearchResultTreeItem::~SearchResultTreeItem() clearChildren(); } +bool SearchResultTreeItem::isLeaf() const +{ + return childrenCount() == 0 && parent() != 0; +} + bool SearchResultTreeItem::isUserCheckable() const { return m_isUserCheckable; @@ -67,11 +77,6 @@ void SearchResultTreeItem::clearChildren() m_children.clear(); } -SearchResultTreeItem::ItemType SearchResultTreeItem::itemType() const -{ - return m_type; -} - int SearchResultTreeItem::childrenCount() const { return m_children.count(); @@ -92,16 +97,28 @@ const SearchResultTreeItem *SearchResultTreeItem::parent() const return m_parent; } -static bool compareResultFiles(SearchResultTreeItem *a, SearchResultTreeItem *b) +static bool lessThanByText(SearchResultTreeItem *a, const QString &b) { - return static_cast(a)->fileName() < - static_cast(b)->fileName(); + return a->item.text < b; } -int SearchResultTreeItem::insertionIndex(SearchResultFile *child) const +int SearchResultTreeItem::insertionIndex(const QString &text, SearchResultTreeItem **existingItem) const { - Q_ASSERT(m_type == Root); - return qLowerBound(m_children.begin(), m_children.end(), child, compareResultFiles) - m_children.begin(); + QList::const_iterator insertionPosition = + qLowerBound(m_children.begin(), m_children.end(), text, lessThanByText); + if (existingItem) { + if (insertionPosition != m_children.end() && (*insertionPosition)->item.text == text) { + (*existingItem) = (*insertionPosition); + } else { + *existingItem = 0; + } + } + return insertionPosition - m_children.begin(); +} + +int SearchResultTreeItem::insertionIndex(const SearchResultItem &item, SearchResultTreeItem **existingItem) const +{ + return insertionIndex(item.text, existingItem); } void SearchResultTreeItem::insertChild(int index, SearchResultTreeItem *child) @@ -109,68 +126,17 @@ void SearchResultTreeItem::insertChild(int index, SearchResultTreeItem *child) m_children.insert(index, child); } -void SearchResultTreeItem::appendChild(SearchResultTreeItem *child) +void SearchResultTreeItem::insertChild(int index, const SearchResultItem &item) { - m_children.append(child); -} - -SearchResultTextRow::SearchResultTextRow(int index, int lineNumber, - const QString &rowText, - int searchTermStart, int searchTermLength, - const SearchResultTreeItem *parent): - SearchResultTreeItem(ResultRow, parent), - m_index(index), - m_lineNumber(lineNumber), - m_rowText(rowText), - m_searchTermStart(searchTermStart), - m_searchTermLength(searchTermLength) -{ -} - -int SearchResultTextRow::index() const -{ - return m_index; -} - -QString SearchResultTextRow::rowText() const -{ - return m_rowText; -} - -int SearchResultTextRow::lineNumber() const -{ - return m_lineNumber; -} - -int SearchResultTextRow::searchTermStart() const -{ - return m_searchTermStart; -} - -int SearchResultTextRow::searchTermLength() const -{ - return m_searchTermLength; -} - -SearchResultFile::SearchResultFile(const QString &fileName, const SearchResultTreeItem *parent): - SearchResultTreeItem(ResultFile, parent), - m_fileName(fileName) -{ -} - -QString SearchResultFile::fileName() const -{ - return m_fileName; -} - -void SearchResultFile::appendResultLine(int index, int lineNumber, const QString &rowText, int searchTermStart, - int searchTermLength) -{ - SearchResultTreeItem *child = new SearchResultTextRow(index, lineNumber, rowText, - searchTermStart, searchTermLength, this); + SearchResultTreeItem *child = new SearchResultTreeItem(item, this); if (isUserCheckable()) { child->setIsUserCheckable(true); child->setCheckState(Qt::Checked); } - appendChild(child); + insertChild(index, child); +} + +void SearchResultTreeItem::appendChild(const SearchResultItem &item) +{ + insertChild(m_children.count(), item); } diff --git a/src/plugins/find/searchresulttreeitems.h b/src/plugins/find/searchresulttreeitems.h index 18ff329a0e0..ac7fab03d17 100644 --- a/src/plugins/find/searchresulttreeitems.h +++ b/src/plugins/find/searchresulttreeitems.h @@ -30,35 +30,31 @@ #ifndef SEARCHRESULTTREEITEMS_H #define SEARCHRESULTTREEITEMS_H +#include "searchresultwindow.h" + #include #include #include +#include namespace Find { namespace Internal { -class SearchResultTreeItem; -class SearchResultFile; - class SearchResultTreeItem { public: - enum ItemType - { - Root, - ResultRow, - ResultFile - }; - - SearchResultTreeItem(ItemType type = Root, const SearchResultTreeItem *parent = NULL); + SearchResultTreeItem(const SearchResultItem &item = SearchResultItem(), + const SearchResultTreeItem *parent = NULL); virtual ~SearchResultTreeItem(); - ItemType itemType() const; + bool isLeaf() const; const SearchResultTreeItem *parent() const; SearchResultTreeItem *childAt(int index) const; - int insertionIndex(SearchResultFile *child) const; + int insertionIndex(const QString &text, SearchResultTreeItem **existingItem) const; + int insertionIndex(const SearchResultItem &item, SearchResultTreeItem **existingItem) const; void insertChild(int index, SearchResultTreeItem *child); - void appendChild(SearchResultTreeItem *child); + void insertChild(int index, const SearchResultItem &item); + void appendChild(const SearchResultItem &item); int childrenCount() const; int rowOfItem() const; void clearChildren(); @@ -69,43 +65,17 @@ public: Qt::CheckState checkState() const; void setCheckState(Qt::CheckState checkState); + bool isGenerated() const { return m_isGenerated; } + void setGenerated(bool value) { m_isGenerated = value; } + + SearchResultItem item; + private: - ItemType m_type; const SearchResultTreeItem *m_parent; QList m_children; bool m_isUserCheckable; Qt::CheckState m_checkState; -}; - -class SearchResultTextRow : public SearchResultTreeItem -{ -public: - SearchResultTextRow(int index, int lineNumber, const QString &rowText, int searchTermStart, - int searchTermLength, const SearchResultTreeItem *parent); - int index() const; - QString rowText() const; - int lineNumber() const; - int searchTermStart() const; - int searchTermLength() const; - -private: - int m_index; - int m_lineNumber; - QString m_rowText; - int m_searchTermStart; - int m_searchTermLength; -}; - -class SearchResultFile : public SearchResultTreeItem -{ -public: - SearchResultFile(const QString &fileName, const SearchResultTreeItem *parent); - QString fileName() const; - void appendResultLine(int index, int lineNumber, const QString &rowText, int searchTermStart, - int searchTermLength); - -private: - QString m_fileName; + bool m_isGenerated; }; } // namespace Internal diff --git a/src/plugins/find/searchresulttreemodel.cpp b/src/plugins/find/searchresulttreemodel.cpp index 080edd5c585..705dedcf4e6 100644 --- a/src/plugins/find/searchresulttreemodel.cpp +++ b/src/plugins/find/searchresulttreemodel.cpp @@ -46,7 +46,7 @@ using namespace Find::Internal; SearchResultTreeModel::SearchResultTreeModel(QObject *parent) : QAbstractItemModel(parent) - , m_lastAddedResultFile(0) + , m_currentParent(0) , m_showReplaceUI(false) { m_rootItem = new SearchResultTreeItem; @@ -70,13 +70,13 @@ void SearchResultTreeModel::setTextEditorFont(const QFont &font) layoutChanged(); } -Qt::ItemFlags SearchResultTreeModel::flags(const QModelIndex &index) const +Qt::ItemFlags SearchResultTreeModel::flags(const QModelIndex &idx) const { - Qt::ItemFlags flags = QAbstractItemModel::flags(index); + Qt::ItemFlags flags = QAbstractItemModel::flags(idx); - if (index.isValid()) { - if (const SearchResultTreeItem *item = static_cast(index.internalPointer())) { - if (item->itemType() == SearchResultTreeItem::ResultRow && item->isUserCheckable()) { + if (idx.isValid()) { + if (const SearchResultTreeItem *item = treeItemAtIndex(idx)) { + if (item->isLeaf() && item->isUserCheckable()) { flags |= Qt::ItemIsUserCheckable; } } @@ -96,7 +96,7 @@ QModelIndex SearchResultTreeModel::index(int row, int column, if (!parent.isValid()) parentItem = m_rootItem; else - parentItem = static_cast(parent.internalPointer()); + parentItem = treeItemAtIndex(parent); const SearchResultTreeItem *childItem = parentItem->childAt(row); if (childItem) @@ -105,12 +105,17 @@ QModelIndex SearchResultTreeModel::index(int row, int column, return QModelIndex(); } -QModelIndex SearchResultTreeModel::parent(const QModelIndex &index) const +QModelIndex SearchResultTreeModel::index(SearchResultTreeItem *item) const { - if (!index.isValid()) + return createIndex(item->rowOfItem(), 0, (void *)item); +} + +QModelIndex SearchResultTreeModel::parent(const QModelIndex &idx) const +{ + if (!idx.isValid()) return QModelIndex(); - const SearchResultTreeItem *childItem = static_cast(index.internalPointer()); + const SearchResultTreeItem *childItem = treeItemAtIndex(idx); const SearchResultTreeItem *parentItem = childItem->parent(); if (parentItem == m_rootItem) @@ -129,7 +134,7 @@ int SearchResultTreeModel::rowCount(const QModelIndex &parent) const if (!parent.isValid()) parentItem = m_rootItem; else - parentItem = static_cast(parent.internalPointer()); + parentItem = treeItemAtIndex(parent); return parentItem->childrenCount(); } @@ -140,48 +145,41 @@ int SearchResultTreeModel::columnCount(const QModelIndex &parent) const return 1; } -QVariant SearchResultTreeModel::data(const QModelIndex &index, int role) const +SearchResultTreeItem *SearchResultTreeModel::treeItemAtIndex(const QModelIndex &idx) const { - if (!index.isValid()) - return QVariant(); + return static_cast(idx.internalPointer()); +} - const SearchResultTreeItem *item = static_cast(index.internalPointer()); +QVariant SearchResultTreeModel::data(const QModelIndex &idx, int role) const +{ + if (!idx.isValid()) + return QVariant(); QVariant result; - if (role == Qt::SizeHintRole) - { + if (role == Qt::SizeHintRole) { + // TODO we should not use editor font height if that is not used by any item const int appFontHeight = QApplication::fontMetrics().height(); const int editorFontHeight = QFontMetrics(m_textEditorFont).height(); result = QSize(0, qMax(appFontHeight, editorFontHeight)); - } - else if (item->itemType() == SearchResultTreeItem::ResultRow) - { - const SearchResultTextRow *row = static_cast(item); - result = data(row, role); - } - else if (item->itemType() == SearchResultTreeItem::ResultFile) - { - const SearchResultFile *file = static_cast(item); - result = data(file, role); + } else { + result = data(treeItemAtIndex(idx), role); } return result; } -bool SearchResultTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool SearchResultTreeModel::setData(const QModelIndex &idx, const QVariant &value, int role) { if (role == Qt::CheckStateRole) { - SearchResultTreeItem *item = static_cast(index.internalPointer()); - SearchResultTextRow *row = static_cast(item); Qt::CheckState checkState = static_cast(value.toInt()); - row->setCheckState(checkState); + treeItemAtIndex(idx)->setCheckState(checkState); return true; } - return QAbstractItemModel::setData(index, value, role); + return QAbstractItemModel::setData(idx, value, role); } -QVariant SearchResultTreeModel::data(const SearchResultTextRow *row, int role) const +QVariant SearchResultTreeModel::data(const SearchResultTreeItem *row, int role) const { QVariant result; @@ -192,39 +190,40 @@ QVariant SearchResultTreeModel::data(const SearchResultTextRow *row, int role) c result = row->checkState(); break; case Qt::ToolTipRole: - result = row->rowText().trimmed(); + result = row->item.text.trimmed(); break; case Qt::FontRole: - result = m_textEditorFont; + if (row->item.useTextEditorFont) + result = m_textEditorFont; + else + result = QVariant(); break; - case ItemDataRoles::TextRole: case ItemDataRoles::ResultLineRole: case Qt::DisplayRole: - result = row->rowText(); + result = row->item.text; break; - case ItemDataRoles::ResultIndexRole: - result = row->index(); + case ItemDataRoles::ResultItemRole: + result = qVariantFromValue(row->item); break; case ItemDataRoles::ResultLineNumberRole: - result = row->lineNumber(); + result = row->item.lineNumber; + break; + case ItemDataRoles::ResultIconRole: + result = row->item.icon; break; case ItemDataRoles::SearchTermStartRole: - result = row->searchTermStart(); + result = row->item.textMarkPos; break; case ItemDataRoles::SearchTermLengthRole: - result = row->searchTermLength(); + result = row->item.textMarkLength; break; - case ItemDataRoles::TypeRole: - result = QLatin1String("row"); - break; - case ItemDataRoles::FileNameRole: - { - if (row->parent()->itemType() == SearchResultTreeItem::ResultFile) { - const SearchResultFile *file = static_cast(row->parent()); - result = file->fileName(); - } - break; - } +// TODO this looks stupid in case of symbol tree, is it necessary? +// case Qt::BackgroundRole: +// if (row->parent() && row->parent()->parent()) +// result = QVariant(); +// else +// result = QApplication::palette().base().color().darker(105); +// break; default: result = QVariant(); break; @@ -233,42 +232,6 @@ QVariant SearchResultTreeModel::data(const SearchResultTextRow *row, int role) c return result; } -QVariant SearchResultTreeModel::data(const SearchResultFile *file, int role) const -{ - switch (role) - { -#if 0 - case Qt::CheckStateRole: - if (file->isUserCheckable()) - return QVariant(file->checkState()); -#endif - case Qt::BackgroundRole: { - const QColor baseColor = QApplication::palette().base().color(); - return QVariant(baseColor.darker(105)); - break; - } - case Qt::DisplayRole: { - const QString result = - QDir::toNativeSeparators(file->fileName()) - + QString::fromLatin1(" (") - + QString::number(file->childrenCount()) - + QLatin1Char(')'); - return QVariant(result); - } - case ItemDataRoles::TextRole: - case ItemDataRoles::FileNameRole: - case Qt::ToolTipRole: - return QVariant(QDir::toNativeSeparators(file->fileName())); - case ItemDataRoles::ResultLinesCountRole: - return QVariant(file->childrenCount()); - case ItemDataRoles::TypeRole: - return QVariant(QLatin1String("file")); - default: - break; - } - return QVariant(); -} - QVariant SearchResultTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { @@ -279,144 +242,196 @@ QVariant SearchResultTreeModel::headerData(int section, Qt::Orientation orientat } /** - * Adds a file to the list of results and returns the index at which it was inserted. + * Makes sure that the nodes for a specific path exist and sets + * m_currentParent to the last final */ -int SearchResultTreeModel::addResultFile(const QString &fileName) +QSet SearchResultTreeModel::addPath(const QStringList &path) { -#ifdef Q_OS_WIN - if (fileName.contains(QLatin1Char('\\'))) - qWarning("SearchResultTreeModel::appendResultFile: File name with native separators added %s.\n", qPrintable(fileName)); -#endif - m_lastAddedResultFile = new SearchResultFile(fileName, m_rootItem); - - if (m_showReplaceUI) { - m_lastAddedResultFile->setIsUserCheckable(true); - m_lastAddedResultFile->setCheckState(Qt::Checked); + QSet pathNodes; + SearchResultTreeItem *currentItem = m_rootItem; + QModelIndex currentItemIndex = QModelIndex(); + SearchResultTreeItem *partItem = 0; + QStringList currentPath; + foreach (const QString &part, path) { + const int insertionIndex = currentItem->insertionIndex(part, &partItem); + if (!partItem) { + SearchResultItem item; + item.path = currentPath; + item.text = part; + partItem = new SearchResultTreeItem(item, currentItem); + if (m_showReplaceUI) { + partItem->setIsUserCheckable(true); + partItem->setCheckState(Qt::Checked); + } + partItem->setGenerated(true); + beginInsertRows(currentItemIndex, insertionIndex, insertionIndex); + currentItem->insertChild(insertionIndex, partItem); + endInsertRows(); + } + pathNodes << partItem; + currentItemIndex = index(insertionIndex, 0, currentItemIndex); + currentItem = partItem; + currentPath << part; } - const int index = m_rootItem->insertionIndex(m_lastAddedResultFile); - beginInsertRows(QModelIndex(), index, index); - m_rootItem->insertChild(index, m_lastAddedResultFile); - endInsertRows(); - return index; + m_currentParent = currentItem; + m_currentPath = currentPath; + m_currentIndex = currentItemIndex; + return pathNodes; } -void SearchResultTreeModel::appendResultLines(const QList &items) +void SearchResultTreeModel::addResultsToCurrentParent(const QList &items, SearchResultWindow::AddMode mode) { - if (!m_lastAddedResultFile) + if (!m_currentParent) return; - QModelIndex lastFile(createIndex(m_lastAddedResultFile->rowOfItem(), 0, m_lastAddedResultFile)); - - beginInsertRows(lastFile, m_lastAddedResultFile->childrenCount(), m_lastAddedResultFile->childrenCount() + items.count()); - foreach (const SearchResultItem &item, items) { - m_lastAddedResultFile->appendResultLine(item.index, - item.lineNumber, - item.lineText, - item.searchTermStart, - item.searchTermLength); + if (mode == SearchResultWindow::AddOrdered) { + // this is the mode for e.g. text search + beginInsertRows(m_currentIndex, m_currentParent->childrenCount(), m_currentParent->childrenCount() + items.count()); + foreach (const SearchResultItem &item, items) { + m_currentParent->appendChild(item); + } + endInsertRows(); + } else if (mode == SearchResultWindow::AddSorted) { + foreach (const SearchResultItem &item, items) { + SearchResultTreeItem *existingItem; + const int insertionIndex = m_currentParent->insertionIndex(item, &existingItem); + if (existingItem) { + existingItem->setGenerated(false); + existingItem->item = item; + QModelIndex itemIndex = m_currentIndex.child(insertionIndex, 0); + dataChanged(itemIndex, itemIndex); + } else { + beginInsertRows(m_currentIndex, insertionIndex, insertionIndex); + m_currentParent->insertChild(insertionIndex, item); + endInsertRows(); + } + } } - endInsertRows(); + dataChanged(m_currentIndex, m_currentIndex); // Make sure that the number after the file name gets updated +} - dataChanged(lastFile, lastFile); // Make sure that the number after the file name gets updated +static bool lessThanByPath(const SearchResultItem &a, const SearchResultItem &b) +{ + if (a.path.size() < b.path.size()) + return true; + if (a.path.size() > b.path.size()) + return false; + for (int i = 0; i < a.path.size(); ++i) { + if (a.path.at(i) < b.path.at(i)) + return true; + if (a.path.at(i) > b.path.at(i)) + return false; + } + return false; } /** - * Adds the search result to the list of results, creating a new file entry when - * necessary. Returns the insertion index when a new file entry was created. + * Adds the search result to the list of results, creating nodes for the path when + * necessary. */ -QList SearchResultTreeModel::addResultLines(const QList &items) +QList SearchResultTreeModel::addResults(const QList &items, SearchResultWindow::AddMode mode) { - QList insertedFileIndices; + QSet pathNodes; + QList sortedItems = items; + qStableSort(sortedItems.begin(), sortedItems.end(), lessThanByPath); QList itemSet; - foreach (const SearchResultItem &item, items) { - if (!m_lastAddedResultFile || (m_lastAddedResultFile->fileName() != item.fileName)) { + foreach (const SearchResultItem &item, sortedItems) { + if (!m_currentParent || (m_currentPath != item.path)) { + // first add all the items from before if (!itemSet.isEmpty()) { - appendResultLines(itemSet); + addResultsToCurrentParent(itemSet, mode); itemSet.clear(); } - insertedFileIndices << addResultFile(item.fileName); + // switch parent + pathNodes += addPath(item.path); } itemSet << item; } if (!itemSet.isEmpty()) { - appendResultLines(itemSet); + addResultsToCurrentParent(itemSet, mode); itemSet.clear(); } - return insertedFileIndices; + QList pathIndices; + foreach (SearchResultTreeItem *item, pathNodes) + pathIndices << index(item); + return pathIndices; } void SearchResultTreeModel::clear() { - m_lastAddedResultFile = NULL; + m_currentParent = NULL; m_rootItem->clearChildren(); reset(); } -QModelIndex SearchResultTreeModel::next(const QModelIndex &idx, bool includeTopLevel) const +QModelIndex SearchResultTreeModel::nextIndex(const QModelIndex &idx) const { - QModelIndex parent = idx.parent(); - if (parent.isValid()) { - int row = idx.row(); - if (row + 1 < rowCount(parent)) { - // Same parent - return index(row + 1, 0, parent); - } else { - // Next parent - int parentRow = parent.row(); - QModelIndex nextParent; - if (parentRow + 1 < rowCount()) { - nextParent = index(parentRow + 1, 0); - } else { - // Wrap around - nextParent = index(0,0); - } - if (includeTopLevel) - return nextParent; - return nextParent.child(0, 0); - } - } else { - // We are on a top level item - return idx.child(0,0); + // pathological + if (!idx.isValid()) + return index(0, 0); + + if (rowCount(idx) > 0) { + // node with children + return idx.child(0, 0); } - return QModelIndex(); + // leaf node + QModelIndex nextIndex; + QModelIndex current = idx; + while (!nextIndex.isValid()) { + int row = current.row(); + current = current.parent(); + if (row + 1 < rowCount(current)) { + // Same parent has another child + nextIndex = index(row + 1, 0, current); + } else { + // go up one parent + if (!current.isValid()) { + nextIndex = index(0, 0); + } + } + } + return nextIndex; } -QModelIndex SearchResultTreeModel::prev(const QModelIndex &idx, bool includeTopLevel) const +QModelIndex SearchResultTreeModel::next(const QModelIndex &idx, bool includeGenerated) const { - QModelIndex parent = idx.parent(); - if (parent.isValid()) { - int row = idx.row(); - if (row > 0) { - // Same parent - return index(row - 1, 0, parent); - } else { - if (includeTopLevel) - return parent; - // Prev parent - int parentRow = parent.row(); - QModelIndex prevParent; - if (parentRow > 0 ) { - prevParent = index(parentRow - 1, 0); - } else { - // Wrap around - prevParent = index(rowCount() - 1, 0); - } - return prevParent.child(rowCount(prevParent) - 1, 0); - } - } else { - // We are on a top level item - int row = idx.row(); - QModelIndex prevParent; + QModelIndex value = idx; + do { + value = nextIndex(value); + } while (value != idx && !includeGenerated && treeItemAtIndex(value)->isGenerated()); + return value; +} + +QModelIndex SearchResultTreeModel::prevIndex(const QModelIndex &idx) const +{ + QModelIndex current = idx; + bool checkForChildren = true; + if (current.isValid()) { + int row = current.row(); if (row > 0) { - prevParent = index(row - 1, 0); + current = index(row - 1, 0, current.parent()); } else { - // wrap around - prevParent = index(rowCount() -1, 0); + current = current.parent(); + checkForChildren = !current.isValid(); } - return prevParent.child(rowCount(prevParent) -1,0); } - return QModelIndex(); + if (checkForChildren) { + // traverse down the hierarchy + while (int rc = rowCount(current)) { + current = index(rc - 1, 0, current); + } + } + return current; +} + +QModelIndex SearchResultTreeModel::prev(const QModelIndex &idx, bool includeGenerated) const +{ + QModelIndex value = idx; + do { + value = prevIndex(value); + } while (value != idx && !includeGenerated && treeItemAtIndex(value)->isGenerated()); + return value; } QModelIndex SearchResultTreeModel::find(const QRegExp &expr, const QModelIndex &index, QTextDocument::FindFlags flags) @@ -431,7 +446,7 @@ QModelIndex SearchResultTreeModel::find(const QRegExp &expr, const QModelIndex & else currentIndex = next(currentIndex, true); if (currentIndex.isValid()) { - const QString &text = data(currentIndex, ItemDataRoles::TextRole).toString(); + const QString &text = data(currentIndex, ItemDataRoles::ResultLineRole).toString(); if (expr.indexIn(text) != -1) resultIndex = currentIndex; } @@ -452,7 +467,7 @@ QModelIndex SearchResultTreeModel::find(const QString &term, const QModelIndex & else currentIndex = next(currentIndex, true); if (currentIndex.isValid()) { - const QString &text = data(currentIndex, ItemDataRoles::TextRole).toString(); + const QString &text = data(currentIndex, ItemDataRoles::ResultLineRole).toString(); QTextDocument doc(text); if (!doc.find(term, 0, flags).isNull()) resultIndex = currentIndex; diff --git a/src/plugins/find/searchresulttreemodel.h b/src/plugins/find/searchresulttreemodel.h index d143c1213a1..ef0cfefce8e 100644 --- a/src/plugins/find/searchresulttreemodel.h +++ b/src/plugins/find/searchresulttreemodel.h @@ -41,8 +41,6 @@ namespace Find { namespace Internal { class SearchResultTreeItem; -class SearchResultTextRow; -class SearchResultFile; class SearchResultTreeModel : public QAbstractItemModel { @@ -64,10 +62,10 @@ public: bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); QVariant headerData(int section, Qt::Orientation orientation, int role) const; - QModelIndex next(const QModelIndex &idx, bool includeTopLevel = false) const; - QModelIndex prev(const QModelIndex &idx, bool includeTopLevel = false) const; + QModelIndex next(const QModelIndex &idx, bool includeGenerated = false) const; + QModelIndex prev(const QModelIndex &idx, bool includeGenerated = false) const; - QList addResultLines(const QList &items); + QList addResults(const QList &items, SearchResultWindow::AddMode mode); QModelIndex find(const QRegExp &expr, const QModelIndex &index, QTextDocument::FindFlags flags); QModelIndex find(const QString &term, const QModelIndex &index, QTextDocument::FindFlags flags); @@ -80,15 +78,18 @@ public slots: void clear(); private: - void appendResultLines(const QList &items); - int addResultFile(const QString &fileName); - QVariant data(const SearchResultTextRow *row, int role) const; - QVariant data(const SearchResultFile *file, int role) const; - void initializeData(); - void disposeData(); + QModelIndex index(SearchResultTreeItem *item) const; + void addResultsToCurrentParent(const QList &items, SearchResultWindow::AddMode mode); + QSet addPath(const QStringList &path); + QVariant data(const SearchResultTreeItem *row, int role) const; + QModelIndex nextIndex(const QModelIndex &idx) const; + QModelIndex prevIndex(const QModelIndex &idx) const; + SearchResultTreeItem *treeItemAtIndex(const QModelIndex &idx) const; SearchResultTreeItem *m_rootItem; - SearchResultFile *m_lastAddedResultFile; + SearchResultTreeItem *m_currentParent; + QModelIndex m_currentIndex; + QStringList m_currentPath; // the path that belongs to the current parent QFont m_textEditorFont; bool m_showReplaceUI; }; diff --git a/src/plugins/find/searchresulttreeview.cpp b/src/plugins/find/searchresulttreeview.cpp index 650cfe0b1a3..d97451d1f79 100644 --- a/src/plugins/find/searchresulttreeview.cpp +++ b/src/plugins/find/searchresulttreeview.cpp @@ -46,6 +46,7 @@ SearchResultTreeView::SearchResultTreeView(QWidget *parent) setItemDelegate(new SearchResultTreeItemDelegate(this)); setIndentation(14); setUniformRowHeights(true); + setExpandsOnDoubleClick(false); header()->hide(); connect(this, SIGNAL(activated(QModelIndex)), this, SLOT(emitJumpToSearchResult(QModelIndex))); @@ -66,24 +67,20 @@ void SearchResultTreeView::clear() m_model->clear(); } -void SearchResultTreeView::appendResultLines(const QList &items) +void SearchResultTreeView::addResults(const QList &items, SearchResultWindow::AddMode mode) { - const QList &insertedFileIndices = m_model->addResultLines(items); - if (m_autoExpandResults && !insertedFileIndices.isEmpty()) { - foreach (int index, insertedFileIndices) - setExpanded(model()->index(index, 0), true); + QList addedParents = m_model->addResults(items, mode); + if (m_autoExpandResults && !addedParents.isEmpty()) { + foreach (const QModelIndex &index, addedParents) + setExpanded(index, true); } } void SearchResultTreeView::emitJumpToSearchResult(const QModelIndex &index) { - if (model()->data(index, ItemDataRoles::TypeRole).toString().compare(QLatin1String("row")) != 0) - return; + SearchResultItem item = model()->data(index, ItemDataRoles::ResultItemRole).value(); - int position = model()->data(index, ItemDataRoles::ResultIndexRole).toInt(); - int checked = model()->data(index, Qt::CheckStateRole).toBool(); - - emit jumpToSearchResult(position, checked); + emit jumpToSearchResult(item); } void SearchResultTreeView::keyPressEvent(QKeyEvent *e) diff --git a/src/plugins/find/searchresulttreeview.h b/src/plugins/find/searchresulttreeview.h index ff9f9984e58..64e8ecc86c8 100644 --- a/src/plugins/find/searchresulttreeview.h +++ b/src/plugins/find/searchresulttreeview.h @@ -50,10 +50,10 @@ public: void setTextEditorFont(const QFont &font); SearchResultTreeModel *model() const; - void appendResultLines(const QList &items); + void addResults(const QList &items, SearchResultWindow::AddMode mode); signals: - void jumpToSearchResult(int index, bool checked); + void jumpToSearchResult(const SearchResultItem &item); public slots: void clear(); diff --git a/src/plugins/find/searchresultwindow.cpp b/src/plugins/find/searchresultwindow.cpp index 90b1cc75633..2dd2cd6228b 100644 --- a/src/plugins/find/searchresultwindow.cpp +++ b/src/plugins/find/searchresultwindow.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -56,7 +57,6 @@ static const char SETTINGSKEYSECTIONNAME[] = "SearchResults"; static const char SETTINGSKEYEXPANDRESULTS[] = "ExpandResults"; - namespace Find { namespace Internal { @@ -204,13 +204,14 @@ namespace Internal { static const bool m_initiallyExpand = false; QStackedWidget *m_widget; SearchResult *m_currentSearch; - QList m_items; + int m_itemCount; bool m_isShowingReplaceUI; bool m_focusReplaceEdit; }; SearchResultWindowPrivate::SearchResultWindowPrivate() : m_currentSearch(0), + m_itemCount(0), m_isShowingReplaceUI(false), m_focusReplaceEdit(false) { @@ -338,8 +339,8 @@ SearchResultWindow::SearchResultWindow() : d(new SearchResultWindowPrivate) d->m_replaceButton->setAutoRaise(true); d->m_replaceTextEdit->setTabOrder(d->m_replaceTextEdit, d->m_searchResultTreeView); - connect(d->m_searchResultTreeView, SIGNAL(jumpToSearchResult(int,bool)), - this, SLOT(handleJumpToSearchResult(int,bool))); + connect(d->m_searchResultTreeView, SIGNAL(jumpToSearchResult(SearchResultItem)), + this, SLOT(handleJumpToSearchResult(SearchResultItem))); connect(d->m_expandCollapseAction, SIGNAL(toggled(bool)), this, SLOT(handleExpandCollapseToolButton(bool))); connect(d->m_replaceTextEdit, SIGNAL(returnPressed()), this, SLOT(handleReplaceButton())); connect(d->m_replaceButton, SIGNAL(clicked()), this, SLOT(handleReplaceButton())); @@ -359,7 +360,7 @@ SearchResultWindow::~SearchResultWindow() d->m_currentSearch = 0; delete d->m_widget; d->m_widget = 0; - d->m_items.clear(); + d->m_itemCount = 0; delete d; } @@ -428,13 +429,13 @@ QList SearchResultWindow::checkedItems() const const int fileCount = model->rowCount(QModelIndex()); for (int i = 0; i < fileCount; ++i) { QModelIndex fileIndex = model->index(i, 0, QModelIndex()); - Internal::SearchResultFile *fileItem = static_cast(fileIndex.internalPointer()); + Internal::SearchResultTreeItem *fileItem = static_cast(fileIndex.internalPointer()); Q_ASSERT(fileItem != 0); for (int rowIndex = 0; rowIndex < fileItem->childrenCount(); ++rowIndex) { QModelIndex textIndex = model->index(rowIndex, 0, fileIndex); - Internal::SearchResultTextRow *rowItem = static_cast(textIndex.internalPointer()); + Internal::SearchResultTreeItem *rowItem = static_cast(textIndex.internalPointer()); if (rowItem->checkState()) - result << d->m_items.at(rowItem->index()); + result << rowItem->item; } } return result; @@ -492,7 +493,7 @@ SearchResult *SearchResultWindow::startNewSearch(SearchMode searchOrSearchAndRep */ void SearchResultWindow::finishSearch() { - if (d->m_items.count()) { + if (d->m_itemCount > 0) { d->m_replaceButton->setEnabled(true); } else { showNoMatchesFound(); @@ -509,7 +510,7 @@ void SearchResultWindow::clearContents() d->m_replaceButton->setEnabled(false); d->m_replaceTextEdit->clear(); d->m_searchResultTreeView->clear(); - d->m_items.clear(); + d->m_itemCount = 0; d->m_widget->setCurrentWidget(d->m_searchResultTreeView); navigateStateChanged(); } @@ -541,7 +542,7 @@ bool SearchResultWindow::isEmpty() const */ int SearchResultWindow::numberOfResults() const { - return d->m_items.count(); + return d->m_itemCount; } /*! @@ -559,7 +560,7 @@ bool SearchResultWindow::hasFocus() */ bool SearchResultWindow::canFocus() { - return !d->m_items.isEmpty(); + return d->m_itemCount > 0; } /*! @@ -568,7 +569,7 @@ bool SearchResultWindow::canFocus() */ void SearchResultWindow::setFocus() { - if (!d->m_items.isEmpty()) { + if (d->m_itemCount > 0) { if (!d->m_isShowingReplaceUI) { d->m_searchResultTreeView->setFocus(); } else { @@ -597,10 +598,10 @@ void SearchResultWindow::setTextEditorFont(const QFont &font) \fn void SearchResultWindow::handleJumpToSearchResult(int index, bool) \internal */ -void SearchResultWindow::handleJumpToSearchResult(int index, bool /* checked */) +void SearchResultWindow::handleJumpToSearchResult(const SearchResultItem &item) { QTC_ASSERT(d->m_currentSearch, return); - d->m_currentSearch->activated(d->m_items.at(index)); + d->m_currentSearch->activated(item); } /*! @@ -619,13 +620,14 @@ void SearchResultWindow::addResult(const QString &fileName, int lineNumber, cons int searchTermStart, int searchTermLength, const QVariant &userData) { SearchResultItem item; - item.fileName = fileName; + item.path = QStringList() << QDir::toNativeSeparators(fileName); item.lineNumber = lineNumber; - item.lineText = rowText; - item.searchTermStart = searchTermStart; - item.searchTermLength = searchTermLength; + item.text = rowText; + item.textMarkPos = searchTermStart; + item.textMarkLength = searchTermLength; + item.useTextEditorFont = true; item.userData = userData; - addResults(QList() << item); + addResults(QList() << item, AddOrdered); } /*! @@ -635,17 +637,11 @@ void SearchResultWindow::addResult(const QString &fileName, int lineNumber, cons \sa addResult() */ -void SearchResultWindow::addResults(QList &items) +void SearchResultWindow::addResults(QList &items, AddMode mode) { - int index = d->m_items.size(); - bool firstItems = (index == 0); - for (int i = 0; i < items.size(); ++i) { - items[i].index = index; - ++index; - } - - d->m_items << items; - d->m_searchResultTreeView->appendResultLines(items); + bool firstItems = (d->m_itemCount == 0); + d->m_itemCount += items.size(); + d->m_searchResultTreeView->addResults(items, mode); if (firstItems) { d->m_replaceTextEdit->setEnabled(true); // We didn't have an item before, set the focus to the search widget @@ -713,7 +709,7 @@ int SearchResultWindow::priorityInStatusBar() const */ bool SearchResultWindow::canNext() { - return d->m_items.count() > 0; + return d->m_itemCount > 0; } /*! @@ -722,7 +718,7 @@ bool SearchResultWindow::canNext() */ bool SearchResultWindow::canPrevious() { - return d->m_items.count() > 0; + return d->m_itemCount > 0; } /*! @@ -731,7 +727,7 @@ bool SearchResultWindow::canPrevious() */ void SearchResultWindow::goToNext() { - if (d->m_items.count() == 0) + if (d->m_itemCount == 0) return; QModelIndex idx = d->m_searchResultTreeView->model()->next(d->m_searchResultTreeView->currentIndex()); if (idx.isValid()) { diff --git a/src/plugins/find/searchresultwindow.h b/src/plugins/find/searchresultwindow.h index 92e58cebbb5..b085f15f1c3 100644 --- a/src/plugins/find/searchresultwindow.h +++ b/src/plugins/find/searchresultwindow.h @@ -32,10 +32,12 @@ #include "find_global.h" -#include - #include +#include +#include +#include + QT_BEGIN_NAMESPACE class QFont; QT_END_NAMESPACE @@ -49,14 +51,34 @@ class SearchResultWindow; struct FIND_EXPORT SearchResultItem { - QString fileName; - int lineNumber; - QString lineText; - int searchTermStart; - int searchTermLength; - int index; // SearchResultWindow sets the index - QVariant userData; - // whatever information we also need here + SearchResultItem() + : textMarkPos(-1), + textMarkLength(0), + lineNumber(-1), + useTextEditorFont(false) + { + } + + SearchResultItem(const SearchResultItem &other) + : path(other.path), + text(other.text), + textMarkPos(other.textMarkPos), + textMarkLength(other.textMarkLength), + icon(other.icon), + lineNumber(other.lineNumber), + useTextEditorFont(other.useTextEditorFont), + userData(other.userData) + { + } + + QStringList path; // hierarchy to the parent item of this item + QString text; // text to show for the item itself + int textMarkPos; // 0-based starting position for a mark (-1 for no mark) + int textMarkLength; // length of the mark (0 for no mark) + QIcon icon; // icon to show in front of the item (by be null icon to hide) + int lineNumber; // (0 or -1 for no line number) + bool useTextEditorFont; + QVariant userData; // user data for identification of the item }; class FIND_EXPORT SearchResult : public QObject @@ -80,6 +102,11 @@ public: SearchAndReplace }; + enum AddMode { + AddSorted, + AddOrdered + }; + SearchResultWindow(); virtual ~SearchResultWindow(); static SearchResultWindow *instance(); @@ -110,7 +137,7 @@ public: // search result object only lives till next startnewsearch call SearchResult *startNewSearch(SearchMode searchOrSearchAndReplace = SearchOnly); - void addResults(QList &items); + void addResults(QList &items, AddMode mode); public slots: void clearContents(); void addResult(const QString &fileName, int lineNumber, const QString &lineText, @@ -119,7 +146,7 @@ public slots: private slots: void handleExpandCollapseToolButton(bool checked); - void handleJumpToSearchResult(int index, bool checked); + void handleJumpToSearchResult(const SearchResultItem &item); void handleReplaceButton(); void showNoMatchesFound(); @@ -135,4 +162,6 @@ private: } // namespace Find +Q_DECLARE_METATYPE(Find::SearchResultItem) + #endif // SEARCHRESULTWINDOW_H diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 094668d4c0f..86df657bf19 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -128,6 +128,7 @@ plugin_cpptools.subdir = cpptools plugin_cpptools.depends = plugin_projectexplorer plugin_cpptools.depends += plugin_coreplugin plugin_cpptools.depends += plugin_texteditor +plugin_cpptools.depends += plugin_find plugin_bookmarks.subdir = bookmarks plugin_bookmarks.depends = plugin_projectexplorer diff --git a/src/plugins/projectexplorer/allprojectsfind.cpp b/src/plugins/projectexplorer/allprojectsfind.cpp index 9bf1c995790..89ae73506ce 100644 --- a/src/plugins/projectexplorer/allprojectsfind.cpp +++ b/src/plugins/projectexplorer/allprojectsfind.cpp @@ -72,11 +72,6 @@ bool AllProjectsFind::isEnabled() const && m_plugin->session()->projects().count() > 0; } -QKeySequence AllProjectsFind::defaultShortcut() const -{ - return QKeySequence(); -} - Utils::FileIterator *AllProjectsFind::files() { Q_ASSERT(m_plugin->session()); diff --git a/src/plugins/projectexplorer/allprojectsfind.h b/src/plugins/projectexplorer/allprojectsfind.h index d4710a7381c..d6484e5d60b 100644 --- a/src/plugins/projectexplorer/allprojectsfind.h +++ b/src/plugins/projectexplorer/allprojectsfind.h @@ -54,7 +54,6 @@ public: QString displayName() const; bool isEnabled() const; - QKeySequence defaultShortcut() const; QWidget *createConfigWidget(); void writeSettings(QSettings *settings); diff --git a/src/plugins/projectexplorer/currentprojectfind.cpp b/src/plugins/projectexplorer/currentprojectfind.cpp index 55a2ba6950d..2e929c5cbe5 100644 --- a/src/plugins/projectexplorer/currentprojectfind.cpp +++ b/src/plugins/projectexplorer/currentprojectfind.cpp @@ -70,11 +70,6 @@ bool CurrentProjectFind::isEnabled() const return m_plugin->currentProject() != 0 && BaseFileFind::isEnabled(); } -QKeySequence CurrentProjectFind::defaultShortcut() const -{ - return QKeySequence(); -} - Utils::FileIterator *CurrentProjectFind::files() { Project *project = m_plugin->currentProject(); diff --git a/src/plugins/projectexplorer/currentprojectfind.h b/src/plugins/projectexplorer/currentprojectfind.h index 5ee35a83c8d..80661895954 100644 --- a/src/plugins/projectexplorer/currentprojectfind.h +++ b/src/plugins/projectexplorer/currentprojectfind.h @@ -57,7 +57,6 @@ public: QString displayName() const; bool isEnabled() const; - QKeySequence defaultShortcut() const; QWidget *createConfigWidget(); void writeSettings(QSettings *settings); diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index 9006e11a736..7360ade7712 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -153,15 +153,16 @@ void BaseFileFind::displayResult(int index) { QList items; // this conversion is stupid... foreach (const Utils::FileSearchResult &result, results) { Find::SearchResultItem item; - item.fileName = result.fileName; + item.path = QStringList() << QDir::toNativeSeparators(result.fileName); item.lineNumber = result.lineNumber; - item.lineText = result.matchingLine; - item.searchTermLength = result.matchLength; - item.searchTermStart = result.matchStart; + item.text = result.matchingLine; + item.textMarkLength = result.matchLength; + item.textMarkPos = result.matchStart; + item.useTextEditorFont = true; item.userData = result.regexpCapturedTexts; items << item; } - m_resultWindow->addResults(items); + m_resultWindow->addResults(items, Find::SearchResultWindow::AddOrdered); if (m_resultLabel) m_resultLabel->setText(tr("%1 found").arg(m_resultWindow->numberOfResults())); } @@ -250,7 +251,11 @@ void BaseFileFind::updateComboEntries(QComboBox *combo, bool onTop) void BaseFileFind::openEditor(const Find::SearchResultItem &item) { - TextEditor::BaseTextEditor::openEditorAt(item.fileName, item.lineNumber, item.searchTermStart); + if (item.path.size() > 0) { + TextEditor::BaseTextEditor::openEditorAt(item.path.first(), item.lineNumber, item.textMarkPos); + } else { + Core::EditorManager::instance()->openEditor(item.text); + } } // #pragma mark Static methods @@ -263,7 +268,7 @@ static void applyChanges(QTextDocument *doc, const QString &text, const QListfindBlockByNumber(blockNumber)); - const int cursorPosition = tc.position() + item.searchTermStart; + const int cursorPosition = tc.position() + item.textMarkPos; int cursorIndex = 0; for (; cursorIndex < changes.size(); ++cursorIndex) { @@ -277,7 +282,7 @@ static void applyChanges(QTextDocument *doc, const QString &text, const QList() && !item.userData.toStringList().isEmpty()) @@ -302,7 +307,7 @@ QStringList BaseFileFind::replaceAll(const QString &text, QHash > changes; foreach (const Find::SearchResultItem &item, items) - changes[item.fileName].append(item); + changes[item.path.first()].append(item); Core::EditorManager *editorManager = Core::EditorManager::instance(); diff --git a/src/plugins/texteditor/findincurrentfile.cpp b/src/plugins/texteditor/findincurrentfile.cpp index cad56dd7a44..01c060133f9 100644 --- a/src/plugins/texteditor/findincurrentfile.cpp +++ b/src/plugins/texteditor/findincurrentfile.cpp @@ -62,11 +62,6 @@ QString FindInCurrentFile::displayName() const return tr("Current File"); } -QKeySequence FindInCurrentFile::defaultShortcut() const -{ - return QKeySequence(); -} - Utils::FileIterator *FindInCurrentFile::files() { QStringList fileList; diff --git a/src/plugins/texteditor/findincurrentfile.h b/src/plugins/texteditor/findincurrentfile.h index b532e4cfa79..b42064d99c8 100644 --- a/src/plugins/texteditor/findincurrentfile.h +++ b/src/plugins/texteditor/findincurrentfile.h @@ -55,7 +55,6 @@ public: QString id() const; QString displayName() const; - QKeySequence defaultShortcut() const; bool isEnabled() const; QWidget *createConfigWidget(); void writeSettings(QSettings *settings); diff --git a/src/plugins/texteditor/findinfiles.cpp b/src/plugins/texteditor/findinfiles.cpp index f2bbc54300e..b6b02ef31b0 100644 --- a/src/plugins/texteditor/findinfiles.cpp +++ b/src/plugins/texteditor/findinfiles.cpp @@ -57,11 +57,6 @@ QString FindInFiles::displayName() const return tr("Files on File System"); } -QKeySequence FindInFiles::defaultShortcut() const -{ - return QKeySequence(); -} - void FindInFiles::findAll(const QString &txt, Find::FindFlags findFlags) { updateComboEntries(m_directory, true); diff --git a/src/plugins/texteditor/findinfiles.h b/src/plugins/texteditor/findinfiles.h index 49172f13b70..569e5ce781d 100644 --- a/src/plugins/texteditor/findinfiles.h +++ b/src/plugins/texteditor/findinfiles.h @@ -53,7 +53,6 @@ public: QString id() const; QString displayName() const; - QKeySequence defaultShortcut() const; void findAll(const QString &txt, Find::FindFlags findFlags); QWidget *createConfigWidget(); void writeSettings(QSettings *settings);