summaryrefslogtreecommitdiff
path: root/src/plugins/coreplugin
diff options
context:
space:
mode:
authorhjk <hjk121@nokiamail.com>2014-01-13 16:17:34 +0100
committerEike Ziller <eike.ziller@digia.com>2014-01-14 07:43:00 +0100
commit4d96fa7aba7be35800d61d8bed89d3f6c3ef9329 (patch)
treec9b102981cf81023e1488224a24758af18aa064e /src/plugins/coreplugin
parent8b854270a6c214479b2cdf302072a3e74fa854da (diff)
downloadqt-creator-4d96fa7aba7be35800d61d8bed89d3f6c3ef9329.tar.gz
Core: Merge Find and Locator into Core plugin
Change-Id: I7053310272235d854c9f409670ff52a10a7add8b Reviewed-by: Christian Kandeler <christian.kandeler@digia.com> Reviewed-by: Orgad Shaneh <orgads@gmail.com> Reviewed-by: Eike Ziller <eike.ziller@digia.com>
Diffstat (limited to 'src/plugins/coreplugin')
-rw-r--r--src/plugins/coreplugin/coreplugin.cpp14
-rw-r--r--src/plugins/coreplugin/coreplugin.h6
-rw-r--r--src/plugins/coreplugin/coreplugin.pro5
-rw-r--r--src/plugins/coreplugin/coreplugin.qbs87
-rw-r--r--src/plugins/coreplugin/find/basetextfind.cpp427
-rw-r--r--src/plugins/coreplugin/find/basetextfind.h91
-rw-r--r--src/plugins/coreplugin/find/currentdocumentfind.cpp259
-rw-r--r--src/plugins/coreplugin/find/currentdocumentfind.h93
-rw-r--r--src/plugins/coreplugin/find/find.pri42
-rw-r--r--src/plugins/coreplugin/find/find.qrc10
-rw-r--r--src/plugins/coreplugin/find/finddialog.ui209
-rw-r--r--src/plugins/coreplugin/find/findplugin.cpp394
-rw-r--r--src/plugins/coreplugin/find/findplugin.h108
-rw-r--r--src/plugins/coreplugin/find/findtoolbar.cpp779
-rw-r--r--src/plugins/coreplugin/find/findtoolbar.h149
-rw-r--r--src/plugins/coreplugin/find/findtoolwindow.cpp264
-rw-r--r--src/plugins/coreplugin/find/findtoolwindow.h87
-rw-r--r--src/plugins/coreplugin/find/findwidget.ui292
-rw-r--r--src/plugins/coreplugin/find/ifindfilter.cpp285
-rw-r--r--src/plugins/coreplugin/find/ifindfilter.h75
-rw-r--r--src/plugins/coreplugin/find/ifindsupport.cpp121
-rw-r--r--src/plugins/coreplugin/find/ifindsupport.h80
-rw-r--r--src/plugins/coreplugin/find/images/all.pngbin0 -> 108 bytes
-rw-r--r--src/plugins/coreplugin/find/images/casesensitively.pngbin0 -> 153 bytes
-rw-r--r--src/plugins/coreplugin/find/images/empty.pngbin0 -> 75 bytes
-rw-r--r--src/plugins/coreplugin/find/images/expand.pngbin0 -> 931 bytes
-rw-r--r--src/plugins/coreplugin/find/images/next.pngbin0 -> 109 bytes
-rw-r--r--src/plugins/coreplugin/find/images/preservecase.pngbin0 -> 196 bytes
-rw-r--r--src/plugins/coreplugin/find/images/previous.pngbin0 -> 103 bytes
-rw-r--r--src/plugins/coreplugin/find/images/regexp.pngbin0 -> 152 bytes
-rw-r--r--src/plugins/coreplugin/find/images/replace_all.pngbin0 -> 934 bytes
-rw-r--r--src/plugins/coreplugin/find/images/wholewords.pngbin0 -> 146 bytes
-rw-r--r--src/plugins/coreplugin/find/images/wordandcase.pngbin0 -> 198 bytes
-rw-r--r--src/plugins/coreplugin/find/images/wrapindicator.pngbin0 -> 1949 bytes
-rw-r--r--src/plugins/coreplugin/find/searchresultcolor.h20
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeitemdelegate.cpp224
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeitemdelegate.h55
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeitemroles.h56
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeitems.cpp145
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeitems.h82
-rw-r--r--src/plugins/coreplugin/find/searchresulttreemodel.cpp504
-rw-r--r--src/plugins/coreplugin/find/searchresulttreemodel.h99
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeview.cpp109
-rw-r--r--src/plugins/coreplugin/find/searchresulttreeview.h73
-rw-r--r--src/plugins/coreplugin/find/searchresultwidget.cpp495
-rw-r--r--src/plugins/coreplugin/find/searchresultwidget.h146
-rw-r--r--src/plugins/coreplugin/find/searchresultwindow.cpp715
-rw-r--r--src/plugins/coreplugin/find/searchresultwindow.h198
-rw-r--r--src/plugins/coreplugin/find/textfindconstants.h84
-rw-r--r--src/plugins/coreplugin/find/treeviewfind.cpp277
-rw-r--r--src/plugins/coreplugin/find/treeviewfind.h75
-rw-r--r--src/plugins/coreplugin/locator/basefilefilter.cpp126
-rw-r--r--src/plugins/coreplugin/locator/basefilefilter.h66
-rw-r--r--src/plugins/coreplugin/locator/commandlocator.cpp109
-rw-r--r--src/plugins/coreplugin/locator/commandlocator.h63
-rw-r--r--src/plugins/coreplugin/locator/directoryfilter.cpp204
-rw-r--r--src/plugins/coreplugin/locator/directoryfilter.h75
-rw-r--r--src/plugins/coreplugin/locator/directoryfilter.ui195
-rw-r--r--src/plugins/coreplugin/locator/executefilter.cpp188
-rw-r--r--src/plugins/coreplugin/locator/executefilter.h83
-rw-r--r--src/plugins/coreplugin/locator/filesystemfilter.cpp187
-rw-r--r--src/plugins/coreplugin/locator/filesystemfilter.h67
-rw-r--r--src/plugins/coreplugin/locator/filesystemfilter.ui111
-rw-r--r--src/plugins/coreplugin/locator/ilocatorfilter.cpp208
-rw-r--r--src/plugins/coreplugin/locator/ilocatorfilter.h167
-rw-r--r--src/plugins/coreplugin/locator/images/locator.pngbin0 -> 767 bytes
-rw-r--r--src/plugins/coreplugin/locator/images/reload.pngbin0 -> 735 bytes
-rw-r--r--src/plugins/coreplugin/locator/locator.pri46
-rw-r--r--src/plugins/coreplugin/locator/locator.qrc6
-rw-r--r--src/plugins/coreplugin/locator/locator_test.cpp173
-rw-r--r--src/plugins/coreplugin/locator/locatorconstants.h44
-rw-r--r--src/plugins/coreplugin/locator/locatorfiltersfilter.cpp95
-rw-r--r--src/plugins/coreplugin/locator/locatorfiltersfilter.h69
-rw-r--r--src/plugins/coreplugin/locator/locatorfiltertest.cpp88
-rw-r--r--src/plugins/coreplugin/locator/locatorfiltertest.h95
-rw-r--r--src/plugins/coreplugin/locator/locatormanager.cpp58
-rw-r--r--src/plugins/coreplugin/locator/locatormanager.h54
-rw-r--r--src/plugins/coreplugin/locator/locatorplugin.cpp261
-rw-r--r--src/plugins/coreplugin/locator/locatorplugin.h134
-rw-r--r--src/plugins/coreplugin/locator/locatorsearchutils.cpp65
-rw-r--r--src/plugins/coreplugin/locator/locatorsearchutils.h46
-rw-r--r--src/plugins/coreplugin/locator/locatorwidget.cpp590
-rw-r--r--src/plugins/coreplugin/locator/locatorwidget.h107
-rw-r--r--src/plugins/coreplugin/locator/opendocumentsfilter.cpp114
-rw-r--r--src/plugins/coreplugin/locator/opendocumentsfilter.h64
-rw-r--r--src/plugins/coreplugin/locator/settingspage.cpp210
-rw-r--r--src/plugins/coreplugin/locator/settingspage.h89
-rw-r--r--src/plugins/coreplugin/locator/settingspage.ui121
-rw-r--r--src/plugins/coreplugin/progressmanager/progressmanager.cpp2
89 files changed, 11612 insertions, 2 deletions
diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp
index c6869dd175..e16a4e7f96 100644
--- a/src/plugins/coreplugin/coreplugin.cpp
+++ b/src/plugins/coreplugin/coreplugin.cpp
@@ -35,8 +35,11 @@
#include "mimedatabase.h"
#include "modemanager.h"
#include "infobar.h"
+
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/find/findplugin.h>
+#include <coreplugin/locator/locatorplugin.h>
#include <utils/savefile.h>
@@ -51,10 +54,15 @@ CorePlugin::CorePlugin() : m_editMode(0), m_designMode(0)
{
qRegisterMetaType<Core::Id>();
m_mainWindow = new MainWindow;
+ m_findPlugin = new FindPlugin;
+ m_locatorPlugin = new LocatorPlugin;
}
CorePlugin::~CorePlugin()
{
+ delete m_findPlugin;
+ delete m_locatorPlugin;
+
if (m_editMode) {
removeObject(m_editMode);
delete m_editMode;
@@ -98,6 +106,9 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage)
// Make sure we respect the process's umask when creating new files
Utils::SaveFile::initializeUmask();
+ m_findPlugin->initialize(arguments, errorMessage);
+ m_locatorPlugin->initialize(this, arguments, errorMessage);
+
return success;
}
@@ -107,11 +118,14 @@ void CorePlugin::extensionsInitialized()
if (m_designMode->designModeIsRequired())
addObject(m_designMode);
m_mainWindow->extensionsInitialized();
+ m_findPlugin->extensionsInitialized();
+ m_locatorPlugin->extensionsInitialized();
}
bool CorePlugin::delayedInitialize()
{
HelpManager::setupHelpManager();
+ m_locatorPlugin->delayedInitialize();
return true;
}
diff --git a/src/plugins/coreplugin/coreplugin.h b/src/plugins/coreplugin/coreplugin.h
index 9855c39eba..fd6d1ad662 100644
--- a/src/plugins/coreplugin/coreplugin.h
+++ b/src/plugins/coreplugin/coreplugin.h
@@ -33,11 +33,15 @@
#include <extensionsystem/iplugin.h>
namespace Core {
+
class DesignMode;
+class FindPlugin;
+
namespace Internal {
class EditMode;
class MainWindow;
+class LocatorPlugin;
class CorePlugin : public ExtensionSystem::IPlugin
{
@@ -69,6 +73,8 @@ private:
MainWindow *m_mainWindow;
EditMode *m_editMode;
DesignMode *m_designMode;
+ FindPlugin *m_findPlugin;
+ LocatorPlugin *m_locatorPlugin;
};
} // namespace Internal
diff --git a/src/plugins/coreplugin/coreplugin.pro b/src/plugins/coreplugin/coreplugin.pro
index 10b4087a43..3b68f06f26 100644
--- a/src/plugins/coreplugin/coreplugin.pro
+++ b/src/plugins/coreplugin/coreplugin.pro
@@ -206,11 +206,14 @@ FORMS += dialogs/newdialog.ui \
mimetypesettingspage.ui \
mimetypemagicdialog.ui \
removefiledialog.ui \
- dialogs/addtovcsdialog.ui
+ dialogs/addtovcsdialog.ui
RESOURCES += core.qrc \
fancyactionbar.qrc
+include(find/find.pri)
+include(locator/locator.pri)
+
win32 {
SOURCES += progressmanager/progressmanager_win.cpp
greaterThan(QT_MAJOR_VERSION, 4): QT += gui-private # Uses QPlatformNativeInterface.
diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs
index b7b6c93b38..a18785cdd3 100644
--- a/src/plugins/coreplugin/coreplugin.qbs
+++ b/src/plugins/coreplugin/coreplugin.qbs
@@ -1,4 +1,5 @@
import qbs.base 1.0
+import qbs.FileInfo
import QtcPlugin
QtcPlugin {
@@ -183,6 +184,92 @@ QtcPlugin {
files: [
"testdatadir.cpp",
"testdatadir.h",
+ "locator/locatorfiltertest.cpp",
+ "locator/locatorfiltertest.h",
+ "locator/locator_test.cpp"
+ ]
+
+ cpp.defines: outer.concat(['SRCDIR="' + FileInfo.path(filePath) + '"'])
+ }
+
+ Group {
+ name: "Find"
+ prefix: "find/"
+ files: [
+ "basetextfind.cpp",
+ "basetextfind.h",
+ "currentdocumentfind.cpp",
+ "currentdocumentfind.h",
+ "find.qrc",
+ "finddialog.ui",
+ "findplugin.cpp",
+ "findplugin.h",
+ "findtoolbar.cpp",
+ "findtoolbar.h",
+ "findtoolwindow.cpp",
+ "findtoolwindow.h",
+ "findwidget.ui",
+ "ifindfilter.cpp",
+ "ifindfilter.h",
+ "ifindsupport.cpp",
+ "ifindsupport.h",
+ "searchresultcolor.h",
+ "searchresulttreeitemdelegate.cpp",
+ "searchresulttreeitemdelegate.h",
+ "searchresulttreeitemroles.h",
+ "searchresulttreeitems.cpp",
+ "searchresulttreeitems.h",
+ "searchresulttreemodel.cpp",
+ "searchresulttreemodel.h",
+ "searchresulttreeview.cpp",
+ "searchresulttreeview.h",
+ "searchresultwidget.cpp",
+ "searchresultwidget.h",
+ "searchresultwindow.cpp",
+ "searchresultwindow.h",
+ "textfindconstants.h",
+ "treeviewfind.cpp",
+ "treeviewfind.h",
+ ]
+ }
+
+ Group {
+ name: "Locator"
+ prefix: "locator/"
+ files: [
+ "basefilefilter.cpp",
+ "basefilefilter.h",
+ "commandlocator.cpp",
+ "commandlocator.h",
+ "directoryfilter.cpp",
+ "directoryfilter.h",
+ "directoryfilter.ui",
+ "executefilter.cpp",
+ "executefilter.h",
+ "filesystemfilter.cpp",
+ "filesystemfilter.h",
+ "filesystemfilter.ui",
+ "ilocatorfilter.cpp",
+ "ilocatorfilter.h",
+ "locator.qrc",
+ "locatorconstants.h",
+ "locatorfiltersfilter.cpp",
+ "locatorfiltersfilter.h",
+ "locatormanager.cpp",
+ "locatormanager.h",
+ "locatorplugin.cpp",
+ "locatorplugin.h",
+ "locatorsearchutils.cpp",
+ "locatorsearchutils.h",
+ "locatorwidget.cpp",
+ "locatorwidget.h",
+ "opendocumentsfilter.cpp",
+ "opendocumentsfilter.h",
+ "settingspage.cpp",
+ "settingspage.h",
+ "settingspage.ui",
+ "images/locator.png",
+ "images/reload.png",
]
}
diff --git a/src/plugins/coreplugin/find/basetextfind.cpp b/src/plugins/coreplugin/find/basetextfind.cpp
new file mode 100644
index 0000000000..70fdef51bd
--- /dev/null
+++ b/src/plugins/coreplugin/find/basetextfind.cpp
@@ -0,0 +1,427 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "basetextfind.h"
+
+#include <utils/qtcassert.h>
+#include <utils/filesearch.h>
+
+#include <QPointer>
+
+#include <QTextBlock>
+#include <QPlainTextEdit>
+#include <QTextCursor>
+
+namespace Core {
+
+struct BaseTextFindPrivate
+{
+ explicit BaseTextFindPrivate(QPlainTextEdit *editor);
+ explicit BaseTextFindPrivate(QTextEdit *editor);
+
+ QPointer<QTextEdit> m_editor;
+ QPointer<QPlainTextEdit> m_plaineditor;
+ QPointer<QWidget> m_widget;
+ QTextCursor m_findScopeStart;
+ QTextCursor m_findScopeEnd;
+ int m_findScopeVerticalBlockSelectionFirstColumn;
+ int m_findScopeVerticalBlockSelectionLastColumn;
+ int m_incrementalStartPos;
+ bool m_incrementalWrappedState;
+};
+
+BaseTextFindPrivate::BaseTextFindPrivate(QTextEdit *editor)
+ : m_editor(editor)
+ , m_widget(editor)
+ , m_findScopeVerticalBlockSelectionFirstColumn(-1)
+ , m_findScopeVerticalBlockSelectionLastColumn(-1)
+ , m_incrementalStartPos(-1)
+ , m_incrementalWrappedState(false)
+{
+}
+
+BaseTextFindPrivate::BaseTextFindPrivate(QPlainTextEdit *editor)
+ : m_plaineditor(editor)
+ , m_widget(editor)
+ , m_findScopeVerticalBlockSelectionFirstColumn(-1)
+ , m_findScopeVerticalBlockSelectionLastColumn(-1)
+ , m_incrementalStartPos(-1)
+ , m_incrementalWrappedState(false)
+{
+}
+
+BaseTextFind::BaseTextFind(QTextEdit *editor)
+ : d(new BaseTextFindPrivate(editor))
+{
+}
+
+
+BaseTextFind::BaseTextFind(QPlainTextEdit *editor)
+ : d(new BaseTextFindPrivate(editor))
+{
+}
+
+BaseTextFind::~BaseTextFind()
+{
+ delete d;
+}
+
+QTextCursor BaseTextFind::textCursor() const
+{
+ QTC_ASSERT(d->m_editor || d->m_plaineditor, return QTextCursor());
+ return d->m_editor ? d->m_editor->textCursor() : d->m_plaineditor->textCursor();
+}
+
+void BaseTextFind::setTextCursor(const QTextCursor &cursor)
+{
+ QTC_ASSERT(d->m_editor || d->m_plaineditor, return);
+ d->m_editor ? d->m_editor->setTextCursor(cursor) : d->m_plaineditor->setTextCursor(cursor);
+}
+
+QTextDocument *BaseTextFind::document() const
+{
+ QTC_ASSERT(d->m_editor || d->m_plaineditor, return 0);
+ return d->m_editor ? d->m_editor->document() : d->m_plaineditor->document();
+}
+
+bool BaseTextFind::isReadOnly() const
+{
+ QTC_ASSERT(d->m_editor || d->m_plaineditor, return true);
+ return d->m_editor ? d->m_editor->isReadOnly() : d->m_plaineditor->isReadOnly();
+}
+
+bool BaseTextFind::supportsReplace() const
+{
+ return !isReadOnly();
+}
+
+FindFlags BaseTextFind::supportedFindFlags() const
+{
+ return FindBackward | FindCaseSensitively | FindRegularExpression
+ | FindWholeWords | FindPreserveCase;
+}
+
+void BaseTextFind::resetIncrementalSearch()
+{
+ d->m_incrementalStartPos = -1;
+ d->m_incrementalWrappedState = false;
+}
+
+void BaseTextFind::clearResults()
+{
+ emit highlightAll(QString(), 0);
+}
+
+QString BaseTextFind::currentFindString() const
+{
+ QTextCursor cursor = textCursor();
+ if (cursor.hasSelection() && cursor.block() != cursor.document()->findBlock(cursor.anchor()))
+ return QString(); // multi block selection
+
+ if (cursor.hasSelection())
+ return cursor.selectedText();
+
+ if (!cursor.atBlockEnd() && !cursor.hasSelection()) {
+ cursor.movePosition(QTextCursor::StartOfWord);
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
+ QString s = cursor.selectedText();
+ foreach (QChar c, s) {
+ if (!c.isLetterOrNumber() && c != QLatin1Char('_')) {
+ s.clear();
+ break;
+ }
+ }
+ return s;
+ }
+
+ return QString();
+}
+
+QString BaseTextFind::completedFindString() const
+{
+ QTextCursor cursor = textCursor();
+ cursor.setPosition(textCursor().selectionStart());
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
+ return cursor.selectedText();
+}
+
+IFindSupport::Result BaseTextFind::findIncremental(const QString &txt, FindFlags findFlags)
+{
+ QTextCursor cursor = textCursor();
+ if (d->m_incrementalStartPos < 0)
+ d->m_incrementalStartPos = cursor.selectionStart();
+ cursor.setPosition(d->m_incrementalStartPos);
+ bool wrapped = false;
+ bool found = find(txt, findFlags, cursor, &wrapped);
+ if (wrapped != d->m_incrementalWrappedState && found) {
+ d->m_incrementalWrappedState = wrapped;
+ showWrapIndicator(d->m_widget);
+ }
+ if (found)
+ emit highlightAll(txt, findFlags);
+ else
+ emit highlightAll(QString(), 0);
+ return found ? Found : NotFound;
+}
+
+IFindSupport::Result BaseTextFind::findStep(const QString &txt, FindFlags findFlags)
+{
+ bool wrapped = false;
+ bool found = find(txt, findFlags, textCursor(), &wrapped);
+ if (wrapped)
+ showWrapIndicator(d->m_widget);
+ if (found) {
+ d->m_incrementalStartPos = textCursor().selectionStart();
+ d->m_incrementalWrappedState = false;
+ }
+ return found ? Found : NotFound;
+}
+
+void BaseTextFind::replace(const QString &before, const QString &after, FindFlags findFlags)
+{
+ QTextCursor cursor = replaceInternal(before, after, findFlags);
+ setTextCursor(cursor);
+}
+
+QTextCursor BaseTextFind::replaceInternal(const QString &before, const QString &after,
+ FindFlags findFlags)
+{
+ QTextCursor cursor = textCursor();
+ bool usesRegExp = (findFlags & FindRegularExpression);
+ bool preserveCase = (findFlags & FindPreserveCase);
+ QRegExp regexp(before,
+ (findFlags & FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive,
+ usesRegExp ? QRegExp::RegExp : QRegExp::FixedString);
+
+ if (regexp.exactMatch(cursor.selectedText())) {
+ QString realAfter;
+ if (usesRegExp)
+ realAfter = Utils::expandRegExpReplacement(after, regexp.capturedTexts());
+ else if (preserveCase)
+ realAfter = Utils::matchCaseReplacement(cursor.selectedText(), after);
+ else
+ realAfter = after;
+ int start = cursor.selectionStart();
+ cursor.insertText(realAfter);
+ if ((findFlags & FindBackward) != 0)
+ cursor.setPosition(start);
+ }
+ return cursor;
+}
+
+bool BaseTextFind::replaceStep(const QString &before, const QString &after, FindFlags findFlags)
+{
+ QTextCursor cursor = replaceInternal(before, after, findFlags);
+ bool wrapped = false;
+ bool found = find(before, findFlags, cursor, &wrapped);
+ if (wrapped)
+ showWrapIndicator(d->m_widget);
+ return found;
+}
+
+int BaseTextFind::replaceAll(const QString &before, const QString &after, FindFlags findFlags)
+{
+ QTextCursor editCursor = textCursor();
+ if (!d->m_findScopeStart.isNull())
+ editCursor.setPosition(d->m_findScopeStart.position());
+ else
+ editCursor.movePosition(QTextCursor::Start);
+ editCursor.beginEditBlock();
+ int count = 0;
+ bool usesRegExp = (findFlags & FindRegularExpression);
+ bool preserveCase = (findFlags & FindPreserveCase);
+ QRegExp regexp(before);
+ regexp.setPatternSyntax(usesRegExp ? QRegExp::RegExp : QRegExp::FixedString);
+ regexp.setCaseSensitivity((findFlags & FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
+ QTextCursor found = findOne(regexp, editCursor, textDocumentFlagsForFindFlags(findFlags));
+ bool first = true;
+ while (!found.isNull() && inScope(found.selectionStart(), found.selectionEnd())) {
+ if (found == editCursor && !first) {
+ if (editCursor.atEnd())
+ break;
+ // If the newly found QTextCursor is the same as recently edit one we have to move on,
+ // otherwise we would run into an endless loop for some regular expressions
+ // like ^ or \b.
+ QTextCursor newPosCursor = editCursor;
+ newPosCursor.movePosition(findFlags & FindBackward ?
+ QTextCursor::PreviousCharacter :
+ QTextCursor::NextCharacter);
+ found = findOne(regexp, newPosCursor, textDocumentFlagsForFindFlags(findFlags));
+ continue;
+ }
+ if (first)
+ first = false;
+ ++count;
+ editCursor.setPosition(found.selectionStart());
+ editCursor.setPosition(found.selectionEnd(), QTextCursor::KeepAnchor);
+ regexp.exactMatch(found.selectedText());
+
+ QString realAfter;
+ if (usesRegExp)
+ realAfter = Utils::expandRegExpReplacement(after, regexp.capturedTexts());
+ else if (preserveCase)
+ realAfter = Utils::matchCaseReplacement(found.selectedText(), after);
+ else
+ realAfter = after;
+ editCursor.insertText(realAfter);
+ found = findOne(regexp, editCursor, textDocumentFlagsForFindFlags(findFlags));
+ }
+ editCursor.endEditBlock();
+ return count;
+}
+
+bool BaseTextFind::find(const QString &txt, FindFlags findFlags,
+ QTextCursor start, bool *wrapped)
+{
+ if (txt.isEmpty()) {
+ setTextCursor(start);
+ return true;
+ }
+ QRegExp regexp(txt);
+ regexp.setPatternSyntax((findFlags & FindRegularExpression) ? QRegExp::RegExp : QRegExp::FixedString);
+ regexp.setCaseSensitivity((findFlags & FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
+ QTextCursor found = findOne(regexp, start, textDocumentFlagsForFindFlags(findFlags));
+ if (wrapped)
+ *wrapped = false;
+
+ if (!d->m_findScopeStart.isNull()) {
+
+ // scoped
+ if (found.isNull() || !inScope(found.selectionStart(), found.selectionEnd())) {
+ if ((findFlags & FindBackward) == 0)
+ start.setPosition(d->m_findScopeStart.position());
+ else
+ start.setPosition(d->m_findScopeEnd.position());
+ found = findOne(regexp, start, textDocumentFlagsForFindFlags(findFlags));
+ if (found.isNull() || !inScope(found.selectionStart(), found.selectionEnd()))
+ return false;
+ if (wrapped)
+ *wrapped = true;
+ }
+ } else {
+
+ // entire document
+ if (found.isNull()) {
+ if ((findFlags & FindBackward) == 0)
+ start.movePosition(QTextCursor::Start);
+ else
+ start.movePosition(QTextCursor::End);
+ found = findOne(regexp, start, textDocumentFlagsForFindFlags(findFlags));
+ if (found.isNull())
+ return false;
+ if (wrapped)
+ *wrapped = true;
+ }
+ }
+ if (!found.isNull())
+ setTextCursor(found);
+ return true;
+}
+
+
+// helper function. Works just like QTextDocument::find() but supports vertical block selection
+QTextCursor BaseTextFind::findOne(const QRegExp &expr, const QTextCursor &from, QTextDocument::FindFlags options) const
+{
+ QTextCursor candidate = document()->find(expr, from, options);
+ if (candidate.isNull())
+ return candidate;
+
+ if (d->m_findScopeVerticalBlockSelectionFirstColumn < 0)
+ return candidate;
+ forever {
+ if (!inScope(candidate.selectionStart(), candidate.selectionEnd()))
+ return candidate;
+ bool inVerticalFindScope = false;
+ QMetaObject::invokeMethod(d->m_plaineditor, "inFindScope", Qt::DirectConnection,
+ Q_RETURN_ARG(bool, inVerticalFindScope),
+ Q_ARG(QTextCursor, candidate));
+ if (inVerticalFindScope)
+ return candidate;
+
+ QTextCursor newCandidate = document()->find(expr, candidate, options);
+ if (newCandidate == candidate) {
+ // When searching for regular expressions that match "zero length" strings (like ^ or \b)
+ // we need to move away from the match before searching for the next one.
+ candidate.movePosition(options & QTextDocument::FindBackward
+ ? QTextCursor::PreviousCharacter
+ : QTextCursor::NextCharacter);
+ candidate = document()->find(expr, candidate, options);
+ } else {
+ candidate = newCandidate;
+ }
+ }
+ return candidate;
+}
+
+bool BaseTextFind::inScope(int startPosition, int endPosition) const
+{
+ if (d->m_findScopeStart.isNull())
+ return true;
+ return (d->m_findScopeStart.position() <= startPosition
+ && d->m_findScopeEnd.position() >= endPosition);
+}
+
+void BaseTextFind::defineFindScope()
+{
+ QTextCursor cursor = textCursor();
+ if (cursor.hasSelection() && cursor.block() != cursor.document()->findBlock(cursor.anchor())) {
+ d->m_findScopeStart = QTextCursor(document()->docHandle(), qMax(0, cursor.selectionStart()));
+ d->m_findScopeEnd = QTextCursor(document()->docHandle(), cursor.selectionEnd());
+ d->m_findScopeVerticalBlockSelectionFirstColumn = -1;
+ d->m_findScopeVerticalBlockSelectionLastColumn = -1;
+
+ if (d->m_plaineditor && d->m_plaineditor->metaObject()->indexOfProperty("verticalBlockSelectionFirstColumn") >= 0) {
+ d->m_findScopeVerticalBlockSelectionFirstColumn
+ = d->m_plaineditor->property("verticalBlockSelectionFirstColumn").toInt();
+ d->m_findScopeVerticalBlockSelectionLastColumn
+ = d->m_plaineditor->property("verticalBlockSelectionLastColumn").toInt();
+ }
+
+ emit findScopeChanged(d->m_findScopeStart, d->m_findScopeEnd,
+ d->m_findScopeVerticalBlockSelectionFirstColumn,
+ d->m_findScopeVerticalBlockSelectionLastColumn);
+ cursor.setPosition(d->m_findScopeStart.position());
+ setTextCursor(cursor);
+ } else {
+ clearFindScope();
+ }
+}
+
+void BaseTextFind::clearFindScope()
+{
+ d->m_findScopeStart = QTextCursor();
+ d->m_findScopeEnd = QTextCursor();
+ d->m_findScopeVerticalBlockSelectionFirstColumn = -1;
+ d->m_findScopeVerticalBlockSelectionLastColumn = -1;
+ emit findScopeChanged(d->m_findScopeStart, d->m_findScopeEnd,
+ d->m_findScopeVerticalBlockSelectionFirstColumn,
+ d->m_findScopeVerticalBlockSelectionLastColumn);
+}
+
+} // namespace Core
diff --git a/src/plugins/coreplugin/find/basetextfind.h b/src/plugins/coreplugin/find/basetextfind.h
new file mode 100644
index 0000000000..1d1d54d9ef
--- /dev/null
+++ b/src/plugins/coreplugin/find/basetextfind.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef BASETEXTFIND_H
+#define BASETEXTFIND_H
+
+#include "ifindsupport.h"
+
+QT_BEGIN_NAMESPACE
+class QPlainTextEdit;
+class QTextEdit;
+class QTextCursor;
+QT_END_NAMESPACE
+
+namespace Core {
+struct BaseTextFindPrivate;
+
+class CORE_EXPORT BaseTextFind : public IFindSupport
+{
+ Q_OBJECT
+
+public:
+ explicit BaseTextFind(QPlainTextEdit *editor);
+ explicit BaseTextFind(QTextEdit *editor);
+ virtual ~BaseTextFind();
+
+ bool supportsReplace() const;
+ FindFlags supportedFindFlags() const;
+ void resetIncrementalSearch();
+ void clearResults();
+ QString currentFindString() const;
+ QString completedFindString() const;
+
+ Result findIncremental(const QString &txt, FindFlags findFlags);
+ Result findStep(const QString &txt, FindFlags findFlags);
+ void replace(const QString &before, const QString &after, FindFlags findFlags);
+ bool replaceStep(const QString &before, const QString &after, FindFlags findFlags);
+ int replaceAll(const QString &before, const QString &after, FindFlags findFlags);
+
+ void defineFindScope();
+ void clearFindScope();
+
+signals:
+ void highlightAll(const QString &txt, Core::FindFlags findFlags);
+ void findScopeChanged(const QTextCursor &start, const QTextCursor &end,
+ int verticalBlockSelectionFirstColumn,
+ int verticalBlockSelectionLastColumn);
+
+private:
+ bool find(const QString &txt, FindFlags findFlags, QTextCursor start, bool *wrapped);
+ QTextCursor replaceInternal(const QString &before, const QString &after, FindFlags findFlags);
+
+ QTextCursor textCursor() const;
+ void setTextCursor(const QTextCursor&);
+ QTextDocument *document() const;
+ bool isReadOnly() const;
+ bool inScope(int startPosition, int endPosition) const;
+ QTextCursor findOne(const QRegExp &expr, const QTextCursor &from, QTextDocument::FindFlags options) const;
+
+ BaseTextFindPrivate *d;
+};
+
+} // namespace Core
+
+#endif // BASETEXTFIND_H
diff --git a/src/plugins/coreplugin/find/currentdocumentfind.cpp b/src/plugins/coreplugin/find/currentdocumentfind.cpp
new file mode 100644
index 0000000000..854a391003
--- /dev/null
+++ b/src/plugins/coreplugin/find/currentdocumentfind.cpp
@@ -0,0 +1,259 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "currentdocumentfind.h"
+
+#include <aggregation/aggregate.h>
+#include <coreplugin/coreconstants.h>
+#include <utils/qtcassert.h>
+
+#include <QDebug>
+#include <QApplication>
+#include <QWidget>
+
+using namespace Core;
+using namespace Core;
+using namespace Core::Internal;
+
+CurrentDocumentFind::CurrentDocumentFind()
+ : m_currentFind(0)
+{
+ connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)),
+ this, SLOT(updateCandidateFindFilter(QWidget*,QWidget*)));
+}
+
+void CurrentDocumentFind::removeConnections()
+{
+ disconnect(qApp, 0, this, 0);
+ removeFindSupportConnections();
+}
+
+void CurrentDocumentFind::resetIncrementalSearch()
+{
+ QTC_ASSERT(m_currentFind, return);
+ m_currentFind->resetIncrementalSearch();
+}
+
+void CurrentDocumentFind::clearResults()
+{
+ QTC_ASSERT(m_currentFind, return);
+ m_currentFind->clearResults();
+}
+
+bool CurrentDocumentFind::isEnabled() const
+{
+ return m_currentFind && (!m_currentWidget || m_currentWidget->isVisible());
+}
+
+bool CurrentDocumentFind::candidateIsEnabled() const
+{
+ return (m_candidateFind != 0);
+}
+
+bool CurrentDocumentFind::supportsReplace() const
+{
+ QTC_ASSERT(m_currentFind, return false);
+ return m_currentFind->supportsReplace();
+}
+
+FindFlags CurrentDocumentFind::supportedFindFlags() const
+{
+ QTC_ASSERT(m_currentFind, return 0);
+ return m_currentFind->supportedFindFlags();
+}
+
+QString CurrentDocumentFind::currentFindString() const
+{
+ QTC_ASSERT(m_currentFind, return QString());
+ return m_currentFind->currentFindString();
+}
+
+QString CurrentDocumentFind::completedFindString() const
+{
+ QTC_ASSERT(m_currentFind, return QString());
+ return m_currentFind->completedFindString();
+}
+
+void CurrentDocumentFind::highlightAll(const QString &txt, FindFlags findFlags)
+{
+ QTC_ASSERT(m_currentFind, return);
+ m_currentFind->highlightAll(txt, findFlags);
+}
+
+IFindSupport::Result CurrentDocumentFind::findIncremental(const QString &txt, FindFlags findFlags)
+{
+ QTC_ASSERT(m_currentFind, return IFindSupport::NotFound);
+ return m_currentFind->findIncremental(txt, findFlags);
+}
+
+IFindSupport::Result CurrentDocumentFind::findStep(const QString &txt, FindFlags findFlags)
+{
+ QTC_ASSERT(m_currentFind, return IFindSupport::NotFound);
+ return m_currentFind->findStep(txt, findFlags);
+}
+
+void CurrentDocumentFind::replace(const QString &before, const QString &after, FindFlags findFlags)
+{
+ QTC_ASSERT(m_currentFind, return);
+ m_currentFind->replace(before, after, findFlags);
+}
+
+bool CurrentDocumentFind::replaceStep(const QString &before, const QString &after, FindFlags findFlags)
+{
+ QTC_ASSERT(m_currentFind, return false);
+ return m_currentFind->replaceStep(before, after, findFlags);
+}
+
+int CurrentDocumentFind::replaceAll(const QString &before, const QString &after, FindFlags findFlags)
+{
+ QTC_ASSERT(m_currentFind, return 0);
+ return m_currentFind->replaceAll(before, after, findFlags);
+}
+
+void CurrentDocumentFind::defineFindScope()
+{
+ QTC_ASSERT(m_currentFind, return);
+ m_currentFind->defineFindScope();
+}
+
+void CurrentDocumentFind::clearFindScope()
+{
+ QTC_ASSERT(m_currentFind, return);
+ m_currentFind->clearFindScope();
+}
+
+void CurrentDocumentFind::updateCandidateFindFilter(QWidget *old, QWidget *now)
+{
+ Q_UNUSED(old)
+ QWidget *candidate = now;
+ QPointer<IFindSupport> impl = 0;
+ while (!impl && candidate) {
+ impl = Aggregation::query<IFindSupport>(candidate);
+ if (!impl)
+ candidate = candidate->parentWidget();
+ }
+ if (m_candidateWidget)
+ disconnect(Aggregation::Aggregate::parentAggregate(m_candidateWidget), SIGNAL(changed()),
+ this, SLOT(candidateAggregationChanged()));
+ m_candidateWidget = candidate;
+ m_candidateFind = impl;
+ if (m_candidateWidget)
+ connect(Aggregation::Aggregate::parentAggregate(m_candidateWidget), SIGNAL(changed()),
+ this, SLOT(candidateAggregationChanged()));
+ emit candidateChanged();
+}
+
+void CurrentDocumentFind::acceptCandidate()
+{
+ if (!m_candidateFind || m_candidateFind == m_currentFind)
+ return;
+ removeFindSupportConnections();
+ if (m_currentFind)
+ m_currentFind->clearResults();
+
+ if (m_currentWidget)
+ disconnect(Aggregation::Aggregate::parentAggregate(m_currentWidget), SIGNAL(changed()),
+ this, SLOT(aggregationChanged()));
+ m_currentWidget = m_candidateWidget;
+ connect(Aggregation::Aggregate::parentAggregate(m_currentWidget), SIGNAL(changed()),
+ this, SLOT(aggregationChanged()));
+
+ m_currentFind = m_candidateFind;
+ if (m_currentFind) {
+ connect(m_currentFind, SIGNAL(changed()), this, SIGNAL(changed()));
+ connect(m_currentFind, SIGNAL(destroyed(QObject*)), SLOT(clearFindSupport()));
+ }
+ if (m_currentWidget)
+ m_currentWidget->installEventFilter(this);
+ emit changed();
+}
+
+void CurrentDocumentFind::removeFindSupportConnections()
+{
+ if (m_currentFind) {
+ disconnect(m_currentFind, SIGNAL(changed()), this, SIGNAL(changed()));
+ disconnect(m_currentFind, SIGNAL(destroyed(QObject*)), this, SLOT(clearFindSupport()));
+ }
+ if (m_currentWidget)
+ m_currentWidget->removeEventFilter(this);
+}
+
+void CurrentDocumentFind::clearFindSupport()
+{
+ removeFindSupportConnections();
+ m_currentWidget = 0;
+ m_currentFind = 0;
+ emit changed();
+}
+
+bool CurrentDocumentFind::setFocusToCurrentFindSupport()
+{
+ if (m_currentFind && m_currentWidget) {
+ QWidget *w = m_currentWidget->focusWidget();
+ if (!w)
+ w = m_currentWidget;
+ w->setFocus();
+ return true;
+ }
+ return false;
+}
+
+bool CurrentDocumentFind::eventFilter(QObject *obj, QEvent *event)
+{
+ if (m_currentWidget && obj == m_currentWidget) {
+ if (event->type() == QEvent::Hide || event->type() == QEvent::Show)
+ emit changed();
+ }
+ return QObject::eventFilter(obj, event);
+}
+
+void CurrentDocumentFind::aggregationChanged()
+{
+ if (m_currentWidget) {
+ QPointer<IFindSupport> currentFind = Aggregation::query<IFindSupport>(m_currentWidget);
+ if (currentFind != m_currentFind) {
+ // There's a change in the find support
+ if (currentFind) {
+ m_candidateWidget = m_currentWidget;
+ m_candidateFind = currentFind;
+ acceptCandidate();
+ } else {
+ clearFindSupport();
+ }
+ }
+ }
+}
+
+void CurrentDocumentFind::candidateAggregationChanged()
+{
+ if (m_candidateWidget && m_candidateWidget != m_currentWidget) {
+ m_candidateFind = Aggregation::query<IFindSupport>(m_candidateWidget);
+ emit candidateChanged();
+ }
+}
diff --git a/src/plugins/coreplugin/find/currentdocumentfind.h b/src/plugins/coreplugin/find/currentdocumentfind.h
new file mode 100644
index 0000000000..2b428e0583
--- /dev/null
+++ b/src/plugins/coreplugin/find/currentdocumentfind.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CURRENTDOCUMENTFIND_H
+#define CURRENTDOCUMENTFIND_H
+
+#include "ifindsupport.h"
+
+#include <QPointer>
+
+namespace Core {
+namespace Internal {
+
+class CurrentDocumentFind : public QObject
+{
+ Q_OBJECT
+
+public:
+ CurrentDocumentFind();
+
+ void resetIncrementalSearch();
+ void clearResults();
+ bool supportsReplace() const;
+ FindFlags supportedFindFlags() const;
+ QString currentFindString() const;
+ QString completedFindString() const;
+
+ bool isEnabled() const;
+ bool candidateIsEnabled() const;
+ void highlightAll(const QString &txt, FindFlags findFlags);
+ IFindSupport::Result findIncremental(const QString &txt, FindFlags findFlags);
+ IFindSupport::Result findStep(const QString &txt, FindFlags findFlags);
+ void replace(const QString &before, const QString &after, FindFlags findFlags);
+ bool replaceStep(const QString &before, const QString &after, FindFlags findFlags);
+ int replaceAll(const QString &before, const QString &after, FindFlags findFlags);
+ void defineFindScope();
+ void clearFindScope();
+ void acceptCandidate();
+
+ void removeConnections();
+ bool setFocusToCurrentFindSupport();
+
+ bool eventFilter(QObject *obj, QEvent *event);
+
+signals:
+ void changed();
+ void candidateChanged();
+
+private slots:
+ void updateCandidateFindFilter(QWidget *old, QWidget *now);
+ void clearFindSupport();
+ void aggregationChanged();
+ void candidateAggregationChanged();
+
+private:
+ void removeFindSupportConnections();
+
+ QPointer<IFindSupport> m_currentFind;
+ QPointer<QWidget> m_currentWidget;
+ QPointer<IFindSupport> m_candidateFind;
+ QPointer<QWidget> m_candidateWidget;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // CURRENTDOCUMENTFIND_H
diff --git a/src/plugins/coreplugin/find/find.pri b/src/plugins/coreplugin/find/find.pri
new file mode 100644
index 0000000000..c7e8c836bc
--- /dev/null
+++ b/src/plugins/coreplugin/find/find.pri
@@ -0,0 +1,42 @@
+HEADERS += \
+ $$PWD/findtoolwindow.h \
+ $$PWD/textfindconstants.h \
+ $$PWD/ifindsupport.h \
+ $$PWD/ifindfilter.h \
+ $$PWD/currentdocumentfind.h \
+ $$PWD/basetextfind.h \
+ $$PWD/findtoolbar.h \
+ $$PWD/findplugin.h \
+ $$PWD/searchresultcolor.h \
+ $$PWD/searchresulttreeitemdelegate.h \
+ $$PWD/searchresulttreeitemroles.h \
+ $$PWD/searchresulttreeitems.h \
+ $$PWD/searchresulttreemodel.h \
+ $$PWD/searchresulttreeview.h \
+ $$PWD/searchresultwindow.h \
+ $$PWD/searchresultwidget.h \
+ $$PWD/treeviewfind.h
+
+SOURCES += \
+ $$PWD/findtoolwindow.cpp \
+ $$PWD/currentdocumentfind.cpp \
+ $$PWD/basetextfind.cpp \
+ $$PWD/findtoolbar.cpp \
+ $$PWD/findplugin.cpp \
+ $$PWD/searchresulttreeitemdelegate.cpp \
+ $$PWD/searchresulttreeitems.cpp \
+ $$PWD/searchresulttreemodel.cpp \
+ $$PWD/searchresulttreeview.cpp \
+ $$PWD/searchresultwindow.cpp \
+ $$PWD/ifindfilter.cpp \
+ $$PWD/ifindsupport.cpp \
+ $$PWD/searchresultwidget.cpp \
+ $$PWD/treeviewfind.cpp
+
+FORMS += \
+ $$PWD/findwidget.ui \
+ $$PWD/finddialog.ui
+
+RESOURCES += \
+ $$PWD/find.qrc
+
diff --git a/src/plugins/coreplugin/find/find.qrc b/src/plugins/coreplugin/find/find.qrc
new file mode 100644
index 0000000000..0c4e128101
--- /dev/null
+++ b/src/plugins/coreplugin/find/find.qrc
@@ -0,0 +1,10 @@
+<RCC>
+ <qresource prefix="/find">
+ <file>images/casesensitively.png</file>
+ <file>images/wholewords.png</file>
+ <file>images/regexp.png</file>
+ <file>images/expand.png</file>
+ <file>images/wrapindicator.png</file>
+ <file>images/preservecase.png</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/coreplugin/find/finddialog.ui b/src/plugins/coreplugin/find/finddialog.ui
new file mode 100644
index 0000000000..19d41c938c
--- /dev/null
+++ b/src/plugins/coreplugin/find/finddialog.ui
@@ -0,0 +1,209 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Core::Internal::FindDialog</class>
+ <widget class="QWidget" name="Core::Internal::FindDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>673</width>
+ <height>240</height>
+ </rect>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>680</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetFixedSize</enum>
+ </property>
+ <item row="4" column="0" colspan="2">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="searchButton">
+ <property name="text">
+ <string>&amp;Search</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="replaceButton">
+ <property name="text">
+ <string>Search &amp;&amp; &amp;Replace</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Sear&amp;ch for:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="buddy">
+ <cstring>searchTerm</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QWidget" name="widget_2" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QComboBox" name="filterList">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QWidget" name="widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="matchCase">
+ <property name="text">
+ <string>Case sensiti&amp;ve</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="wholeWords">
+ <property name="text">
+ <string>Whole words o&amp;nly</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="regExp">
+ <property name="text">
+ <string>Use re&amp;gular expressions</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Sco&amp;pe:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="buddy">
+ <cstring>filterList</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="Utils::FilterLineEdit" name="searchTerm"/>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <widget class="QWidget" name="configWidget" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>10</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>Utils::FancyLineEdit</class>
+ <extends>QLineEdit</extends>
+ <header location="global">utils/fancylineedit.h</header>
+ </customwidget>
+ <customwidget>
+ <class>Utils::FilterLineEdit</class>
+ <extends>Utils::FancyLineEdit</extends>
+ <header location="global">utils/filterlineedit.h</header>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>filterList</tabstop>
+ <tabstop>searchTerm</tabstop>
+ <tabstop>matchCase</tabstop>
+ <tabstop>wholeWords</tabstop>
+ <tabstop>regExp</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/coreplugin/find/findplugin.cpp b/src/plugins/coreplugin/find/findplugin.cpp
new file mode 100644
index 0000000000..ebb9f4b183
--- /dev/null
+++ b/src/plugins/coreplugin/find/findplugin.cpp
@@ -0,0 +1,394 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "findplugin.h"
+
+#include "currentdocumentfind.h"
+#include "findtoolbar.h"
+#include "findtoolwindow.h"
+#include "searchresultwindow.h"
+#include "ifindfilter.h"
+
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/actionmanager/actioncontainer.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/id.h>
+#include <coreplugin/coreplugin.h>
+
+#include <extensionsystem/pluginmanager.h>
+
+#include <utils/qtcassert.h>
+
+#include <QMenu>
+#include <QStringListModel>
+#include <QAction>
+
+#include <QtPlugin>
+#include <QSettings>
+
+/*!
+ \namespace Core::Internal
+ \internal
+*/
+/*!
+ \namespace Core::Internal::ItemDataRoles
+ \internal
+*/
+
+Q_DECLARE_METATYPE(Core::IFindFilter*)
+
+namespace {
+ const int MAX_COMPLETIONS = 50;
+}
+
+namespace Core {
+
+class FindPluginPrivate {
+public:
+ explicit FindPluginPrivate(FindPlugin *q);
+
+ //variables
+ static FindPlugin *m_instance;
+
+ QHash<IFindFilter *, QAction *> m_filterActions;
+
+ Internal::CurrentDocumentFind *m_currentDocumentFind;
+ Internal::FindToolBar *m_findToolBar;
+ Internal::FindToolWindow *m_findDialog;
+ SearchResultWindow *m_searchResultWindow;
+ FindFlags m_findFlags;
+ QStringListModel *m_findCompletionModel;
+ QStringListModel *m_replaceCompletionModel;
+ QStringList m_findCompletions;
+ QStringList m_replaceCompletions;
+ QAction *m_openFindDialog;
+};
+
+FindPluginPrivate::FindPluginPrivate(FindPlugin *q) :
+ m_currentDocumentFind(0), m_findToolBar(0), m_findDialog(0),
+ m_findCompletionModel(new QStringListModel(q)),
+ m_replaceCompletionModel(new QStringListModel(q))
+{
+}
+
+FindPlugin *FindPluginPrivate::m_instance = 0;
+
+FindPlugin::FindPlugin() : d(new FindPluginPrivate(this))
+{
+ QTC_ASSERT(!FindPluginPrivate::m_instance, return);
+ FindPluginPrivate::m_instance = this;
+}
+
+FindPlugin::~FindPlugin()
+{
+ FindPluginPrivate::m_instance = 0;
+ delete d->m_currentDocumentFind;
+ delete d->m_findToolBar;
+ delete d->m_findDialog;
+ ExtensionSystem::PluginManager::removeObject(d->m_searchResultWindow);
+ delete d->m_searchResultWindow;
+ delete d;
+}
+
+FindPlugin *FindPlugin::instance()
+{
+ return FindPluginPrivate::m_instance;
+}
+
+void FindPlugin::initialize(const QStringList &, QString *)
+{
+ setupMenu();
+
+ d->m_currentDocumentFind = new Internal::CurrentDocumentFind;
+
+ d->m_findToolBar = new Internal::FindToolBar(this, d->m_currentDocumentFind);
+ d->m_findDialog = new Internal::FindToolWindow(this);
+ d->m_searchResultWindow = new SearchResultWindow(d->m_findDialog);
+ ExtensionSystem::PluginManager::addObject(d->m_searchResultWindow);
+}
+
+void FindPlugin::extensionsInitialized()
+{
+ setupFilterMenuItems();
+ readSettings();
+}
+
+void FindPlugin::aboutToShutdown()
+{
+ d->m_findToolBar->setVisible(false);
+ d->m_findToolBar->setParent(0);
+ d->m_currentDocumentFind->removeConnections();
+ writeSettings();
+}
+
+void FindPlugin::filterChanged()
+{
+ IFindFilter *changedFilter = qobject_cast<IFindFilter *>(sender());
+ QAction *action = d->m_filterActions.value(changedFilter);
+ QTC_ASSERT(changedFilter, return);
+ QTC_ASSERT(action, return);
+ action->setEnabled(changedFilter->isEnabled());
+ bool haveEnabledFilters = false;
+ foreach (const IFindFilter *filter, d->m_filterActions.keys()) {
+ if (filter->isEnabled()) {
+ haveEnabledFilters = true;
+ break;
+ }
+ }
+ d->m_openFindDialog->setEnabled(haveEnabledFilters);
+}
+
+void FindPlugin::openFindFilter()
+{
+ QAction *action = qobject_cast<QAction*>(sender());
+ QTC_ASSERT(action, return);
+ IFindFilter *filter = action->data().value<IFindFilter *>();
+ openFindDialog(filter);
+}
+
+void FindPlugin::openFindDialog(IFindFilter *filter)
+{
+ if (d->m_currentDocumentFind->candidateIsEnabled())
+ d->m_currentDocumentFind->acceptCandidate();
+ const QString currentFindString =
+ d->m_currentDocumentFind->isEnabled() ?
+ d->m_currentDocumentFind->currentFindString() : QString();
+ if (!currentFindString.isEmpty())
+ d->m_findDialog->setFindText(currentFindString);
+ d->m_findDialog->setCurrentFilter(filter);
+ SearchResultWindow::instance()->openNewSearchPanel();
+}
+
+void FindPlugin::setupMenu()
+{
+ Core::ActionContainer *medit = Core::ActionManager::actionContainer(Core::Constants::M_EDIT);
+ Core::ActionContainer *mfind = Core::ActionManager::createMenu(Constants::M_FIND);
+ medit->addMenu(mfind, Core::Constants::G_EDIT_FIND);
+ mfind->menu()->setTitle(tr("&Find/Replace"));
+ mfind->appendGroup(Constants::G_FIND_CURRENTDOCUMENT);
+ mfind->appendGroup(Constants::G_FIND_FILTERS);
+ mfind->appendGroup(Constants::G_FIND_FLAGS);
+ mfind->appendGroup(Constants::G_FIND_ACTIONS);
+ Core::Context globalcontext(Core::Constants::C_GLOBAL);
+ Core::Command *cmd;
+ mfind->addSeparator(globalcontext, Constants::G_FIND_FLAGS);
+ mfind->addSeparator(globalcontext, Constants::G_FIND_ACTIONS);
+
+ Core::ActionContainer *mfindadvanced = Core::ActionManager::createMenu(Constants::M_FIND_ADVANCED);
+ mfindadvanced->menu()->setTitle(tr("Advanced Find"));
+ mfind->addMenu(mfindadvanced, Constants::G_FIND_FILTERS);
+ d->m_openFindDialog = new QAction(tr("Open Advanced Find..."), this);
+ d->m_openFindDialog->setIconText(tr("Advanced..."));
+ cmd = Core::ActionManager::registerAction(d->m_openFindDialog, Constants::ADVANCED_FIND, globalcontext);
+ cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+F")));
+ mfindadvanced->addAction(cmd);
+ connect(d->m_openFindDialog, SIGNAL(triggered()), this, SLOT(openFindFilter()));
+}
+
+void FindPlugin::setupFilterMenuItems()
+{
+ QList<IFindFilter*> findInterfaces =
+ ExtensionSystem::PluginManager::getObjects<IFindFilter>();
+ Core::Command *cmd;
+ Core::Context globalcontext(Core::Constants::C_GLOBAL);
+
+ Core::ActionContainer *mfindadvanced = Core::ActionManager::actionContainer(Constants::M_FIND_ADVANCED);
+ d->m_filterActions.clear();
+ bool haveEnabledFilters = false;
+ const Core::Id base("FindFilter.");
+ foreach (IFindFilter *filter, findInterfaces) {
+ QAction *action = new QAction(QLatin1String(" ") + filter->displayName(), this);
+ bool isEnabled = filter->isEnabled();
+ if (isEnabled)
+ haveEnabledFilters = true;
+ action->setEnabled(isEnabled);
+ action->setData(qVariantFromValue(filter));
+ cmd = Core::ActionManager::registerAction(action,
+ base.withSuffix(filter->id()), globalcontext);
+ cmd->setDefaultKeySequence(filter->defaultShortcut());
+ mfindadvanced->addAction(cmd);
+ d->m_filterActions.insert(filter, action);
+ connect(action, SIGNAL(triggered(bool)), this, SLOT(openFindFilter()));
+ connect(filter, SIGNAL(enabledChanged(bool)), this, SLOT(filterChanged()));
+ }
+ d->m_findDialog->setFindFilters(findInterfaces);
+ d->m_openFindDialog->setEnabled(haveEnabledFilters);
+}
+
+FindFlags FindPlugin::findFlags() const
+{
+ return d->m_findFlags;
+}
+
+void FindPlugin::setCaseSensitive(bool sensitive)
+{
+ setFindFlag(FindCaseSensitively, sensitive);
+}
+
+void FindPlugin::setWholeWord(bool wholeOnly)
+{
+ setFindFlag(FindWholeWords, wholeOnly);
+}
+
+void FindPlugin::setBackward(bool backward)
+{
+ setFindFlag(FindBackward, backward);
+}
+
+void FindPlugin::setRegularExpression(bool regExp)
+{
+ setFindFlag(FindRegularExpression, regExp);
+}
+
+void FindPlugin::setPreserveCase(bool preserveCase)
+{
+ setFindFlag(FindPreserveCase, preserveCase);
+}
+
+void FindPlugin::setFindFlag(FindFlag flag, bool enabled)
+{
+ bool hasFlag = hasFindFlag(flag);
+ if ((hasFlag && enabled) || (!hasFlag && !enabled))
+ return;
+ if (enabled)
+ d->m_findFlags |= flag;
+ else
+ d->m_findFlags &= ~flag;
+ if (flag != FindBackward)
+ emit findFlagsChanged();
+}
+
+bool FindPlugin::hasFindFlag(FindFlag flag)
+{
+ return d->m_findFlags & flag;
+}
+
+void FindPlugin::writeSettings()
+{
+ QSettings *settings = Core::ICore::settings();
+ settings->beginGroup(QLatin1String("Find"));
+ settings->setValue(QLatin1String("Backward"), hasFindFlag(FindBackward));
+ settings->setValue(QLatin1String("CaseSensitively"), hasFindFlag(FindCaseSensitively));
+ settings->setValue(QLatin1String("WholeWords"), hasFindFlag(FindWholeWords));
+ settings->setValue(QLatin1String("RegularExpression"), hasFindFlag(FindRegularExpression));
+ settings->setValue(QLatin1String("PreserveCase"), hasFindFlag(FindPreserveCase));
+ settings->setValue(QLatin1String("FindStrings"), d->m_findCompletions);
+ settings->setValue(QLatin1String("ReplaceStrings"), d->m_replaceCompletions);
+ settings->endGroup();
+ d->m_findToolBar->writeSettings();
+ d->m_findDialog->writeSettings();
+ d->m_searchResultWindow->writeSettings();
+}
+
+void FindPlugin::readSettings()
+{
+ QSettings *settings = Core::ICore::settings();
+ settings->beginGroup(QLatin1String("Find"));
+ bool block = blockSignals(true);
+ setBackward(settings->value(QLatin1String("Backward"), false).toBool());
+ setCaseSensitive(settings->value(QLatin1String("CaseSensitively"), false).toBool());
+ setWholeWord(settings->value(QLatin1String("WholeWords"), false).toBool());
+ setRegularExpression(settings->value(QLatin1String("RegularExpression"), false).toBool());
+ setPreserveCase(settings->value(QLatin1String("PreserveCase"), false).toBool());
+ blockSignals(block);
+ d->m_findCompletions = settings->value(QLatin1String("FindStrings")).toStringList();
+ d->m_replaceCompletions = settings->value(QLatin1String("ReplaceStrings")).toStringList();
+ d->m_findCompletionModel->setStringList(d->m_findCompletions);
+ d->m_replaceCompletionModel->setStringList(d->m_replaceCompletions);
+ settings->endGroup();
+ d->m_findToolBar->readSettings();
+ d->m_findDialog->readSettings();
+ emit findFlagsChanged(); // would have been done in the setXXX methods above
+}
+
+void FindPlugin::updateFindCompletion(const QString &text)
+{
+ updateCompletion(text, d->m_findCompletions, d->m_findCompletionModel);
+}
+
+void FindPlugin::updateReplaceCompletion(const QString &text)
+{
+ updateCompletion(text, d->m_replaceCompletions, d->m_replaceCompletionModel);
+}
+
+void FindPlugin::updateCompletion(const QString &text, QStringList &completions, QStringListModel *model)
+{
+ if (text.isEmpty())
+ return;
+ completions.removeAll(text);
+ completions.prepend(text);
+ while (completions.size() > MAX_COMPLETIONS)
+ completions.removeLast();
+ model->setStringList(completions);
+}
+
+void FindPlugin::setUseFakeVim(bool on)
+{
+ if (d->m_findToolBar)
+ d->m_findToolBar->setUseFakeVim(on);
+}
+
+void FindPlugin::openFindToolBar(FindDirection direction)
+{
+ if (d->m_findToolBar) {
+ d->m_findToolBar->setBackward(direction == FindBackwardDirection);
+ d->m_findToolBar->openFindToolBar();
+ }
+}
+
+QStringListModel *FindPlugin::findCompletionModel() const
+{
+ return d->m_findCompletionModel;
+}
+
+QStringListModel *FindPlugin::replaceCompletionModel() const
+{
+ return d->m_replaceCompletionModel;
+}
+
+QKeySequence IFindFilter::defaultShortcut() const
+{
+ return QKeySequence();
+}
+
+// declared in textfindconstants.h
+QTextDocument::FindFlags textDocumentFlagsForFindFlags(FindFlags flags)
+{
+ QTextDocument::FindFlags textDocFlags;
+ if (flags & FindBackward)
+ textDocFlags |= QTextDocument::FindBackward;
+ if (flags & FindCaseSensitively)
+ textDocFlags |= QTextDocument::FindCaseSensitively;
+ if (flags & FindWholeWords)
+ textDocFlags |= QTextDocument::FindWholeWords;
+ return textDocFlags;
+}
+
+} // namespace Core
diff --git a/src/plugins/coreplugin/find/findplugin.h b/src/plugins/coreplugin/find/findplugin.h
new file mode 100644
index 0000000000..0c8298052f
--- /dev/null
+++ b/src/plugins/coreplugin/find/findplugin.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef FINDPLUGIN_H
+#define FINDPLUGIN_H
+
+#include "textfindconstants.h"
+
+#include <extensionsystem/iplugin.h>
+
+QT_BEGIN_NAMESPACE
+class QStringListModel;
+QT_END_NAMESPACE
+
+namespace Core {
+class IFindFilter;
+class FindPluginPrivate;
+
+namespace Internal {
+class CorePlugin;
+class FindToolBar;
+class CurrentDocumentFind;
+} // namespace Internal
+
+class CORE_EXPORT FindPlugin : public QObject
+{
+ Q_OBJECT
+
+public:
+ FindPlugin();
+ virtual ~FindPlugin();
+
+ static FindPlugin *instance();
+
+ enum FindDirection {
+ FindForwardDirection,
+ FindBackwardDirection
+ };
+
+ Core::FindFlags findFlags() const;
+ bool hasFindFlag(Core::FindFlag flag);
+ void updateFindCompletion(const QString &text);
+ void updateReplaceCompletion(const QString &text);
+ QStringListModel *findCompletionModel() const;
+ QStringListModel *replaceCompletionModel() const;
+ void setUseFakeVim(bool on);
+ void openFindToolBar(FindDirection direction);
+ void openFindDialog(IFindFilter *filter);
+
+ void initialize(const QStringList &, QString *);
+ void extensionsInitialized();
+ void aboutToShutdown();
+
+public slots:
+ void setCaseSensitive(bool sensitive);
+ void setWholeWord(bool wholeOnly);
+ void setBackward(bool backward);
+ void setRegularExpression(bool regExp);
+ void setPreserveCase(bool preserveCase);
+
+signals:
+ void findFlagsChanged();
+
+private slots:
+ void filterChanged();
+ void openFindFilter();
+
+private:
+ void setFindFlag(Core::FindFlag flag, bool enabled);
+ void updateCompletion(const QString &text, QStringList &completions, QStringListModel *model);
+ void setupMenu();
+ void setupFilterMenuItems();
+ void writeSettings();
+ void readSettings();
+
+ //variables
+ FindPluginPrivate *d;
+};
+
+} // namespace Core
+
+#endif // FINDPLUGIN_H
diff --git a/src/plugins/coreplugin/find/findtoolbar.cpp b/src/plugins/coreplugin/find/findtoolbar.cpp
new file mode 100644
index 0000000000..e686c3fd48
--- /dev/null
+++ b/src/plugins/coreplugin/find/findtoolbar.cpp
@@ -0,0 +1,779 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "findtoolbar.h"
+#include "ifindfilter.h"
+#include "findplugin.h"
+
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/coreplugin.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/actionmanager/actioncontainer.h>
+#include <coreplugin/findplaceholder.h>
+
+#include <extensionsystem/pluginmanager.h>
+
+#include <utils/hostosinfo.h>
+#include <utils/flowlayout.h>
+
+#include <QDebug>
+#include <QSettings>
+
+#include <QClipboard>
+#include <QCompleter>
+#include <QKeyEvent>
+#include <QMenu>
+#include <QPainter>
+#include <QStringListModel>
+
+Q_DECLARE_METATYPE(QStringList)
+Q_DECLARE_METATYPE(Core::IFindFilter*)
+
+using namespace Core;
+using namespace Core::Internal;
+
+FindToolBar::FindToolBar(FindPlugin *plugin, CurrentDocumentFind *currentDocumentFind)
+ : m_plugin(plugin),
+ m_currentDocumentFind(currentDocumentFind),
+ m_findCompleter(new QCompleter(this)),
+ m_replaceCompleter(new QCompleter(this)),
+ m_enterFindStringAction(0),
+ m_findNextAction(0),
+ m_findPreviousAction(0),
+ m_replaceAction(0),
+ m_replaceNextAction(0),
+ m_replacePreviousAction(0),
+ m_findIncrementalTimer(this), m_findStepTimer(this),
+ m_useFakeVim(false),
+ m_eventFiltersInstalled(false)
+{
+ //setup ui
+ m_ui.setupUi(this);
+ // compensate for a vertically expanding spacer below the label
+ m_ui.replaceLabel->setMinimumHeight(m_ui.replaceEdit->sizeHint().height());
+ delete m_ui.replaceButtonsWidget->layout();
+ Utils::FlowLayout *flowlayout = new Utils::FlowLayout(m_ui.replaceButtonsWidget, 0, 3, 3);
+ flowlayout->addWidget(m_ui.replaceButton);
+ flowlayout->addWidget(m_ui.replaceNextButton);
+ flowlayout->addWidget(m_ui.replaceAllButton);
+ m_ui.replaceButtonsWidget->setLayout(flowlayout);
+ setFocusProxy(m_ui.findEdit);
+ setProperty("topBorder", true);
+ setSingleRow(false);
+ m_ui.findEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
+ m_ui.replaceEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
+
+ connect(m_ui.findEdit, SIGNAL(editingFinished()), this, SLOT(invokeResetIncrementalSearch()));
+
+ m_ui.close->setIcon(QIcon(QLatin1String(Core::Constants::ICON_CLOSE_DOCUMENT)));
+ connect(m_ui.close, SIGNAL(clicked()), this, SLOT(hideAndResetFocus()));
+
+ m_findCompleter->setModel(m_plugin->findCompletionModel());
+ m_replaceCompleter->setModel(m_plugin->replaceCompletionModel());
+ m_ui.findEdit->setSpecialCompleter(m_findCompleter);
+ m_ui.replaceEdit->setSpecialCompleter(m_replaceCompleter);
+
+ QMenu *lineEditMenu = new QMenu(m_ui.findEdit);
+ m_ui.findEdit->setButtonMenu(Utils::FancyLineEdit::Left, lineEditMenu);
+ m_ui.findEdit->setButtonVisible(Utils::FancyLineEdit::Left, true);
+ m_ui.findEdit->setPlaceholderText(QString());
+ m_ui.replaceEdit->setPlaceholderText(QString());
+
+ connect(m_ui.findEdit, SIGNAL(textChanged(QString)), this, SLOT(invokeFindIncremental()));
+
+ // invoke{Find,Replace}Helper change the completion model. QueuedConnection is used to perform these
+ // changes only after the completer's activated() signal is handled (QTCREATORBUG-8408)
+ connect(m_ui.findEdit, SIGNAL(returnPressed()), this, SLOT(invokeFindEnter()), Qt::QueuedConnection);
+ connect(m_ui.replaceEdit, SIGNAL(returnPressed()), this, SLOT(invokeReplaceEnter()), Qt::QueuedConnection);
+
+ QAction *shiftEnterAction = new QAction(m_ui.findEdit);
+ shiftEnterAction->setShortcut(QKeySequence(tr("Shift+Enter")));
+ shiftEnterAction->setShortcutContext(Qt::WidgetShortcut);
+ connect(shiftEnterAction, SIGNAL(triggered()), this, SLOT(invokeFindPrevious()));
+ m_ui.findEdit->addAction(shiftEnterAction);
+ QAction *shiftReturnAction = new QAction(m_ui.findEdit);
+ shiftReturnAction->setShortcut(QKeySequence(tr("Shift+Return")));
+ shiftReturnAction->setShortcutContext(Qt::WidgetShortcut);
+ connect(shiftReturnAction, SIGNAL(triggered()), this, SLOT(invokeFindPrevious()));
+ m_ui.findEdit->addAction(shiftReturnAction);
+
+ QAction *shiftEnterReplaceAction = new QAction(m_ui.replaceEdit);
+ shiftEnterReplaceAction->setShortcut(QKeySequence(tr("Shift+Enter")));
+ shiftEnterReplaceAction->setShortcutContext(Qt::WidgetShortcut);
+ connect(shiftEnterReplaceAction, SIGNAL(triggered()), this, SLOT(invokeReplacePrevious()));
+ m_ui.replaceEdit->addAction(shiftEnterReplaceAction);
+ QAction *shiftReturnReplaceAction = new QAction(m_ui.replaceEdit);
+ shiftReturnReplaceAction->setShortcut(QKeySequence(tr("Shift+Return")));
+ shiftReturnReplaceAction->setShortcutContext(Qt::WidgetShortcut);
+ connect(shiftReturnReplaceAction, SIGNAL(triggered()), this, SLOT(invokeReplacePrevious()));
+ m_ui.replaceEdit->addAction(shiftReturnReplaceAction);
+
+ // need to make sure QStringList is registered as metatype
+ QMetaTypeId<QStringList>::qt_metatype_id();
+
+ // register actions
+ Core::Context globalcontext(Core::Constants::C_GLOBAL);
+ Core::ActionContainer *mfind = Core::ActionManager::actionContainer(Constants::M_FIND);
+ Core::Command *cmd;
+
+ m_ui.advancedButton->setDefaultAction(Core::ActionManager::command(Constants::ADVANCED_FIND)->action());
+
+ QIcon icon = QIcon::fromTheme(QLatin1String("edit-find-replace"));
+ m_findInDocumentAction = new QAction(icon, tr("Find/Replace"), this);
+ cmd = Core::ActionManager::registerAction(m_findInDocumentAction, Constants::FIND_IN_DOCUMENT, globalcontext);
+ cmd->setDefaultKeySequence(QKeySequence::Find);
+ mfind->addAction(cmd, Constants::G_FIND_CURRENTDOCUMENT);
+ connect(m_findInDocumentAction, SIGNAL(triggered()), this, SLOT(openFind()));
+
+ if (QApplication::clipboard()->supportsFindBuffer()) {
+ m_enterFindStringAction = new QAction(tr("Enter Find String"), this);
+ cmd = Core::ActionManager::registerAction(m_enterFindStringAction, "Find.EnterFindString", globalcontext);
+ cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+E")));
+ mfind->addAction(cmd, Constants::G_FIND_ACTIONS);
+ connect(m_enterFindStringAction, SIGNAL(triggered()), this, SLOT(putSelectionToFindClipboard()));
+ connect(QApplication::clipboard(), SIGNAL(findBufferChanged()), this, SLOT(updateFromFindClipboard()));
+ }
+
+ m_findNextAction = new QAction(tr("Find Next"), this);
+ cmd = Core::ActionManager::registerAction(m_findNextAction, Constants::FIND_NEXT, globalcontext);
+ cmd->setDefaultKeySequence(QKeySequence::FindNext);
+ mfind->addAction(cmd, Constants::G_FIND_ACTIONS);
+ connect(m_findNextAction, SIGNAL(triggered()), this, SLOT(invokeFindNext()));
+ m_ui.findNextButton->setDefaultAction(cmd->action());
+
+ m_findPreviousAction = new QAction(tr("Find Previous"), this);
+ cmd = Core::ActionManager::registerAction(m_findPreviousAction, Constants::FIND_PREVIOUS, globalcontext);
+ cmd->setDefaultKeySequence(QKeySequence::FindPrevious);
+ mfind->addAction(cmd, Constants::G_FIND_ACTIONS);
+ connect(m_findPreviousAction, SIGNAL(triggered()), this, SLOT(invokeFindPrevious()));
+ m_ui.findPreviousButton->setDefaultAction(cmd->action());
+
+ m_findNextSelectedAction = new QAction(tr("Find Next (Selected)"), this);
+ cmd = Core::ActionManager::registerAction(m_findNextSelectedAction, Constants::FIND_NEXT_SELECTED, globalcontext);
+ cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+F3")));
+ mfind->addAction(cmd, Constants::G_FIND_ACTIONS);
+ connect(m_findNextSelectedAction, SIGNAL(triggered()), this, SLOT(findNextSelected()));
+
+ m_findPreviousSelectedAction = new QAction(tr("Find Previous (Selected)"), this);
+ cmd = Core::ActionManager::registerAction(m_findPreviousSelectedAction, Constants::FIND_PREV_SELECTED, globalcontext);
+ cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+F3")));
+ mfind->addAction(cmd, Constants::G_FIND_ACTIONS);
+ connect(m_findPreviousSelectedAction, SIGNAL(triggered()), this, SLOT(findPreviousSelected()));
+
+ m_replaceAction = new QAction(tr("Replace"), this);
+ cmd = Core::ActionManager::registerAction(m_replaceAction, Constants::REPLACE, globalcontext);
+ cmd->setDefaultKeySequence(QKeySequence());
+ mfind->addAction(cmd, Constants::G_FIND_ACTIONS);
+ connect(m_replaceAction, SIGNAL(triggered()), this, SLOT(invokeReplace()));
+ m_ui.replaceButton->setDefaultAction(cmd->action());
+
+ m_replaceNextAction = new QAction(tr("Replace && Find"), this);
+ m_replaceNextAction->setIconText(tr("Replace && Find")); // work around bug in Qt that kills ampersands in tool button
+ cmd = Core::ActionManager::registerAction(m_replaceNextAction, Constants::REPLACE_NEXT, globalcontext);
+ cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+=")));
+ mfind->addAction(cmd, Constants::G_FIND_ACTIONS);
+ connect(m_replaceNextAction, SIGNAL(triggered()), this, SLOT(invokeReplaceNext()));
+ m_ui.replaceNextButton->setDefaultAction(cmd->action());
+
+ m_replacePreviousAction = new QAction(tr("Replace && Find Previous"), this);
+ cmd = Core::ActionManager::registerAction(m_replacePreviousAction, Constants::REPLACE_PREVIOUS, globalcontext);
+ // shortcut removed, clashes with Ctrl++ on many keyboard layouts
+ //cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+=")));
+ mfind->addAction(cmd, Constants::G_FIND_ACTIONS);
+ connect(m_replacePreviousAction, SIGNAL(triggered()), this, SLOT(invokeReplacePrevious()));
+
+ m_replaceAllAction = new QAction(tr("Replace All"), this);
+ cmd = Core::ActionManager::registerAction(m_replaceAllAction, Constants::REPLACE_ALL, globalcontext);
+ mfind->addAction(cmd, Constants::G_FIND_ACTIONS);
+ connect(m_replaceAllAction, SIGNAL(triggered()), this, SLOT(invokeReplaceAll()));
+ m_ui.replaceAllButton->setDefaultAction(cmd->action());
+
+ m_caseSensitiveAction = new QAction(tr("Case Sensitive"), this);
+ m_caseSensitiveAction->setIcon(QIcon(QLatin1String(":/find/images/casesensitively.png")));
+ m_caseSensitiveAction->setCheckable(true);
+ m_caseSensitiveAction->setChecked(false);
+ cmd = Core::ActionManager::registerAction(m_caseSensitiveAction, Constants::CASE_SENSITIVE, globalcontext);
+ mfind->addAction(cmd, Constants::G_FIND_FLAGS);
+ connect(m_caseSensitiveAction, SIGNAL(triggered(bool)), this, SLOT(setCaseSensitive(bool)));
+ lineEditMenu->addAction(m_caseSensitiveAction);
+
+ m_wholeWordAction = new QAction(tr("Whole Words Only"), this);
+ m_wholeWordAction->setIcon(QIcon(QLatin1String(":/find/images/wholewords.png")));
+ m_wholeWordAction->setCheckable(true);
+ m_wholeWordAction->setChecked(false);
+ cmd = Core::ActionManager::registerAction(m_wholeWordAction, Constants::WHOLE_WORDS, globalcontext);
+ mfind->addAction(cmd, Constants::G_FIND_FLAGS);
+ connect(m_wholeWordAction, SIGNAL(triggered(bool)), this, SLOT(setWholeWord(bool)));
+ lineEditMenu->addAction(m_wholeWordAction);
+
+ m_regularExpressionAction = new QAction(tr("Use Regular Expressions"), this);
+ m_regularExpressionAction->setIcon(QIcon(QLatin1String(":/find/images/regexp.png")));
+ m_regularExpressionAction->setCheckable(true);
+ m_regularExpressionAction->setChecked(false);
+ cmd = Core::ActionManager::registerAction(m_regularExpressionAction, Constants::REGULAR_EXPRESSIONS, globalcontext);
+ mfind->addAction(cmd, Constants::G_FIND_FLAGS);
+ connect(m_regularExpressionAction, SIGNAL(triggered(bool)), this, SLOT(setRegularExpressions(bool)));
+ lineEditMenu->addAction(m_regularExpressionAction);
+
+ m_preserveCaseAction = new QAction(tr("Preserve Case when Replacing"), this);
+ m_preserveCaseAction->setIcon(QPixmap(QLatin1String(":/find/images/preservecase.png")));
+ m_preserveCaseAction->setCheckable(true);
+ m_preserveCaseAction->setChecked(false);
+ cmd = Core::ActionManager::registerAction(m_preserveCaseAction, Constants::PRESERVE_CASE, globalcontext);
+ mfind->addAction(cmd, Constants::G_FIND_FLAGS);
+ connect(m_preserveCaseAction, SIGNAL(triggered(bool)), this, SLOT(setPreserveCase(bool)));
+ lineEditMenu->addAction(m_preserveCaseAction);
+
+ connect(m_currentDocumentFind, SIGNAL(candidateChanged()), this, SLOT(adaptToCandidate()));
+ connect(m_currentDocumentFind, SIGNAL(changed()), this, SLOT(updateToolBar()));
+ updateToolBar();
+
+ m_findIncrementalTimer.setSingleShot(true);
+ m_findStepTimer.setSingleShot(true);
+ connect(&m_findIncrementalTimer, SIGNAL(timeout()),
+ this, SLOT(invokeFindIncremental()));
+ connect(&m_findStepTimer, SIGNAL(timeout()), this, SLOT(invokeFindStep()));
+}
+
+FindToolBar::~FindToolBar()
+{
+}
+
+void FindToolBar::installEventFilters()
+{
+ if (!m_eventFiltersInstalled) {
+ m_findCompleter->popup()->installEventFilter(this);
+ m_ui.findEdit->installEventFilter(this);
+ m_ui.replaceEdit->installEventFilter(this);
+ this->installEventFilter(this);
+ m_eventFiltersInstalled = true;
+ }
+}
+
+bool FindToolBar::shouldSetFocusOnKeyEvent(QKeyEvent *event)
+{
+ return event->key() == Qt::Key_Escape && !event->modifiers()
+ && !m_findCompleter->popup()->isVisible()
+ && !m_replaceCompleter->popup()->isVisible()
+ && m_currentDocumentFind->isEnabled();
+}
+
+bool FindToolBar::eventFilter(QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::KeyPress) {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(event);
+ if (ke->key() == Qt::Key_Down) {
+ if (obj == m_ui.findEdit) {
+ if (m_ui.findEdit->text().isEmpty())
+ m_findCompleter->setCompletionPrefix(QString());
+ m_findCompleter->complete();
+ } else if (obj == m_ui.replaceEdit) {
+ if (m_ui.replaceEdit->text().isEmpty())
+ m_replaceCompleter->setCompletionPrefix(QString());
+ m_replaceCompleter->complete();
+ }
+ }
+ }
+
+ if ((obj == m_ui.findEdit || obj == m_findCompleter->popup())
+ && event->type() == QEvent::KeyPress) {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(event);
+ if (ke->key() == Qt::Key_Space && (ke->modifiers() & Utils::HostOsInfo::controlModifier())) {
+ QString completedText = m_currentDocumentFind->completedFindString();
+ if (!completedText.isEmpty()) {
+ setFindText(completedText);
+ ke->accept();
+ return true;
+ }
+ }
+ } else if (obj == this && event->type() == QEvent::ShortcutOverride) {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(event);
+ if (shouldSetFocusOnKeyEvent(ke)) {
+ event->accept();
+ return true;
+ } else if (ke->key() == Qt::Key_Space && (ke->modifiers() & Utils::HostOsInfo::controlModifier())) {
+ event->accept();
+ return true;
+ }
+ } else if (obj == this && event->type() == QEvent::Hide) {
+ invokeClearResults();
+ if (m_currentDocumentFind->isEnabled())
+ m_currentDocumentFind->clearFindScope();
+ }
+ return Utils::StyledBar::eventFilter(obj, event);
+}
+
+void FindToolBar::keyPressEvent(QKeyEvent *event)
+{
+ if (shouldSetFocusOnKeyEvent(event)) {
+ if (setFocusToCurrentFindSupport())
+ event->accept();
+ return;
+ }
+ return Utils::StyledBar::keyPressEvent(event);
+}
+
+void FindToolBar::adaptToCandidate()
+{
+ updateFindAction();
+ if (findToolBarPlaceHolder() == Core::FindToolBarPlaceHolder::getCurrent())
+ m_currentDocumentFind->acceptCandidate();
+}
+
+void FindToolBar::updateFindAction()
+{
+ bool enabled = m_currentDocumentFind->candidateIsEnabled();
+ m_findInDocumentAction->setEnabled(enabled);
+ m_findNextSelectedAction->setEnabled(enabled);
+ m_findPreviousSelectedAction->setEnabled(enabled);
+}
+
+void FindToolBar::updateToolBar()
+{
+ bool enabled = m_currentDocumentFind->isEnabled();
+ bool replaceEnabled = enabled && m_currentDocumentFind->supportsReplace();
+ m_findNextAction->setEnabled(enabled);
+ m_findPreviousAction->setEnabled(enabled);
+
+ m_replaceAction->setEnabled(replaceEnabled);
+ m_replaceNextAction->setEnabled(replaceEnabled);
+ m_replacePreviousAction->setEnabled(replaceEnabled);
+ m_replaceAllAction->setEnabled(replaceEnabled);
+
+ m_caseSensitiveAction->setEnabled(enabled);
+ m_wholeWordAction->setEnabled(enabled);
+ m_regularExpressionAction->setEnabled(enabled);
+ m_preserveCaseAction->setEnabled(replaceEnabled && !hasFindFlag(FindRegularExpression));
+ if (QApplication::clipboard()->supportsFindBuffer())
+ m_enterFindStringAction->setEnabled(enabled);
+ bool replaceFocus = m_ui.replaceEdit->hasFocus();
+ m_ui.findEdit->setEnabled(enabled);
+ m_ui.findLabel->setEnabled(enabled);
+
+ m_ui.replaceEdit->setEnabled(replaceEnabled);
+ m_ui.replaceLabel->setEnabled(replaceEnabled);
+ m_ui.replaceEdit->setVisible(replaceEnabled);
+ m_ui.replaceLabel->setVisible(replaceEnabled);
+ m_ui.replaceButtonsWidget->setVisible(replaceEnabled);
+ m_ui.advancedButton->setVisible(replaceEnabled);
+ layout()->invalidate();
+
+ if (!replaceEnabled && enabled && replaceFocus)
+ m_ui.findEdit->setFocus();
+ updateIcons();
+ updateFlagMenus();
+}
+
+void FindToolBar::invokeFindEnter()
+{
+ if (m_currentDocumentFind->isEnabled()) {
+ if (m_useFakeVim)
+ setFocusToCurrentFindSupport();
+ else
+ invokeFindNext();
+ }
+}
+
+void FindToolBar::invokeReplaceEnter()
+{
+ if (m_currentDocumentFind->isEnabled() && m_currentDocumentFind->supportsReplace())
+ invokeReplaceNext();
+}
+
+void FindToolBar::invokeClearResults()
+{
+ if (m_currentDocumentFind->isEnabled())
+ m_currentDocumentFind->clearResults();
+}
+
+
+void FindToolBar::invokeFindNext()
+{
+ setFindFlag(FindBackward, false);
+ invokeFindStep();
+}
+
+void FindToolBar::invokeFindPrevious()
+{
+ setFindFlag(FindBackward, true);
+ invokeFindStep();
+}
+
+QString FindToolBar::getFindText()
+{
+ return m_ui.findEdit->text();
+}
+
+QString FindToolBar::getReplaceText()
+{
+ return m_ui.replaceEdit->text();
+}
+
+void FindToolBar::setFindText(const QString &text)
+{
+ disconnect(m_ui.findEdit, SIGNAL(textChanged(QString)), this, SLOT(invokeFindIncremental()));
+ if (hasFindFlag(FindRegularExpression))
+ m_ui.findEdit->setText(QRegExp::escape(text));
+ else
+ m_ui.findEdit->setText(text);
+ connect(m_ui.findEdit, SIGNAL(textChanged(QString)), this, SLOT(invokeFindIncremental()));
+}
+
+void FindToolBar::selectFindText()
+{
+ m_ui.findEdit->selectAll();
+}
+
+void FindToolBar::invokeFindStep()
+{
+ m_findStepTimer.stop();
+ m_findIncrementalTimer.stop();
+ if (m_currentDocumentFind->isEnabled()) {
+ m_plugin->updateFindCompletion(getFindText());
+ IFindSupport::Result result =
+ m_currentDocumentFind->findStep(getFindText(), effectiveFindFlags());
+ if (result == IFindSupport::NotYetFound)
+ m_findStepTimer.start(50);
+ }
+}
+
+void FindToolBar::invokeFindIncremental()
+{
+ m_findIncrementalTimer.stop();
+ m_findStepTimer.stop();
+ if (m_currentDocumentFind->isEnabled()) {
+ QString text = getFindText();
+ IFindSupport::Result result =
+ m_currentDocumentFind->findIncremental(text, effectiveFindFlags());
+ if (result == IFindSupport::NotYetFound)
+ m_findIncrementalTimer.start(50);
+ if (text.isEmpty())
+ m_currentDocumentFind->clearResults();
+ }
+}
+
+void FindToolBar::invokeReplace()
+{
+ setFindFlag(FindBackward, false);
+ if (m_currentDocumentFind->isEnabled() && m_currentDocumentFind->supportsReplace()) {
+ m_plugin->updateFindCompletion(getFindText());
+ m_plugin->updateReplaceCompletion(getReplaceText());
+ m_currentDocumentFind->replace(getFindText(), getReplaceText(), effectiveFindFlags());
+ }
+}
+
+void FindToolBar::invokeReplaceNext()
+{
+ setFindFlag(FindBackward, false);
+ invokeReplaceStep();
+}
+
+void FindToolBar::invokeReplacePrevious()
+{
+ setFindFlag(FindBackward, true);
+ invokeReplaceStep();
+}
+
+void FindToolBar::invokeReplaceStep()
+{
+ if (m_currentDocumentFind->isEnabled() && m_currentDocumentFind->supportsReplace()) {
+ m_plugin->updateFindCompletion(getFindText());
+ m_plugin->updateReplaceCompletion(getReplaceText());
+ m_currentDocumentFind->replaceStep(getFindText(), getReplaceText(), effectiveFindFlags());
+ }
+}
+
+void FindToolBar::invokeReplaceAll()
+{
+ m_plugin->updateFindCompletion(getFindText());
+ m_plugin->updateReplaceCompletion(getReplaceText());
+ if (m_currentDocumentFind->isEnabled() && m_currentDocumentFind->supportsReplace())
+ m_currentDocumentFind->replaceAll(getFindText(), getReplaceText(), effectiveFindFlags());
+}
+
+void FindToolBar::invokeResetIncrementalSearch()
+{
+ m_findIncrementalTimer.stop();
+ m_findStepTimer.stop();
+ if (m_currentDocumentFind->isEnabled())
+ m_currentDocumentFind->resetIncrementalSearch();
+}
+
+
+void FindToolBar::putSelectionToFindClipboard()
+{
+ const QString text = m_currentDocumentFind->currentFindString();
+ QApplication::clipboard()->setText(text, QClipboard::FindBuffer);
+ setFindText(text);
+}
+
+
+void FindToolBar::updateFromFindClipboard()
+{
+ if (QApplication::clipboard()->supportsFindBuffer()) {
+ const bool blocks = m_ui.findEdit->blockSignals(true);
+ setFindText(QApplication::clipboard()->text(QClipboard::FindBuffer));
+ m_ui.findEdit->blockSignals(blocks);
+ }
+}
+
+void FindToolBar::findFlagsChanged()
+{
+ updateIcons();
+ updateFlagMenus();
+ invokeClearResults();
+ if (isVisible())
+ m_currentDocumentFind->highlightAll(getFindText(), effectiveFindFlags());
+}
+
+void FindToolBar::updateIcons()
+{
+ FindFlags effectiveFlags = effectiveFindFlags();
+ bool casesensitive = effectiveFlags & FindCaseSensitively;
+ bool wholewords = effectiveFlags & FindWholeWords;
+ bool regexp = effectiveFlags & FindRegularExpression;
+ bool preserveCase = effectiveFlags & FindPreserveCase;
+ if (!casesensitive && !wholewords && !regexp && !preserveCase) {
+ QPixmap pixmap(17, 17);
+ pixmap.fill(Qt::transparent);
+ QPainter painter(&pixmap);
+ const QPixmap mag = QPixmap(QLatin1String(Core::Constants::ICON_MAGNIFIER));
+ painter.drawPixmap(0, (pixmap.height() - mag.height()) / 2, mag);
+ m_ui.findEdit->setButtonPixmap(Utils::FancyLineEdit::Left, pixmap);
+ } else {
+ m_ui.findEdit->setButtonPixmap(Utils::FancyLineEdit::Left,
+ IFindFilter::pixmapForFindFlags(effectiveFlags));
+ }
+}
+
+FindFlags FindToolBar::effectiveFindFlags()
+{
+ FindFlags supportedFlags;
+ bool supportsReplace = true;
+ if (m_currentDocumentFind->isEnabled()) {
+ supportedFlags = m_currentDocumentFind->supportedFindFlags();
+ supportsReplace = m_currentDocumentFind->supportsReplace();
+ } else {
+ supportedFlags = (FindFlags)0xFFFFFF;
+ }
+ if (!supportsReplace || m_findFlags & FindRegularExpression)
+ supportedFlags &= ~FindPreserveCase;
+ return supportedFlags & m_findFlags;
+}
+
+void FindToolBar::updateFlagMenus()
+{
+ bool wholeOnly = ((m_findFlags & FindWholeWords));
+ bool sensitive = ((m_findFlags & FindCaseSensitively));
+ bool regexp = ((m_findFlags & FindRegularExpression));
+ bool preserveCase = ((m_findFlags & FindPreserveCase));
+ if (m_wholeWordAction->isChecked() != wholeOnly)
+ m_wholeWordAction->setChecked(wholeOnly);
+ if (m_caseSensitiveAction->isChecked() != sensitive)
+ m_caseSensitiveAction->setChecked(sensitive);
+ if (m_regularExpressionAction->isChecked() != regexp)
+ m_regularExpressionAction->setChecked(regexp);
+ if (m_preserveCaseAction->isChecked() != preserveCase)
+ m_preserveCaseAction->setChecked(preserveCase);
+ FindFlags supportedFlags;
+ if (m_currentDocumentFind->isEnabled())
+ supportedFlags = m_currentDocumentFind->supportedFindFlags();
+ m_wholeWordAction->setEnabled(supportedFlags & FindWholeWords);
+ m_caseSensitiveAction->setEnabled(supportedFlags & FindCaseSensitively);
+ m_regularExpressionAction->setEnabled(supportedFlags & FindRegularExpression);
+ bool replaceEnabled = m_currentDocumentFind->isEnabled() && m_currentDocumentFind->supportsReplace();
+ m_preserveCaseAction->setEnabled((supportedFlags & FindPreserveCase) && !regexp && replaceEnabled);
+}
+
+bool FindToolBar::setFocusToCurrentFindSupport()
+{
+ return m_currentDocumentFind->setFocusToCurrentFindSupport();
+}
+
+void FindToolBar::hideAndResetFocus()
+{
+ m_currentDocumentFind->setFocusToCurrentFindSupport();
+ hide();
+}
+
+Core::FindToolBarPlaceHolder *FindToolBar::findToolBarPlaceHolder() const
+{
+ QList<Core::FindToolBarPlaceHolder*> placeholders = ExtensionSystem::PluginManager::getObjects<Core::FindToolBarPlaceHolder>();
+ QWidget *candidate = QApplication::focusWidget();
+ while (candidate) {
+ foreach (Core::FindToolBarPlaceHolder *ph, placeholders) {
+ if (ph->owner() == candidate)
+ return ph;
+ }
+ candidate = candidate->parentWidget();
+ }
+ return 0;
+}
+
+void FindToolBar::openFind(bool focus)
+{
+ setBackward(false);
+ openFindToolBar(focus);
+}
+
+void FindToolBar::openFindToolBar(bool focus)
+{
+ installEventFilters();
+ if (!m_currentDocumentFind->candidateIsEnabled())
+ return;
+ Core::FindToolBarPlaceHolder *holder = findToolBarPlaceHolder();
+ if (!holder)
+ return;
+ Core::FindToolBarPlaceHolder *previousHolder = Core::FindToolBarPlaceHolder::getCurrent();
+ if (previousHolder)
+ previousHolder->setWidget(0);
+ Core::FindToolBarPlaceHolder::setCurrent(holder);
+ m_currentDocumentFind->acceptCandidate();
+ holder->setWidget(this);
+ holder->setVisible(true);
+ setVisible(true);
+ if (focus)
+ setFocus();
+ QString text = m_currentDocumentFind->currentFindString();
+ if (!text.isEmpty())
+ setFindText(text);
+ m_currentDocumentFind->defineFindScope();
+ m_currentDocumentFind->highlightAll(getFindText(), effectiveFindFlags());
+ if (focus)
+ selectFindText();
+}
+
+void FindToolBar::findNextSelected()
+{
+ openFind(false);
+ invokeFindNext();
+}
+
+void FindToolBar::findPreviousSelected()
+{
+ openFind(false);
+ invokeFindPrevious();
+}
+
+bool FindToolBar::focusNextPrevChild(bool next)
+{
+ // close tab order change
+ if (next && m_ui.replaceAllButton->hasFocus())
+ m_ui.findEdit->setFocus(Qt::TabFocusReason);
+ else if (!next && m_ui.findEdit->hasFocus())
+ m_ui.replaceAllButton->setFocus(Qt::TabFocusReason);
+ else
+ return Utils::StyledBar::focusNextPrevChild(next);
+ return true;
+}
+
+void FindToolBar::writeSettings()
+{
+ QSettings *settings = Core::ICore::settings();
+ settings->beginGroup(QLatin1String("Find"));
+ settings->beginGroup(QLatin1String("FindToolBar"));
+ settings->setValue(QLatin1String("Backward"), QVariant((m_findFlags & FindBackward) != 0));
+ settings->setValue(QLatin1String("CaseSensitively"), QVariant((m_findFlags & FindCaseSensitively) != 0));
+ settings->setValue(QLatin1String("WholeWords"), QVariant((m_findFlags & FindWholeWords) != 0));
+ settings->setValue(QLatin1String("RegularExpression"), QVariant((m_findFlags & FindRegularExpression) != 0));
+ settings->setValue(QLatin1String("PreserveCase"), QVariant((m_findFlags & FindPreserveCase) != 0));
+ settings->endGroup();
+ settings->endGroup();
+}
+
+void FindToolBar::readSettings()
+{
+ QSettings *settings = Core::ICore::settings();
+ settings->beginGroup(QLatin1String("Find"));
+ settings->beginGroup(QLatin1String("FindToolBar"));
+ FindFlags flags;
+ if (settings->value(QLatin1String("Backward"), false).toBool())
+ flags |= FindBackward;
+ if (settings->value(QLatin1String("CaseSensitively"), false).toBool())
+ flags |= FindCaseSensitively;
+ if (settings->value(QLatin1String("WholeWords"), false).toBool())
+ flags |= FindWholeWords;
+ if (settings->value(QLatin1String("RegularExpression"), false).toBool())
+ flags |= FindRegularExpression;
+ if (settings->value(QLatin1String("PreserveCase"), false).toBool())
+ flags |= FindPreserveCase;
+ settings->endGroup();
+ settings->endGroup();
+ m_findFlags = flags;
+ findFlagsChanged();
+}
+
+void FindToolBar::setUseFakeVim(bool on)
+{
+ m_useFakeVim = on;
+}
+
+void FindToolBar::setFindFlag(FindFlag flag, bool enabled)
+{
+ bool hasFlag = hasFindFlag(flag);
+ if ((hasFlag && enabled) || (!hasFlag && !enabled))
+ return;
+ if (enabled)
+ m_findFlags |= flag;
+ else
+ m_findFlags &= ~flag;
+ if (flag != FindBackward)
+ findFlagsChanged();
+}
+
+bool FindToolBar::hasFindFlag(FindFlag flag)
+{
+ return m_findFlags & flag;
+}
+
+void FindToolBar::setCaseSensitive(bool sensitive)
+{
+ setFindFlag(FindCaseSensitively, sensitive);
+}
+
+void FindToolBar::setWholeWord(bool wholeOnly)
+{
+ setFindFlag(FindWholeWords, wholeOnly);
+}
+
+void FindToolBar::setRegularExpressions(bool regexp)
+{
+ setFindFlag(FindRegularExpression, regexp);
+}
+
+void FindToolBar::setPreserveCase(bool preserveCase)
+{
+ setFindFlag(FindPreserveCase, preserveCase);
+}
+
+void FindToolBar::setBackward(bool backward)
+{
+ setFindFlag(FindBackward, backward);
+}
diff --git a/src/plugins/coreplugin/find/findtoolbar.h b/src/plugins/coreplugin/find/findtoolbar.h
new file mode 100644
index 0000000000..fb90dfbf1f
--- /dev/null
+++ b/src/plugins/coreplugin/find/findtoolbar.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef FINDTOOLBAR_H
+#define FINDTOOLBAR_H
+
+#include "ui_findwidget.h"
+#include "currentdocumentfind.h"
+
+#include <utils/styledbar.h>
+
+#include <QTimer>
+
+namespace Core {
+
+class FindToolBarPlaceHolder;
+class FindPlugin;
+
+namespace Internal {
+
+class FindToolBar : public Utils::StyledBar
+{
+ Q_OBJECT
+
+public:
+ explicit FindToolBar(FindPlugin *plugin, CurrentDocumentFind *currentDocumentFind);
+ ~FindToolBar();
+
+ void readSettings();
+ void writeSettings();
+
+ void openFindToolBar(bool focus = true);
+ void setUseFakeVim(bool on);
+
+public slots:
+ void setBackward(bool backward);
+
+private slots:
+ void invokeFindNext();
+ void invokeFindPrevious();
+ void invokeFindStep();
+ void invokeReplace();
+ void invokeReplaceNext();
+ void invokeReplacePrevious();
+ void invokeReplaceStep();
+ void invokeReplaceAll();
+ void invokeResetIncrementalSearch();
+
+ void invokeFindIncremental();
+ void invokeFindEnter();
+ void invokeReplaceEnter();
+ void putSelectionToFindClipboard();
+ void updateFromFindClipboard();
+
+ void hideAndResetFocus();
+ void openFind(bool focus = true);
+ void findNextSelected();
+ void findPreviousSelected();
+ void updateFindAction();
+ void updateToolBar();
+ void findFlagsChanged();
+
+ void setCaseSensitive(bool sensitive);
+ void setWholeWord(bool wholeOnly);
+ void setRegularExpressions(bool regexp);
+ void setPreserveCase(bool preserveCase);
+
+ void adaptToCandidate();
+
+protected:
+ bool focusNextPrevChild(bool next);
+ void keyPressEvent(QKeyEvent *event);
+
+private:
+ void installEventFilters();
+ void invokeClearResults();
+ bool setFocusToCurrentFindSupport();
+ void setFindFlag(FindFlag flag, bool enabled);
+ bool hasFindFlag(FindFlag flag);
+ FindFlags effectiveFindFlags();
+ Core::FindToolBarPlaceHolder *findToolBarPlaceHolder() const;
+
+ bool eventFilter(QObject *obj, QEvent *event);
+ void setFindText(const QString &text);
+ QString getFindText();
+ QString getReplaceText();
+ void selectFindText();
+ void updateIcons();
+ void updateFlagMenus();
+
+ bool shouldSetFocusOnKeyEvent(QKeyEvent *event);
+
+ FindPlugin *m_plugin;
+ CurrentDocumentFind *m_currentDocumentFind;
+ Ui::FindWidget m_ui;
+ QCompleter *m_findCompleter;
+ QCompleter *m_replaceCompleter;
+ QAction *m_findInDocumentAction;
+ QAction *m_findNextSelectedAction;
+ QAction *m_findPreviousSelectedAction;
+ QAction *m_enterFindStringAction;
+ QAction *m_findNextAction;
+ QAction *m_findPreviousAction;
+ QAction *m_replaceAction;
+ QAction *m_replaceNextAction;
+ QAction *m_replacePreviousAction;
+ QAction *m_replaceAllAction;
+ QAction *m_caseSensitiveAction;
+ QAction *m_wholeWordAction;
+ QAction *m_regularExpressionAction;
+ QAction *m_preserveCaseAction;
+ FindFlags m_findFlags;
+
+ QTimer m_findIncrementalTimer;
+ QTimer m_findStepTimer;
+ bool m_useFakeVim;
+ bool m_eventFiltersInstalled;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // FINDTOOLBAR_H
diff --git a/src/plugins/coreplugin/find/findtoolwindow.cpp b/src/plugins/coreplugin/find/findtoolwindow.cpp
new file mode 100644
index 0000000000..66521dc32b
--- /dev/null
+++ b/src/plugins/coreplugin/find/findtoolwindow.cpp
@@ -0,0 +1,264 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "findtoolwindow.h"
+#include "ifindfilter.h"
+#include "findplugin.h"
+
+#include <coreplugin/icore.h>
+
+#include <QSettings>
+#include <QStringListModel>
+#include <QCompleter>
+#include <QKeyEvent>
+#include <QScrollArea>
+
+using namespace Core;
+using namespace Core::Internal;
+
+static FindToolWindow *m_instance = 0;
+
+FindToolWindow::FindToolWindow(FindPlugin *plugin, QWidget *parent)
+ : QWidget(parent),
+ m_plugin(plugin),
+ m_findCompleter(new QCompleter(this)),
+ m_currentFilter(0),
+ m_configWidget(0)
+{
+ m_instance = this;
+ m_ui.setupUi(this);
+ m_ui.searchTerm->setPlaceholderText(QString());
+ setFocusProxy(m_ui.searchTerm);
+
+ connect(m_ui.searchButton, SIGNAL(clicked()), this, SLOT(search()));
+ connect(m_ui.replaceButton, SIGNAL(clicked()), this, SLOT(replace()));
+ connect(m_ui.matchCase, SIGNAL(toggled(bool)), m_plugin, SLOT(setCaseSensitive(bool)));
+ connect(m_ui.wholeWords, SIGNAL(toggled(bool)), m_plugin, SLOT(setWholeWord(bool)));
+ connect(m_ui.regExp, SIGNAL(toggled(bool)), m_plugin, SLOT(setRegularExpression(bool)));
+ connect(m_ui.filterList, SIGNAL(activated(int)), this, SLOT(setCurrentFilter(int)));
+ connect(m_ui.searchTerm, SIGNAL(textChanged(QString)), this, SLOT(updateButtonStates()));
+
+ m_findCompleter->setModel(m_plugin->findCompletionModel());
+ m_ui.searchTerm->setSpecialCompleter(m_findCompleter);
+ m_ui.searchTerm->installEventFilter(this);
+ QVBoxLayout *layout = new QVBoxLayout;
+ layout->setMargin(0);
+ layout->setSpacing(0);
+ m_ui.configWidget->setLayout(layout);
+ updateButtonStates();
+
+ connect(m_plugin, SIGNAL(findFlagsChanged()), this, SLOT(updateFindFlags()));
+}
+
+FindToolWindow::~FindToolWindow()
+{
+ qDeleteAll(m_configWidgets);
+}
+
+FindToolWindow *FindToolWindow::instance()
+{
+ return m_instance;
+}
+
+bool FindToolWindow::event(QEvent *event)
+{
+ if (event->type() == QEvent::KeyPress) {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(event);
+ if ((ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter)
+ && (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::KeypadModifier)) {
+ ke->accept();
+ search();
+ return true;
+ }
+ }
+ return QWidget::event(event);
+}
+
+bool FindToolWindow::eventFilter(QObject *obj, QEvent *event)
+{
+ if (obj == m_ui.searchTerm && event->type() == QEvent::KeyPress) {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(event);
+ if (ke->key() == Qt::Key_Down) {
+ if (m_ui.searchTerm->text().isEmpty())
+ m_findCompleter->setCompletionPrefix(QString());
+ m_findCompleter->complete();
+ }
+ }
+ return QWidget::eventFilter(obj, event);
+}
+
+void FindToolWindow::updateButtonStates()
+{
+ bool filterEnabled = m_currentFilter && m_currentFilter->isEnabled();
+ bool enabled = !m_ui.searchTerm->text().isEmpty() && filterEnabled;
+ m_ui.searchButton->setEnabled(enabled);
+ m_ui.replaceButton->setEnabled(m_currentFilter
+ && m_currentFilter->isReplaceSupported() && enabled);
+ if (m_configWidget)
+ m_configWidget->setEnabled(filterEnabled);
+
+ m_ui.matchCase->setEnabled(filterEnabled
+ && (m_currentFilter->supportedFindFlags() & FindCaseSensitively));
+ m_ui.wholeWords->setEnabled(filterEnabled
+ && (m_currentFilter->supportedFindFlags() & FindWholeWords));
+ m_ui.regExp->setEnabled(filterEnabled
+ && (m_currentFilter->supportedFindFlags() & FindRegularExpression));
+ m_ui.searchTerm->setEnabled(filterEnabled);
+}
+
+void FindToolWindow::updateFindFlags()
+{
+ m_ui.matchCase->setChecked(m_plugin->hasFindFlag(FindCaseSensitively));
+ m_ui.wholeWords->setChecked(m_plugin->hasFindFlag(FindWholeWords));
+ m_ui.regExp->setChecked(m_plugin->hasFindFlag(FindRegularExpression));
+}
+
+
+void FindToolWindow::setFindFilters(const QList<IFindFilter *> &filters)
+{
+ qDeleteAll(m_configWidgets);
+ m_configWidgets.clear();
+ m_filters = filters;
+ m_ui.filterList->clear();
+ QStringList names;
+ foreach (IFindFilter *filter, filters) {
+ names << filter->displayName();
+ m_configWidgets.append(filter->createConfigWidget());
+ }
+ m_ui.filterList->addItems(names);
+ if (m_filters.size() > 0)
+ setCurrentFilter(0);
+}
+
+void FindToolWindow::setFindText(const QString &text)
+{
+ m_ui.searchTerm->setText(text);
+}
+
+void FindToolWindow::setCurrentFilter(IFindFilter *filter)
+{
+ if (!filter)
+ filter = m_currentFilter;
+ int index = m_filters.indexOf(filter);
+ if (index >= 0)
+ setCurrentFilter(index);
+ updateFindFlags();
+ m_ui.searchTerm->setFocus();
+ m_ui.searchTerm->selectAll();
+}
+
+void FindToolWindow::setCurrentFilter(int index)
+{
+ m_ui.filterList->setCurrentIndex(index);
+ for (int i = 0; i < m_configWidgets.size(); ++i) {
+ QWidget *configWidget = m_configWidgets.at(i);
+ if (i == index) {
+ m_configWidget = configWidget;
+ if (m_currentFilter)
+ disconnect(m_currentFilter, SIGNAL(enabledChanged(bool)), this, SLOT(updateButtonStates()));
+ m_currentFilter = m_filters.at(i);
+ connect(m_currentFilter, SIGNAL(enabledChanged(bool)), this, SLOT(updateButtonStates()));
+ updateButtonStates();
+ if (m_configWidget)
+ m_ui.configWidget->layout()->addWidget(m_configWidget);
+ } else {
+ if (configWidget)
+ configWidget->setParent(0);
+ }
+ }
+ QWidget *w = m_ui.configWidget;
+ while (w) {
+ QScrollArea *sa = qobject_cast<QScrollArea *>(w);
+ if (sa) {
+ sa->updateGeometry();
+ break;
+ }
+ w = w->parentWidget();
+ }
+ for (w = m_configWidget ? m_configWidget : m_ui.configWidget; w; w = w->parentWidget()) {
+ if (w->layout())
+ w->layout()->activate();
+ }
+}
+
+void FindToolWindow::acceptAndGetParameters(QString *term, IFindFilter **filter)
+{
+ if (filter)
+ *filter = 0;
+ m_plugin->updateFindCompletion(m_ui.searchTerm->text());
+ int index = m_ui.filterList->currentIndex();
+ QString searchTerm = m_ui.searchTerm->text();
+ if (term)
+ *term = searchTerm;
+ if (searchTerm.isEmpty() || index < 0)
+ return;
+ if (filter)
+ *filter = m_filters.at(index);
+}
+
+void FindToolWindow::search()
+{
+ QString term;
+ IFindFilter *filter;
+ acceptAndGetParameters(&term, &filter);
+ if (filter)
+ filter->findAll(term, m_plugin->findFlags());
+}
+
+void FindToolWindow::replace()
+{
+ QString term;
+ IFindFilter *filter;
+ acceptAndGetParameters(&term, &filter);
+ filter->replaceAll(term, m_plugin->findFlags());
+}
+
+void FindToolWindow::writeSettings()
+{
+ QSettings *settings = Core::ICore::settings();
+ settings->beginGroup(QLatin1String("Find"));
+ settings->setValue(QLatin1String("CurrentFilter"), m_currentFilter ? m_currentFilter->id() : QString());
+ foreach (IFindFilter *filter, m_filters)
+ filter->writeSettings(settings);
+ settings->endGroup();
+}
+
+void FindToolWindow::readSettings()
+{
+ QSettings *settings = Core::ICore::settings();
+ settings->beginGroup(QLatin1String("Find"));
+ const QString currentFilter = settings->value(QLatin1String("CurrentFilter")).toString();
+ for (int i = 0; i < m_filters.size(); ++i) {
+ IFindFilter *filter = m_filters.at(i);
+ filter->readSettings(settings);
+ if (filter->id() == currentFilter)
+ setCurrentFilter(i);
+ }
+ settings->endGroup();
+}
diff --git a/src/plugins/coreplugin/find/findtoolwindow.h b/src/plugins/coreplugin/find/findtoolwindow.h
new file mode 100644
index 0000000000..e78d0075ec
--- /dev/null
+++ b/src/plugins/coreplugin/find/findtoolwindow.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef FINDTOOLWINDOW_H
+#define FINDTOOLWINDOW_H
+
+#include "ui_finddialog.h"
+#include "findplugin.h"
+
+#include <QList>
+
+QT_FORWARD_DECLARE_CLASS(QCompleter)
+
+namespace Core {
+class IFindFilter;
+
+namespace Internal {
+
+class FindToolWindow : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit FindToolWindow(FindPlugin *plugin, QWidget *parent = 0);
+ ~FindToolWindow();
+ static FindToolWindow *instance();
+
+ void setFindFilters(const QList<IFindFilter *> &filters);
+
+ void setFindText(const QString &text);
+ void setCurrentFilter(IFindFilter *filter);
+ void readSettings();
+ void writeSettings();
+
+protected:
+ bool event(QEvent *event);
+ bool eventFilter(QObject *obj, QEvent *event);
+
+private slots:
+ void search();
+ void replace();
+ void setCurrentFilter(int index);
+ void updateButtonStates();
+ void updateFindFlags();
+
+private:
+ void acceptAndGetParameters(QString *term, IFindFilter **filter);
+
+ Ui::FindDialog m_ui;
+ FindPlugin *m_plugin;
+ QList<IFindFilter *> m_filters;
+ QCompleter *m_findCompleter;
+ QWidgetList m_configWidgets;
+ IFindFilter *m_currentFilter;
+ QWidget *m_configWidget;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // FINDTOOLWINDOW_H
diff --git a/src/plugins/coreplugin/find/findwidget.ui b/src/plugins/coreplugin/find/findwidget.ui
new file mode 100644
index 0000000000..4472102781
--- /dev/null
+++ b/src/plugins/coreplugin/find/findwidget.ui
@@ -0,0 +1,292 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Core::Internal::FindWidget</class>
+ <widget class="QWidget" name="Core::Internal::FindWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>681</width>
+ <height>88</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Find</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>2</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>3</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="findLabel">
+ <property name="text">
+ <string>Find:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="Utils::FilterLineEdit" name="findEdit">
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QWidget" name="findButtonsWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QToolButton" name="findPreviousButton">
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="arrowType">
+ <enum>Qt::LeftArrow</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="findNextButton">
+ <property name="font">
+ <font/>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="arrowType">
+ <enum>Qt::RightArrow</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QToolButton" name="close">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="replaceLabel">
+ <property name="text">
+ <string>Replace with:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="Utils::FilterLineEdit" name="replaceEdit">
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="2">
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="horizontalSpacing">
+ <number>3</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QWidget" name="replaceButtonsWidget" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QHBoxLayout" name="replaceButtonsLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QToolButton" name="replaceButton">
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="text">
+ <string>Replace</string>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextOnly</enum>
+ </property>
+ <property name="arrowType">
+ <enum>Qt::LeftArrow</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="replaceNextButton">
+ <property name="font">
+ <font/>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="text">
+ <string>Replace &amp;&amp; Find</string>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextOnly</enum>
+ </property>
+ <property name="arrowType">
+ <enum>Qt::RightArrow</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="replaceAllButton">
+ <property name="font">
+ <font/>
+ </property>
+ <property name="text">
+ <string>Replace All</string>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextOnly</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QToolButton" name="advancedButton">
+ <property name="text">
+ <string>Advanced...</string>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextOnly</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>Utils::FancyLineEdit</class>
+ <extends>QLineEdit</extends>
+ <header location="global">utils/fancylineedit.h</header>
+ </customwidget>
+ <customwidget>
+ <class>Utils::FilterLineEdit</class>
+ <extends>Utils::FancyLineEdit</extends>
+ <header location="global">utils/filterlineedit.h</header>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>findEdit</tabstop>
+ <tabstop>replaceEdit</tabstop>
+ <tabstop>close</tabstop>
+ <tabstop>replaceAllButton</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/coreplugin/find/ifindfilter.cpp b/src/plugins/coreplugin/find/ifindfilter.cpp
new file mode 100644
index 0000000000..c9fda43f2a
--- /dev/null
+++ b/src/plugins/coreplugin/find/ifindfilter.cpp
@@ -0,0 +1,285 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "ifindfilter.h"
+
+#include <QPainter>
+#include <QPixmap>
+
+/*!
+ \class Find::IFindFilter
+ \brief The IFindFilter class is the base class for find implementations
+ that are invoked by selecting \gui Edit > \gui {Find/Replace} >
+ \gui {Advanced Find}.
+
+ Implementations of this class add an additional \gui Scope to the \gui {Advanced
+ Find} dialog. That can be any search that requires the user to provide
+ a text based search term (potentially with find flags like
+ searching case sensitively or using regular expressions). Existing
+ scopes are \gui {All Projects} that searches from all files in all projects
+ and \gui {Files on File System} where the user provides a directory and file
+ patterns to search.
+
+ To make your find scope available to the user, you need to implement this
+ class, and register an instance of your subclass in the plugin manager.
+
+ A common way to present the search results to the user, is to use the
+ shared \gui{Search Results} panel.
+
+ If you want to implement a find filter that is doing a file based text
+ search, you should use Find::BaseFileFind, which already implements all
+ the details for this kind of search, only requiring you to provide an
+ iterator over the file names of the files that should be searched.
+
+ If you want to implement a more specialized find filter, you need to:
+ \list
+ \li Start your search in a separate thread
+ \li Make this known to the Core::ProgressManager, for a progress bar
+ and the ability to cancel the search
+ \li Interface with the shared \gui{Search Results} panel, to show
+ the search results, handle the event that the user click on one
+ of the search result items, and possible handle a global replace
+ of all or some of the search result items.
+ \endlist
+
+ Luckily QtConcurrent and the search result panel provide the frameworks
+ that make it relatively easy to implement,
+ while ensuring a common way for the user.
+
+ The common pattern is roughly this:
+
+ Implement the actual search within a QtConcurrent based function, that is
+ a function that takes a \c{QFutureInterface<MySearchResult> &future}
+ as the first parameter and the other information needed for the search
+ as additional parameters. It should set useful progress information
+ on the QFutureInterface, regularly check for \c{future.isPaused()}
+ and \c{future.isCanceled()}, and report the search results
+ (possibly in chunks) via \c{future.reportResult}.
+
+ In the find filter's find/replaceAll function, get the shared
+ \gui{Search Results} window, initiate a new search and connect the
+ signals for handling selection of results and the replace action
+ (see the Core::SearchResultWindow class for details).
+ Start your search implementation via the corresponding QtConcurrent
+ functions. Add the returned QFuture object to the Core::ProgressManager.
+ Use a QFutureWatcher on the returned QFuture object to receive a signal
+ when your search implementation reports search results, and add these
+ to the shared \gui{Search Results} window.
+*/
+
+/*!
+ \fn IFindFilter::~IFindFilter()
+ \internal
+*/
+
+/*!
+ \fn QString IFindFilter::id() const
+ Returns the unique string identifier for this find filter.
+
+ Usually should be something like "MyPlugin.MyFindFilter".
+*/
+
+/*!
+ \fn QString IFindFilter::displayName() const
+ Returns the name of the find filter or scope as presented to the user.
+
+ This is the name that appears in the scope selection combo box, for example.
+ Always return a translatable string (that is, use tr() for the return value).
+*/
+
+/*!
+ \fn bool IFindFilter::isEnabled() const
+ Returns whether the user should be able to select this find filter
+ at the moment.
+
+ This is used for the \gui {Current Projects} scope, for example. If the user
+ has not
+ opened a project, the scope is disabled.
+
+ \sa changed()
+*/
+
+/*!
+ \fn QKeySequence IFindFilter::defaultShortcut() const
+ Returns the shortcut that can be used to open the advanced find
+ dialog with this filter or scope preselected.
+
+ Usually return an empty shortcut here, the user can still choose and
+ assign a specific shortcut to this find scope via the preferences.
+*/
+
+/*!
+ \fn bool IFindFilter::isReplaceSupported() const
+ Returns whether the find filter supports search and replace.
+
+ The default value is false, override this function to return \c true, if
+ your find filter supports global search and replace.
+*/
+
+/*!
+ \fn void IFindFilter::findAll(const QString &txt, Core::FindFlags findFlags)
+ This function is called when the user selected this find scope and
+ initiated a search.
+
+ You should start a thread which actually performs the search for \a txt
+ using the given \a findFlags
+ (add it to Core::ProgressManager for a progress bar!) and presents the
+ search results to the user (using the \gui{Search Results} output pane).
+ For more information, see the descriptions of this class,
+ Core::ProgressManager, and Core::SearchResultWindow.
+
+ \sa replaceAll()
+ \sa Core::ProgressManager
+ \sa Core::SearchResultWindow
+*/
+
+/*!
+ \fn void IFindFilter::replaceAll(const QString &txt, Core::FindFlags findFlags)
+ Override this function if you want to support search and replace.
+
+ This function is called when the user selected this find scope and
+ initiated a search and replace.
+ The default implementation does nothing.
+
+ You should start a thread which actually performs the search for \a txt
+ using the given \a findFlags
+ (add it to Core::ProgressManager for a progress bar!) and presents the
+ search results to the user (using the \gui{Search Results} output pane).
+ For more information see the descriptions of this class,
+ Core::ProgressManager, and Core::SearchResultWindow.
+
+ \sa findAll()
+ \sa Core::ProgressManager
+ \sa Core::SearchResultWindow
+*/
+
+/*!
+ \fn QWidget *IFindFilter::createConfigWidget()
+ Returns a widget that contains additional controls for options
+ for this find filter.
+
+ The widget will be shown below the common options in the \gui {Advanced Find}
+ dialog. It will be reparented and deleted by the find plugin.
+*/
+
+/*!
+ \fn void IFindFilter::writeSettings(QSettings *settings)
+ Called at shutdown to write the state of the additional options
+ for this find filter to the \a settings.
+*/
+
+/*!
+ \fn void IFindFilter::readSettings(QSettings *settings)
+ Called at startup to read the state of the additional options
+ for this find filter from the \a settings.
+*/
+
+/*!
+ \fn void IFindFilter::enabledChanged(bool enabled)
+
+ Signals that the enabled state of this find filter has changed.
+*/
+
+/*!
+ \fn Core::FindFlags BaseTextFind::supportedFindFlags() const
+ Returns the find flags, like whole words or regular expressions,
+ that this find filter supports.
+
+ Depending on the returned value, the default find option widgets are
+ enabled or disabled.
+ The default is Find::FindCaseSensitively, Find::FindRegularExpression
+ and Find::FindWholeWords
+*/
+
+namespace Core {
+
+FindFlags IFindFilter::supportedFindFlags() const
+{
+ return FindCaseSensitively
+ | FindRegularExpression | FindWholeWords;
+}
+
+QPixmap IFindFilter::pixmapForFindFlags(FindFlags flags)
+{
+ static const QPixmap casesensitiveIcon = QPixmap(QLatin1String(":/find/images/casesensitively.png"));
+ static const QPixmap regexpIcon = QPixmap(QLatin1String(":/find/images/regexp.png"));
+ static const QPixmap wholewordsIcon = QPixmap(QLatin1String(":/find/images/wholewords.png"));
+ static const QPixmap preservecaseIcon = QPixmap(QLatin1String(":/find/images/preservecase.png"));
+ bool casesensitive = flags & FindCaseSensitively;
+ bool wholewords = flags & FindWholeWords;
+ bool regexp = flags & FindRegularExpression;
+ bool preservecase = flags & FindPreserveCase;
+ int width = 0;
+ if (casesensitive) width += 6;
+ if (wholewords) width += 6;
+ if (regexp) width += 6;
+ if (preservecase) width += 6;
+ if (width > 0) --width;
+ QPixmap pixmap(width, 17);
+ pixmap.fill(Qt::transparent);
+ QPainter painter(&pixmap);
+ int x = 0;
+
+ if (casesensitive) {
+ painter.drawPixmap(x - 6, 0, casesensitiveIcon);
+ x += 6;
+ }
+ if (wholewords) {
+ painter.drawPixmap(x - 6, 0, wholewordsIcon);
+ x += 6;
+ }
+ if (regexp) {
+ painter.drawPixmap(x - 6, 0, regexpIcon);
+ x += 6;
+ }
+ if (preservecase)
+ painter.drawPixmap(x - 6, 0, preservecaseIcon);
+ return pixmap;
+}
+
+QString IFindFilter::descriptionForFindFlags(FindFlags flags)
+{
+ QStringList flagStrings;
+ if (flags & FindCaseSensitively)
+ flagStrings.append(tr("Case sensitive"));
+ if (flags & FindWholeWords)
+ flagStrings.append(tr("Whole words"));
+ if (flags & FindRegularExpression)
+ flagStrings.append(tr("Regular expressions"));
+ if (flags & FindPreserveCase)
+ flagStrings.append(tr("Preserve case"));
+ QString description = tr("Flags: %1");
+ if (flagStrings.isEmpty())
+ description = description.arg(tr("None"));
+ else
+ description = description.arg(flagStrings.join(tr(", ")));
+ return description;
+}
+
+} // namespace Core
diff --git a/src/plugins/coreplugin/find/ifindfilter.h b/src/plugins/coreplugin/find/ifindfilter.h
new file mode 100644
index 0000000000..00bb66a04e
--- /dev/null
+++ b/src/plugins/coreplugin/find/ifindfilter.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef IFINDFILTER_H
+#define IFINDFILTER_H
+
+#include "textfindconstants.h"
+
+QT_BEGIN_NAMESPACE
+class QWidget;
+class QSettings;
+class QKeySequence;
+class Pixmap;
+QT_END_NAMESPACE
+
+namespace Core {
+
+class CORE_EXPORT IFindFilter : public QObject
+{
+ Q_OBJECT
+public:
+
+ virtual ~IFindFilter() {}
+
+ virtual QString id() const = 0;
+ virtual QString displayName() const = 0;
+ ///
+ virtual bool isEnabled() const = 0;
+ virtual QKeySequence defaultShortcut() const;
+ virtual bool isReplaceSupported() const { return false; }
+ virtual FindFlags supportedFindFlags() const;
+
+ virtual void findAll(const QString &txt, FindFlags findFlags) = 0;
+ virtual void replaceAll(const QString &txt, FindFlags findFlags)
+ { Q_UNUSED(txt) Q_UNUSED(findFlags) }
+
+ virtual QWidget *createConfigWidget() { return 0; }
+ virtual void writeSettings(QSettings *settings) { Q_UNUSED(settings) }
+ virtual void readSettings(QSettings *settings) { Q_UNUSED(settings) }
+
+ static QPixmap pixmapForFindFlags(FindFlags flags);
+ static QString descriptionForFindFlags(FindFlags flags);
+signals:
+ void enabledChanged(bool enabled);
+};
+
+} // namespace Core
+
+#endif // IFINDFILTER_H
diff --git a/src/plugins/coreplugin/find/ifindsupport.cpp b/src/plugins/coreplugin/find/ifindsupport.cpp
new file mode 100644
index 0000000000..f057352de6
--- /dev/null
+++ b/src/plugins/coreplugin/find/ifindsupport.cpp
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "ifindsupport.h"
+
+#include <QTimer>
+#include <QPropertyAnimation>
+#include <QWidget>
+#include <QPaintEvent>
+#include <QPainter>
+
+namespace Core {
+namespace Internal {
+
+class WrapIndicator : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity USER true)
+
+public:
+ WrapIndicator(QWidget *parent = 0)
+ : QWidget(parent),
+ m_opacity(1.0)
+ {
+ if (parent)
+ setGeometry(QRect(parent->rect().center() - QPoint(25, 25),
+ parent->rect().center() + QPoint(25, 25)));
+ }
+
+ qreal opacity() const { return m_opacity; }
+ void setOpacity(qreal value) { m_opacity = value; update(); }
+
+ void run()
+ {
+ show();
+ QTimer::singleShot(300, this, SLOT(runInternal()));
+ }
+
+protected:
+ void paintEvent(QPaintEvent *)
+ {
+ static QPixmap foreground(QLatin1String(":/find/images/wrapindicator.png"));
+ QPainter p(this);
+ p.setOpacity(m_opacity);
+ p.drawPixmap(rect(), foreground);
+ }
+
+private slots:
+ void runInternal()
+ {
+ QPropertyAnimation *anim = new QPropertyAnimation(this, "opacity", this);
+ anim->setDuration(200);
+ anim->setEndValue(0.);
+ connect(anim, SIGNAL(finished()), this, SLOT(deleteLater()));
+ anim->start(QAbstractAnimation::DeleteWhenStopped);
+ }
+
+private:
+ qreal m_opacity;
+};
+
+} // Internal
+} // Find
+
+using namespace Core;
+
+void IFindSupport::replace(const QString &before, const QString &after, FindFlags findFlags)
+{
+ Q_UNUSED(before)
+ Q_UNUSED(after)
+ Q_UNUSED(findFlags)
+}
+
+bool IFindSupport::replaceStep(const QString &before, const QString &after, FindFlags findFlags)
+{
+ Q_UNUSED(before)
+ Q_UNUSED(after)
+ Q_UNUSED(findFlags)
+ return false;
+}
+
+int IFindSupport::replaceAll(const QString &before, const QString &after, FindFlags findFlags)
+{
+ Q_UNUSED(before)
+ Q_UNUSED(after)
+ Q_UNUSED(findFlags)
+ return 0;
+}
+
+void IFindSupport::showWrapIndicator(QWidget *parent)
+{
+ (new Internal::WrapIndicator(parent))->run();
+}
+
+#include "ifindsupport.moc"
diff --git a/src/plugins/coreplugin/find/ifindsupport.h b/src/plugins/coreplugin/find/ifindsupport.h
new file mode 100644
index 0000000000..041de57fdd
--- /dev/null
+++ b/src/plugins/coreplugin/find/ifindsupport.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef IFINDSUPPORT_H
+#define IFINDSUPPORT_H
+
+#include "textfindconstants.h"
+
+#include <QObject>
+#include <QString>
+
+namespace Core {
+
+class CORE_EXPORT IFindSupport : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Result { Found, NotFound, NotYetFound };
+
+ IFindSupport() : QObject(0) {}
+ virtual ~IFindSupport() {}
+
+ virtual bool supportsReplace() const = 0;
+ virtual FindFlags supportedFindFlags() const = 0;
+ virtual void resetIncrementalSearch() = 0;
+ virtual void clearResults() = 0;
+ virtual QString currentFindString() const = 0;
+ virtual QString completedFindString() const = 0;
+
+ virtual void highlightAll(const QString &txt, FindFlags findFlags);
+ virtual Result findIncremental(const QString &txt, FindFlags findFlags) = 0;
+ virtual Result findStep(const QString &txt, FindFlags findFlags) = 0;
+ virtual void replace(const QString &before, const QString &after,
+ FindFlags findFlags);
+ virtual bool replaceStep(const QString &before, const QString &after,
+ FindFlags findFlags);
+ virtual int replaceAll(const QString &before, const QString &after,
+ FindFlags findFlags);
+
+ virtual void defineFindScope(){}
+ virtual void clearFindScope(){}
+
+ static void showWrapIndicator(QWidget *parent);
+
+signals:
+ void changed();
+};
+
+inline void IFindSupport::highlightAll(const QString &, FindFlags) {}
+
+} // namespace Core
+
+#endif // IFINDSUPPORT_H
diff --git a/src/plugins/coreplugin/find/images/all.png b/src/plugins/coreplugin/find/images/all.png
new file mode 100644
index 0000000000..f5c1c1f767
--- /dev/null
+++ b/src/plugins/coreplugin/find/images/all.png
Binary files differ
diff --git a/src/plugins/coreplugin/find/images/casesensitively.png b/src/plugins/coreplugin/find/images/casesensitively.png
new file mode 100644
index 0000000000..029b41faa4
--- /dev/null
+++ b/src/plugins/coreplugin/find/images/casesensitively.png
Binary files differ
diff --git a/src/plugins/coreplugin/find/images/empty.png b/src/plugins/coreplugin/find/images/empty.png
new file mode 100644
index 0000000000..f02154247c
--- /dev/null
+++ b/src/plugins/coreplugin/find/images/empty.png
Binary files differ
diff --git a/src/plugins/coreplugin/find/images/expand.png b/src/plugins/coreplugin/find/images/expand.png
new file mode 100644
index 0000000000..48fcb9b703
--- /dev/null
+++ b/src/plugins/coreplugin/find/images/expand.png
Binary files differ
diff --git a/src/plugins/coreplugin/find/images/next.png b/src/plugins/coreplugin/find/images/next.png
new file mode 100644
index 0000000000..1844929119
--- /dev/null
+++ b/src/plugins/coreplugin/find/images/next.png
Binary files differ
diff --git a/src/plugins/coreplugin/find/images/preservecase.png b/src/plugins/coreplugin/find/images/preservecase.png
new file mode 100644
index 0000000000..4869aabd71
--- /dev/null
+++ b/src/plugins/coreplugin/find/images/preservecase.png
Binary files differ
diff --git a/src/plugins/coreplugin/find/images/previous.png b/src/plugins/coreplugin/find/images/previous.png
new file mode 100644
index 0000000000..4fe50af9a8
--- /dev/null
+++ b/src/plugins/coreplugin/find/images/previous.png
Binary files differ
diff --git a/src/plugins/coreplugin/find/images/regexp.png b/src/plugins/coreplugin/find/images/regexp.png
new file mode 100644
index 0000000000..be8a5cc48c
--- /dev/null
+++ b/src/plugins/coreplugin/find/images/regexp.png
Binary files differ
diff --git a/src/plugins/coreplugin/find/images/replace_all.png b/src/plugins/coreplugin/find/images/replace_all.png
new file mode 100644
index 0000000000..d2298a8aad
--- /dev/null
+++ b/src/plugins/coreplugin/find/images/replace_all.png
Binary files differ
diff --git a/src/plugins/coreplugin/find/images/wholewords.png b/src/plugins/coreplugin/find/images/wholewords.png
new file mode 100644
index 0000000000..0ffcecd963
--- /dev/null
+++ b/src/plugins/coreplugin/find/images/wholewords.png
Binary files differ
diff --git a/src/plugins/coreplugin/find/images/wordandcase.png b/src/plugins/coreplugin/find/images/wordandcase.png
new file mode 100644
index 0000000000..34c0ac3190
--- /dev/null
+++ b/src/plugins/coreplugin/find/images/wordandcase.png
Binary files differ
diff --git a/src/plugins/coreplugin/find/images/wrapindicator.png b/src/plugins/coreplugin/find/images/wrapindicator.png
new file mode 100644
index 0000000000..a4f8ddf417
--- /dev/null
+++ b/src/plugins/coreplugin/find/images/wrapindicator.png
Binary files differ
diff --git a/src/plugins/coreplugin/find/searchresultcolor.h b/src/plugins/coreplugin/find/searchresultcolor.h
new file mode 100644
index 0000000000..0547f07749
--- /dev/null
+++ b/src/plugins/coreplugin/find/searchresultcolor.h
@@ -0,0 +1,20 @@
+#ifndef SEARCHRESULTCOLOR_H
+#define SEARCHRESULTCOLOR_H
+
+#include <QColor>
+
+namespace Core {
+namespace Internal {
+
+class SearchResultColor{
+public:
+ QColor textBackground;
+ QColor textForeground;
+ QColor highlightBackground;
+ QColor highlightForeground;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SEARCHRESULTCOLOR_H
diff --git a/src/plugins/coreplugin/find/searchresulttreeitemdelegate.cpp b/src/plugins/coreplugin/find/searchresulttreeitemdelegate.cpp
new file mode 100644
index 0000000000..949a298f62
--- /dev/null
+++ b/src/plugins/coreplugin/find/searchresulttreeitemdelegate.cpp
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "searchresulttreeitemdelegate.h"
+#include "searchresulttreeitemroles.h"
+
+#include <QPainter>
+#include <QApplication>
+
+#include <QModelIndex>
+#include <QDebug>
+
+using namespace Core::Internal;
+
+SearchResultTreeItemDelegate::SearchResultTreeItemDelegate(QObject *parent)
+ : QItemDelegate(parent)
+{
+}
+
+void SearchResultTreeItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ static const int iconSize = 16;
+
+ painter->save();
+
+ QStyleOptionViewItemV3 opt = setOptions(index, option);
+ painter->setFont(opt.font);
+
+ QItemDelegate::drawBackground(painter, opt, index);
+
+ // ---- do the layout
+ QRect checkRect;
+ QRect pixmapRect;
+ QRect textRect;
+
+ // check mark
+ bool checkable = (index.model()->flags(index) & Qt::ItemIsUserCheckable);
+ Qt::CheckState checkState = Qt::Unchecked;
+ if (checkable) {
+ QVariant checkStateData = index.data(Qt::CheckStateRole);
+ checkState = static_cast<Qt::CheckState>(checkStateData.toInt());
+#if QT_VERSION >= 0x050000
+ checkRect = doCheck(opt, opt.rect, checkStateData);
+#else // Qt4
+ checkRect = check(opt, opt.rect, checkStateData);
+#endif
+ }
+
+ // icon
+ QIcon icon = index.model()->data(index, ItemDataRoles::ResultIconRole).value<QIcon>();
+ if (!icon.isNull())
+ pixmapRect = QRect(0, 0, iconSize, iconSize);
+
+ // text
+ textRect = opt.rect.adjusted(0, 0, checkRect.width() + pixmapRect.width(), 0);
+
+ // do layout
+ doLayout(opt, &checkRect, &pixmapRect, &textRect, false);
+
+ // ---- draw the items
+ // icon
+ if (!icon.isNull())
+ QItemDelegate::drawDecoration(painter, opt, pixmapRect, icon.pixmap(iconSize));
+
+ // line numbers
+ int lineNumberAreaWidth = drawLineNumber(painter, opt, textRect, index);
+ textRect.adjust(lineNumberAreaWidth, 0, 0, 0);
+
+ // text and focus/selection
+ drawText(painter, opt, textRect, index);
+ QItemDelegate::drawFocus(painter, opt, opt.rect);
+
+ // check mark
+ if (checkable)
+ QItemDelegate::drawCheck(painter, opt, checkRect, checkState);
+
+ painter->restore();
+}
+
+// returns the width of the line number area
+int SearchResultTreeItemDelegate::drawLineNumber(QPainter *painter, const QStyleOptionViewItemV3 &option,
+ const QRect &rect,
+ const QModelIndex &index) const
+{
+ static const int lineNumberAreaHorizontalPadding = 4;
+ int lineNumber = index.model()->data(index, ItemDataRoles::ResultLineNumberRole).toInt();
+ if (lineNumber < 1)
+ return 0;
+ const bool isSelected = option.state & QStyle::State_Selected;
+ QString lineText = QString::number(lineNumber);
+ int minimumLineNumberDigits = qMax((int)m_minimumLineNumberDigits, lineText.count());
+ int fontWidth = painter->fontMetrics().width(QString(minimumLineNumberDigits, QLatin1Char('0')));
+ int lineNumberAreaWidth = lineNumberAreaHorizontalPadding + fontWidth + lineNumberAreaHorizontalPadding;
+ QRect lineNumberAreaRect(rect);
+ lineNumberAreaRect.setWidth(lineNumberAreaWidth);
+
+ QPalette::ColorGroup cg = QPalette::Normal;
+ if (!(option.state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+ else if (!(option.state & QStyle::State_Enabled))
+ cg = QPalette::Disabled;
+
+ painter->fillRect(lineNumberAreaRect, QBrush(isSelected ?
+ option.palette.brush(cg, QPalette::Highlight) :
+ option.palette.color(cg, QPalette::Base).darker(111)));
+
+ QStyleOptionViewItemV3 opt = option;
+ opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
+ opt.palette.setColor(cg, QPalette::Text, Qt::darkGray);
+
+ const QStyle *style = QApplication::style();
+ const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, 0) + 1;
+
+ const QRect rowRect = lineNumberAreaRect.adjusted(-textMargin, 0, textMargin-lineNumberAreaHorizontalPadding, 0);
+ QItemDelegate::drawDisplay(painter, opt, rowRect, lineText);
+
+ return lineNumberAreaWidth;
+}
+
+void SearchResultTreeItemDelegate::drawText(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QRect &rect,
+ const QModelIndex &index) const
+{
+ QString text = index.model()->data(index, Qt::DisplayRole).toString();
+ // show number of subresults in displayString
+ if (index.model()->hasChildren(index)) {
+ text += QLatin1String(" (")
+ + QString::number(index.model()->rowCount(index))
+ + QLatin1Char(')');
+ }
+
+ const int searchTermStart = index.model()->data(index, ItemDataRoles::SearchTermStartRole).toInt();
+ int searchTermLength = index.model()->data(index, ItemDataRoles::SearchTermLengthRole).toInt();
+ if (searchTermStart < 0 || searchTermStart >= text.length() || searchTermLength < 1) {
+ QItemDelegate::drawDisplay(painter, option, rect, text);
+ return;
+ }
+ // clip searchTermLength to end of line
+ searchTermLength = qMin(searchTermLength, text.length() - searchTermStart);
+ 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));
+
+ // rects
+ QRect beforeHighlightRect(rect);
+ beforeHighlightRect.setRight(beforeHighlightRect.left() + searchTermStartPixels);
+
+ QRect resultHighlightRect(rect);
+ resultHighlightRect.setLeft(beforeHighlightRect.right());
+ resultHighlightRect.setRight(resultHighlightRect.left() + searchTermLengthPixels);
+
+ QRect afterHighlightRect(rect);
+ afterHighlightRect.setLeft(resultHighlightRect.right());
+
+ // paint all highlight backgrounds
+ // qitemdelegate has problems with painting background when highlighted
+ // (highlighted background at wrong position because text is offset with textMargin)
+ // so we duplicate a lot here, see qitemdelegate for reference
+ bool isSelected = option.state & QStyle::State_Selected;
+ QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
+ ? QPalette::Normal : QPalette::Disabled;
+ if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+ QStyleOptionViewItem baseOption = option;
+ baseOption.state &= ~QStyle::State_Selected;
+ if (isSelected) {
+ painter->fillRect(beforeHighlightRect.adjusted(textMargin, 0, textMargin, 0),
+ option.palette.brush(cg, QPalette::Highlight));
+ painter->fillRect(afterHighlightRect.adjusted(textMargin, 0, textMargin, 0),
+ option.palette.brush(cg, QPalette::Highlight));
+ }
+ const QColor highlightBackground =
+ index.model()->data(index, ItemDataRoles::ResultHighlightBackgroundColor).value<QColor>();
+ painter->fillRect(resultHighlightRect.adjusted(textMargin, 0, textMargin - 1, 0), QBrush(highlightBackground));
+
+ // Text before the highlighting
+ QStyleOptionViewItem noHighlightOpt = baseOption;
+ noHighlightOpt.rect = beforeHighlightRect;
+ noHighlightOpt.textElideMode = Qt::ElideNone;
+ if (isSelected)
+ noHighlightOpt.palette.setColor(QPalette::Text, noHighlightOpt.palette.color(cg, QPalette::HighlightedText));
+ QItemDelegate::drawDisplay(painter, noHighlightOpt,
+ beforeHighlightRect, text.mid(0, searchTermStart));
+
+ // Highlight text
+ QStyleOptionViewItem highlightOpt = noHighlightOpt;
+ const QColor highlightForeground =
+ index.model()->data(index, ItemDataRoles::ResultHighlightForegroundColor).value<QColor>();
+ highlightOpt.palette.setColor(QPalette::Text, highlightForeground);
+ QItemDelegate::drawDisplay(painter, highlightOpt, resultHighlightRect,
+ text.mid(searchTermStart, searchTermLength));
+
+ // Text after the Highlight
+ noHighlightOpt.rect = afterHighlightRect;
+ QItemDelegate::drawDisplay(painter, noHighlightOpt, afterHighlightRect,
+ text.mid(searchTermStart + searchTermLength));
+}
diff --git a/src/plugins/coreplugin/find/searchresulttreeitemdelegate.h b/src/plugins/coreplugin/find/searchresulttreeitemdelegate.h
new file mode 100644
index 0000000000..af46024ede
--- /dev/null
+++ b/src/plugins/coreplugin/find/searchresulttreeitemdelegate.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef SEARCHRESULTTREEITEMDELEGATE_H
+#define SEARCHRESULTTREEITEMDELEGATE_H
+
+#include <QItemDelegate>
+
+namespace Core {
+namespace Internal {
+
+class SearchResultTreeItemDelegate: public QItemDelegate
+{
+public:
+ SearchResultTreeItemDelegate(QObject *parent = 0);
+ void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+private:
+ int drawLineNumber(QPainter *painter, const QStyleOptionViewItemV3 &option, const QRect &rect, const QModelIndex &index) const;
+ void drawText(QPainter *painter, const QStyleOptionViewItem &option,
+ const QRect &rect, const QModelIndex &index) const;
+
+ static const int m_minimumLineNumberDigits = 6;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SEARCHRESULTTREEITEMDELEGATE_H
diff --git a/src/plugins/coreplugin/find/searchresulttreeitemroles.h b/src/plugins/coreplugin/find/searchresulttreeitemroles.h
new file mode 100644
index 0000000000..bd771d7c6a
--- /dev/null
+++ b/src/plugins/coreplugin/find/searchresulttreeitemroles.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef SEARCHRESULTTREEITEMROLES_H
+#define SEARCHRESULTTREEITEMROLES_H
+
+#include <QAbstractItemView>
+
+namespace Core {
+namespace Internal {
+namespace ItemDataRoles {
+
+enum Roles
+{
+ ResultItemRole = Qt::UserRole,
+ ResultLineRole,
+ ResultLineNumberRole,
+ ResultIconRole,
+ ResultHighlightBackgroundColor,
+ ResultHighlightForegroundColor,
+ SearchTermStartRole,
+ SearchTermLengthRole,
+ IsGeneratedRole
+};
+
+} // namespace Internal
+} // namespace Core
+} // namespace ItemDataRoles
+
+#endif // SEARCHRESULTTREEITEMROLES_H
diff --git a/src/plugins/coreplugin/find/searchresulttreeitems.cpp b/src/plugins/coreplugin/find/searchresulttreeitems.cpp
new file mode 100644
index 0000000000..d9f941bd02
--- /dev/null
+++ b/src/plugins/coreplugin/find/searchresulttreeitems.cpp
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "searchresulttreeitems.h"
+
+namespace Core {
+namespace Internal {
+
+SearchResultTreeItem::SearchResultTreeItem(const SearchResultItem &item,
+ SearchResultTreeItem *parent)
+ : item(item),
+ m_parent(parent),
+ m_isUserCheckable(false),
+ m_isGenerated(false),
+ m_checkState(Qt::Unchecked)
+{
+}
+
+SearchResultTreeItem::~SearchResultTreeItem()
+{
+ clearChildren();
+}
+
+bool SearchResultTreeItem::isLeaf() const
+{
+ return childrenCount() == 0 && parent() != 0;
+}
+
+bool SearchResultTreeItem::isUserCheckable() const
+{
+ return m_isUserCheckable;
+}
+
+void SearchResultTreeItem::setIsUserCheckable(bool isUserCheckable)
+{
+ m_isUserCheckable = isUserCheckable;
+}
+
+Qt::CheckState SearchResultTreeItem::checkState() const
+{
+ return m_checkState;
+}
+
+void SearchResultTreeItem::setCheckState(Qt::CheckState checkState)
+{
+ m_checkState = checkState;
+}
+
+void SearchResultTreeItem::clearChildren()
+{
+ qDeleteAll(m_children);
+ m_children.clear();
+}
+
+int SearchResultTreeItem::childrenCount() const
+{
+ return m_children.count();
+}
+
+int SearchResultTreeItem::rowOfItem() const
+{
+ return (m_parent ? m_parent->m_children.indexOf(const_cast<SearchResultTreeItem*>(this)):0);
+}
+
+SearchResultTreeItem* SearchResultTreeItem::childAt(int index) const
+{
+ return m_children.at(index);
+}
+
+SearchResultTreeItem *SearchResultTreeItem::parent() const
+{
+ return m_parent;
+}
+
+static bool lessThanByText(SearchResultTreeItem *a, const QString &b)
+{
+ return a->item.text < b;
+}
+
+int SearchResultTreeItem::insertionIndex(const QString &text, SearchResultTreeItem **existingItem) const
+{
+ QList<SearchResultTreeItem *>::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)
+{
+ m_children.insert(index, child);
+}
+
+void SearchResultTreeItem::insertChild(int index, const SearchResultItem &item)
+{
+ SearchResultTreeItem *child = new SearchResultTreeItem(item, this);
+ if (isUserCheckable()) {
+ child->setIsUserCheckable(true);
+ child->setCheckState(Qt::Checked);
+ }
+ insertChild(index, child);
+}
+
+void SearchResultTreeItem::appendChild(const SearchResultItem &item)
+{
+ insertChild(m_children.count(), item);
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/find/searchresulttreeitems.h b/src/plugins/coreplugin/find/searchresulttreeitems.h
new file mode 100644
index 0000000000..3f41818ee1
--- /dev/null
+++ b/src/plugins/coreplugin/find/searchresulttreeitems.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef SEARCHRESULTTREEITEMS_H
+#define SEARCHRESULTTREEITEMS_H
+
+#include "searchresultwindow.h"
+
+#include <QString>
+#include <QList>
+
+namespace Core {
+namespace Internal {
+
+class SearchResultTreeItem
+{
+public:
+ explicit SearchResultTreeItem(const SearchResultItem &item = SearchResultItem(),
+ SearchResultTreeItem *parent = NULL);
+ virtual ~SearchResultTreeItem();
+
+ bool isLeaf() const;
+ SearchResultTreeItem *parent() const;
+ SearchResultTreeItem *childAt(int index) const;
+ int insertionIndex(const QString &text, SearchResultTreeItem **existingItem) const;
+ int insertionIndex(const SearchResultItem &item, SearchResultTreeItem **existingItem) const;
+ void insertChild(int index, SearchResultTreeItem *child);
+ void insertChild(int index, const SearchResultItem &item);
+ void appendChild(const SearchResultItem &item);
+ int childrenCount() const;
+ int rowOfItem() const;
+ void clearChildren();
+
+ bool isUserCheckable() const;
+ void setIsUserCheckable(bool isUserCheckable);
+
+ 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:
+ SearchResultTreeItem *m_parent;
+ QList<SearchResultTreeItem *> m_children;
+ bool m_isUserCheckable;
+ bool m_isGenerated;
+ Qt::CheckState m_checkState;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SEARCHRESULTTREEITEMS_H
diff --git a/src/plugins/coreplugin/find/searchresulttreemodel.cpp b/src/plugins/coreplugin/find/searchresulttreemodel.cpp
new file mode 100644
index 0000000000..7aafe033bb
--- /dev/null
+++ b/src/plugins/coreplugin/find/searchresulttreemodel.cpp
@@ -0,0 +1,504 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "searchresulttreemodel.h"
+#include "searchresulttreeitems.h"
+#include "searchresulttreeitemroles.h"
+
+#include <QApplication>
+#include <QFont>
+#include <QFontMetrics>
+#include <QDebug>
+
+using namespace Core;
+using namespace Core::Internal;
+
+SearchResultTreeModel::SearchResultTreeModel(QObject *parent)
+ : QAbstractItemModel(parent)
+ , m_currentParent(0)
+ , m_showReplaceUI(false)
+ , m_editorFontIsUsed(false)
+{
+ m_rootItem = new SearchResultTreeItem;
+ m_textEditorFont = QFont(QLatin1String("Courier"));
+}
+
+SearchResultTreeModel::~SearchResultTreeModel()
+{
+ delete m_rootItem;
+}
+
+void SearchResultTreeModel::setShowReplaceUI(bool show)
+{
+ m_showReplaceUI = show;
+}
+
+void SearchResultTreeModel::setTextEditorFont(const QFont &font, const SearchResultColor color)
+{
+ layoutAboutToBeChanged();
+ m_textEditorFont = font;
+ m_color = color;
+ layoutChanged();
+}
+
+Qt::ItemFlags SearchResultTreeModel::flags(const QModelIndex &idx) const
+{
+ Qt::ItemFlags flags = QAbstractItemModel::flags(idx);
+
+ if (idx.isValid()) {
+ if (const SearchResultTreeItem *item = treeItemAtIndex(idx)) {
+ if (item->isUserCheckable())
+ flags |= Qt::ItemIsUserCheckable;
+ }
+ }
+
+ return flags;
+}
+
+QModelIndex SearchResultTreeModel::index(int row, int column,
+ const QModelIndex &parent) const
+{
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ const SearchResultTreeItem *parentItem;
+
+ if (!parent.isValid())
+ parentItem = m_rootItem;
+ else
+ parentItem = treeItemAtIndex(parent);
+
+ const SearchResultTreeItem *childItem = parentItem->childAt(row);
+ if (childItem)
+ return createIndex(row, column, (void *)childItem);
+ else
+ return QModelIndex();
+}
+
+QModelIndex SearchResultTreeModel::index(SearchResultTreeItem *item) const
+{
+ return createIndex(item->rowOfItem(), 0, (void *)item);
+}
+
+QModelIndex SearchResultTreeModel::parent(const QModelIndex &idx) const
+{
+ if (!idx.isValid())
+ return QModelIndex();
+
+ const SearchResultTreeItem *childItem = treeItemAtIndex(idx);
+ const SearchResultTreeItem *parentItem = childItem->parent();
+
+ if (parentItem == m_rootItem)
+ return QModelIndex();
+
+ return createIndex(parentItem->rowOfItem(), 0, (void *)parentItem);
+}
+
+int SearchResultTreeModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.column() > 0)
+ return 0;
+
+ const SearchResultTreeItem *parentItem;
+
+ if (!parent.isValid())
+ parentItem = m_rootItem;
+ else
+ parentItem = treeItemAtIndex(parent);
+
+ return parentItem->childrenCount();
+}
+
+int SearchResultTreeModel::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return 1;
+}
+
+SearchResultTreeItem *SearchResultTreeModel::treeItemAtIndex(const QModelIndex &idx) const
+{
+ return static_cast<SearchResultTreeItem*>(idx.internalPointer());
+}
+
+QVariant SearchResultTreeModel::data(const QModelIndex &idx, int role) const
+{
+ if (!idx.isValid())
+ return QVariant();
+
+ QVariant result;
+
+ if (role == Qt::SizeHintRole) {
+ int height = QApplication::fontMetrics().height();
+ if (m_editorFontIsUsed) {
+ const int editorFontHeight = QFontMetrics(m_textEditorFont).height();
+ height = qMax(height, editorFontHeight);
+ }
+ result = QSize(0, height);
+ } else {
+ result = data(treeItemAtIndex(idx), role);
+ }
+
+ return result;
+}
+
+bool SearchResultTreeModel::setData(const QModelIndex &idx, const QVariant &value, int role)
+{
+ if (role == Qt::CheckStateRole) {
+ Qt::CheckState checkState = static_cast<Qt::CheckState>(value.toInt());
+ return setCheckState(idx, checkState);
+ }
+ return QAbstractItemModel::setData(idx, value, role);
+}
+
+bool SearchResultTreeModel::setCheckState(const QModelIndex &idx, Qt::CheckState checkState, bool firstCall)
+{
+ SearchResultTreeItem *item = treeItemAtIndex(idx);
+ if (item->checkState() == checkState)
+ return false;
+ item->setCheckState(checkState);
+ if (firstCall) {
+ emit dataChanged(idx, idx);
+ // check parents
+ SearchResultTreeItem *currentItem = item;
+ QModelIndex currentIndex = idx;
+ while (SearchResultTreeItem *parent = currentItem->parent()) {
+ if (parent->isUserCheckable()) {
+ bool hasChecked = false;
+ bool hasUnchecked = false;
+ for (int i = 0; i < parent->childrenCount(); ++i) {
+ SearchResultTreeItem *child = parent->childAt(i);
+ if (!child->isUserCheckable())
+ continue;
+ if (child->checkState() == Qt::Checked)
+ hasChecked = true;
+ else if (child->checkState() == Qt::Unchecked)
+ hasUnchecked = true;
+ else if (child->checkState() == Qt::PartiallyChecked)
+ hasChecked = hasUnchecked = true;
+ }
+ if (hasChecked && hasUnchecked)
+ parent->setCheckState(Qt::PartiallyChecked);
+ else if (hasChecked)
+ parent->setCheckState(Qt::Checked);
+ else
+ parent->setCheckState(Qt::Unchecked);
+ emit dataChanged(idx.parent(), idx.parent());
+ }
+ currentItem = parent;
+ currentIndex = idx.parent();
+ }
+ }
+ // check children
+ if (int children = item->childrenCount()) {
+ for (int i = 0; i < children; ++i) {
+ setCheckState(idx.child(i, 0), checkState, false);
+ }
+ emit dataChanged(idx.child(0, 0), idx.child(children-1, 0));
+ }
+ return true;
+}
+
+void setDataInternal(const QModelIndex &index, const QVariant &value, int role);
+
+QVariant SearchResultTreeModel::data(const SearchResultTreeItem *row, int role) const
+{
+ QVariant result;
+
+ switch (role)
+ {
+ case Qt::CheckStateRole:
+ if (row->isUserCheckable())
+ result = row->checkState();
+ break;
+ case Qt::ToolTipRole:
+ result = row->item.text.trimmed();
+ break;
+ case Qt::FontRole:
+ if (row->item.useTextEditorFont)
+ result = m_textEditorFont;
+ else
+ result = QVariant();
+ break;
+ case Qt::TextColorRole:
+ result = m_color.textForeground;
+ break;
+ case Qt::BackgroundRole:
+ result = m_color.textBackground;
+ break;
+ case ItemDataRoles::ResultLineRole:
+ case Qt::DisplayRole:
+ result = row->item.text;
+ break;
+ case ItemDataRoles::ResultItemRole:
+ result = qVariantFromValue(row->item);
+ break;
+ case ItemDataRoles::ResultLineNumberRole:
+ result = row->item.lineNumber;
+ break;
+ case ItemDataRoles::ResultIconRole:
+ result = row->item.icon;
+ break;
+ case ItemDataRoles::ResultHighlightBackgroundColor:
+ result = m_color.highlightBackground;
+ break;
+ case ItemDataRoles::ResultHighlightForegroundColor:
+ result = m_color.highlightForeground;
+ break;
+ case ItemDataRoles::SearchTermStartRole:
+ result = row->item.textMarkPos;
+ break;
+ case ItemDataRoles::SearchTermLengthRole:
+ result = row->item.textMarkLength;
+ break;
+ case ItemDataRoles::IsGeneratedRole:
+ result = row->isGenerated();
+ break;
+ default:
+ result = QVariant();
+ break;
+ }
+
+ return result;
+}
+
+QVariant SearchResultTreeModel::headerData(int section, Qt::Orientation orientation,
+ int role) const
+{
+ Q_UNUSED(section)
+ Q_UNUSED(orientation)
+ Q_UNUSED(role)
+ return QVariant();
+}
+
+/**
+ * Makes sure that the nodes for a specific path exist and sets
+ * m_currentParent to the last final
+ */
+QSet<SearchResultTreeItem *> SearchResultTreeModel::addPath(const QStringList &path)
+{
+ QSet<SearchResultTreeItem *> 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;
+ }
+
+ m_currentParent = currentItem;
+ m_currentPath = currentPath;
+ m_currentIndex = currentItemIndex;
+ return pathNodes;
+}
+
+void SearchResultTreeModel::addResultsToCurrentParent(const QList<SearchResultItem> &items, SearchResult::AddMode mode)
+{
+ if (!m_currentParent)
+ return;
+
+ if (mode == SearchResult::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 == SearchResult::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();
+ }
+ }
+ }
+ dataChanged(m_currentIndex, m_currentIndex); // 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 nodes for the path when
+ * necessary.
+ */
+QList<QModelIndex> SearchResultTreeModel::addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode)
+{
+ QSet<SearchResultTreeItem *> pathNodes;
+ QList<SearchResultItem> sortedItems = items;
+ qStableSort(sortedItems.begin(), sortedItems.end(), lessThanByPath);
+ QList<SearchResultItem> itemSet;
+ foreach (const SearchResultItem &item, sortedItems) {
+ m_editorFontIsUsed |= item.useTextEditorFont;
+ if (!m_currentParent || (m_currentPath != item.path)) {
+ // first add all the items from before
+ if (!itemSet.isEmpty()) {
+ addResultsToCurrentParent(itemSet, mode);
+ itemSet.clear();
+ }
+ // switch parent
+ pathNodes += addPath(item.path);
+ }
+ itemSet << item;
+ }
+ if (!itemSet.isEmpty()) {
+ addResultsToCurrentParent(itemSet, mode);
+ itemSet.clear();
+ }
+ QList<QModelIndex> pathIndices;
+ foreach (SearchResultTreeItem *item, pathNodes)
+ pathIndices << index(item);
+ return pathIndices;
+}
+
+void SearchResultTreeModel::clear()
+{
+ beginResetModel();
+ m_currentParent = NULL;
+ m_rootItem->clearChildren();
+ m_editorFontIsUsed = false;
+ endResetModel();
+}
+
+QModelIndex SearchResultTreeModel::nextIndex(const QModelIndex &idx, bool *wrapped) const
+{
+ if (wrapped)
+ *wrapped = false;
+ // pathological
+ if (!idx.isValid())
+ return index(0, 0);
+
+ if (rowCount(idx) > 0) {
+ // node with children
+ return idx.child(0, 0);
+ }
+ // 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()) {
+ // we start from the beginning
+ if (wrapped)
+ *wrapped = true;
+ nextIndex = index(0, 0);
+ }
+ }
+ }
+ return nextIndex;
+}
+
+QModelIndex SearchResultTreeModel::next(const QModelIndex &idx, bool includeGenerated, bool *wrapped) const
+{
+ QModelIndex value = idx;
+ do {
+ value = nextIndex(value, wrapped);
+ } while (value != idx && !includeGenerated && treeItemAtIndex(value)->isGenerated());
+ return value;
+}
+
+QModelIndex SearchResultTreeModel::prevIndex(const QModelIndex &idx, bool *wrapped) const
+{
+ if (wrapped)
+ *wrapped = false;
+ QModelIndex current = idx;
+ bool checkForChildren = true;
+ if (current.isValid()) {
+ int row = current.row();
+ if (row > 0) {
+ current = index(row - 1, 0, current.parent());
+ } else {
+ current = current.parent();
+ checkForChildren = !current.isValid();
+ if (checkForChildren && wrapped) {
+ // we start from the end
+ *wrapped = true;
+ }
+ }
+ }
+ 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, bool *wrapped) const
+{
+ QModelIndex value = idx;
+ do {
+ value = prevIndex(value, wrapped);
+ } while (value != idx && !includeGenerated && treeItemAtIndex(value)->isGenerated());
+ return value;
+}
diff --git a/src/plugins/coreplugin/find/searchresulttreemodel.h b/src/plugins/coreplugin/find/searchresulttreemodel.h
new file mode 100644
index 0000000000..36ca6d94ac
--- /dev/null
+++ b/src/plugins/coreplugin/find/searchresulttreemodel.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef SEARCHRESULTTREEMODEL_H
+#define SEARCHRESULTTREEMODEL_H
+
+#include "searchresultwindow.h"
+#include "searchresultcolor.h"
+
+#include <QAbstractItemModel>
+#include <QFont>
+
+namespace Core {
+namespace Internal {
+
+class SearchResultTreeItem;
+
+class SearchResultTreeModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ SearchResultTreeModel(QObject *parent = 0);
+ ~SearchResultTreeModel();
+
+ void setShowReplaceUI(bool show);
+ void setTextEditorFont(const QFont &font, const SearchResultColor color);
+
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex &child) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ 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 includeGenerated = false, bool *wrapped = 0) const;
+ QModelIndex prev(const QModelIndex &idx, bool includeGenerated = false, bool *wrapped = 0) const;
+
+ QList<QModelIndex> addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
+
+signals:
+ void jumpToSearchResult(const QString &fileName, int lineNumber,
+ int searchTermStart, int searchTermLength);
+
+public slots:
+ void clear();
+
+private:
+ QModelIndex index(SearchResultTreeItem *item) const;
+ void addResultsToCurrentParent(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
+ QSet<SearchResultTreeItem *> addPath(const QStringList &path);
+ QVariant data(const SearchResultTreeItem *row, int role) const;
+ bool setCheckState(const QModelIndex &idx, Qt::CheckState checkState, bool firstCall = true);
+ QModelIndex nextIndex(const QModelIndex &idx, bool *wrapped = 0) const;
+ QModelIndex prevIndex(const QModelIndex &idx, bool *wrapped = 0) const;
+ SearchResultTreeItem *treeItemAtIndex(const QModelIndex &idx) const;
+
+ SearchResultTreeItem *m_rootItem;
+ SearchResultTreeItem *m_currentParent;
+ SearchResultColor m_color;
+ QModelIndex m_currentIndex;
+ QStringList m_currentPath; // the path that belongs to the current parent
+ QFont m_textEditorFont;
+ bool m_showReplaceUI;
+ bool m_editorFontIsUsed;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SEARCHRESULTTREEMODEL_H
diff --git a/src/plugins/coreplugin/find/searchresulttreeview.cpp b/src/plugins/coreplugin/find/searchresulttreeview.cpp
new file mode 100644
index 0000000000..d48efabd97
--- /dev/null
+++ b/src/plugins/coreplugin/find/searchresulttreeview.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "searchresulttreeview.h"
+#include "searchresulttreeitemroles.h"
+#include "searchresulttreemodel.h"
+#include "searchresulttreeitemdelegate.h"
+
+#include <QHeaderView>
+#include <QKeyEvent>
+
+namespace Core {
+namespace Internal {
+
+SearchResultTreeView::SearchResultTreeView(QWidget *parent)
+ : QTreeView(parent)
+ , m_model(new SearchResultTreeModel(this))
+ , m_autoExpandResults(false)
+{
+ setModel(m_model);
+ setItemDelegate(new SearchResultTreeItemDelegate(this));
+ setIndentation(14);
+ setUniformRowHeights(true);
+ setExpandsOnDoubleClick(true);
+ header()->hide();
+
+ connect(this, SIGNAL(activated(QModelIndex)), this, SLOT(emitJumpToSearchResult(QModelIndex)));
+}
+
+void SearchResultTreeView::setAutoExpandResults(bool expand)
+{
+ m_autoExpandResults = expand;
+}
+
+void SearchResultTreeView::setTextEditorFont(const QFont &font, const SearchResultColor color)
+{
+ m_model->setTextEditorFont(font, color);
+
+ QPalette p = palette();
+ p.setColor(QPalette::Base, color.textBackground);
+ setPalette(p);
+}
+
+void SearchResultTreeView::clear()
+{
+ m_model->clear();
+}
+
+void SearchResultTreeView::addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode)
+{
+ QList<QModelIndex> 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::IsGeneratedRole).toBool())
+ return;
+ SearchResultItem item = model()->data(index, ItemDataRoles::ResultItemRole).value<SearchResultItem>();
+
+ emit jumpToSearchResult(item);
+}
+
+void SearchResultTreeView::keyPressEvent(QKeyEvent *e)
+{
+ if (!e->modifiers() && e->key() == Qt::Key_Return) {
+ emit activated(currentIndex());
+ e->accept();
+ return;
+ }
+ QTreeView::keyPressEvent(e);
+}
+
+SearchResultTreeModel *SearchResultTreeView::model() const
+{
+ return m_model;
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/find/searchresulttreeview.h b/src/plugins/coreplugin/find/searchresulttreeview.h
new file mode 100644
index 0000000000..b5dcd792d3
--- /dev/null
+++ b/src/plugins/coreplugin/find/searchresulttreeview.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef SEARCHRESULTTREEVIEW_H
+#define SEARCHRESULTTREEVIEW_H
+
+#include "searchresultwindow.h"
+
+#include <QTreeView>
+
+namespace Core {
+namespace Internal {
+
+class SearchResultTreeModel;
+class SearchResultColor;
+
+class SearchResultTreeView : public QTreeView
+{
+ Q_OBJECT
+
+public:
+ explicit SearchResultTreeView(QWidget *parent = 0);
+
+ void setAutoExpandResults(bool expand);
+ void setTextEditorFont(const QFont &font, const SearchResultColor color);
+
+ SearchResultTreeModel *model() const;
+ void addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
+
+signals:
+ void jumpToSearchResult(const SearchResultItem &item);
+
+public slots:
+ void clear();
+ void emitJumpToSearchResult(const QModelIndex &index);
+
+protected:
+ void keyPressEvent(QKeyEvent *e);
+
+ SearchResultTreeModel *m_model;
+ bool m_autoExpandResults;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SEARCHRESULTTREEVIEW_H
diff --git a/src/plugins/coreplugin/find/searchresultwidget.cpp b/src/plugins/coreplugin/find/searchresultwidget.cpp
new file mode 100644
index 0000000000..6539cebdad
--- /dev/null
+++ b/src/plugins/coreplugin/find/searchresultwidget.cpp
@@ -0,0 +1,495 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "searchresultwidget.h"
+#include "searchresulttreeview.h"
+#include "searchresulttreemodel.h"
+#include "searchresulttreeitems.h"
+#include "searchresulttreeitemroles.h"
+
+#include "findplugin.h"
+#include "treeviewfind.h"
+
+#include <aggregation/aggregate.h>
+#include <coreplugin/coreplugin.h>
+
+#include <QDir>
+#include <QFrame>
+#include <QLabel>
+#include <QLineEdit>
+#include <QToolButton>
+#include <QCheckBox>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+
+static const int SEARCHRESULT_WARNING_LIMIT = 200000;
+static const char SIZE_WARNING_ID[] = "sizeWarningLabel";
+
+namespace Core {
+namespace Internal {
+
+class WideEnoughLineEdit : public QLineEdit {
+ Q_OBJECT
+public:
+ WideEnoughLineEdit(QWidget *parent):QLineEdit(parent){
+ connect(this, SIGNAL(textChanged(QString)),
+ this, SLOT(updateGeometry()));
+ }
+ ~WideEnoughLineEdit(){}
+ QSize sizeHint() const {
+ QSize sh = QLineEdit::minimumSizeHint();
+ sh.rwidth() += qMax(25 * fontMetrics().width(QLatin1Char('x')),
+ fontMetrics().width(text()));
+ return sh;
+ }
+public slots:
+ void updateGeometry() { QLineEdit::updateGeometry(); }
+};
+
+} // namespace Internal
+} // namespace Core
+
+using namespace Core;
+using namespace Core::Internal;
+
+SearchResultWidget::SearchResultWidget(QWidget *parent) :
+ QWidget(parent),
+ m_count(0),
+ m_isShowingReplaceUI(false),
+ m_searchAgainSupported(false)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ layout->setMargin(0);
+ layout->setSpacing(0);
+ setLayout(layout);
+
+ QFrame *topWidget = new QFrame;
+ QPalette pal = topWidget->palette();
+ pal.setColor(QPalette::Window, QColor(255, 255, 225));
+ pal.setColor(QPalette::WindowText, Qt::black);
+ topWidget->setPalette(pal);
+ topWidget->setFrameStyle(QFrame::Panel | QFrame::Raised);
+ topWidget->setLineWidth(1);
+ topWidget->setAutoFillBackground(true);
+ QHBoxLayout *topLayout = new QHBoxLayout(topWidget);
+ topLayout->setMargin(2);
+ topWidget->setLayout(topLayout);
+ layout->addWidget(topWidget);
+
+ m_messageWidget = new QFrame;
+ pal.setColor(QPalette::Window, QColor(255, 255, 225));
+ pal.setColor(QPalette::WindowText, Qt::red);
+ m_messageWidget->setPalette(pal);
+ m_messageWidget->setFrameStyle(QFrame::Panel | QFrame::Raised);
+ m_messageWidget->setLineWidth(1);
+ m_messageWidget->setAutoFillBackground(true);
+ QHBoxLayout *messageLayout = new QHBoxLayout(m_messageWidget);
+ messageLayout->setMargin(2);
+ m_messageWidget->setLayout(messageLayout);
+ QLabel *messageLabel = new QLabel(tr("Search was canceled."));
+ messageLabel->setPalette(pal);
+ messageLayout->addWidget(messageLabel);
+ layout->addWidget(m_messageWidget);
+ m_messageWidget->setVisible(false);
+
+ m_searchResultTreeView = new Internal::SearchResultTreeView(this);
+ m_searchResultTreeView->setFrameStyle(QFrame::NoFrame);
+ m_searchResultTreeView->setAttribute(Qt::WA_MacShowFocusRect, false);
+ Aggregation::Aggregate * agg = new Aggregation::Aggregate;
+ agg->add(m_searchResultTreeView);
+ agg->add(new TreeViewFind(m_searchResultTreeView,
+ ItemDataRoles::ResultLineRole));
+ layout->addWidget(m_searchResultTreeView);
+
+ m_infoBarDisplay.setTarget(layout, 2);
+ m_infoBarDisplay.setInfoBar(&m_infoBar);
+
+ m_descriptionContainer = new QWidget(topWidget);
+ QHBoxLayout *descriptionLayout = new QHBoxLayout(m_descriptionContainer);
+ m_descriptionContainer->setLayout(descriptionLayout);
+ descriptionLayout->setMargin(0);
+ m_descriptionContainer->setMinimumWidth(200);
+ m_descriptionContainer->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ m_label = new QLabel(m_descriptionContainer);
+ m_label->setVisible(false);
+ m_searchTerm = new QLabel(m_descriptionContainer);
+ m_searchTerm->setVisible(false);
+ descriptionLayout->addWidget(m_label);
+ descriptionLayout->addWidget(m_searchTerm);
+ m_cancelButton = new QToolButton(topWidget);
+ m_cancelButton->setText(tr("Cancel"));
+ m_cancelButton->setToolButtonStyle(Qt::ToolButtonTextOnly);
+ connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(cancel()));
+ m_searchAgainButton = new QToolButton(topWidget);
+ m_searchAgainButton->setToolTip(tr("Repeat the search with same parameters"));
+ m_searchAgainButton->setText(tr("Search again"));
+ m_searchAgainButton->setToolButtonStyle(Qt::ToolButtonTextOnly);
+ m_searchAgainButton->setVisible(false);
+ connect(m_searchAgainButton, SIGNAL(clicked()), this, SLOT(searchAgain()));
+
+ m_replaceLabel = new QLabel(tr("Replace with:"), topWidget);
+ m_replaceTextEdit = new WideEnoughLineEdit(topWidget);
+ m_replaceTextEdit->setMinimumWidth(120);
+ m_replaceTextEdit->setEnabled(false);
+ m_replaceTextEdit->setTabOrder(m_replaceTextEdit, m_searchResultTreeView);
+ m_replaceButton = new QToolButton(topWidget);
+ m_replaceButton->setToolTip(tr("Replace all occurrences"));
+ m_replaceButton->setText(tr("Replace"));
+ m_replaceButton->setToolButtonStyle(Qt::ToolButtonTextOnly);
+ m_replaceButton->setEnabled(false);
+ m_preserveCaseCheck = new QCheckBox(topWidget);
+ m_preserveCaseCheck->setText(tr("Preserve case"));
+ m_preserveCaseCheck->setEnabled(false);
+
+ if (FindPlugin * plugin = FindPlugin::instance()) {
+ m_preserveCaseCheck->setChecked(plugin->hasFindFlag(FindPreserveCase));
+ connect(m_preserveCaseCheck, SIGNAL(clicked(bool)), plugin, SLOT(setPreserveCase(bool)));
+ }
+
+ m_matchesFoundLabel = new QLabel(topWidget);
+ updateMatchesFoundLabel();
+
+ topLayout->addWidget(m_descriptionContainer);
+ topLayout->addWidget(m_cancelButton);
+ topLayout->addWidget(m_searchAgainButton);
+ topLayout->addWidget(m_replaceLabel);
+ topLayout->addWidget(m_replaceTextEdit);
+ topLayout->addWidget(m_replaceButton);
+ topLayout->addWidget(m_preserveCaseCheck);
+ topLayout->addStretch(2);
+ topLayout->addWidget(m_matchesFoundLabel);
+ topWidget->setMinimumHeight(m_cancelButton->sizeHint().height()
+ + topLayout->contentsMargins().top() + topLayout->contentsMargins().bottom()
+ + topWidget->lineWidth());
+ setShowReplaceUI(false);
+
+ connect(m_searchResultTreeView, SIGNAL(jumpToSearchResult(SearchResultItem)),
+ this, SLOT(handleJumpToSearchResult(SearchResultItem)));
+ connect(m_replaceTextEdit, SIGNAL(returnPressed()), this, SLOT(handleReplaceButton()));
+ connect(m_replaceButton, SIGNAL(clicked()), this, SLOT(handleReplaceButton()));
+}
+
+SearchResultWidget::~SearchResultWidget()
+{
+ if (m_infoBar.containsInfo(Core::Id(SIZE_WARNING_ID)))
+ cancelAfterSizeWarning();
+}
+
+void SearchResultWidget::setInfo(const QString &label, const QString &toolTip, const QString &term)
+{
+ m_label->setText(label);
+ m_label->setVisible(!label.isEmpty());
+ m_descriptionContainer->setToolTip(toolTip);
+ m_searchTerm->setText(term);
+ m_searchTerm->setVisible(!term.isEmpty());
+}
+
+void SearchResultWidget::addResult(const QString &fileName, int lineNumber, const QString &rowText,
+ int searchTermStart, int searchTermLength, const QVariant &userData)
+{
+ SearchResultItem item;
+ item.path = QStringList() << QDir::toNativeSeparators(fileName);
+ item.lineNumber = lineNumber;
+ item.text = rowText;
+ item.textMarkPos = searchTermStart;
+ item.textMarkLength = searchTermLength;
+ item.useTextEditorFont = true;
+ item.userData = userData;
+ addResults(QList<SearchResultItem>() << item, SearchResult::AddOrdered);
+}
+
+void SearchResultWidget::addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode)
+{
+ bool firstItems = (m_count == 0);
+ m_count += items.size();
+ m_searchResultTreeView->addResults(items, mode);
+ updateMatchesFoundLabel();
+ if (firstItems) {
+ if (!m_dontAskAgainGroup.isEmpty()) {
+ Core::Id undoWarningId = Core::Id("warninglabel/").withSuffix(m_dontAskAgainGroup);
+ if (m_infoBar.canInfoBeAdded(undoWarningId)) {
+ Core::InfoBarEntry info(undoWarningId, tr("This change cannot be undone."),
+ Core::InfoBarEntry::GlobalSuppressionEnabled);
+ m_infoBar.addInfo(info);
+ }
+ }
+
+ m_replaceTextEdit->setEnabled(true);
+ // We didn't have an item before, set the focus to the search widget or replace text edit
+ if (m_isShowingReplaceUI) {
+ m_replaceTextEdit->setFocus();
+ m_replaceTextEdit->selectAll();
+ } else {
+ m_searchResultTreeView->setFocus();
+ }
+ m_searchResultTreeView->selectionModel()->select(m_searchResultTreeView->model()->index(0, 0, QModelIndex()), QItemSelectionModel::Select);
+ emit navigateStateChanged();
+ } else if (m_count <= SEARCHRESULT_WARNING_LIMIT) {
+ return;
+ } else {
+ Core::Id sizeWarningId(SIZE_WARNING_ID);
+ if (!m_infoBar.canInfoBeAdded(sizeWarningId))
+ return;
+ emit paused(true);
+ Core::InfoBarEntry info(sizeWarningId,
+ tr("The search resulted in more than %n items, do you still want to continue?",
+ 0, SEARCHRESULT_WARNING_LIMIT));
+ info.setCancelButtonInfo(tr("Cancel"), this, SLOT(cancelAfterSizeWarning()));
+ info.setCustomButtonInfo(tr("Continue"), this, SLOT(continueAfterSizeWarning()));
+ m_infoBar.addInfo(info);
+ emit requestPopup(false/*no focus*/);
+ }
+}
+
+
+
+int SearchResultWidget::count() const
+{
+ return m_count;
+}
+
+QString SearchResultWidget::dontAskAgainGroup() const
+{
+ return m_dontAskAgainGroup;
+}
+
+void SearchResultWidget::setDontAskAgainGroup(const QString &group)
+{
+ m_dontAskAgainGroup = group;
+}
+
+
+void SearchResultWidget::setTextToReplace(const QString &textToReplace)
+{
+ m_replaceTextEdit->setText(textToReplace);
+}
+
+QString SearchResultWidget::textToReplace() const
+{
+ return m_replaceTextEdit->text();
+}
+
+void SearchResultWidget::setShowReplaceUI(bool visible)
+{
+ m_searchResultTreeView->model()->setShowReplaceUI(visible);
+ m_replaceLabel->setVisible(visible);
+ m_replaceTextEdit->setVisible(visible);
+ m_replaceButton->setVisible(visible);
+ m_preserveCaseCheck->setVisible(visible);
+ m_isShowingReplaceUI = visible;
+}
+
+bool SearchResultWidget::hasFocusInternally() const
+{
+ return m_searchResultTreeView->hasFocus() || (m_isShowingReplaceUI && m_replaceTextEdit->hasFocus());
+}
+
+void SearchResultWidget::setFocusInternally()
+{
+ if (m_count > 0) {
+ if (m_isShowingReplaceUI) {
+ if (!focusWidget() || focusWidget() == m_replaceTextEdit) {
+ m_replaceTextEdit->setFocus();
+ m_replaceTextEdit->selectAll();
+ } else {
+ m_searchResultTreeView->setFocus();
+ }
+ } else {
+ m_searchResultTreeView->setFocus();
+ }
+ }
+}
+
+bool SearchResultWidget::canFocusInternally() const
+{
+ return m_count > 0;
+}
+
+void SearchResultWidget::notifyVisibilityChanged(bool visible)
+{
+ emit visibilityChanged(visible);
+}
+
+void SearchResultWidget::setTextEditorFont(const QFont &font, const SearchResultColor color)
+{
+ m_searchResultTreeView->setTextEditorFont(font, color);
+}
+
+void SearchResultWidget::setAutoExpandResults(bool expand)
+{
+ m_searchResultTreeView->setAutoExpandResults(expand);
+}
+
+void SearchResultWidget::expandAll()
+{
+ m_searchResultTreeView->expandAll();
+}
+
+void SearchResultWidget::collapseAll()
+{
+ m_searchResultTreeView->collapseAll();
+}
+
+void SearchResultWidget::goToNext()
+{
+ if (m_count == 0)
+ return;
+ QModelIndex idx = m_searchResultTreeView->model()->next(m_searchResultTreeView->currentIndex());
+ if (idx.isValid()) {
+ m_searchResultTreeView->setCurrentIndex(idx);
+ m_searchResultTreeView->emitJumpToSearchResult(idx);
+ }
+}
+
+void SearchResultWidget::goToPrevious()
+{
+ if (!m_searchResultTreeView->model()->rowCount())
+ return;
+ QModelIndex idx = m_searchResultTreeView->model()->prev(m_searchResultTreeView->currentIndex());
+ if (idx.isValid()) {
+ m_searchResultTreeView->setCurrentIndex(idx);
+ m_searchResultTreeView->emitJumpToSearchResult(idx);
+ }
+}
+
+void SearchResultWidget::restart()
+{
+ m_replaceTextEdit->setEnabled(false);
+ m_replaceButton->setEnabled(false);
+ m_searchResultTreeView->clear();
+ m_count = 0;
+ Core::Id sizeWarningId(SIZE_WARNING_ID);
+ m_infoBar.removeInfo(sizeWarningId);
+ m_infoBar.enableInfo(sizeWarningId);
+ m_cancelButton->setVisible(true);
+ m_searchAgainButton->setVisible(false);
+ m_messageWidget->setVisible(false);
+ updateMatchesFoundLabel();
+ emit restarted();
+}
+
+void SearchResultWidget::setSearchAgainSupported(bool supported)
+{
+ m_searchAgainSupported = supported;
+ m_searchAgainButton->setVisible(supported && !m_cancelButton->isVisible());
+}
+
+void SearchResultWidget::setSearchAgainEnabled(bool enabled)
+{
+ m_searchAgainButton->setEnabled(enabled);
+}
+
+void SearchResultWidget::finishSearch(bool canceled)
+{
+ Core::Id sizeWarningId(SIZE_WARNING_ID);
+ m_infoBar.removeInfo(sizeWarningId);
+ m_infoBar.enableInfo(sizeWarningId);
+ m_replaceTextEdit->setEnabled(m_count > 0);
+ m_replaceButton->setEnabled(m_count > 0);
+ m_preserveCaseCheck->setEnabled(m_count > 0);
+ m_cancelButton->setVisible(false);
+ m_messageWidget->setVisible(canceled);
+ m_searchAgainButton->setVisible(m_searchAgainSupported);
+}
+
+void SearchResultWidget::sendRequestPopup()
+{
+ emit requestPopup(true/*focus*/);
+}
+
+void SearchResultWidget::continueAfterSizeWarning()
+{
+ m_infoBar.suppressInfo(Core::Id(SIZE_WARNING_ID));
+ emit paused(false);
+}
+
+void SearchResultWidget::cancelAfterSizeWarning()
+{
+ m_infoBar.suppressInfo(Core::Id(SIZE_WARNING_ID));
+ emit cancelled();
+ emit paused(false);
+}
+
+void SearchResultWidget::handleJumpToSearchResult(const SearchResultItem &item)
+{
+ emit activated(item);
+}
+
+void SearchResultWidget::handleReplaceButton()
+{
+ // check if button is actually enabled, because this is also triggered
+ // by pressing return in replace line edit
+ if (m_replaceButton->isEnabled()) {
+ m_infoBar.clear();
+ emit replaceButtonClicked(m_replaceTextEdit->text(), checkedItems(), m_preserveCaseCheck->isChecked());
+ }
+}
+
+void SearchResultWidget::cancel()
+{
+ m_cancelButton->setVisible(false);
+ if (m_infoBar.containsInfo(Core::Id(SIZE_WARNING_ID)))
+ cancelAfterSizeWarning();
+ else
+ emit cancelled();
+}
+
+void SearchResultWidget::searchAgain()
+{
+ emit searchAgainRequested();
+}
+
+QList<SearchResultItem> SearchResultWidget::checkedItems() const
+{
+ QList<SearchResultItem> result;
+ Internal::SearchResultTreeModel *model = m_searchResultTreeView->model();
+ const int fileCount = model->rowCount(QModelIndex());
+ for (int i = 0; i < fileCount; ++i) {
+ QModelIndex fileIndex = model->index(i, 0, QModelIndex());
+ Internal::SearchResultTreeItem *fileItem = static_cast<Internal::SearchResultTreeItem *>(fileIndex.internalPointer());
+ Q_ASSERT(fileItem != 0);
+ for (int rowIndex = 0; rowIndex < fileItem->childrenCount(); ++rowIndex) {
+ QModelIndex textIndex = model->index(rowIndex, 0, fileIndex);
+ Internal::SearchResultTreeItem *rowItem = static_cast<Internal::SearchResultTreeItem *>(textIndex.internalPointer());
+ if (rowItem->checkState())
+ result << rowItem->item;
+ }
+ }
+ return result;
+}
+
+void SearchResultWidget::updateMatchesFoundLabel()
+{
+ if (m_count == 0)
+ m_matchesFoundLabel->setText(tr("No matches found."));
+ else
+ m_matchesFoundLabel->setText(tr("%n matches found.", 0, m_count));
+}
+
+#include "searchresultwidget.moc"
diff --git a/src/plugins/coreplugin/find/searchresultwidget.h b/src/plugins/coreplugin/find/searchresultwidget.h
new file mode 100644
index 0000000000..e9f64b4164
--- /dev/null
+++ b/src/plugins/coreplugin/find/searchresultwidget.h
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef SEARCHRESULTWIDGET_H
+#define SEARCHRESULTWIDGET_H
+
+#include "searchresultwindow.h"
+
+#include <coreplugin/infobar.h>
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QFrame;
+class QLabel;
+class QLineEdit;
+class QToolButton;
+class QCheckBox;
+QT_END_NAMESPACE
+
+namespace Core {
+namespace Internal {
+
+class SearchResultTreeView;
+class SearchResultColor;
+
+class SearchResultWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit SearchResultWidget(QWidget *parent = 0);
+ ~SearchResultWidget();
+
+ void setInfo(const QString &label, const QString &toolTip, const QString &term);
+
+ void addResult(const QString &fileName, int lineNumber, const QString &lineText,
+ int searchTermStart, int searchTermLength, const QVariant &userData = QVariant());
+ void addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
+
+ int count() const;
+
+ QString dontAskAgainGroup() const;
+ void setDontAskAgainGroup(const QString &group);
+
+ void setTextToReplace(const QString &textToReplace);
+ QString textToReplace() const;
+ void setShowReplaceUI(bool visible);
+
+ bool hasFocusInternally() const;
+ void setFocusInternally();
+ bool canFocusInternally() const;
+
+ void notifyVisibilityChanged(bool visible);
+
+ void setTextEditorFont(const QFont &font, const SearchResultColor color);
+
+ void setAutoExpandResults(bool expand);
+ void expandAll();
+ void collapseAll();
+
+ void goToNext();
+ void goToPrevious();
+
+ void restart();
+
+ void setSearchAgainSupported(bool supported);
+ void setSearchAgainEnabled(bool enabled);
+
+public slots:
+ void finishSearch(bool canceled);
+ void sendRequestPopup();
+
+signals:
+ void activated(const Core::SearchResultItem &item);
+ void replaceButtonClicked(const QString &replaceText, const QList<Core::SearchResultItem> &checkedItems, bool preserveCase);
+ void searchAgainRequested();
+ void cancelled();
+ void paused(bool paused);
+ void restarted();
+ void visibilityChanged(bool visible);
+ void requestPopup(bool focus);
+
+ void navigateStateChanged();
+
+private slots:
+ void continueAfterSizeWarning();
+ void cancelAfterSizeWarning();
+ void handleJumpToSearchResult(const SearchResultItem &item);
+ void handleReplaceButton();
+ void cancel();
+ void searchAgain();
+
+private:
+ QList<SearchResultItem> checkedItems() const;
+ void updateMatchesFoundLabel();
+
+ SearchResultTreeView *m_searchResultTreeView;
+ int m_count;
+ QString m_dontAskAgainGroup;
+ QFrame *m_messageWidget;
+ Core::InfoBar m_infoBar;
+ Core::InfoBarDisplay m_infoBarDisplay;
+ bool m_isShowingReplaceUI;
+ QLabel *m_replaceLabel;
+ QLineEdit *m_replaceTextEdit;
+ QToolButton *m_replaceButton;
+ QToolButton *m_searchAgainButton;
+ QCheckBox *m_preserveCaseCheck;
+ bool m_searchAgainSupported;
+ QWidget *m_descriptionContainer;
+ QLabel *m_label;
+ QLabel *m_searchTerm;
+ QToolButton *m_cancelButton;
+ QLabel *m_matchesFoundLabel;
+};
+
+} // Internal
+} // Find
+
+#endif // SEARCHRESULTWIDGET_H
diff --git a/src/plugins/coreplugin/find/searchresultwindow.cpp b/src/plugins/coreplugin/find/searchresultwindow.cpp
new file mode 100644
index 0000000000..dd4f87475f
--- /dev/null
+++ b/src/plugins/coreplugin/find/searchresultwindow.cpp
@@ -0,0 +1,715 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "searchresultwindow.h"
+#include "searchresultwidget.h"
+#include "searchresultcolor.h"
+
+#include <coreplugin/icore.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/icontext.h>
+#include <utils/qtcassert.h>
+
+#include <QSettings>
+#include <QDebug>
+#include <QFont>
+#include <QAction>
+#include <QToolButton>
+#include <QComboBox>
+#include <QScrollArea>
+#include <QStackedWidget>
+
+static const char SETTINGSKEYSECTIONNAME[] = "SearchResults";
+static const char SETTINGSKEYEXPANDRESULTS[] = "ExpandResults";
+static const int MAX_SEARCH_HISTORY = 12;
+
+namespace Core {
+
+namespace Internal {
+
+ class InternalScrollArea : public QScrollArea
+ {
+ Q_OBJECT
+ public:
+ explicit InternalScrollArea(QWidget *parent)
+ : QScrollArea(parent)
+ {
+ setFrameStyle(QFrame::NoFrame);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ }
+
+ QSize sizeHint() const
+ {
+ if (widget())
+ return widget()->size();
+ return QScrollArea::sizeHint();
+ }
+ };
+
+ class SearchResultWindowPrivate : public QObject
+ {
+ Q_OBJECT
+ public:
+ SearchResultWindowPrivate(SearchResultWindow *window);
+ bool isSearchVisible() const;
+ int visibleSearchIndex() const;
+ void setCurrentIndex(int index, bool focus);
+
+ FindPlugin *m_plugin;
+ SearchResultWindow *q;
+ QList<Internal::SearchResultWidget *> m_searchResultWidgets;
+ QToolButton *m_expandCollapseButton;
+ QAction *m_expandCollapseAction;
+ static const bool m_initiallyExpand = false;
+ QWidget *m_spacer;
+ QComboBox *m_recentSearchesBox;
+ QStackedWidget *m_widget;
+ QList<SearchResult *> m_searchResults;
+ int m_currentIndex;
+ QFont m_font;
+ SearchResultColor m_color;
+
+ public slots:
+ void setCurrentIndex(int index);
+ void moveWidgetToTop();
+ void popupRequested(bool focus);
+ };
+
+ SearchResultWindowPrivate::SearchResultWindowPrivate(SearchResultWindow *window)
+ : q(window)
+ {
+ }
+
+ bool SearchResultWindowPrivate::isSearchVisible() const
+ {
+ return m_currentIndex > 0;
+ }
+
+ int SearchResultWindowPrivate::visibleSearchIndex() const
+ {
+ return m_currentIndex - 1;
+ }
+
+ void SearchResultWindowPrivate::setCurrentIndex(int index, bool focus)
+ {
+ if (isSearchVisible())
+ m_searchResultWidgets.at(visibleSearchIndex())->notifyVisibilityChanged(false);
+ m_currentIndex = index;
+ m_widget->setCurrentIndex(index);
+ m_recentSearchesBox->setCurrentIndex(index);
+ if (!isSearchVisible()) {
+ if (focus)
+ m_widget->currentWidget()->setFocus();
+ m_expandCollapseButton->setEnabled(false);
+ } else {
+ if (focus)
+ m_searchResultWidgets.at(visibleSearchIndex())->setFocusInternally();
+ m_searchResultWidgets.at(visibleSearchIndex())->notifyVisibilityChanged(true);
+ m_expandCollapseButton->setEnabled(true);
+ }
+ q->navigateStateChanged();
+ }
+
+ void SearchResultWindowPrivate::setCurrentIndex(int index)
+ {
+ setCurrentIndex(index, true/*focus*/);
+ }
+
+ void SearchResultWindowPrivate::moveWidgetToTop()
+ {
+ SearchResultWidget *widget = qobject_cast<SearchResultWidget *>(sender());
+ QTC_ASSERT(widget, return);
+ int index = m_searchResultWidgets.indexOf(widget);
+ if (index == 0)
+ return; // nothing to do
+ int internalIndex = index + 1/*account for "new search" entry*/;
+ QString searchEntry = m_recentSearchesBox->itemText(internalIndex);
+
+ m_searchResultWidgets.removeAt(index);
+ m_widget->removeWidget(widget);
+ m_recentSearchesBox->removeItem(internalIndex);
+ SearchResult *result = m_searchResults.takeAt(index);
+
+ m_searchResultWidgets.prepend(widget);
+ m_widget->insertWidget(1, widget);
+ m_recentSearchesBox->insertItem(1, searchEntry);
+ m_searchResults.prepend(result);
+
+ // adapt the current index
+ if (index == visibleSearchIndex()) {
+ // was visible, so we switch
+ // this is the default case
+ m_currentIndex = 1;
+ m_widget->setCurrentIndex(1);
+ m_recentSearchesBox->setCurrentIndex(1);
+ } else if (visibleSearchIndex() < index) {
+ // academical case where the widget moved before the current widget
+ // only our internal book keeping needed
+ ++m_currentIndex;
+ }
+ }
+
+ void SearchResultWindowPrivate::popupRequested(bool focus)
+ {
+ SearchResultWidget *widget = qobject_cast<SearchResultWidget *>(sender());
+ QTC_ASSERT(widget, return);
+ int internalIndex = m_searchResultWidgets.indexOf(widget) + 1/*account for "new search" entry*/;
+ setCurrentIndex(internalIndex, focus);
+ q->popup(focus ? Core::IOutputPane::ModeSwitch | Core::IOutputPane::WithFocus
+ : Core::IOutputPane::NoModeSwitch);
+ }
+}
+
+using namespace Core::Internal;
+
+/*!
+ \enum Core::SearchResultWindow::SearchMode
+ This enum type specifies whether a search should show the replace UI or not:
+
+ \value SearchOnly
+ The search does not support replace.
+ \value SearchAndReplace
+ The search supports replace, so show the UI for it.
+*/
+
+/*!
+ \class Core::SearchResult
+ \brief The SearchResult class reports user interaction, such as the
+ activation of a search result item.
+
+ Whenever a new search is initiated via startNewSearch, an instance of this
+ class is returned to provide the initiator with the hooks for handling user
+ interaction.
+*/
+
+/*!
+ \fn void SearchResult::activated(const Core::SearchResultItem &item)
+ Indicates that the user activated the search result \a item by
+ double-clicking it, for example.
+*/
+
+/*!
+ \fn void SearchResult::replaceButtonClicked(const QString &replaceText, const QList<Core::SearchResultItem> &checkedItems, bool preserveCase)
+ Indicates that the user initiated a text replace by selecting
+ \gui {Replace All}, for example.
+
+ The signal reports the text to use for replacement in \a replaceText,
+ and the list of search result items that were selected by the user
+ in \a checkedItems.
+ The handler of this signal should apply the replace only on the selected
+ items.
+*/
+
+/*!
+ \class Core::SearchResultWindow
+ \brief The SearchResultWindow class is the implementation of a commonly
+ shared \gui{Search Results} output pane. Use it to show search results
+ to a user.
+
+ Whenever you want to show the user a list of search results, or want
+ to present UI for a global search and replace, use the single instance
+ of this class.
+
+ Except for being an implementation of a output pane, the
+ SearchResultWindow has a few functions and one enum that allows other
+ plugins to show their search results and hook into the user actions for
+ selecting an entry and performing a global replace.
+
+ Whenever you start a search, call startNewSearch(SearchMode) to initialize
+ the \gui {Search Results} output pane. The parameter determines if the GUI for
+ replacing should be shown.
+ The function returns a SearchResult object that is your
+ hook into the signals from user interaction for this search.
+ When you produce search results, call addResults or addResult to add them
+ to the \gui {Search Results} output pane.
+ After the search has finished call finishSearch to inform the
+ \gui {Search Results} output pane about it.
+
+ You will get activated signals via your SearchResult instance when
+ the user selects a search result item, and, if you started the search
+ with the SearchAndReplace option, the replaceButtonClicked signal
+ when the user requests a replace.
+*/
+
+/*!
+ \fn QString SearchResultWindow::displayName() const
+ \internal
+*/
+
+SearchResultWindow *SearchResultWindow::m_instance = 0;
+
+/*!
+ \internal
+*/
+SearchResultWindow::SearchResultWindow(QWidget *newSearchPanel)
+ : d(new SearchResultWindowPrivate(this))
+{
+ m_instance = this;
+
+ d->m_spacer = new QWidget;
+ d->m_spacer->setMinimumWidth(30);
+ d->m_recentSearchesBox = new QComboBox;
+ d->m_recentSearchesBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ d->m_recentSearchesBox->addItem(tr("New Search"));
+ connect(d->m_recentSearchesBox, SIGNAL(activated(int)), d, SLOT(setCurrentIndex(int)));
+
+ d->m_widget = new QStackedWidget;
+ d->m_widget->setWindowTitle(displayName());
+
+ InternalScrollArea *newSearchArea = new InternalScrollArea(d->m_widget);
+ newSearchArea->setWidget(newSearchPanel);
+ newSearchArea->setFocusProxy(newSearchPanel);
+ d->m_widget->addWidget(newSearchArea);
+ d->m_currentIndex = 0;
+
+ d->m_expandCollapseButton = new QToolButton(d->m_widget);
+ d->m_expandCollapseButton->setAutoRaise(true);
+
+ d->m_expandCollapseAction = new QAction(tr("Expand All"), this);
+ d->m_expandCollapseAction->setCheckable(true);
+ d->m_expandCollapseAction->setIcon(QIcon(QLatin1String(":/find/images/expand.png")));
+ Core::Command *cmd = Core::ActionManager::registerAction(
+ d->m_expandCollapseAction, "Find.ExpandAll",
+ Core::Context(Core::Constants::C_GLOBAL));
+ cmd->setAttribute(Core::Command::CA_UpdateText);
+ d->m_expandCollapseButton->setDefaultAction(cmd->action());
+
+ connect(d->m_expandCollapseAction, SIGNAL(toggled(bool)), this, SLOT(handleExpandCollapseToolButton(bool)));
+ readSettings();
+}
+
+/*!
+ \internal
+*/
+SearchResultWindow::~SearchResultWindow()
+{
+ qDeleteAll(d->m_searchResults);
+ delete d->m_widget;
+ d->m_widget = 0;
+ delete d;
+}
+
+/*!
+ Returns the single shared instance of the \gui {Search Results} output pane.
+*/
+SearchResultWindow *SearchResultWindow::instance()
+{
+ return m_instance;
+}
+
+/*!
+ \internal
+*/
+void SearchResultWindow::visibilityChanged(bool visible)
+{
+ if (d->isSearchVisible())
+ d->m_searchResultWidgets.at(d->visibleSearchIndex())->notifyVisibilityChanged(visible);
+}
+
+/*!
+ \internal
+*/
+QWidget *SearchResultWindow::outputWidget(QWidget *)
+{
+ return d->m_widget;
+}
+
+/*!
+ \internal
+*/
+QList<QWidget*> SearchResultWindow::toolBarWidgets() const
+{
+ return QList<QWidget*>() << d->m_expandCollapseButton << d->m_spacer << d->m_recentSearchesBox;
+}
+
+/*!
+ Tells the \gui {Search Results} output pane to start a new search.
+
+ The \a label should be a string that shortly describes the type of the
+ search, that is, the search filter and possibly the most relevant search
+ option, followed by a colon ':'. For example: \c {Project 'myproject':}
+ The \a searchTerm is shown after the colon.
+ The \a toolTip should elaborate on the search parameters, like file patterns that are searched and
+ find flags.
+ If \a cfgGroup is not empty, it will be used for storing the "do not ask again"
+ setting of a "this change cannot be undone" warning (which is implicitly requested
+ by passing a non-empty group).
+ Returns a SearchResult object that is used for signaling user interaction
+ with the results of this search.
+ The search result window owns the returned SearchResult
+ and might delete it any time, even while the search is running
+ (for example, when the user clears the \gui {Search Results} pane, or when
+ the user opens so many other searches
+ that this search falls out of the history).
+
+*/
+SearchResult *SearchResultWindow::startNewSearch(const QString &label,
+ const QString &toolTip,
+ const QString &searchTerm,
+ SearchMode searchOrSearchAndReplace,
+ const QString &cfgGroup)
+{
+ if (d->m_searchResults.size() >= MAX_SEARCH_HISTORY) {
+ d->m_searchResultWidgets.last()->notifyVisibilityChanged(false);
+ // widget first, because that might send interesting signals to SearchResult
+ delete d->m_searchResultWidgets.takeLast();
+ delete d->m_searchResults.takeLast();
+ d->m_recentSearchesBox->removeItem(d->m_recentSearchesBox->count()-1);
+ if (d->m_currentIndex >= d->m_recentSearchesBox->count()) {
+ // temporarily set the index to the last existing
+ d->m_currentIndex = d->m_recentSearchesBox->count() - 1;
+ }
+ }
+ Internal::SearchResultWidget *widget = new Internal::SearchResultWidget;
+ d->m_searchResultWidgets.prepend(widget);
+ d->m_widget->insertWidget(1, widget);
+ connect(widget, SIGNAL(navigateStateChanged()), this, SLOT(navigateStateChanged()));
+ connect(widget, SIGNAL(restarted()), d, SLOT(moveWidgetToTop()));
+ connect(widget, SIGNAL(requestPopup(bool)), d, SLOT(popupRequested(bool)));
+ widget->setTextEditorFont(d->m_font, d->m_color);
+ widget->setShowReplaceUI(searchOrSearchAndReplace != SearchOnly);
+ widget->setAutoExpandResults(d->m_expandCollapseAction->isChecked());
+ widget->setInfo(label, toolTip, searchTerm);
+ if (searchOrSearchAndReplace == SearchAndReplace)
+ widget->setDontAskAgainGroup(cfgGroup);
+ SearchResult *result = new SearchResult(widget);
+ d->m_searchResults.prepend(result);
+ d->m_recentSearchesBox->insertItem(1, tr("%1 %2").arg(label, searchTerm));
+ if (d->m_currentIndex > 0)
+ ++d->m_currentIndex; // so setCurrentIndex still knows about the right "currentIndex" and its widget
+ d->setCurrentIndex(1);
+ return result;
+}
+
+/*!
+ Clears the current contents of the \gui {Search Results} output pane.
+*/
+void SearchResultWindow::clearContents()
+{
+ for (int i = d->m_recentSearchesBox->count() - 1; i > 0 /* don't want i==0 */; --i)
+ d->m_recentSearchesBox->removeItem(i);
+ foreach (Internal::SearchResultWidget *widget, d->m_searchResultWidgets)
+ widget->notifyVisibilityChanged(false);
+ qDeleteAll(d->m_searchResultWidgets);
+ d->m_searchResultWidgets.clear();
+ qDeleteAll(d->m_searchResults);
+ d->m_searchResults.clear();
+
+ d->m_currentIndex = 0;
+ d->m_widget->currentWidget()->setFocus();
+ d->m_expandCollapseButton->setEnabled(false);
+ navigateStateChanged();
+}
+
+/*!
+ \internal
+*/
+bool SearchResultWindow::hasFocus() const
+{
+ return d->m_widget->focusWidget() && d->m_widget->focusWidget()->hasFocus();
+}
+
+/*!
+ \internal
+*/
+bool SearchResultWindow::canFocus() const
+{
+ if (d->isSearchVisible())
+ return d->m_searchResultWidgets.at(d->visibleSearchIndex())->canFocusInternally();
+ return true;
+}
+
+/*!
+ \internal
+*/
+void SearchResultWindow::setFocus()
+{
+ if (!d->isSearchVisible())
+ d->m_widget->currentWidget()->setFocus();
+ else
+ d->m_searchResultWidgets.at(d->visibleSearchIndex())->setFocusInternally();
+}
+
+/*!
+ \internal
+*/
+void SearchResultWindow::setTextEditorFont(const QFont &font,
+ const QColor &textForegroundColor,
+ const QColor &textBackgroundColor,
+ const QColor &highlightForegroundColor,
+ const QColor &highlightBackgroundColor)
+{
+ d->m_font = font;
+ Internal::SearchResultColor color;
+ color.textBackground = textBackgroundColor;
+ color.textForeground = textForegroundColor;
+ color.highlightBackground = highlightBackgroundColor.isValid()
+ ? highlightBackgroundColor
+ : textBackgroundColor;
+ color.highlightForeground = highlightForegroundColor.isValid()
+ ? highlightForegroundColor
+ : textForegroundColor;
+ d->m_color = color;
+ foreach (Internal::SearchResultWidget *widget, d->m_searchResultWidgets)
+ widget->setTextEditorFont(font, color);
+}
+
+void SearchResultWindow::openNewSearchPanel()
+{
+ d->setCurrentIndex(0);
+ popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus | IOutputPane::EnsureSizeHint);
+}
+
+/*!
+ \internal
+*/
+void SearchResultWindow::handleExpandCollapseToolButton(bool checked)
+{
+ if (!d->isSearchVisible())
+ return;
+ d->m_searchResultWidgets.at(d->visibleSearchIndex())->setAutoExpandResults(checked);
+ if (checked) {
+ d->m_expandCollapseAction->setText(tr("Collapse All"));
+ d->m_searchResultWidgets.at(d->visibleSearchIndex())->expandAll();
+ } else {
+ d->m_expandCollapseAction->setText(tr("Expand All"));
+ d->m_searchResultWidgets.at(d->visibleSearchIndex())->collapseAll();
+ }
+}
+
+/*!
+ \internal
+*/
+void SearchResultWindow::readSettings()
+{
+ QSettings *s = Core::ICore::settings();
+ s->beginGroup(QLatin1String(SETTINGSKEYSECTIONNAME));
+ d->m_expandCollapseAction->setChecked(s->value(QLatin1String(SETTINGSKEYEXPANDRESULTS), d->m_initiallyExpand).toBool());
+ s->endGroup();
+}
+
+/*!
+ \internal
+*/
+void SearchResultWindow::writeSettings()
+{
+ QSettings *s = Core::ICore::settings();
+ s->beginGroup(QLatin1String(SETTINGSKEYSECTIONNAME));
+ s->setValue(QLatin1String(SETTINGSKEYEXPANDRESULTS), d->m_expandCollapseAction->isChecked());
+ s->endGroup();
+}
+
+/*!
+ \internal
+*/
+int SearchResultWindow::priorityInStatusBar() const
+{
+ return 80;
+}
+
+/*!
+ \internal
+*/
+bool SearchResultWindow::canNext() const
+{
+ if (d->isSearchVisible())
+ return d->m_searchResultWidgets.at(d->visibleSearchIndex())->count() > 0;
+ return false;
+}
+
+/*!
+ \internal
+*/
+bool SearchResultWindow::canPrevious() const
+{
+ return canNext();
+}
+
+/*!
+ \internal
+*/
+void SearchResultWindow::goToNext()
+{
+ int index = d->m_widget->currentIndex();
+ if (index != 0)
+ d->m_searchResultWidgets.at(index-1)->goToNext();
+}
+
+/*!
+ \internal
+*/
+void SearchResultWindow::goToPrev()
+{
+ int index = d->m_widget->currentIndex();
+ if (index != 0)
+ d->m_searchResultWidgets.at(index-1)->goToPrevious();
+}
+
+/*!
+ \internal
+*/
+bool SearchResultWindow::canNavigate() const
+{
+ return true;
+}
+
+/*!
+ \internal
+*/
+SearchResult::SearchResult(SearchResultWidget *widget)
+ : m_widget(widget)
+{
+ connect(widget, SIGNAL(activated(Core::SearchResultItem)),
+ this, SIGNAL(activated(Core::SearchResultItem)));
+ connect(widget, SIGNAL(replaceButtonClicked(QString,QList<Core::SearchResultItem>,bool)),
+ this, SIGNAL(replaceButtonClicked(QString,QList<Core::SearchResultItem>,bool)));
+ connect(widget, SIGNAL(cancelled()),
+ this, SIGNAL(cancelled()));
+ connect(widget, SIGNAL(paused(bool)),
+ this, SIGNAL(paused(bool)));
+ connect(widget, SIGNAL(visibilityChanged(bool)),
+ this, SIGNAL(visibilityChanged(bool)));
+ connect(widget, SIGNAL(searchAgainRequested()),
+ this, SIGNAL(searchAgainRequested()));
+}
+
+/*!
+ Attaches some random \a data to this search, that you can use later.
+
+ \sa userData()
+*/
+void SearchResult::setUserData(const QVariant &data)
+{
+ m_userData = data;
+}
+
+/*!
+ Returns the data that was attached to this search by calling
+ setUserData().
+
+ \sa setUserData()
+*/
+QVariant SearchResult::userData() const
+{
+ return m_userData;
+}
+
+/*!
+ Returns the text that should replace the text in search results.
+*/
+QString SearchResult::textToReplace() const
+{
+ return m_widget->textToReplace();
+}
+
+int SearchResult::count() const
+{
+ return m_widget->count();
+}
+
+void SearchResult::setSearchAgainSupported(bool supported)
+{
+ m_widget->setSearchAgainSupported(supported);
+}
+
+/*!
+ Adds a single result line to the \gui {Search Results} output pane.
+
+ \a fileName, \a lineNumber, and \a lineText are shown on the result line.
+ \a searchTermStart and \a searchTermLength specify the region that
+ should be visually marked (string position and length in \a lineText).
+ You can attach arbitrary \a userData to the search result, which can
+ be used, for example, when reacting to the signals of the search results
+ for your search.
+
+ \sa addResults()
+*/
+void SearchResult::addResult(const QString &fileName, int lineNumber, const QString &lineText,
+ int searchTermStart, int searchTermLength, const QVariant &userData)
+{
+ m_widget->addResult(fileName, lineNumber, lineText,
+ searchTermStart, searchTermLength, userData);
+ emit countChanged(m_widget->count());
+}
+
+/*!
+ Adds the search result \a items to the \gui {Search Results} output pane.
+
+ \sa addResult()
+*/
+void SearchResult::addResults(const QList<SearchResultItem> &items, AddMode mode)
+{
+ m_widget->addResults(items, mode);
+ emit countChanged(m_widget->count());
+}
+
+/*!
+ Notifies the \gui {Search Results} output pane that the current search
+ has finished, and the UI should reflect that.
+*/
+void SearchResult::finishSearch(bool canceled)
+{
+ m_widget->finishSearch(canceled);
+}
+
+/*!
+ Sets the value in the UI element that allows the user to type
+ the text that should replace text in search results to \a textToReplace.
+*/
+void SearchResult::setTextToReplace(const QString &textToReplace)
+{
+ m_widget->setTextToReplace(textToReplace);
+}
+
+/*!
+ * Removes all search results.
+ */
+void SearchResult::restart()
+{
+ m_widget->restart();
+}
+
+void SearchResult::setSearchAgainEnabled(bool enabled)
+{
+ m_widget->setSearchAgainEnabled(enabled);
+}
+
+/*!
+ * Opens the \gui {Search Results} output pane with this search.
+ */
+void SearchResult::popup()
+{
+ m_widget->sendRequestPopup();
+}
+
+} // namespace Core
+
+#include "searchresultwindow.moc"
diff --git a/src/plugins/coreplugin/find/searchresultwindow.h b/src/plugins/coreplugin/find/searchresultwindow.h
new file mode 100644
index 0000000000..c13b9ce32f
--- /dev/null
+++ b/src/plugins/coreplugin/find/searchresultwindow.h
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef SEARCHRESULTWINDOW_H
+#define SEARCHRESULTWINDOW_H
+
+#include <coreplugin/ioutputpane.h>
+
+#include <QVariant>
+#include <QStringList>
+#include <QIcon>
+
+QT_BEGIN_NAMESPACE
+class QFont;
+QT_END_NAMESPACE
+
+namespace Core {
+namespace Internal {
+ class SearchResultTreeView;
+ class SearchResultWindowPrivate;
+ class SearchResultWidget;
+}
+class FindPlugin;
+class SearchResultWindow;
+
+class CORE_EXPORT SearchResultItem
+{
+public:
+ 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 CORE_EXPORT SearchResult : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum AddMode {
+ AddSorted,
+ AddOrdered
+ };
+
+ void setUserData(const QVariant &data);
+ QVariant userData() const;
+ QString textToReplace() const;
+ int count() const;
+ void setSearchAgainSupported(bool supported);
+
+public slots:
+ void addResult(const QString &fileName, int lineNumber, const QString &lineText,
+ int searchTermStart, int searchTermLength, const QVariant &userData = QVariant());
+ void addResults(const QList<SearchResultItem> &items, AddMode mode);
+ void finishSearch(bool canceled);
+ void setTextToReplace(const QString &textToReplace);
+ void restart();
+ void setSearchAgainEnabled(bool enabled);
+ void popup();
+
+signals:
+ void activated(const Core::SearchResultItem &item);
+ void replaceButtonClicked(const QString &replaceText, const QList<Core::SearchResultItem> &checkedItems, bool preserveCase);
+ void cancelled();
+ void paused(bool paused);
+ void visibilityChanged(bool visible);
+ void countChanged(int count);
+ void searchAgainRequested();
+ void requestEnabledCheck();
+
+private:
+ SearchResult(Internal::SearchResultWidget *widget);
+ friend class SearchResultWindow; // for the constructor
+
+private:
+ Internal::SearchResultWidget *m_widget;
+ QVariant m_userData;
+};
+
+class CORE_EXPORT SearchResultWindow : public Core::IOutputPane
+{
+ Q_OBJECT
+
+public:
+ enum SearchMode {
+ SearchOnly,
+ SearchAndReplace
+ };
+
+
+ SearchResultWindow(QWidget *newSearchPanel);
+ virtual ~SearchResultWindow();
+ static SearchResultWindow *instance();
+
+ QWidget *outputWidget(QWidget *);
+ QList<QWidget*> toolBarWidgets() const;
+
+ QString displayName() const { return tr("Search Results"); }
+ int priorityInStatusBar() const;
+ void visibilityChanged(bool visible);
+ bool hasFocus() const;
+ bool canFocus() const;
+ void setFocus();
+
+ bool canNext() const;
+ bool canPrevious() const;
+ void goToNext();
+ void goToPrev();
+ bool canNavigate() const;
+
+ void setTextEditorFont(const QFont &font,
+ const QColor &textForegroundColor,
+ const QColor &textBackgroundColor,
+ const QColor &highlightForegroundColor,
+ const QColor &highlightBackgroundColor);
+ void openNewSearchPanel();
+
+ // The search result window owns the returned SearchResult
+ // and might delete it any time, even while the search is running
+ // (e.g. when the user clears the search result pane, or if the user opens so many other searches
+ // that this search falls out of the history).
+ SearchResult *startNewSearch(const QString &label,
+ const QString &toolTip,
+ const QString &searchTerm,
+ SearchMode searchOrSearchAndReplace = SearchOnly,
+ const QString &cfgGroup = QString());
+
+public slots:
+ void clearContents();
+
+private slots:
+ void handleExpandCollapseToolButton(bool checked);
+
+public: // Used by plugin, do not use
+ void writeSettings();
+
+private:
+ void readSettings();
+
+ Internal::SearchResultWindowPrivate *d;
+ static SearchResultWindow *m_instance;
+};
+
+} // namespace Core
+
+Q_DECLARE_METATYPE(Core::SearchResultItem)
+
+#endif // SEARCHRESULTWINDOW_H
diff --git a/src/plugins/coreplugin/find/textfindconstants.h b/src/plugins/coreplugin/find/textfindconstants.h
new file mode 100644
index 0000000000..53292327b3
--- /dev/null
+++ b/src/plugins/coreplugin/find/textfindconstants.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef TEXTFINDCONSTANTS_H
+#define TEXTFINDCONSTANTS_H
+
+#include <coreplugin/core_global.h>
+
+#include <QMetaType>
+#include <QFlags>
+#include <QTextDocument>
+
+namespace Core {
+namespace Constants {
+
+const char M_FIND[] = "Find.FindMenu";
+const char M_FIND_ADVANCED[] = "Find.FindAdvancedMenu";
+const char G_FIND_CURRENTDOCUMENT[] = "Find.FindMenu.CurrentDocument";
+const char G_FIND_FILTERS[] = "Find.FindMenu.Filters";
+const char G_FIND_FLAGS[] = "Find.FindMenu.Flags";
+const char G_FIND_ACTIONS[] = "Find.FindMenu.Actions";
+
+const char ADVANCED_FIND[] = "Find.Dialog";
+const char FIND_IN_DOCUMENT[] = "Find.FindInCurrentDocument";
+const char FIND_NEXT_SELECTED[]= "Find.FindNextSelected";
+const char FIND_PREV_SELECTED[]= "Find.FindPreviousSelected";
+const char FIND_NEXT[] = "Find.FindNext";
+const char FIND_PREVIOUS[] = "Find.FindPrevious";
+const char REPLACE[] = "Find.Replace";
+const char REPLACE_NEXT[] = "Find.ReplaceNext";
+const char REPLACE_PREVIOUS[] = "Find.ReplacePrevious";
+const char REPLACE_ALL[] = "Find.ReplaceAll";
+const char CASE_SENSITIVE[] = "Find.CaseSensitive";
+const char WHOLE_WORDS[] = "Find.WholeWords";
+const char REGULAR_EXPRESSIONS[] = "Find.RegularExpressions";
+const char PRESERVE_CASE[] = "Find.PreserveCase";
+const char TASK_SEARCH[] = "Find.Task.Search";
+
+} // namespace Constants
+
+enum FindFlag {
+ FindBackward = 0x01,
+ FindCaseSensitively = 0x02,
+ FindWholeWords = 0x04,
+ FindRegularExpression = 0x08,
+ FindPreserveCase = 0x10
+};
+Q_DECLARE_FLAGS(FindFlags, FindFlag)
+
+// defined in findplugin.cpp
+QTextDocument::FindFlags CORE_EXPORT textDocumentFlagsForFindFlags(FindFlags flags);
+
+} // namespace Core
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(Core::FindFlags)
+Q_DECLARE_METATYPE(Core::FindFlags)
+
+#endif // TEXTFINDCONSTANTS_H
diff --git a/src/plugins/coreplugin/find/treeviewfind.cpp b/src/plugins/coreplugin/find/treeviewfind.cpp
new file mode 100644
index 0000000000..6a1c937557
--- /dev/null
+++ b/src/plugins/coreplugin/find/treeviewfind.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "treeviewfind.h"
+
+#include <QTreeView>
+#include <QTextCursor>
+#include <QModelIndex>
+
+namespace Core {
+
+class ItemModelFindPrivate
+{
+public:
+ explicit ItemModelFindPrivate(QTreeView *view, int role)
+ : m_view(view)
+ , m_incrementalWrappedState(false),
+ m_role(role)
+ {
+ }
+
+ QTreeView *m_view;
+ QModelIndex m_incrementalFindStart;
+ bool m_incrementalWrappedState;
+ int m_role;
+};
+
+TreeViewFind::TreeViewFind(QTreeView *view, int role)
+ : d(new ItemModelFindPrivate(view, role))
+{
+}
+
+TreeViewFind::~TreeViewFind()
+{
+ delete d;
+}
+
+bool TreeViewFind::supportsReplace() const
+{
+ return false;
+}
+
+FindFlags TreeViewFind::supportedFindFlags() const
+{
+ return FindBackward | FindCaseSensitively | FindRegularExpression | FindWholeWords;
+}
+
+void TreeViewFind::resetIncrementalSearch()
+{
+ d->m_incrementalFindStart = QModelIndex();
+ d->m_incrementalWrappedState = false;
+}
+
+void TreeViewFind::clearResults()
+{
+}
+
+QString TreeViewFind::currentFindString() const
+{
+ return QString();
+}
+
+QString TreeViewFind::completedFindString() const
+{
+ return QString();
+}
+
+void TreeViewFind::highlightAll(const QString &/*txt*/, FindFlags /*findFlags*/)
+{
+}
+
+IFindSupport::Result TreeViewFind::findIncremental(const QString &txt, FindFlags findFlags)
+{
+ if (!d->m_incrementalFindStart.isValid()) {
+ d->m_incrementalFindStart = d->m_view->currentIndex();
+ d->m_incrementalWrappedState = false;
+ }
+ d->m_view->setCurrentIndex(d->m_incrementalFindStart);
+ bool wrapped = false;
+ IFindSupport::Result result = find(txt, findFlags, true/*startFromCurrent*/,
+ &wrapped);
+ if (wrapped != d->m_incrementalWrappedState) {
+ d->m_incrementalWrappedState = wrapped;
+ showWrapIndicator(d->m_view);
+ }
+ return result;
+}
+
+IFindSupport::Result TreeViewFind::findStep(const QString &txt, FindFlags findFlags)
+{
+ bool wrapped = false;
+ IFindSupport::Result result = find(txt, findFlags, false/*startFromNext*/,
+ &wrapped);
+ if (wrapped)
+ showWrapIndicator(d->m_view);
+ if (result == IFindSupport::Found) {
+ d->m_incrementalFindStart = d->m_view->currentIndex();
+ d->m_incrementalWrappedState = false;
+ }
+ return result;
+}
+
+IFindSupport::Result TreeViewFind::find(const QString &searchTxt,
+ FindFlags findFlags,
+ bool startFromCurrentIndex,
+ bool *wrapped)
+{
+ if (wrapped)
+ *wrapped = false;
+ if (searchTxt.isEmpty())
+ return IFindSupport::NotFound;
+
+ QTextDocument::FindFlags flags = textDocumentFlagsForFindFlags(findFlags);
+ QModelIndex resultIndex;
+ QModelIndex currentIndex = d->m_view->currentIndex();
+ QModelIndex index = currentIndex;
+ int currentRow = currentIndex.row();
+
+ bool backward = (flags & QTextDocument::FindBackward);
+ if (wrapped)
+ *wrapped = false;
+ bool anyWrapped = false;
+ bool stepWrapped = false;
+ if (!startFromCurrentIndex)
+ index = followingIndex(index, backward, &stepWrapped);
+ else
+ currentRow = -1;
+ do {
+ anyWrapped |= stepWrapped; // update wrapped state if we actually stepped to next/prev item
+ if (index.isValid()) {
+ const QString &text = d->m_view->model()->data(
+ index, d->m_role).toString();
+ if (findFlags & FindRegularExpression) {
+ bool sensitive = (findFlags & FindCaseSensitively);
+ QRegExp searchExpr = QRegExp(searchTxt,
+ (sensitive ? Qt::CaseSensitive :
+ Qt::CaseInsensitive));
+ if (searchExpr.indexIn(text) != -1
+ && d->m_view->model()->flags(index) & Qt::ItemIsSelectable
+ && (index.row() != currentRow || index.parent() != currentIndex.parent()))
+ resultIndex = index;
+ } else {
+ QTextDocument doc(text);
+ if (!doc.find(searchTxt, 0,
+ flags & (FindCaseSensitively | FindWholeWords)).isNull()
+ && d->m_view->model()->flags(index) & Qt::ItemIsSelectable
+ && (index.row() != currentRow || index.parent() != currentIndex.parent()))
+ resultIndex = index;
+ }
+ }
+ index = followingIndex(index, backward, &stepWrapped);
+ } while (!resultIndex.isValid() && index.isValid() && index != currentIndex);
+
+ if (resultIndex.isValid()) {
+ d->m_view->setCurrentIndex(resultIndex);
+ d->m_view->scrollTo(resultIndex);
+ if (resultIndex.parent().isValid())
+ d->m_view->expand(resultIndex.parent());
+ if (wrapped)
+ *wrapped = anyWrapped;
+ return IFindSupport::Found;
+ }
+ return IFindSupport::NotFound;
+}
+
+QModelIndex TreeViewFind::nextIndex(const QModelIndex &idx, bool *wrapped) const
+{
+ if (wrapped)
+ *wrapped = false;
+ QAbstractItemModel *model = d->m_view->model();
+ // pathological
+ if (!idx.isValid())
+ return model->index(0, 0);
+
+ // same parent has more columns, go to next column
+ if (idx.column() + 1 < model->columnCount(idx.parent()))
+ return model->index(idx.row(), idx.column() + 1, idx.parent());
+
+ // tree views have their children attached to first column
+ // make sure we are at first column
+ QModelIndex current = model->index(idx.row(), 0, idx.parent());
+
+ // check for children
+ if (model->rowCount(current) > 0)
+ return current.child(0, 0);
+
+ // no more children, go up and look for parent with more children
+ QModelIndex nextIndex;
+ while (!nextIndex.isValid()) {
+ int row = current.row();
+ current = current.parent();
+
+ if (row + 1 < model->rowCount(current)) {
+ // Same parent has another child
+ nextIndex = model->index(row + 1, 0, current);
+ } else {
+ // go up one parent
+ if (!current.isValid()) {
+ // we start from the beginning
+ if (wrapped)
+ *wrapped = true;
+ nextIndex = model->index(0, 0);
+ }
+ }
+ }
+ return nextIndex;
+}
+
+QModelIndex TreeViewFind::prevIndex(const QModelIndex &idx, bool *wrapped) const
+{
+ if (wrapped)
+ *wrapped = false;
+ QAbstractItemModel *model = d->m_view->model();
+ // if same parent has earlier columns, just move there
+ if (idx.column() > 0)
+ return model->index(idx.row(), idx.column() - 1, idx.parent());
+
+ QModelIndex current = idx;
+ bool checkForChildren = true;
+ if (current.isValid()) {
+ int row = current.row();
+ if (row > 0) {
+ current = model->index(row - 1, 0, current.parent());
+ } else {
+ current = current.parent();
+ checkForChildren = !current.isValid();
+ if (checkForChildren && wrapped) {
+ // we start from the end
+ *wrapped = true;
+ }
+ }
+ }
+ if (checkForChildren) {
+ // traverse down the hierarchy
+ while (int rc = model->rowCount(current)) {
+ current = model->index(rc - 1, 0, current);
+ }
+ }
+ // set to last column
+ current = model->index(current.row(), model->columnCount(current.parent()) - 1, current.parent());
+ return current;
+}
+
+QModelIndex TreeViewFind::followingIndex(const QModelIndex &idx, bool backward, bool *wrapped)
+{
+ if (backward)
+ return prevIndex(idx, wrapped);
+ return nextIndex(idx, wrapped);
+}
+
+} // namespace Core
diff --git a/src/plugins/coreplugin/find/treeviewfind.h b/src/plugins/coreplugin/find/treeviewfind.h
new file mode 100644
index 0000000000..06842d2b79
--- /dev/null
+++ b/src/plugins/coreplugin/find/treeviewfind.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef TREEVIEWFIND_H
+#define TREEVIEWFIND_H
+
+#include "ifindsupport.h"
+
+QT_BEGIN_NAMESPACE
+class QTreeView;
+class QModelIndex;
+QT_END_NAMESPACE
+
+namespace Core {
+class ItemModelFindPrivate;
+
+class CORE_EXPORT TreeViewFind : public IFindSupport
+{
+ Q_OBJECT
+public:
+ explicit TreeViewFind(QTreeView *view, int role = Qt::DisplayRole);
+ virtual ~TreeViewFind();
+
+ bool supportsReplace() const;
+ FindFlags supportedFindFlags() const;
+ void resetIncrementalSearch();
+ void clearResults();
+ QString currentFindString() const;
+ QString completedFindString() const;
+
+ virtual void highlightAll(const QString &txt, FindFlags findFlags);
+ Result findIncremental(const QString &txt, FindFlags findFlags);
+ Result findStep(const QString &txt, FindFlags findFlags);
+
+private:
+ Result find(const QString &txt, FindFlags findFlags,
+ bool startFromCurrentIndex, bool *wrapped);
+ QModelIndex nextIndex(const QModelIndex &idx, bool *wrapped) const;
+ QModelIndex prevIndex(const QModelIndex &idx, bool *wrapped) const;
+ QModelIndex followingIndex(const QModelIndex &idx, bool backward,
+ bool *wrapped);
+
+private:
+ ItemModelFindPrivate *d;
+};
+
+} // namespace Core
+
+#endif // TREEVIEWFIND_H
diff --git a/src/plugins/coreplugin/locator/basefilefilter.cpp b/src/plugins/coreplugin/locator/basefilefilter.cpp
new file mode 100644
index 0000000000..920f5b570d
--- /dev/null
+++ b/src/plugins/coreplugin/locator/basefilefilter.cpp
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "basefilefilter.h"
+
+#include <coreplugin/editormanager/editormanager.h>
+#include <utils/fileutils.h>
+
+#include <QDir>
+#include <QStringMatcher>
+
+using namespace Core;
+using namespace Core;
+using namespace Utils;
+
+BaseFileFilter::BaseFileFilter()
+ : m_forceNewSearchList(false)
+{
+}
+
+QList<LocatorFilterEntry> BaseFileFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &origEntry)
+{
+ updateFiles();
+ QList<LocatorFilterEntry> betterEntries;
+ QList<LocatorFilterEntry> goodEntries;
+ QString needle = trimWildcards(origEntry);
+ const QString lineNoSuffix = EditorManager::splitLineNumber(&needle);
+ QStringMatcher matcher(needle, Qt::CaseInsensitive);
+ const QChar asterisk = QLatin1Char('*');
+ QRegExp regexp(asterisk + needle+ asterisk, Qt::CaseInsensitive, QRegExp::Wildcard);
+ if (!regexp.isValid())
+ return betterEntries;
+ const QChar pathSeparator = QDir::separator();
+ const bool hasPathSeparator = needle.contains(pathSeparator);
+ const bool hasWildcard = needle.contains(asterisk) || needle.contains(QLatin1Char('?'));
+ QStringList searchListPaths;
+ QStringList searchListNames;
+ const bool containsPreviousEntry = !m_previousEntry.isEmpty()
+ && needle.contains(m_previousEntry);
+ const bool pathSeparatorAdded = !m_previousEntry.contains(pathSeparator)
+ && needle.contains(pathSeparator);
+ if (!m_forceNewSearchList && containsPreviousEntry && !pathSeparatorAdded) {
+ searchListPaths = m_previousResultPaths;
+ searchListNames = m_previousResultNames;
+ } else {
+ searchListPaths = m_files;
+ searchListNames = m_fileNames;
+ }
+ m_previousResultPaths.clear();
+ m_previousResultNames.clear();
+ m_forceNewSearchList = false;
+ m_previousEntry = needle;
+ const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(needle);
+ QStringListIterator paths(searchListPaths);
+ QStringListIterator names(searchListNames);
+ while (paths.hasNext() && names.hasNext()) {
+ if (future.isCanceled())
+ break;
+
+ QString path = paths.next();
+ QString name = names.next();
+ QString matchText = hasPathSeparator ? path : name;
+ if ((hasWildcard && regexp.exactMatch(matchText))
+ || (!hasWildcard && matcher.indexIn(matchText) != -1)) {
+ QFileInfo fi(path);
+ LocatorFilterEntry entry(this, fi.fileName(), QString(path + lineNoSuffix));
+ entry.extraInfo = FileUtils::shortNativePath(FileName(fi));
+ entry.fileName = path;
+ if (matchText.startsWith(needle, caseSensitivityForPrefix))
+ betterEntries.append(entry);
+ else
+ goodEntries.append(entry);
+ m_previousResultPaths.append(path);
+ m_previousResultNames.append(name);
+ }
+ }
+
+ betterEntries.append(goodEntries);
+ return betterEntries;
+}
+
+void BaseFileFilter::accept(Core::LocatorFilterEntry selection) const
+{
+ EditorManager::openEditor(selection.internalData.toString(), Id(),
+ EditorManager::CanContainLineNumber);
+}
+
+void BaseFileFilter::generateFileNames()
+{
+ m_fileNames.clear();
+ foreach (const QString &fileName, m_files) {
+ QFileInfo fi(fileName);
+ m_fileNames.append(fi.fileName());
+ }
+ m_forceNewSearchList = true;
+}
+
+void BaseFileFilter::updateFiles()
+{
+}
diff --git a/src/plugins/coreplugin/locator/basefilefilter.h b/src/plugins/coreplugin/locator/basefilefilter.h
new file mode 100644
index 0000000000..44102b30e4
--- /dev/null
+++ b/src/plugins/coreplugin/locator/basefilefilter.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef BASEFILEFILTER_H
+#define BASEFILEFILTER_H
+
+#include "ilocatorfilter.h"
+
+#include <QStringList>
+
+namespace Core {
+
+class CORE_EXPORT BaseFileFilter : public Core::ILocatorFilter
+{
+ Q_OBJECT
+
+public:
+ BaseFileFilter();
+ QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry);
+ void accept(Core::LocatorFilterEntry selection) const;
+
+protected:
+ virtual void updateFiles();
+ void generateFileNames();
+
+ inline QStringList &files() { return m_files; }
+ inline const QStringList &files() const { return m_files; }
+
+private:
+ QStringList m_files;
+ QStringList m_fileNames;
+ QStringList m_previousResultPaths;
+ QStringList m_previousResultNames;
+ bool m_forceNewSearchList;
+ QString m_previousEntry;
+};
+
+} // namespace Core
+
+#endif // BASEFILEFILTER_H
diff --git a/src/plugins/coreplugin/locator/commandlocator.cpp b/src/plugins/coreplugin/locator/commandlocator.cpp
new file mode 100644
index 0000000000..13b4ec1485
--- /dev/null
+++ b/src/plugins/coreplugin/locator/commandlocator.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "commandlocator.h"
+
+#include <coreplugin/actionmanager/command.h>
+
+#include <utils/qtcassert.h>
+
+#include <QAction>
+
+namespace Core {
+
+struct CommandLocatorPrivate
+{
+ QList<Core::Command *> commands;
+};
+
+CommandLocator::CommandLocator(Core::Id id,
+ const QString &displayName,
+ const QString &shortCutString,
+ QObject *parent) :
+ Core::ILocatorFilter(parent),
+ d(new CommandLocatorPrivate)
+{
+ setId(id);
+ setDisplayName(displayName);
+ setShortcutString(shortCutString);
+}
+
+CommandLocator::~CommandLocator()
+{
+ delete d;
+}
+
+void CommandLocator::appendCommand(Core::Command *cmd)
+{
+ d->commands.push_back(cmd);
+}
+
+QList<Core::LocatorFilterEntry> CommandLocator::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+{
+ QList<LocatorFilterEntry> goodEntries;
+ QList<LocatorFilterEntry> betterEntries;
+ // Get active, enabled actions matching text, store in list.
+ // Reference via index in extraInfo.
+ const QChar ampersand = QLatin1Char('&');
+ const Qt::CaseSensitivity caseSensitivity_ = caseSensitivity(entry);
+ const int count = d->commands.size();
+ for (int i = 0; i < count; i++) {
+ if (future.isCanceled())
+ break;
+ if (d->commands.at(i)->isActive()) {
+ if (QAction *action = d->commands.at(i)->action())
+ if (action->isEnabled()) {
+ QString text = action->text();
+ text.remove(ampersand);
+ if (text.startsWith(entry, caseSensitivity_))
+ betterEntries.append(LocatorFilterEntry(this, text, QVariant(i)));
+ else if (text.contains(entry, caseSensitivity_))
+ goodEntries.append(LocatorFilterEntry(this, text, QVariant(i)));
+ }
+ }
+ }
+ betterEntries.append(goodEntries);
+ return betterEntries;
+}
+
+void CommandLocator::accept(Core::LocatorFilterEntry entry) const
+{
+ // Retrieve action via index.
+ const int index = entry.internalData.toInt();
+ QTC_ASSERT(index >= 0 && index < d->commands.size(), return);
+ QAction *action = d->commands.at(index)->action();
+ QTC_ASSERT(action->isEnabled(), return);
+ action->trigger();
+}
+
+void CommandLocator::refresh(QFutureInterface<void> &)
+{
+}
+
+} // namespace Core
diff --git a/src/plugins/coreplugin/locator/commandlocator.h b/src/plugins/coreplugin/locator/commandlocator.h
new file mode 100644
index 0000000000..d62f5bdfd4
--- /dev/null
+++ b/src/plugins/coreplugin/locator/commandlocator.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef COMMANDLOCATOR_H
+#define COMMANDLOCATOR_H
+
+#include "ilocatorfilter.h"
+
+namespace Core {
+
+/* Command locators: Provides completion for a set of
+ * Core::Command's by sub-string of their action's text. */
+class Command;
+struct CommandLocatorPrivate;
+
+class CORE_EXPORT CommandLocator : public Core::ILocatorFilter
+{
+ Q_OBJECT
+
+public:
+ CommandLocator(Core::Id id, const QString &displayName,
+ const QString &shortCutString, QObject *parent = 0);
+ ~CommandLocator();
+
+ void appendCommand(Core::Command *cmd);
+
+ QList<LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry);
+ void accept(LocatorFilterEntry selection) const;
+ void refresh(QFutureInterface<void> &future);
+
+private:
+ CommandLocatorPrivate *d;
+};
+
+} // namespace Core
+
+#endif // COMMANDLOCATOR_H
diff --git a/src/plugins/coreplugin/locator/directoryfilter.cpp b/src/plugins/coreplugin/locator/directoryfilter.cpp
new file mode 100644
index 0000000000..dd7639cfe4
--- /dev/null
+++ b/src/plugins/coreplugin/locator/directoryfilter.cpp
@@ -0,0 +1,204 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "directoryfilter.h"
+
+#include <QFileDialog>
+#include <utils/filesearch.h>
+
+using namespace Core;
+using namespace Core::Internal;
+
+DirectoryFilter::DirectoryFilter()
+ : m_name(tr("Generic Directory Filter")),
+ m_dialog(0)
+{
+ setId(Core::Id::fromString(m_name));
+ setIncludedByDefault(true);
+ setDisplayName(m_name);
+
+ m_filters.append(QLatin1String("*.h"));
+ m_filters.append(QLatin1String("*.cpp"));
+ m_filters.append(QLatin1String("*.ui"));
+ m_filters.append(QLatin1String("*.qrc"));
+}
+
+QByteArray DirectoryFilter::saveState() const
+{
+ QMutexLocker locker(&m_lock);
+ QByteArray value;
+ QDataStream out(&value, QIODevice::WriteOnly);
+ out << m_name;
+ out << m_directories;
+ out << m_filters;
+ out << shortcutString();
+ out << isIncludedByDefault();
+ out << files();
+ return value;
+}
+
+bool DirectoryFilter::restoreState(const QByteArray &state)
+{
+ QMutexLocker locker(&m_lock);
+
+ QString shortcut;
+ bool defaultFilter;
+
+ QDataStream in(state);
+ in >> m_name;
+ in >> m_directories;
+ in >> m_filters;
+ in >> shortcut;
+ in >> defaultFilter;
+ in >> files();
+
+ setShortcutString(shortcut);
+ setIncludedByDefault(defaultFilter);
+
+ generateFileNames();
+ return true;
+}
+
+bool DirectoryFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
+{
+ bool success = false;
+ QDialog dialog(parent);
+ m_dialog = &dialog;
+ m_ui.setupUi(&dialog);
+ dialog.setWindowTitle(tr("Filter Configuration"));
+ connect(m_ui.addButton, SIGNAL(clicked()),
+ this, SLOT(addDirectory()), Qt::DirectConnection);
+ connect(m_ui.editButton, SIGNAL(clicked()),
+ this, SLOT(editDirectory()), Qt::DirectConnection);
+ connect(m_ui.removeButton, SIGNAL(clicked()),
+ this, SLOT(removeDirectory()), Qt::DirectConnection);
+ connect(m_ui.directoryList, SIGNAL(itemSelectionChanged()),
+ this, SLOT(updateOptionButtons()), Qt::DirectConnection);
+ m_ui.nameEdit->setText(m_name);
+ m_ui.nameEdit->selectAll();
+ m_ui.directoryList->clear();
+ m_ui.directoryList->addItems(m_directories);
+ m_ui.fileTypeEdit->setText(m_filters.join(QString(QLatin1Char(','))));
+ m_ui.shortcutEdit->setText(shortcutString());
+ m_ui.defaultFlag->setChecked(!isIncludedByDefault());
+ updateOptionButtons();
+ if (dialog.exec() == QDialog::Accepted) {
+ QMutexLocker locker(&m_lock);
+ bool directoriesChanged = false;
+ QStringList oldDirectories = m_directories;
+ QStringList oldFilters = m_filters;
+ m_name = m_ui.nameEdit->text().trimmed();
+ m_directories.clear();
+ int oldCount = oldDirectories.count();
+ int newCount = m_ui.directoryList->count();
+ if (oldCount != newCount)
+ directoriesChanged = true;
+ for (int i = 0; i < newCount; ++i) {
+ m_directories.append(m_ui.directoryList->item(i)->text());
+ if (!directoriesChanged && m_directories.at(i) != oldDirectories.at(i))
+ directoriesChanged = true;
+ }
+ m_filters = m_ui.fileTypeEdit->text().trimmed().split(QLatin1Char(','));
+ setShortcutString(m_ui.shortcutEdit->text().trimmed());
+ setIncludedByDefault(!m_ui.defaultFlag->isChecked());
+ if (directoriesChanged || oldFilters != m_filters)
+ needsRefresh = true;
+ success = true;
+ }
+ return success;
+}
+
+void DirectoryFilter::addDirectory()
+{
+ QString dir = QFileDialog::getExistingDirectory(m_dialog, tr("Select Directory"));
+ if (!dir.isEmpty())
+ m_ui.directoryList->addItem(dir);
+}
+
+void DirectoryFilter::editDirectory()
+{
+ if (m_ui.directoryList->selectedItems().count() < 1)
+ return;
+ QListWidgetItem *currentItem = m_ui.directoryList->selectedItems().at(0);
+ QString dir = QFileDialog::getExistingDirectory(m_dialog, tr("Select Directory"),
+ currentItem->text());
+ if (!dir.isEmpty())
+ currentItem->setText(dir);
+}
+
+void DirectoryFilter::removeDirectory()
+{
+ if (m_ui.directoryList->selectedItems().count() < 1)
+ return;
+ QListWidgetItem *currentItem = m_ui.directoryList->selectedItems().at(0);
+ delete m_ui.directoryList->takeItem(m_ui.directoryList->row(currentItem));
+}
+
+void DirectoryFilter::updateOptionButtons()
+{
+ bool haveSelectedItem = (m_ui.directoryList->selectedItems().count() > 0);
+ m_ui.editButton->setEnabled(haveSelectedItem);
+ m_ui.removeButton->setEnabled(haveSelectedItem);
+}
+
+void DirectoryFilter::refresh(QFutureInterface<void> &future)
+{
+ QStringList directories;
+ {
+ QMutexLocker locker(&m_lock);
+ if (m_directories.count() < 1) {
+ files().clear();
+ generateFileNames();
+ future.setProgressRange(0, 1);
+ future.setProgressValueAndText(1, tr("%1 filter update: 0 files").arg(m_name));
+ return;
+ }
+ directories = m_directories;
+ }
+ Utils::SubDirFileIterator it(directories, m_filters);
+ future.setProgressRange(0, it.maxProgress());
+ QStringList filesFound;
+ while (!future.isCanceled() && it.hasNext()) {
+ filesFound << it.next();
+ if (future.isProgressUpdateNeeded()
+ || future.progressValue() == 0 /*workaround for regression in Qt*/) {
+ future.setProgressValueAndText(it.currentProgress(),
+ tr("%1 filter update: %n files", 0, filesFound.size()).arg(m_name));
+ }
+ }
+
+ if (!future.isCanceled()) {
+ QMutexLocker locker(&m_lock);
+ files() = filesFound;
+ generateFileNames();
+ future.setProgressValue(it.maxProgress());
+ } else {
+ future.setProgressValueAndText(it.currentProgress(), tr("%1 filter update: canceled").arg(m_name));
+ }
+}
diff --git a/src/plugins/coreplugin/locator/directoryfilter.h b/src/plugins/coreplugin/locator/directoryfilter.h
new file mode 100644
index 0000000000..67e97a6e30
--- /dev/null
+++ b/src/plugins/coreplugin/locator/directoryfilter.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef DIRECTORYFILTER_H
+#define DIRECTORYFILTER_H
+
+#include "ui_directoryfilter.h"
+#include "basefilefilter.h"
+
+#include <QString>
+#include <QByteArray>
+#include <QFutureInterface>
+#include <QMutex>
+
+namespace Core {
+namespace Internal {
+
+class DirectoryFilter : public BaseFileFilter
+{
+ Q_OBJECT
+
+public:
+ DirectoryFilter();
+ QByteArray saveState() const;
+ bool restoreState(const QByteArray &state);
+ bool openConfigDialog(QWidget *parent, bool &needsRefresh);
+ void refresh(QFutureInterface<void> &future);
+
+private slots:
+ void addDirectory();
+ void editDirectory();
+ void removeDirectory();
+ void updateOptionButtons();
+
+private:
+ QString m_name;
+ QStringList m_directories;
+ QStringList m_filters;
+ // Our config dialog, uses in addDirectory and editDirectory
+ // to give their dialogs the right parent
+ QDialog *m_dialog;
+ Ui::DirectoryFilterOptions m_ui;
+ mutable QMutex m_lock;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // DIRECTORYFILTER_H
diff --git a/src/plugins/coreplugin/locator/directoryfilter.ui b/src/plugins/coreplugin/locator/directoryfilter.ui
new file mode 100644
index 0000000000..051f7c5104
--- /dev/null
+++ b/src/plugins/coreplugin/locator/directoryfilter.ui
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Core::Internal::DirectoryFilterOptions</class>
+ <widget class="QDialog" name="Core::Internal::DirectoryFilterOptions">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>393</width>
+ <height>275</height>
+ </rect>
+ </property>
+ <layout class="QGridLayout">
+ <item row="0" column="0">
+ <layout class="QGridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Name:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="3">
+ <widget class="QLineEdit" name="nameEdit"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>File types:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" colspan="3">
+ <widget class="QLineEdit" name="fileTypeEdit">
+ <property name="toolTip">
+ <string>Specify file name filters, separated by comma. Filters may contain wildcards.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Prefix:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="shortcutEdit">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Specify a short word/abbreviation that can be used to restrict completions to files from this directory tree.
+To do this, you type this shortcut and a space in the Locator entry field, and then the word to search for.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2" colspan="2">
+ <widget class="QCheckBox" name="defaultFlag">
+ <property name="text">
+ <string>Limit to prefix</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QPushButton" name="addButton">
+ <property name="text">
+ <string>Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="editButton">
+ <property name="text">
+ <string>Edit</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="removeButton">
+ <property name="text">
+ <string>Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Directories:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1" colspan="2">
+ <widget class="QListWidget" name="directoryList">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>Core::Internal::DirectoryFilterOptions</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>353</x>
+ <y>174</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>390</x>
+ <y>152</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>Core::Internal::DirectoryFilterOptions</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>280</x>
+ <y>176</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>391</x>
+ <y>141</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/plugins/coreplugin/locator/executefilter.cpp b/src/plugins/coreplugin/locator/executefilter.cpp
new file mode 100644
index 0000000000..6c2e5f83bd
--- /dev/null
+++ b/src/plugins/coreplugin/locator/executefilter.cpp
@@ -0,0 +1,188 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "executefilter.h"
+
+#include <coreplugin/icore.h>
+#include <coreplugin/messagemanager.h>
+#include <coreplugin/variablemanager.h>
+
+#include <QMessageBox>
+
+using namespace Core;
+using namespace Core;
+using namespace Core::Internal;
+
+ExecuteFilter::ExecuteFilter()
+{
+ setId("Execute custom commands");
+ setDisplayName(tr("Execute Custom Commands"));
+ setShortcutString(QString(QLatin1Char('!')));
+ setIncludedByDefault(false);
+
+ m_process = new Utils::QtcProcess(this);
+ m_process->setEnvironment(Utils::Environment::systemEnvironment());
+ connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), this,
+ SLOT(finished(int,QProcess::ExitStatus)));
+ connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readStandardOutput()));
+ connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(readStandardError()));
+
+ m_runTimer.setSingleShot(true);
+ connect(&m_runTimer, SIGNAL(timeout()), this, SLOT(runHeadCommand()));
+}
+
+QList<LocatorFilterEntry> ExecuteFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
+ const QString &entry)
+{
+ QList<LocatorFilterEntry> value;
+ if (!entry.isEmpty()) // avoid empty entry
+ value.append(LocatorFilterEntry(this, entry, QVariant()));
+ QList<LocatorFilterEntry> others;
+ const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry);
+ foreach (const QString &i, m_commandHistory) {
+ if (future.isCanceled())
+ break;
+ if (i == entry) // avoid repeated entry
+ continue;
+ if (i.startsWith(entry, caseSensitivityForPrefix))
+ value.append(LocatorFilterEntry(this, i, QVariant()));
+ else
+ others.append(LocatorFilterEntry(this, i, QVariant()));
+ }
+ value.append(others);
+ return value;
+}
+
+void ExecuteFilter::accept(LocatorFilterEntry selection) const
+{
+ ExecuteFilter *p = const_cast<ExecuteFilter *>(this);
+
+ const QString value = selection.displayName.trimmed();
+ const int index = m_commandHistory.indexOf(value);
+ if (index != -1 && index != 0)
+ p->m_commandHistory.removeAt(index);
+ if (index != 0)
+ p->m_commandHistory.prepend(value);
+
+ bool found;
+ QString workingDirectory = Core::VariableManager::value("CurrentDocument:Path", &found);
+ if (!found || workingDirectory.isEmpty())
+ workingDirectory = Core::VariableManager::value("CurrentProject:Path", &found);
+
+ ExecuteData d;
+ d.workingDirectory = workingDirectory;
+ const int pos = value.indexOf(QLatin1Char(' '));
+ if (pos == -1) {
+ d.executable = value;
+ } else {
+ d.executable = value.left(pos);
+ d.arguments = value.right(value.length() - pos - 1);
+ }
+
+ if (m_process->state() != QProcess::NotRunning) {
+ const QString info(tr("Previous command is still running ('%1').\nDo you want to kill it?")
+ .arg(p->headCommand()));
+ int r = QMessageBox::question(0, tr("Kill Previous Process?"), info,
+ QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
+ QMessageBox::Yes);
+ if (r == QMessageBox::Yes)
+ m_process->kill();
+ if (r != QMessageBox::Cancel)
+ p->m_taskQueue.enqueue(d);
+ return;
+ }
+
+ p->m_taskQueue.enqueue(d);
+ p->runHeadCommand();
+}
+
+void ExecuteFilter::finished(int exitCode, QProcess::ExitStatus status)
+{
+ const QString commandName = headCommand();
+ QString message;
+ if (status == QProcess::NormalExit && exitCode == 0)
+ message = tr("Command '%1' finished.").arg(commandName);
+ else
+ message = tr("Command '%1' failed.").arg(commandName);
+ MessageManager::write(message);
+
+ m_taskQueue.dequeue();
+ if (!m_taskQueue.isEmpty())
+ m_runTimer.start(500);
+}
+
+void ExecuteFilter::readStandardOutput()
+{
+ QByteArray data = m_process->readAllStandardOutput();
+ MessageManager::write(QTextCodec::codecForLocale()->toUnicode(data.constData(), data.size(),
+ &m_stdoutState));
+}
+
+void ExecuteFilter::readStandardError()
+{
+ static QTextCodec::ConverterState state;
+ QByteArray data = m_process->readAllStandardError();
+ MessageManager::write(QTextCodec::codecForLocale()->toUnicode(data.constData(), data.size(),
+ &m_stderrState));
+}
+
+void ExecuteFilter::runHeadCommand()
+{
+ if (!m_taskQueue.isEmpty()) {
+ const ExecuteData &d = m_taskQueue.head();
+ const QString fullPath = Utils::Environment::systemEnvironment().searchInPath(d.executable);
+ if (fullPath.isEmpty()) {
+ MessageManager::write(tr("Could not find executable for '%1'").arg(d.executable));
+ m_taskQueue.dequeue();
+ runHeadCommand();
+ return;
+ }
+ MessageManager::write(tr("Starting command '%1'").arg(headCommand()));
+ m_process->setWorkingDirectory(d.workingDirectory);
+ m_process->setCommand(fullPath, d.arguments);
+ m_process->start();
+ m_process->closeWriteChannel();
+ if (!m_process->waitForStarted(1000)) {
+ MessageManager::write(tr("Could not start process: %1").arg(m_process->errorString()));
+ m_taskQueue.dequeue();
+ runHeadCommand();
+ }
+ }
+}
+
+QString ExecuteFilter::headCommand() const
+{
+ if (m_taskQueue.isEmpty())
+ return QString();
+ const ExecuteData &data = m_taskQueue.head();
+ if (data.arguments.isEmpty())
+ return data.executable;
+ else
+ return data.executable + QLatin1Char(' ') + data.arguments;
+}
diff --git a/src/plugins/coreplugin/locator/executefilter.h b/src/plugins/coreplugin/locator/executefilter.h
new file mode 100644
index 0000000000..719d8514c8
--- /dev/null
+++ b/src/plugins/coreplugin/locator/executefilter.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef EXECUTEFILTER_H
+#define EXECUTEFILTER_H
+
+#include "ilocatorfilter.h"
+
+#include <utils/qtcprocess.h>
+
+#include <QQueue>
+#include <QStringList>
+#include <QTimer>
+#include <QTextCodec>
+
+namespace Core {
+namespace Internal {
+
+class ExecuteFilter : public Core::ILocatorFilter
+{
+ Q_OBJECT
+ struct ExecuteData
+ {
+ QString executable;
+ QString arguments;
+ QString workingDirectory;
+ };
+
+public:
+ ExecuteFilter();
+ QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future,
+ const QString &entry);
+ void accept(Core::LocatorFilterEntry selection) const;
+ void refresh(QFutureInterface<void> &) {}
+
+private slots:
+ void finished(int exitCode, QProcess::ExitStatus status);
+ void readStandardOutput();
+ void readStandardError();
+ void runHeadCommand();
+
+private:
+ QString headCommand() const;
+
+private:
+ QQueue<ExecuteData> m_taskQueue;
+ QStringList m_commandHistory;
+ Utils::QtcProcess *m_process;
+ QTimer m_runTimer;
+ QTextCodec::ConverterState m_stdoutState;
+ QTextCodec::ConverterState m_stderrState;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // EXECUTEFILTER_H
diff --git a/src/plugins/coreplugin/locator/filesystemfilter.cpp b/src/plugins/coreplugin/locator/filesystemfilter.cpp
new file mode 100644
index 0000000000..84b05cfd5e
--- /dev/null
+++ b/src/plugins/coreplugin/locator/filesystemfilter.cpp
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "filesystemfilter.h"
+#include "locatorwidget.h"
+#include <coreplugin/editormanager/ieditor.h>
+#include <coreplugin/editormanager/editormanager.h>
+
+#include <QDir>
+
+using namespace Core;
+using namespace Core;
+using namespace Core::Internal;
+
+namespace {
+
+QList<LocatorFilterEntry> *categorize(const QString &entry, const QString &candidate,
+ Qt::CaseSensitivity caseSensitivity,
+ QList<LocatorFilterEntry> *betterEntries, QList<LocatorFilterEntry> *goodEntries)
+{
+ if (entry.isEmpty() || candidate.startsWith(entry, caseSensitivity))
+ return betterEntries;
+ else if (candidate.contains(entry, caseSensitivity))
+ return goodEntries;
+ return 0;
+}
+
+} // anynoumous namespace
+
+FileSystemFilter::FileSystemFilter(LocatorWidget *locatorWidget)
+ : m_locatorWidget(locatorWidget), m_includeHidden(true)
+{
+ setId("Files in file system");
+ setDisplayName(tr("Files in File System"));
+ setShortcutString(QString(QLatin1Char('f')));
+ setIncludedByDefault(false);
+}
+
+QList<LocatorFilterEntry> FileSystemFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+{
+ QList<LocatorFilterEntry> goodEntries;
+ QList<LocatorFilterEntry> betterEntries;
+ QFileInfo entryInfo(entry);
+ QString name = entryInfo.fileName();
+ QString directory = entryInfo.path();
+ QString filePath = entryInfo.filePath();
+ if (entryInfo.isRelative()) {
+ if (filePath.startsWith(QLatin1String("~/"))) {
+ directory.replace(0, 1, QDir::homePath());
+ } else {
+ IDocument *document= EditorManager::currentDocument();
+ if (document && !document->filePath().isEmpty()) {
+ QFileInfo info(document->filePath());
+ directory.prepend(info.absolutePath() + QLatin1Char('/'));
+ }
+ }
+ }
+ QDir dirInfo(directory);
+ QDir::Filters dirFilter = QDir::Dirs|QDir::Drives|QDir::NoDot;
+ QDir::Filters fileFilter = QDir::Files;
+ if (m_includeHidden) {
+ dirFilter |= QDir::Hidden;
+ fileFilter |= QDir::Hidden;
+ }
+ const Qt::CaseSensitivity caseSensitivity_ = caseSensitivity(entry);
+ QStringList dirs = dirInfo.entryList(dirFilter,
+ QDir::Name|QDir::IgnoreCase|QDir::LocaleAware);
+ QStringList files = dirInfo.entryList(fileFilter,
+ QDir::Name|QDir::IgnoreCase|QDir::LocaleAware);
+ foreach (const QString &dir, dirs) {
+ if (future.isCanceled())
+ break;
+ if (QList<LocatorFilterEntry> *category = categorize(name, dir, caseSensitivity_, &betterEntries,
+ &goodEntries)) {
+ const QString fullPath = dirInfo.filePath(dir);
+ LocatorFilterEntry filterEntry(this, dir, QVariant());
+ filterEntry.fileName = fullPath;
+ category->append(filterEntry);
+ }
+ }
+ // file names can match with +linenumber or :linenumber
+ name = entry;
+ const QString lineNoSuffix = EditorManager::splitLineNumber(&name);
+ name = QFileInfo(name).fileName();
+ foreach (const QString &file, files) {
+ if (future.isCanceled())
+ break;
+ if (QList<LocatorFilterEntry> *category = categorize(name, file, caseSensitivity_, &betterEntries,
+ &goodEntries)) {
+ const QString fullPath = dirInfo.filePath(file);
+ LocatorFilterEntry filterEntry(this, file, QString(fullPath + lineNoSuffix));
+ filterEntry.fileName = fullPath;
+ category->append(filterEntry);
+ }
+ }
+ betterEntries.append(goodEntries);
+ return betterEntries;
+}
+
+void FileSystemFilter::accept(LocatorFilterEntry selection) const
+{
+ QString fileName = selection.fileName;
+ QFileInfo info(fileName);
+ if (info.isDir()) {
+ QString value = shortcutString();
+ value += QLatin1Char(' ');
+ value += QDir::toNativeSeparators(info.absoluteFilePath() + QLatin1Char('/'));
+ m_locatorWidget->show(value, value.length());
+ return;
+ }
+ EditorManager::openEditor(selection.internalData.toString(), Id(),
+ EditorManager::CanContainLineNumber);
+}
+
+bool FileSystemFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
+{
+ Q_UNUSED(needsRefresh)
+ Ui::FileSystemFilterOptions ui;
+ QDialog dialog(parent);
+ ui.setupUi(&dialog);
+
+ ui.hiddenFilesFlag->setChecked(m_includeHidden);
+ ui.limitCheck->setChecked(!isIncludedByDefault());
+ ui.shortcutEdit->setText(shortcutString());
+
+ if (dialog.exec() == QDialog::Accepted) {
+ m_includeHidden = ui.hiddenFilesFlag->isChecked();
+ setShortcutString(ui.shortcutEdit->text().trimmed());
+ setIncludedByDefault(!ui.limitCheck->isChecked());
+ return true;
+ }
+ return false;
+}
+
+QByteArray FileSystemFilter::saveState() const
+{
+ QByteArray value;
+ QDataStream out(&value, QIODevice::WriteOnly);
+ out << m_includeHidden;
+ out << shortcutString();
+ out << isIncludedByDefault();
+ return value;
+}
+
+bool FileSystemFilter::restoreState(const QByteArray &state)
+{
+ QDataStream in(state);
+ in >> m_includeHidden;
+
+ // An attempt to prevent setting this on old configuration
+ if (!in.atEnd()) {
+ QString shortcut;
+ bool defaultFilter;
+ in >> shortcut;
+ in >> defaultFilter;
+ setShortcutString(shortcut);
+ setIncludedByDefault(defaultFilter);
+ }
+
+ return true;
+}
diff --git a/src/plugins/coreplugin/locator/filesystemfilter.h b/src/plugins/coreplugin/locator/filesystemfilter.h
new file mode 100644
index 0000000000..a6f0283065
--- /dev/null
+++ b/src/plugins/coreplugin/locator/filesystemfilter.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef FILESYSTEMFILTER_H
+#define FILESYSTEMFILTER_H
+
+#include "ilocatorfilter.h"
+#include "ui_filesystemfilter.h"
+
+#include <QString>
+#include <QList>
+#include <QByteArray>
+#include <QFutureInterface>
+
+namespace Core {
+namespace Internal {
+
+class LocatorWidget;
+
+class FileSystemFilter : public Core::ILocatorFilter
+{
+ Q_OBJECT
+
+public:
+ explicit FileSystemFilter(LocatorWidget *locatorWidget);
+ QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry);
+ void accept(Core::LocatorFilterEntry selection) const;
+ QByteArray saveState() const;
+ bool restoreState(const QByteArray &state);
+ bool openConfigDialog(QWidget *parent, bool &needsRefresh);
+ void refresh(QFutureInterface<void> &) {}
+
+private:
+ LocatorWidget *m_locatorWidget;
+ bool m_includeHidden;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // FILESYSTEMFILTER_H
diff --git a/src/plugins/coreplugin/locator/filesystemfilter.ui b/src/plugins/coreplugin/locator/filesystemfilter.ui
new file mode 100644
index 0000000000..b6a8944c70
--- /dev/null
+++ b/src/plugins/coreplugin/locator/filesystemfilter.ui
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Core::Internal::FileSystemFilterOptions</class>
+ <widget class="QDialog" name="Core::Internal::FileSystemFilterOptions">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>360</width>
+ <height>131</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Add Filter Configuration</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Prefix:</string>
+ </property>
+ <property name="buddy">
+ <cstring>shortcutEdit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="shortcutEdit"/>
+ </item>
+ <item row="1" column="2">
+ <widget class="QCheckBox" name="limitCheck">
+ <property name="text">
+ <string>Limit to prefix</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" colspan="2">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="hiddenFilesFlag">
+ <property name="text">
+ <string>Include hidden files</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Filter:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>Core::Internal::FileSystemFilterOptions</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>Core::Internal::FileSystemFilterOptions</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.cpp b/src/plugins/coreplugin/locator/ilocatorfilter.cpp
new file mode 100644
index 0000000000..ea91197cc6
--- /dev/null
+++ b/src/plugins/coreplugin/locator/ilocatorfilter.cpp
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "ilocatorfilter.h"
+
+#include <QBoxLayout>
+#include <QCheckBox>
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QLabel>
+#include <QLineEdit>
+
+using namespace Core;
+
+ILocatorFilter::ILocatorFilter(QObject *parent):
+ QObject(parent),
+ m_priority(Medium),
+ m_includedByDefault(false),
+ m_hidden(false),
+ m_enabled(true),
+ m_isConfigurable(true)
+{
+}
+
+QString ILocatorFilter::shortcutString() const
+{
+ return m_shortcut;
+}
+
+void ILocatorFilter::setShortcutString(const QString &shortcut)
+{
+ m_shortcut = shortcut;
+}
+
+QByteArray ILocatorFilter::saveState() const
+{
+ QByteArray value;
+ QDataStream out(&value, QIODevice::WriteOnly);
+ out << shortcutString();
+ out << isIncludedByDefault();
+ return value;
+}
+
+bool ILocatorFilter::restoreState(const QByteArray &state)
+{
+ QString shortcut;
+ bool defaultFilter;
+
+ QDataStream in(state);
+ in >> shortcut;
+ in >> defaultFilter;
+
+ setShortcutString(shortcut);
+ setIncludedByDefault(defaultFilter);
+ return true;
+}
+
+bool ILocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
+{
+ Q_UNUSED(needsRefresh)
+
+ QDialog dialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
+ dialog.setWindowTitle(tr("Filter Configuration"));
+
+ QVBoxLayout *vlayout = new QVBoxLayout(&dialog);
+ QHBoxLayout *hlayout = new QHBoxLayout;
+ QLineEdit *shortcutEdit = new QLineEdit(shortcutString());
+ QCheckBox *limitCheck = new QCheckBox(tr("Limit to prefix"));
+ limitCheck->setChecked(!isIncludedByDefault());
+
+ hlayout->addWidget(new QLabel(tr("Prefix:")));
+ hlayout->addWidget(shortcutEdit);
+ hlayout->addWidget(limitCheck);
+
+ QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok |
+ QDialogButtonBox::Cancel);
+ connect(buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept()));
+ connect(buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject()));
+
+ vlayout->addLayout(hlayout);
+ vlayout->addStretch();
+ vlayout->addWidget(buttonBox);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ setShortcutString(shortcutEdit->text().trimmed());
+ setIncludedByDefault(!limitCheck->isChecked());
+ return true;
+ }
+
+ return false;
+}
+
+QString ILocatorFilter::trimWildcards(const QString &str)
+{
+ if (str.isEmpty())
+ return str;
+ int first = 0, last = str.size() - 1;
+ const QChar asterisk = QLatin1Char('*');
+ const QChar question = QLatin1Char('?');
+ while (first < str.size() && (str.at(first) == asterisk || str.at(first) == question))
+ ++first;
+ while (last >= 0 && (str.at(last) == asterisk || str.at(last) == question))
+ --last;
+ if (first > last)
+ return QString();
+ return str.mid(first, last - first + 1);
+}
+
+Qt::CaseSensitivity ILocatorFilter::caseSensitivity(const QString &str)
+{
+ return str == str.toLower() ? Qt::CaseInsensitive : Qt::CaseSensitive;
+}
+
+bool ILocatorFilter::isConfigurable() const
+{
+ return m_isConfigurable;
+}
+
+bool ILocatorFilter::isIncludedByDefault() const
+{
+ return m_includedByDefault;
+}
+
+void ILocatorFilter::setIncludedByDefault(bool includedByDefault)
+{
+ m_includedByDefault = includedByDefault;
+}
+
+bool ILocatorFilter::isHidden() const
+{
+ return m_hidden;
+}
+
+void ILocatorFilter::setHidden(bool hidden)
+{
+ m_hidden = hidden;
+}
+
+bool ILocatorFilter::isEnabled() const
+{
+ return m_enabled;
+}
+
+Core::Id ILocatorFilter::id() const
+{
+ return m_id;
+}
+
+QString ILocatorFilter::displayName() const
+{
+ return m_displayName;
+}
+
+ILocatorFilter::Priority ILocatorFilter::priority() const
+{
+ return m_priority;
+}
+
+void ILocatorFilter::setEnabled(bool enabled)
+{
+ m_enabled = enabled;
+}
+
+void ILocatorFilter::setId(Core::Id id)
+{
+ m_id = id;
+}
+
+void ILocatorFilter::setPriority(Priority priority)
+{
+ m_priority = priority;
+}
+
+void ILocatorFilter::setDisplayName(const QString &displayString)
+{
+ m_displayName = displayString;
+}
+
+void ILocatorFilter::setConfigurable(bool configurable)
+{
+ m_isConfigurable = configurable;
+}
diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.h b/src/plugins/coreplugin/locator/ilocatorfilter.h
new file mode 100644
index 0000000000..3c6e213fa0
--- /dev/null
+++ b/src/plugins/coreplugin/locator/ilocatorfilter.h
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef ILOCATORFILTER_H
+#define ILOCATORFILTER_H
+
+#include <coreplugin/id.h>
+
+#include <QVariant>
+#include <QFutureInterface>
+#include <QIcon>
+
+namespace Core {
+
+class ILocatorFilter;
+
+struct LocatorFilterEntry
+{
+ LocatorFilterEntry()
+ : filter(0)
+ , fileIconResolved(false)
+ {}
+
+ LocatorFilterEntry(ILocatorFilter *fromFilter, const QString &name, const QVariant &data,
+ const QIcon &icon = QIcon())
+ : filter(fromFilter)
+ , displayName(name)
+ , internalData(data)
+ , displayIcon(icon)
+ , fileIconResolved(false)
+ {}
+
+ bool operator==(const LocatorFilterEntry &other) const {
+ if (internalData.canConvert(QVariant::String))
+ return (internalData.toString() == other.internalData.toString());
+ return internalData.constData() == other.internalData.constData();
+ }
+
+ /* backpointer to creating filter */
+ ILocatorFilter *filter;
+ /* displayed string */
+ QString displayName;
+ /* extra information displayed in light-gray in a second column (optional) */
+ QString extraInfo;
+ /* can be used by the filter to save more information about the entry */
+ QVariant internalData;
+ /* icon to display along with the entry */
+ QIcon displayIcon;
+ /* file name, if the entry is related to a file, is used e.g. for resolving a file icon */
+ QString fileName;
+ /* internal */
+ bool fileIconResolved;
+};
+
+class CORE_EXPORT ILocatorFilter : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Priority {High = 0, Medium = 1, Low = 2};
+
+ ILocatorFilter(QObject *parent = 0);
+ virtual ~ILocatorFilter() {}
+
+ /* Internal Id. */
+ Core::Id id() const;
+
+ /* Visible name. */
+ QString displayName() const;
+
+ /* Selection list order in case of multiple active filters (high goes on top). */
+ Priority priority() const;
+
+ /* String to type to use this filter exclusively. */
+ QString shortcutString() const;
+
+ /* List of matches for the given user entry. */
+ virtual QList<LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry) = 0;
+
+ /* User has selected the given entry that belongs to this filter. */
+ virtual void accept(LocatorFilterEntry selection) const = 0;
+
+ /* Implement to update caches on user request, if that's a long operation. */
+ virtual void refresh(QFutureInterface<void> &future) = 0;
+
+ /* Saved state is used to restore the filter at start up. */
+ virtual QByteArray saveState() const;
+
+ /* Used to restore the filter at start up. */
+ virtual bool restoreState(const QByteArray &state);
+
+ /* User wants to configure this filter (if supported). Use it to pop up a dialog.
+ * needsRefresh is used as a hint to indicate that refresh should be called.
+ * The default implementation allows changing the shortcut and whether the filter
+ * is enabled by default.
+ */
+ virtual bool openConfigDialog(QWidget *parent, bool &needsRefresh);
+
+ /* If there is a configure dialog available for this filter. The default
+ * implementation returns true. */
+ bool isConfigurable() const;
+
+ /* Is this filter used also when the shortcutString is not used? */
+ bool isIncludedByDefault() const;
+
+ /* Returns whether the filter should be hidden from configuration and menus. */
+ bool isHidden() const;
+
+ /* Returns whether the filter should be enabled and used in menus. */
+ bool isEnabled() const;
+
+ static QString trimWildcards(const QString &str);
+ static Qt::CaseSensitivity caseSensitivity(const QString &str);
+
+public slots:
+ /* Enable or disable the filter. */
+ void setEnabled(bool enabled);
+
+protected:
+ void setShortcutString(const QString &shortcut);
+ void setIncludedByDefault(bool includedByDefault);
+ void setHidden(bool hidden);
+ void setId(Core::Id id);
+ void setPriority(Priority priority);
+ void setDisplayName(const QString &displayString);
+ void setConfigurable(bool configurable);
+
+private:
+ Core::Id m_id;
+ QString m_shortcut;
+ Priority m_priority;
+ QString m_displayName;
+ bool m_includedByDefault;
+ bool m_hidden;
+ bool m_enabled;
+ bool m_isConfigurable;
+};
+
+} // namespace Core
+
+#endif // ILOCATORFILTER_H
diff --git a/src/plugins/coreplugin/locator/images/locator.png b/src/plugins/coreplugin/locator/images/locator.png
new file mode 100644
index 0000000000..000ee1c018
--- /dev/null
+++ b/src/plugins/coreplugin/locator/images/locator.png
Binary files differ
diff --git a/src/plugins/coreplugin/locator/images/reload.png b/src/plugins/coreplugin/locator/images/reload.png
new file mode 100644
index 0000000000..b5afefb32b
--- /dev/null
+++ b/src/plugins/coreplugin/locator/images/reload.png
Binary files differ
diff --git a/src/plugins/coreplugin/locator/locator.pri b/src/plugins/coreplugin/locator/locator.pri
new file mode 100644
index 0000000000..174e1684fe
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locator.pri
@@ -0,0 +1,46 @@
+HEADERS += \
+ $$PWD/locatorplugin.h \
+ $$PWD/commandlocator.h \
+ $$PWD/locatorwidget.h \
+ $$PWD/locatorfiltersfilter.h \
+ $$PWD/settingspage.h \
+ $$PWD/ilocatorfilter.h \
+ $$PWD/opendocumentsfilter.h \
+ $$PWD/filesystemfilter.h \
+ $$PWD/locatorconstants.h \
+ $$PWD/directoryfilter.h \
+ $$PWD/locatormanager.h \
+ $$PWD/basefilefilter.h \
+ $$PWD/executefilter.h \
+ $$PWD/locatorsearchutils.h
+
+SOURCES += \
+ $$PWD/locatorplugin.cpp \
+ $$PWD/commandlocator.cpp \
+ $$PWD/locatorwidget.cpp \
+ $$PWD/locatorfiltersfilter.cpp \
+ $$PWD/opendocumentsfilter.cpp \
+ $$PWD/filesystemfilter.cpp \
+ $$PWD/settingspage.cpp \
+ $$PWD/directoryfilter.cpp \
+ $$PWD/locatormanager.cpp \
+ $$PWD/basefilefilter.cpp \
+ $$PWD/ilocatorfilter.cpp \
+ $$PWD/executefilter.cpp \
+ $$PWD/locatorsearchutils.cpp
+
+FORMS += \
+ $$PWD/settingspage.ui \
+ $$PWD/filesystemfilter.ui \
+ $$PWD/directoryfilter.ui
+
+RESOURCES += \
+ $$PWD/locator.qrc
+
+equals(TEST, 1) {
+ HEADERS += $$PWD/locatorfiltertest.h
+ SOURCES += \
+ $$PWD/locatorfiltertest.cpp \
+ $$PWD/locator_test.cpp
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
diff --git a/src/plugins/coreplugin/locator/locator.qrc b/src/plugins/coreplugin/locator/locator.qrc
new file mode 100644
index 0000000000..4cd5df4f13
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locator.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/locator">
+ <file>images/reload.png</file>
+ <file>images/locator.png</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/coreplugin/locator/locator_test.cpp b/src/plugins/coreplugin/locator/locator_test.cpp
new file mode 100644
index 0000000000..35640285ec
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locator_test.cpp
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+
+#include "locatorplugin.h"
+
+#include "basefilefilter.h"
+#include "locatorfiltertest.h"
+
+#include <coreplugin/testdatadir.h>
+#include <utils/fileutils.h>
+
+#include <QDir>
+#include <QTextStream>
+#include <QtTest>
+
+using namespace Core::Tests;
+
+namespace {
+
+QTC_DECLARE_MYTESTDATADIR("../../../tests/locators/")
+
+class MyBaseFileFilter : public Core::BaseFileFilter
+{
+public:
+ MyBaseFileFilter(const QStringList &theFiles)
+ {
+ files().clear();
+ files().append(theFiles);
+ BaseFileFilter::generateFileNames();
+ }
+
+ void refresh(QFutureInterface<void> &) {}
+
+protected:
+ void updateFiles() {}
+};
+
+inline QString _(const QByteArray &ba) { return QString::fromLatin1(ba, ba.size()); }
+
+class ReferenceData
+{
+public:
+ ReferenceData() {}
+ ReferenceData(const QString &searchText, const ResultDataList &results)
+ : searchText(searchText), results(results) {}
+
+ QString searchText;
+ ResultDataList results;
+};
+
+} // anonymous namespace
+
+Q_DECLARE_METATYPE(ReferenceData)
+Q_DECLARE_METATYPE(QList<ReferenceData>)
+
+void Core::Internal::LocatorPlugin::test_basefilefilter()
+{
+ QFETCH(QStringList, testFiles);
+ QFETCH(QList<ReferenceData>, referenceDataList);
+
+ MyBaseFileFilter filter(testFiles);
+ BasicLocatorFilterTest test(&filter);
+
+ foreach (const ReferenceData &reference, referenceDataList) {
+ const QList<LocatorFilterEntry> filterEntries = test.matchesFor(reference.searchText);
+ const ResultDataList results = ResultData::fromFilterEntryList(filterEntries);
+// QTextStream(stdout) << "----" << endl;
+// ResultData::printFilterEntries(results);
+ QCOMPARE(results, reference.results);
+ }
+}
+
+void Core::Internal::LocatorPlugin::test_basefilefilter_data()
+{
+ QTest::addColumn<QStringList>("testFiles");
+ QTest::addColumn<QList<ReferenceData> >("referenceDataList");
+
+ const QChar pathSeparator = QDir::separator();
+ const MyTestDataDir testDir(QLatin1String("testdata_basic"));
+ const QStringList testFiles = QStringList()
+ << QDir::toNativeSeparators(testDir.file(QLatin1String("file.cpp")))
+ << QDir::toNativeSeparators(testDir.file(QLatin1String("main.cpp")))
+ << QDir::toNativeSeparators(testDir.file(QLatin1String("subdir/main.cpp")));
+ QStringList testFilesShort;
+ foreach (const QString &file, testFiles)
+ testFilesShort << Utils::FileUtils::shortNativePath(Utils::FileName::fromString(file));
+
+ QTest::newRow("BaseFileFilter-EmptyInput")
+ << testFiles
+ << (QList<ReferenceData>()
+ << ReferenceData(
+ QString(),
+ (QList<ResultData>()
+ << ResultData(_("file.cpp"), testFilesShort.at(0))
+ << ResultData(_("main.cpp"), testFilesShort.at(1))
+ << ResultData(_("main.cpp"), testFilesShort.at(2))))
+ );
+
+ QTest::newRow("BaseFileFilter-InputIsFileName")
+ << testFiles
+ << (QList<ReferenceData>()
+ << ReferenceData(
+ _("main.cpp"),
+ (QList<ResultData>()
+ << ResultData(_("main.cpp"), testFilesShort.at(1))
+ << ResultData(_("main.cpp"), testFilesShort.at(2))))
+ );
+
+ QTest::newRow("BaseFileFilter-InputIsFilePath")
+ << testFiles
+ << (QList<ReferenceData>()
+ << ReferenceData(
+ QString(_("subdir") + pathSeparator + _("main.cpp")),
+ (QList<ResultData>()
+ << ResultData(_("main.cpp"), testFilesShort.at(2))))
+ );
+
+ QTest::newRow("BaseFileFilter-InputIsDirIsPath")
+ << testFiles
+ << (QList<ReferenceData>()
+ << ReferenceData( _("subdir"), QList<ResultData>())
+ << ReferenceData(
+ QString(_("subdir") + pathSeparator + _("main.cpp")),
+ (QList<ResultData>()
+ << ResultData(_("main.cpp"), testFilesShort.at(2))))
+ );
+
+ QTest::newRow("BaseFileFilter-InputIsFileNameFilePathFileName")
+ << testFiles
+ << (QList<ReferenceData>()
+ << ReferenceData(
+ _("main.cpp"),
+ (QList<ResultData>()
+ << ResultData(_("main.cpp"), testFilesShort.at(1))
+ << ResultData(_("main.cpp"), testFilesShort.at(2))))
+ << ReferenceData(
+ QString(_("subdir") + pathSeparator + _("main.cpp")),
+ (QList<ResultData>()
+ << ResultData(_("main.cpp"), testFilesShort.at(2))))
+ << ReferenceData(
+ _("main.cpp"),
+ (QList<ResultData>()
+ << ResultData(_("main.cpp"), testFilesShort.at(1))
+ << ResultData(_("main.cpp"), testFilesShort.at(2))))
+ );
+}
diff --git a/src/plugins/coreplugin/locator/locatorconstants.h b/src/plugins/coreplugin/locator/locatorconstants.h
new file mode 100644
index 0000000000..bd39b411d3
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locatorconstants.h
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef LOCATORCONSTANTS_H
+#define LOCATORCONSTANTS_H
+
+#include <QtGlobal>
+
+namespace Core {
+namespace Constants {
+
+const char FILTER_OPTIONS_PAGE[] = QT_TRANSLATE_NOOP("Locator", "Locator");
+const char TASK_INDEX[] = "Locator.Task.Index";
+
+} // namespace Constants
+} // namespace Core
+
+#endif // LOCATORCONSTANTS_H
diff --git a/src/plugins/coreplugin/locator/locatorfiltersfilter.cpp b/src/plugins/coreplugin/locator/locatorfiltersfilter.cpp
new file mode 100644
index 0000000000..3c52ad72d9
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locatorfiltersfilter.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "locatorfiltersfilter.h"
+#include "locatorplugin.h"
+#include "locatorwidget.h"
+
+#include <coreplugin/coreconstants.h>
+
+using namespace Core;
+using namespace Core::Internal;
+
+Q_DECLARE_METATYPE(ILocatorFilter*)
+
+LocatorFiltersFilter::LocatorFiltersFilter(LocatorPlugin *plugin,
+ LocatorWidget *locatorWidget):
+ m_plugin(plugin),
+ m_locatorWidget(locatorWidget),
+ m_icon(QIcon(QLatin1String(Core::Constants::ICON_NEXT)))
+{
+ setId("FiltersFilter");
+ setDisplayName(tr("Available filters"));
+ setIncludedByDefault(true);
+ setHidden(true);
+ setPriority(High);
+ setConfigurable(false);
+}
+
+QList<LocatorFilterEntry> LocatorFiltersFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
+{
+ QList<LocatorFilterEntry> entries;
+ if (!entry.isEmpty())
+ return entries;
+
+ QMap<QString, ILocatorFilter *> uniqueFilters;
+ foreach (ILocatorFilter *filter, m_plugin->filters()) {
+ const QString filterId = filter->shortcutString() + QLatin1Char(',') + filter->displayName();
+ uniqueFilters.insert(filterId, filter);
+ }
+
+ foreach (ILocatorFilter *filter, uniqueFilters) {
+ if (future.isCanceled())
+ break;
+ if (!filter->shortcutString().isEmpty() && !filter->isHidden() && filter->isEnabled()) {
+ LocatorFilterEntry filterEntry(this,
+ filter->shortcutString(),
+ QVariant::fromValue(filter),
+ m_icon);
+ filterEntry.extraInfo = filter->displayName();
+ entries.append(filterEntry);
+ }
+ }
+
+ return entries;
+}
+
+void LocatorFiltersFilter::accept(LocatorFilterEntry selection) const
+{
+ ILocatorFilter *filter = selection.internalData.value<ILocatorFilter *>();
+ if (filter)
+ m_locatorWidget->show(filter->shortcutString() + QLatin1Char(' '),
+ filter->shortcutString().length() + 1);
+}
+
+void LocatorFiltersFilter::refresh(QFutureInterface<void> &future)
+{
+ Q_UNUSED(future)
+ // Nothing to refresh
+}
diff --git a/src/plugins/coreplugin/locator/locatorfiltersfilter.h b/src/plugins/coreplugin/locator/locatorfiltersfilter.h
new file mode 100644
index 0000000000..c775c3a824
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locatorfiltersfilter.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef LOCATORFILTERSFILTER_H
+#define LOCATORFILTERSFILTER_H
+
+#include "ilocatorfilter.h"
+
+#include <QIcon>
+
+namespace Core {
+namespace Internal {
+
+class LocatorPlugin;
+class LocatorWidget;
+
+/*!
+ This filter provides the user with the list of available Locator filters.
+ The list is only shown when nothing has been typed yet.
+ */
+class LocatorFiltersFilter : public ILocatorFilter
+{
+ Q_OBJECT
+
+public:
+ LocatorFiltersFilter(LocatorPlugin *plugin,
+ LocatorWidget *locatorWidget);
+
+ // ILocatorFilter
+ QList<LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry);
+ void accept(LocatorFilterEntry selection) const;
+ void refresh(QFutureInterface<void> &future);
+
+private:
+ LocatorPlugin *m_plugin;
+ LocatorWidget *m_locatorWidget;
+ QIcon m_icon;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // LOCATORFILTERSFILTER_H
diff --git a/src/plugins/coreplugin/locator/locatorfiltertest.cpp b/src/plugins/coreplugin/locator/locatorfiltertest.cpp
new file mode 100644
index 0000000000..1bdf4998e0
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locatorfiltertest.cpp
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+
+#include "locatorfiltertest.h"
+#include "locatorsearchutils.h"
+
+#include <utils/runextensions.h>
+
+#include <QFuture>
+#include <QList>
+#include <QString>
+#include <QTextStream>
+
+using namespace Core;
+using namespace Core::Tests;
+
+BasicLocatorFilterTest::BasicLocatorFilterTest(ILocatorFilter *filter) : m_filter(filter)
+{
+}
+
+QList<LocatorFilterEntry> BasicLocatorFilterTest::matchesFor(const QString &searchText)
+{
+ doBeforeLocatorRun();
+ const QList<ILocatorFilter *> filters = QList<ILocatorFilter *>() << m_filter;
+ QFuture<LocatorFilterEntry> locatorSearch = QtConcurrent::run(Core::Internal::runSearch,
+ filters, searchText);
+ locatorSearch.waitForFinished();
+ doAfterLocatorRun();
+ return locatorSearch.results();
+}
+
+ResultData::ResultData()
+{
+}
+
+ResultData::ResultData(const QString &textColumn1, const QString &textColumn2)
+ : textColumn1(textColumn1), textColumn2(textColumn2)
+{
+}
+
+bool ResultData::operator==(const ResultData &other) const
+{
+ return textColumn1 == other.textColumn1 && textColumn2 == other.textColumn2;
+}
+
+ResultData::ResultDataList ResultData::fromFilterEntryList(const QList<LocatorFilterEntry> &entries)
+{
+ ResultDataList result;
+ foreach (const LocatorFilterEntry &entry, entries)
+ result << ResultData(entry.displayName, entry.extraInfo);
+ return result;
+}
+
+void ResultData::printFilterEntries(const ResultData::ResultDataList &entries)
+{
+ QTextStream out(stdout);
+ foreach (const ResultData entry, entries) {
+ out << "<< ResultData(_(\"" << entry.textColumn1 << "\"), _(\"" << entry.textColumn2
+ << "\"))" << endl;
+ }
+}
diff --git a/src/plugins/coreplugin/locator/locatorfiltertest.h b/src/plugins/coreplugin/locator/locatorfiltertest.h
new file mode 100644
index 0000000000..6cbf1e115c
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locatorfiltertest.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+
+#ifndef LOCATORFILTERTEST_H
+#define LOCATORFILTERTEST_H
+
+#include "ilocatorfilter.h"
+
+#include <QTest>
+
+namespace Core {
+namespace Tests {
+
+/// Runs a locator filter for a search text and returns the results.
+class CORE_EXPORT BasicLocatorFilterTest
+{
+public:
+ BasicLocatorFilterTest(Core::ILocatorFilter *filter);
+
+ QList<Core::LocatorFilterEntry> matchesFor(const QString &searchText = QString());
+
+private:
+ virtual void doBeforeLocatorRun() {}
+ virtual void doAfterLocatorRun() {}
+
+ Core::ILocatorFilter *m_filter;
+};
+
+class CORE_EXPORT ResultData
+{
+public:
+ typedef QList<ResultData> ResultDataList;
+
+ ResultData();
+ ResultData(const QString &textColumn1, const QString &textColumn2);
+
+ bool operator==(const ResultData &other) const;
+
+ static ResultDataList fromFilterEntryList(const QList<LocatorFilterEntry> &entries);
+
+ /// For debugging and creating reference data
+ static void printFilterEntries(const ResultDataList &entries);
+
+ QString textColumn1;
+ QString textColumn2;
+};
+
+typedef ResultData::ResultDataList ResultDataList;
+
+} // namespace Tests
+} // namespace Core
+
+Q_DECLARE_METATYPE(Core::Tests::ResultData)
+Q_DECLARE_METATYPE(Core::Tests::ResultDataList)
+
+QT_BEGIN_NAMESPACE
+namespace QTest {
+
+template<> inline char *toString(const Core::Tests::ResultData &data)
+{
+ QByteArray ba = "\"" + data.textColumn1.toUtf8() + "\", \"" + data.textColumn2.toUtf8() + "\"";
+ return qstrdup(ba.data());
+}
+
+} // namespace QTest
+QT_END_NAMESPACE
+
+#endif // LOCATORFILTERTEST_H
diff --git a/src/plugins/coreplugin/locator/locatormanager.cpp b/src/plugins/coreplugin/locator/locatormanager.cpp
new file mode 100644
index 0000000000..9cc893f5f6
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locatormanager.cpp
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "locatormanager.h"
+#include "locatorwidget.h"
+
+#include <extensionsystem/pluginmanager.h>
+#include <utils/qtcassert.h>
+
+namespace Core {
+
+static Internal::LocatorWidget *m_locatorWidget = 0;
+
+LocatorManager::LocatorManager(Internal::LocatorWidget *locatorWidget)
+ : QObject(locatorWidget)
+{
+ m_locatorWidget = locatorWidget;
+}
+
+LocatorManager::~LocatorManager()
+{
+ ExtensionSystem::PluginManager::removeObject(this);
+}
+
+void LocatorManager::show(const QString &text,
+ int selectionStart, int selectionLength)
+{
+ QTC_ASSERT(m_locatorWidget, return);
+ m_locatorWidget->show(text, selectionStart, selectionLength);
+}
+
+} // namespace Internal
diff --git a/src/plugins/coreplugin/locator/locatormanager.h b/src/plugins/coreplugin/locator/locatormanager.h
new file mode 100644
index 0000000000..67d2106c04
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locatormanager.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef LOCATORMANAGER_H
+#define LOCATORMANAGER_H
+
+#include <coreplugin/core_global.h>
+
+#include <QObject>
+
+namespace Core {
+
+namespace Internal { class LocatorWidget; }
+
+class CORE_EXPORT LocatorManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ LocatorManager(Internal::LocatorWidget *locatorWidget);
+ ~LocatorManager();
+
+ static void show(const QString &text, int selectionStart = -1, int selectionLength = 0);
+};
+
+} // namespace Core
+
+#endif // LOCATORMANAGER_H
diff --git a/src/plugins/coreplugin/locator/locatorplugin.cpp b/src/plugins/coreplugin/locator/locatorplugin.cpp
new file mode 100644
index 0000000000..085c0071d2
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locatorplugin.cpp
@@ -0,0 +1,261 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "locatorplugin.h"
+#include "locatorconstants.h"
+#include "locatorfiltersfilter.h"
+#include "locatormanager.h"
+#include "locatorwidget.h"
+#include "opendocumentsfilter.h"
+#include "filesystemfilter.h"
+#include "settingspage.h"
+
+#include <coreplugin/coreplugin.h>
+#include <coreplugin/statusbarwidget.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/settingsdatabase.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/actionmanager/actioncontainer.h>
+#include <coreplugin/progressmanager/progressmanager.h>
+#include <coreplugin/progressmanager/futureprogress.h>
+#include <extensionsystem/pluginmanager.h>
+#include <utils/QtConcurrentTools>
+#include <utils/qtcassert.h>
+
+#include <QSettings>
+#include <QtPlugin>
+#include <QFuture>
+#include <QAction>
+
+namespace Core {
+namespace Internal {
+
+namespace {
+ static bool filterLessThan(const ILocatorFilter *first, const ILocatorFilter *second)
+ {
+ if (first->priority() < second->priority())
+ return true;
+ if (first->priority() > second->priority())
+ return false;
+ return first->id().alphabeticallyBefore(second->id());
+ }
+}
+
+LocatorPlugin::LocatorPlugin()
+ : m_settingsInitialized(false)
+{
+ m_corePlugin = 0;
+ m_refreshTimer.setSingleShot(false);
+ connect(&m_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));
+}
+
+LocatorPlugin::~LocatorPlugin()
+{
+ m_corePlugin->removeObject(m_openDocumentsFilter);
+ m_corePlugin->removeObject(m_fileSystemFilter);
+ m_corePlugin->removeObject(m_executeFilter);
+ m_corePlugin->removeObject(m_settingsPage);
+ delete m_openDocumentsFilter;
+ delete m_fileSystemFilter;
+ delete m_executeFilter;
+ delete m_settingsPage;
+ qDeleteAll(m_customFilters);
+}
+
+void LocatorPlugin::initialize(CorePlugin *corePlugin, const QStringList &, QString *)
+{
+ m_corePlugin = corePlugin;
+
+ m_settingsPage = new SettingsPage(this);
+ m_corePlugin->addObject(m_settingsPage);
+
+ m_locatorWidget = new LocatorWidget(this);
+ m_locatorWidget->setEnabled(false);
+ StatusBarWidget *view = new StatusBarWidget;
+ view->setWidget(m_locatorWidget);
+ view->setContext(Context("LocatorWidget"));
+ view->setPosition(StatusBarWidget::First);
+ m_corePlugin->addAutoReleasedObject(view);
+
+ QAction *action = new QAction(m_locatorWidget->windowIcon(), m_locatorWidget->windowTitle(), this);
+ Command *cmd = ActionManager::registerAction(action, "QtCreator.Locate",
+ Context(Core::Constants::C_GLOBAL));
+ cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+K")));
+ connect(action, SIGNAL(triggered()), this, SLOT(openLocator()));
+ connect(cmd, SIGNAL(keySequenceChanged()), this, SLOT(updatePlaceholderText()));
+ updatePlaceholderText(cmd);
+
+ ActionContainer *mtools = ActionManager::actionContainer(Core::Constants::M_TOOLS);
+ mtools->addAction(cmd);
+
+ m_corePlugin->addObject(new LocatorManager(m_locatorWidget));
+
+ m_openDocumentsFilter = new OpenDocumentsFilter;
+ m_corePlugin->addObject(m_openDocumentsFilter);
+
+ m_fileSystemFilter = new FileSystemFilter(m_locatorWidget);
+ m_corePlugin->addObject(m_fileSystemFilter);
+
+ m_executeFilter = new ExecuteFilter();
+ m_corePlugin->addObject(m_executeFilter);
+
+ m_corePlugin->addAutoReleasedObject(new LocatorFiltersFilter(this, m_locatorWidget));
+}
+
+void LocatorPlugin::updatePlaceholderText(Command *command)
+{
+ if (!command)
+ command = qobject_cast<Command *>(sender());
+ QTC_ASSERT(command, return);
+ if (command->keySequence().isEmpty())
+ m_locatorWidget->setPlaceholderText(tr("Type to locate"));
+ else
+ m_locatorWidget->setPlaceholderText(tr("Type to locate (%1)").arg(
+ command->keySequence().toString(QKeySequence::NativeText)));
+}
+
+void LocatorPlugin::openLocator()
+{
+ m_locatorWidget->show(QString());
+}
+
+void LocatorPlugin::extensionsInitialized()
+{
+ m_filters = ExtensionSystem::PluginManager::getObjects<ILocatorFilter>();
+ qSort(m_filters.begin(), m_filters.end(), filterLessThan);
+ setFilters(m_filters);
+}
+
+bool LocatorPlugin::delayedInitialize()
+{
+ loadSettings();
+ return true;
+}
+
+void LocatorPlugin::loadSettings()
+{
+ QSettings *qs = ICore::settings();
+
+ // Backwards compatibility to old settings location
+ if (qs->contains(QLatin1String("QuickOpen/FiltersFilter"))) {
+ loadSettingsHelper(qs);
+ } else {
+ SettingsDatabase *settings = ICore::settingsDatabase();
+ loadSettingsHelper(settings);
+ }
+
+ qs->remove(QLatin1String("QuickOpen"));
+
+ m_locatorWidget->updateFilterList();
+ m_locatorWidget->setEnabled(true);
+ if (m_refreshTimer.interval() > 0)
+ m_refreshTimer.start();
+ m_settingsInitialized = true;
+}
+
+void LocatorPlugin::saveSettings()
+{
+ if (m_settingsInitialized) {
+ SettingsDatabase *s = ICore::settingsDatabase();
+ s->beginGroup(QLatin1String("QuickOpen"));
+ s->remove(QString());
+ s->setValue(QLatin1String("RefreshInterval"), refreshInterval());
+ foreach (ILocatorFilter *filter, m_filters) {
+ if (!m_customFilters.contains(filter))
+ s->setValue(filter->id().toString(), filter->saveState());
+ }
+ s->beginGroup(QLatin1String("CustomFilters"));
+ int i = 0;
+ foreach (ILocatorFilter *filter, m_customFilters) {
+ s->setValue(QLatin1String("directory") + QString::number(i),
+ filter->saveState());
+ ++i;
+ }
+ s->endGroup();
+ s->endGroup();
+ }
+}
+
+/*!
+ Return all filters, including the ones created by the user.
+*/
+QList<ILocatorFilter *> LocatorPlugin::filters()
+{
+ return m_filters;
+}
+
+/*!
+ This returns a subset of all the filters, that contains only the filters that
+ have been created by the user at some point (maybe in a previous session).
+ */
+QList<ILocatorFilter *> LocatorPlugin::customFilters()
+{
+ return m_customFilters;
+}
+
+void LocatorPlugin::setFilters(QList<ILocatorFilter *> f)
+{
+ m_filters = f;
+ m_locatorWidget->updateFilterList();
+}
+
+void LocatorPlugin::setCustomFilters(QList<ILocatorFilter *> filters)
+{
+ m_customFilters = filters;
+}
+
+int LocatorPlugin::refreshInterval()
+{
+ return m_refreshTimer.interval() / 60000;
+}
+
+void LocatorPlugin::setRefreshInterval(int interval)
+{
+ if (interval < 1) {
+ m_refreshTimer.stop();
+ m_refreshTimer.setInterval(0);
+ return;
+ }
+ m_refreshTimer.setInterval(interval * 60000);
+ m_refreshTimer.start();
+}
+
+void LocatorPlugin::refresh(QList<ILocatorFilter *> filters)
+{
+ if (filters.isEmpty())
+ filters = m_filters;
+ QFuture<void> task = QtConcurrent::run(&ILocatorFilter::refresh, filters);
+ FutureProgress *progress =
+ ProgressManager::addTask(task, tr("Indexing"), Core::Constants::TASK_INDEX);
+ connect(progress, SIGNAL(finished()), this, SLOT(saveSettings()));
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/locator/locatorplugin.h b/src/plugins/coreplugin/locator/locatorplugin.h
new file mode 100644
index 0000000000..f0a8a719b2
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locatorplugin.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef LOCATORPLUGIN_H
+#define LOCATORPLUGIN_H
+
+#include "ilocatorfilter.h"
+#include "directoryfilter.h"
+#include "executefilter.h"
+
+#include <extensionsystem/iplugin.h>
+#include <coreplugin/actionmanager/command.h>
+
+#include <QTimer>
+#include <QFutureWatcher>
+
+namespace Core {
+namespace Internal {
+
+class CorePlugin;
+class LocatorWidget;
+class OpenDocumentsFilter;
+class FileSystemFilter;
+class SettingsPage;
+
+class LocatorPlugin : public QObject
+{
+ Q_OBJECT
+
+public:
+ LocatorPlugin();
+ ~LocatorPlugin();
+
+ void initialize(CorePlugin *corePlugin, const QStringList &arguments, QString *errorMessage);
+ void extensionsInitialized();
+ bool delayedInitialize();
+
+ QList<ILocatorFilter *> filters();
+ QList<ILocatorFilter *> customFilters();
+ void setFilters(QList<ILocatorFilter *> f);
+ void setCustomFilters(QList<ILocatorFilter *> f);
+ int refreshInterval();
+ void setRefreshInterval(int interval);
+
+public slots:
+ void refresh(QList<ILocatorFilter *> filters = QList<ILocatorFilter *>());
+ void saveSettings();
+ void openLocator();
+
+private slots:
+ void updatePlaceholderText(Core::Command *command = 0);
+
+#ifdef WITH_TESTS
+ void test_basefilefilter();
+ void test_basefilefilter_data();
+#endif
+
+private:
+ void loadSettings();
+
+ template <typename S>
+ void loadSettingsHelper(S *settings);
+
+ LocatorWidget *m_locatorWidget;
+ SettingsPage *m_settingsPage;
+
+ bool m_settingsInitialized;
+ QList<ILocatorFilter *> m_filters;
+ QList<ILocatorFilter *> m_customFilters;
+ int m_refreshInterval;
+ QTimer m_refreshTimer;
+ OpenDocumentsFilter *m_openDocumentsFilter;
+ FileSystemFilter *m_fileSystemFilter;
+ ExecuteFilter *m_executeFilter;
+ CorePlugin *m_corePlugin;
+};
+
+template <typename S>
+void LocatorPlugin::loadSettingsHelper(S *settings)
+{
+ settings->beginGroup(QLatin1String("QuickOpen"));
+ m_refreshTimer.setInterval(settings->value(QLatin1String("RefreshInterval"), 60).toInt() * 60000);
+
+ foreach (ILocatorFilter *filter, m_filters) {
+ if (settings->contains(filter->id().toString())) {
+ const QByteArray state = settings->value(filter->id().toString()).toByteArray();
+ if (!state.isEmpty())
+ filter->restoreState(state);
+ }
+ }
+ settings->beginGroup(QLatin1String("CustomFilters"));
+ QList<ILocatorFilter *> customFilters;
+ const QStringList keys = settings->childKeys();
+ foreach (const QString &key, keys) {
+ ILocatorFilter *filter = new DirectoryFilter;
+ filter->restoreState(settings->value(key).toByteArray());
+ m_filters.append(filter);
+ customFilters.append(filter);
+ }
+ setCustomFilters(customFilters);
+ settings->endGroup();
+ settings->endGroup();
+}
+
+} // namespace Internal
+} // namespace Core
+
+#endif // LOCATORPLUGIN_H
diff --git a/src/plugins/coreplugin/locator/locatorsearchutils.cpp b/src/plugins/coreplugin/locator/locatorsearchutils.cpp
new file mode 100644
index 0000000000..189449fc66
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locatorsearchutils.cpp
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+
+#include "locatorsearchutils.h"
+
+#include <QSet>
+#include <QString>
+#include <QVariant>
+
+namespace Core {
+
+uint qHash(const Core::LocatorFilterEntry &entry)
+{
+ if (entry.internalData.canConvert(QVariant::String))
+ return QT_PREPEND_NAMESPACE(qHash)(entry.internalData.toString());
+ return QT_PREPEND_NAMESPACE(qHash)(entry.internalData.constData());
+}
+
+} // namespace Core
+
+void Core::Internal::runSearch(QFutureInterface<Core::LocatorFilterEntry> &entries,
+ QList<ILocatorFilter *> filters, QString searchText)
+{
+ QSet<LocatorFilterEntry> alreadyAdded;
+ const bool checkDuplicates = (filters.size() > 1);
+ foreach (ILocatorFilter *filter, filters) {
+ if (entries.isCanceled())
+ break;
+
+ foreach (const LocatorFilterEntry &entry, filter->matchesFor(entries, searchText)) {
+ if (checkDuplicates && alreadyAdded.contains(entry))
+ continue;
+ entries.reportResult(entry);
+ if (checkDuplicates)
+ alreadyAdded.insert(entry);
+ }
+ }
+}
diff --git a/src/plugins/coreplugin/locator/locatorsearchutils.h b/src/plugins/coreplugin/locator/locatorsearchutils.h
new file mode 100644
index 0000000000..1abf03a8fd
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locatorsearchutils.h
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef LOCATORSEARCHUTILS_H
+#define LOCATORSEARCHUTILS_H
+
+#include "ilocatorfilter.h"
+
+namespace Core {
+namespace Internal {
+
+void CORE_EXPORT runSearch(QFutureInterface<LocatorFilterEntry> &entries,
+ QList<ILocatorFilter *> filters,
+ QString searchText);
+
+} // namespace Internal
+} // namespace Core
+
+#endif // LOCATORSEARCHUTILS_H
+
diff --git a/src/plugins/coreplugin/locator/locatorwidget.cpp b/src/plugins/coreplugin/locator/locatorwidget.cpp
new file mode 100644
index 0000000000..483ebe1a3f
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locatorwidget.cpp
@@ -0,0 +1,590 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "locatorwidget.h"
+#include "locatorplugin.h"
+#include "locatorconstants.h"
+#include "locatorsearchutils.h"
+#include "ilocatorfilter.h"
+
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/modemanager.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/fileiconprovider.h>
+#include <coreplugin/icontext.h>
+#include <utils/appmainwindow.h>
+#include <utils/filterlineedit.h>
+#include <utils/hostosinfo.h>
+#include <utils/qtcassert.h>
+#include <utils/runextensions.h>
+
+#include <QColor>
+#include <QFileInfo>
+#include <QTimer>
+#include <QEvent>
+#include <QAction>
+#include <QApplication>
+#include <QHBoxLayout>
+#include <QHeaderView>
+#include <QKeyEvent>
+#include <QMenu>
+#include <QScrollBar>
+#include <QTreeView>
+#include <QToolTip>
+
+Q_DECLARE_METATYPE(Core::ILocatorFilter*)
+Q_DECLARE_METATYPE(Core::LocatorFilterEntry)
+
+namespace Core {
+namespace Internal {
+
+/* A model to represent the Locator results. */
+class LocatorModel : public QAbstractListModel
+{
+public:
+ LocatorModel(QObject *parent = 0)
+ : QAbstractListModel(parent)
+// , mDisplayCount(64)
+ {}
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+ void setEntries(const QList<LocatorFilterEntry> &entries);
+ //void setDisplayCount(int count);
+
+private:
+ mutable QList<LocatorFilterEntry> mEntries;
+ //int mDisplayCount;
+};
+
+class CompletionList : public QTreeView
+{
+public:
+ CompletionList(QWidget *parent = 0);
+
+ void updatePreferredSize();
+ QSize preferredSize() const { return m_preferredSize; }
+
+ void focusOutEvent (QFocusEvent *event) {
+ if (event->reason() == Qt::ActiveWindowFocusReason)
+ hide();
+ QTreeView::focusOutEvent(event);
+ }
+
+ void next() {
+ int index = currentIndex().row();
+ ++index;
+ if (index >= model()->rowCount(QModelIndex())) {
+ // wrap
+ index = 0;
+ }
+ setCurrentIndex(model()->index(index, 0));
+ }
+
+ void previous() {
+ int index = currentIndex().row();
+ --index;
+ if (index < 0) {
+ // wrap
+ index = model()->rowCount(QModelIndex()) - 1;
+ }
+ setCurrentIndex(model()->index(index, 0));
+ }
+
+private:
+ QSize m_preferredSize;
+};
+
+} // namespace Internal
+
+
+using namespace Core::Internal;
+
+// =========== LocatorModel ===========
+
+int LocatorModel::rowCount(const QModelIndex & /* parent */) const
+{
+ return mEntries.size();
+}
+
+int LocatorModel::columnCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : 2;
+}
+
+/*!
+ * When asked for the icon via Qt::DecorationRole, the LocatorModel lazily
+ * resolves and caches the Greehouse-specific file icon when
+ * FilterEntry::resolveFileIcon is true. FilterEntry::internalData is assumed
+ * to be the filename.
+ */
+QVariant LocatorModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || index.row() >= mEntries.size())
+ return QVariant();
+
+ if (role == Qt::DisplayRole) {
+ if (index.column() == 0)
+ return mEntries.at(index.row()).displayName;
+ else if (index.column() == 1)
+ return mEntries.at(index.row()).extraInfo;
+ } else if (role == Qt::ToolTipRole) {
+ if (mEntries.at(index.row()).extraInfo.isEmpty())
+ return QVariant(mEntries.at(index.row()).displayName);
+ else
+ return QVariant(mEntries.at(index.row()).displayName
+ + QLatin1String("\n\n") + mEntries.at(index.row()).extraInfo);
+ } else if (role == Qt::DecorationRole && index.column() == 0) {
+ LocatorFilterEntry &entry = mEntries[index.row()];
+ if (!entry.fileIconResolved && !entry.fileName.isEmpty() && entry.displayIcon.isNull()) {
+ entry.fileIconResolved = true;
+ entry.displayIcon = FileIconProvider::icon(entry.fileName);
+ }
+ return entry.displayIcon;
+ } else if (role == Qt::ForegroundRole && index.column() == 1) {
+ return QColor(Qt::darkGray);
+ } else if (role == Qt::UserRole) {
+ return qVariantFromValue(mEntries.at(index.row()));
+ }
+
+ return QVariant();
+}
+
+void LocatorModel::setEntries(const QList<LocatorFilterEntry> &entries)
+{
+ beginResetModel();
+ mEntries = entries;
+ endResetModel();
+}
+
+// =========== CompletionList ===========
+
+CompletionList::CompletionList(QWidget *parent)
+ : QTreeView(parent)
+{
+ setRootIsDecorated(false);
+ setUniformRowHeights(true);
+ setMaximumWidth(900);
+ header()->hide();
+ header()->setStretchLastSection(true);
+ // This is too slow when done on all results
+ //header()->setResizeMode(QHeaderView::ResizeToContents);
+ setWindowFlags(Qt::ToolTip);
+ if (Utils::HostOsInfo::isMacHost()) {
+ if (horizontalScrollBar())
+ horizontalScrollBar()->setAttribute(Qt::WA_MacMiniSize);
+ if (verticalScrollBar())
+ verticalScrollBar()->setAttribute(Qt::WA_MacMiniSize);
+ }
+}
+
+void CompletionList::updatePreferredSize()
+{
+ const QStyleOptionViewItem &option = viewOptions();
+ const QSize shint = itemDelegate()->sizeHint(option, model()->index(0, 0));
+
+ m_preferredSize = QSize(730, shint.height() * 17 + frameWidth() * 2);
+}
+
+// =========== LocatorWidget ===========
+
+LocatorWidget::LocatorWidget(LocatorPlugin *qop) :
+ m_locatorPlugin(qop),
+ m_locatorModel(new LocatorModel(this)),
+ m_completionList(new CompletionList(this)),
+ m_filterMenu(new QMenu(this)),
+ m_refreshAction(new QAction(tr("Refresh"), this)),
+ m_configureAction(new QAction(tr("Configure..."), this)),
+ m_fileLineEdit(new Utils::FilterLineEdit),
+ m_updateRequested(false),
+ m_acceptRequested(false),
+ m_possibleToolTipRequest(false)
+{
+ // Explicitly hide the completion list popup.
+ m_completionList->hide();
+
+ setFocusProxy(m_fileLineEdit);
+ setWindowTitle(tr("Locate..."));
+ resize(200, 90);
+ QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ sizePolicy.setHorizontalStretch(0);
+ sizePolicy.setVerticalStretch(0);
+ setSizePolicy(sizePolicy);
+ setMinimumSize(QSize(200, 0));
+
+ QHBoxLayout *layout = new QHBoxLayout(this);
+ setLayout(layout);
+ layout->setMargin(0);
+ layout->addWidget(m_fileLineEdit);
+
+ setWindowIcon(QIcon(QLatin1String(":/locator/images/locator.png")));
+ const QPixmap image = QPixmap(QLatin1String(Core::Constants::ICON_MAGNIFIER));
+ m_fileLineEdit->setButtonPixmap(Utils::FancyLineEdit::Left, image);
+ m_fileLineEdit->setButtonToolTip(Utils::FancyLineEdit::Left, tr("Options"));
+ m_fileLineEdit->setFocusPolicy(Qt::ClickFocus);
+ m_fileLineEdit->setButtonVisible(Utils::FancyLineEdit::Left, true);
+ // We set click focus since otherwise you will always get two popups
+ m_fileLineEdit->setButtonFocusPolicy(Utils::FancyLineEdit::Left, Qt::ClickFocus);
+ m_fileLineEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
+
+ m_fileLineEdit->installEventFilter(this);
+ this->installEventFilter(this);
+
+ m_completionList->setModel(m_locatorModel);
+ m_completionList->header()->resizeSection(0, 300);
+ m_completionList->updatePreferredSize();
+ m_completionList->resize(m_completionList->preferredSize());
+
+ m_filterMenu->addAction(m_refreshAction);
+ m_filterMenu->addAction(m_configureAction);
+
+ m_fileLineEdit->setButtonMenu(Utils::FancyLineEdit::Left, m_filterMenu);
+
+ connect(m_refreshAction, SIGNAL(triggered()), m_locatorPlugin, SLOT(refresh()));
+ connect(m_configureAction, SIGNAL(triggered()), this, SLOT(showConfigureDialog()));
+ connect(m_fileLineEdit, SIGNAL(textChanged(QString)),
+ this, SLOT(showPopup()));
+ connect(m_completionList, SIGNAL(activated(QModelIndex)),
+ this, SLOT(scheduleAcceptCurrentEntry()));
+
+ m_entriesWatcher = new QFutureWatcher<LocatorFilterEntry>(this);
+ connect(m_entriesWatcher, SIGNAL(finished()), SLOT(updateEntries()));
+
+ m_showPopupTimer = new QTimer(this);
+ m_showPopupTimer->setInterval(100);
+ m_showPopupTimer->setSingleShot(true);
+ connect(m_showPopupTimer, SIGNAL(timeout()), SLOT(showPopupNow()));
+}
+
+void LocatorWidget::setPlaceholderText(const QString &text)
+{
+ m_fileLineEdit->setPlaceholderText(text);
+}
+
+void LocatorWidget::updateFilterList()
+{
+ typedef QMap<Id, QAction *> IdActionMap;
+
+ m_filterMenu->clear();
+
+ // update actions and menu
+ IdActionMap actionCopy = m_filterActionMap;
+ m_filterActionMap.clear();
+ // register new actions, update existent
+ foreach (ILocatorFilter *filter, m_locatorPlugin->filters()) {
+ if (filter->shortcutString().isEmpty() || filter->isHidden())
+ continue;
+ Id filterId = filter->id();
+ Id locatorId = filterId.withPrefix("Locator.");
+ QAction *action = 0;
+ Command *cmd = 0;
+ if (!actionCopy.contains(filterId)) {
+ // register new action
+ action = new QAction(filter->displayName(), this);
+ cmd = ActionManager::registerAction(action, locatorId,
+ Context(Core::Constants::C_GLOBAL));
+ cmd->setAttribute(Command::CA_UpdateText);
+ connect(action, SIGNAL(triggered()), this, SLOT(filterSelected()));
+ action->setData(qVariantFromValue(filter));
+ } else {
+ action = actionCopy.take(filterId);
+ action->setText(filter->displayName());
+ cmd = ActionManager::command(locatorId);
+ }
+ m_filterActionMap.insert(filterId, action);
+ m_filterMenu->addAction(cmd->action());
+ }
+
+ // unregister actions that are deleted now
+ const IdActionMap::Iterator end = actionCopy.end();
+ for (IdActionMap::Iterator it = actionCopy.begin(); it != end; ++it) {
+ ActionManager::unregisterAction(it.value(), it.key().withPrefix("Locator."));
+ delete it.value();
+ }
+
+ m_filterMenu->addSeparator();
+ m_filterMenu->addAction(m_refreshAction);
+ m_filterMenu->addAction(m_configureAction);
+}
+
+bool LocatorWidget::eventFilter(QObject *obj, QEvent *event)
+{
+ if (obj == m_fileLineEdit && event->type() == QEvent::KeyPress) {
+ if (m_possibleToolTipRequest)
+ m_possibleToolTipRequest = false;
+ if (QToolTip::isVisible())
+ QToolTip::hideText();
+
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+ switch (keyEvent->key()) {
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_PageUp:
+ case Qt::Key_PageDown:
+ showCompletionList();
+ QApplication::sendEvent(m_completionList, event);
+ return true;
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ scheduleAcceptCurrentEntry();
+ return true;
+ case Qt::Key_Escape:
+ m_completionList->hide();
+ return true;
+ case Qt::Key_Tab:
+ m_completionList->next();
+ return true;
+ case Qt::Key_Backtab:
+ m_completionList->previous();
+ return true;
+ case Qt::Key_Alt:
+ if (keyEvent->modifiers() == Qt::AltModifier) {
+ m_possibleToolTipRequest = true;
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ } else if (obj == m_fileLineEdit && event->type() == QEvent::KeyRelease) {
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+ if (m_possibleToolTipRequest) {
+ m_possibleToolTipRequest = false;
+ if (m_completionList->isVisible()
+ && (keyEvent->key() == Qt::Key_Alt)
+ && (keyEvent->modifiers() == Qt::NoModifier)) {
+ const QModelIndex index = m_completionList->currentIndex();
+ if (index.isValid()) {
+ QToolTip::showText(m_completionList->pos() + m_completionList->visualRect(index).topRight(),
+ m_locatorModel->data(index, Qt::ToolTipRole).toString());
+ return true;
+ }
+ }
+ }
+ } else if (obj == m_fileLineEdit && event->type() == QEvent::FocusOut) {
+ QFocusEvent *fev = static_cast<QFocusEvent *>(event);
+ if (fev->reason() != Qt::ActiveWindowFocusReason || !m_completionList->isActiveWindow()) {
+ m_completionList->hide();
+ m_fileLineEdit->clearFocus();
+ }
+ } else if (obj == m_fileLineEdit && event->type() == QEvent::FocusIn) {
+ showPopupNow();
+ } else if (obj == this && event->type() == QEvent::ShortcutOverride) {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(event);
+ switch (ke->key()) {
+ case Qt::Key_Escape:
+ if (!ke->modifiers()) {
+ event->accept();
+ QTimer::singleShot(0, this, SLOT(setFocusToCurrentMode()));
+ return true;
+ }
+ case Qt::Key_Alt:
+ if (ke->modifiers() == Qt::AltModifier) {
+ event->accept();
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return QWidget::eventFilter(obj, event);
+}
+
+void LocatorWidget::setFocusToCurrentMode()
+{
+ Core::ModeManager::setFocusToCurrentMode();
+}
+
+void LocatorWidget::showCompletionList()
+{
+ const int border = m_completionList->frameWidth();
+ const QSize size = m_completionList->preferredSize();
+ const QRect rect(mapToGlobal(QPoint(-border, -size.height() - border)), size);
+ m_completionList->setGeometry(rect);
+ m_completionList->show();
+}
+
+void LocatorWidget::showPopup()
+{
+ m_updateRequested = true;
+ m_showPopupTimer->start();
+}
+
+void LocatorWidget::showPopupNow()
+{
+ m_showPopupTimer->stop();
+ updateCompletionList(m_fileLineEdit->text());
+ showCompletionList();
+}
+
+QList<ILocatorFilter *> LocatorWidget::filtersFor(const QString &text, QString &searchText)
+{
+ QList<ILocatorFilter *> filters = m_locatorPlugin->filters();
+ const int whiteSpace = text.indexOf(QLatin1Char(' '));
+ QString prefix;
+ if (whiteSpace >= 0)
+ prefix = text.left(whiteSpace);
+ if (!prefix.isEmpty()) {
+ prefix = prefix.toLower();
+ QList<ILocatorFilter *> prefixFilters;
+ foreach (ILocatorFilter *filter, filters) {
+ if (prefix == filter->shortcutString()) {
+ searchText = text.mid(whiteSpace+1);
+ prefixFilters << filter;
+ }
+ }
+ if (!prefixFilters.isEmpty())
+ return prefixFilters;
+ }
+ searchText = text;
+ QList<ILocatorFilter *> activeFilters;
+ foreach (ILocatorFilter *filter, filters)
+ if (filter->isIncludedByDefault())
+ activeFilters << filter;
+ return activeFilters;
+}
+
+void LocatorWidget::updateCompletionList(const QString &text)
+{
+ m_updateRequested = true;
+ QString searchText;
+ const QList<ILocatorFilter *> filters = filtersFor(text, searchText);
+
+ // cancel the old future
+ m_entriesWatcher->future().cancel();
+ m_entriesWatcher->future().waitForFinished();
+
+ QFuture<LocatorFilterEntry> future = QtConcurrent::run(runSearch, filters, searchText);
+ m_entriesWatcher->setFuture(future);
+}
+
+void LocatorWidget::updateEntries()
+{
+ m_updateRequested = false;
+ if (m_entriesWatcher->future().isCanceled()) {
+ // reset to usable state
+ m_acceptRequested = false;
+ return;
+ }
+
+ const QList<LocatorFilterEntry> entries = m_entriesWatcher->future().results();
+ m_locatorModel->setEntries(entries);
+ if (m_locatorModel->rowCount() > 0)
+ m_completionList->setCurrentIndex(m_locatorModel->index(0, 0));
+#if 0
+ m_completionList->updatePreferredSize();
+#endif
+ if (m_acceptRequested)
+ acceptCurrentEntry();
+}
+
+void LocatorWidget::scheduleAcceptCurrentEntry()
+{
+ if (m_updateRequested) {
+ // don't just accept the selected entry, since the list is not up to date
+ // accept will be called after the update finished
+ m_acceptRequested = true;
+ } else {
+ acceptCurrentEntry();
+ }
+}
+
+void LocatorWidget::acceptCurrentEntry()
+{
+ m_acceptRequested = false;
+ if (!m_completionList->isVisible())
+ return;
+ const QModelIndex index = m_completionList->currentIndex();
+ if (!index.isValid())
+ return;
+ const LocatorFilterEntry entry = m_locatorModel->data(index, Qt::UserRole).value<LocatorFilterEntry>();
+ m_completionList->hide();
+ m_fileLineEdit->clearFocus();
+ entry.filter->accept(entry);
+}
+
+void LocatorWidget::show(const QString &text, int selectionStart, int selectionLength)
+{
+ if (!text.isEmpty())
+ m_fileLineEdit->setText(text);
+ if (!m_fileLineEdit->hasFocus())
+ m_fileLineEdit->setFocus();
+ else
+ showPopupNow();
+ ICore::raiseWindow(ICore::mainWindow());
+
+ if (selectionStart >= 0) {
+ m_fileLineEdit->setSelection(selectionStart, selectionLength);
+ if (selectionLength == 0) // make sure the cursor is at the right position (Mac-vs.-rest difference)
+ m_fileLineEdit->setCursorPosition(selectionStart);
+ } else {
+ m_fileLineEdit->selectAll();
+ }
+}
+
+void LocatorWidget::filterSelected()
+{
+ QString searchText = tr("<type here>");
+ QAction *action = qobject_cast<QAction *>(sender());
+ QTC_ASSERT(action, return);
+ ILocatorFilter *filter = action->data().value<ILocatorFilter *>();
+ QTC_ASSERT(filter, return);
+ QString currentText = m_fileLineEdit->text().trimmed();
+ // add shortcut string at front or replace existing shortcut string
+ if (!currentText.isEmpty()) {
+ searchText = currentText;
+ foreach (ILocatorFilter *otherfilter, m_locatorPlugin->filters()) {
+ if (currentText.startsWith(otherfilter->shortcutString() + QLatin1Char(' '))) {
+ searchText = currentText.mid(otherfilter->shortcutString().length() + 1);
+ break;
+ }
+ }
+ }
+ show(filter->shortcutString() + QLatin1Char(' ') + searchText,
+ filter->shortcutString().length() + 1,
+ searchText.length());
+ updateCompletionList(m_fileLineEdit->text());
+ m_fileLineEdit->setFocus();
+}
+
+void LocatorWidget::showEvent(QShowEvent *event)
+{
+ QWidget::showEvent(event);
+}
+
+void LocatorWidget::showConfigureDialog()
+{
+ ICore::showOptionsDialog(Core::Constants::SETTINGS_CATEGORY_CORE, Constants::FILTER_OPTIONS_PAGE);
+}
+
+} // namespace Core
diff --git a/src/plugins/coreplugin/locator/locatorwidget.h b/src/plugins/coreplugin/locator/locatorwidget.h
new file mode 100644
index 0000000000..5bc150b362
--- /dev/null
+++ b/src/plugins/coreplugin/locator/locatorwidget.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef LOCATORWIDGET_H
+#define LOCATORWIDGET_H
+
+#include "locatorplugin.h"
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QLabel;
+class QLineEdit;
+class QMenu;
+class QTreeView;
+QT_END_NAMESPACE
+
+namespace Utils {
+ class FilterLineEdit;
+}
+
+namespace Core {
+namespace Internal {
+
+class LocatorModel;
+class CompletionList;
+
+class LocatorWidget
+ : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit LocatorWidget(LocatorPlugin *qop);
+
+ void updateFilterList();
+
+ void show(const QString &text, int selectionStart = -1, int selectionLength = 0);
+
+ void setPlaceholderText(const QString &text);
+
+private slots:
+ void showPopup();
+ void showPopupNow();
+ void acceptCurrentEntry();
+ void filterSelected();
+ void showConfigureDialog();
+ void updateEntries();
+ void scheduleAcceptCurrentEntry();
+ void setFocusToCurrentMode();
+
+private:
+ bool eventFilter(QObject *obj, QEvent *event);
+
+ void showEvent(QShowEvent *e);
+
+ void showCompletionList();
+ void updateCompletionList(const QString &text);
+ QList<ILocatorFilter*> filtersFor(const QString &text, QString &searchText);
+
+ LocatorPlugin *m_locatorPlugin;
+ LocatorModel *m_locatorModel;
+
+ CompletionList *m_completionList;
+ QMenu *m_filterMenu;
+ QAction *m_refreshAction;
+ QAction *m_configureAction;
+ Utils::FilterLineEdit *m_fileLineEdit;
+ QTimer *m_showPopupTimer;
+ QFutureWatcher<LocatorFilterEntry> *m_entriesWatcher;
+ QMap<Core::Id, QAction *> m_filterActionMap;
+ bool m_updateRequested;
+ bool m_acceptRequested;
+ bool m_possibleToolTipRequest;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // LOCATORWIDGET_H
diff --git a/src/plugins/coreplugin/locator/opendocumentsfilter.cpp b/src/plugins/coreplugin/locator/opendocumentsfilter.cpp
new file mode 100644
index 0000000000..61fb946c0a
--- /dev/null
+++ b/src/plugins/coreplugin/locator/opendocumentsfilter.cpp
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "opendocumentsfilter.h"
+
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/editormanager/ieditor.h>
+#include <utils/fileutils.h>
+
+#include <QFileInfo>
+
+using namespace Core;
+using namespace Core;
+using namespace Core::Internal;
+using namespace Utils;
+
+OpenDocumentsFilter::OpenDocumentsFilter()
+{
+ setId("Open documents");
+ setDisplayName(tr("Open Documents"));
+ setShortcutString(QString(QLatin1Char('o')));
+ setIncludedByDefault(true);
+
+ connect(EditorManager::instance(), SIGNAL(editorOpened(Core::IEditor*)),
+ this, SLOT(refreshInternally()));
+ connect(EditorManager::instance(), SIGNAL(editorsClosed(QList<Core::IEditor*>)),
+ this, SLOT(refreshInternally()));
+}
+
+QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry_)
+{
+ QList<LocatorFilterEntry> goodEntries;
+ QList<LocatorFilterEntry> betterEntries;
+ QString entry = entry_;
+ const QString lineNoSuffix = EditorManager::splitLineNumber(&entry);
+ const QChar asterisk = QLatin1Char('*');
+ QString pattern = QString(asterisk);
+ pattern += entry;
+ pattern += asterisk;
+ QRegExp regexp(pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
+ if (!regexp.isValid())
+ return goodEntries;
+ const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry);
+ foreach (const DocumentModel::Entry &editorEntry, m_editors) {
+ if (future.isCanceled())
+ break;
+ QString fileName = editorEntry.fileName();
+ if (fileName.isEmpty())
+ continue;
+ QString displayName = editorEntry.displayName();
+ if (regexp.exactMatch(displayName)) {
+ QFileInfo fi(fileName);
+ LocatorFilterEntry fiEntry(this, fi.fileName(), QString(fileName + lineNoSuffix));
+ fiEntry.extraInfo = FileUtils::shortNativePath(FileName(fi));
+ fiEntry.fileName = fileName;
+ QList<LocatorFilterEntry> &category = displayName.startsWith(entry, caseSensitivityForPrefix)
+ ? betterEntries : goodEntries;
+ category.append(fiEntry);
+ }
+ }
+ betterEntries.append(goodEntries);
+ return betterEntries;
+}
+
+void OpenDocumentsFilter::refreshInternally()
+{
+ m_editors.clear();
+ foreach (DocumentModel::Entry *e, EditorManager::documentModel()->documents()) {
+ DocumentModel::Entry entry;
+ // create copy with only the information relevant to use
+ // to avoid model deleting entries behind our back
+ entry.m_displayName = e->displayName();
+ entry.m_fileName = e->fileName();
+ m_editors.append(entry);
+ }
+}
+
+void OpenDocumentsFilter::refresh(QFutureInterface<void> &future)
+{
+ Q_UNUSED(future)
+ QMetaObject::invokeMethod(this, "refreshInternally", Qt::BlockingQueuedConnection);
+}
+
+void OpenDocumentsFilter::accept(LocatorFilterEntry selection) const
+{
+ EditorManager::openEditor(selection.internalData.toString(), Id(),
+ EditorManager::CanContainLineNumber);
+}
diff --git a/src/plugins/coreplugin/locator/opendocumentsfilter.h b/src/plugins/coreplugin/locator/opendocumentsfilter.h
new file mode 100644
index 0000000000..b16ca7144b
--- /dev/null
+++ b/src/plugins/coreplugin/locator/opendocumentsfilter.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef OPENDOCUMENTSFILTER_H
+#define OPENDOCUMENTSFILTER_H
+
+#include "ilocatorfilter.h"
+
+#include <coreplugin/editormanager/documentmodel.h>
+
+#include <QString>
+#include <QList>
+#include <QFutureInterface>
+
+namespace Core {
+namespace Internal {
+
+class OpenDocumentsFilter : public Core::ILocatorFilter
+{
+ Q_OBJECT
+
+public:
+ OpenDocumentsFilter();
+ QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry);
+ void accept(Core::LocatorFilterEntry selection) const;
+ void refresh(QFutureInterface<void> &future);
+
+public slots:
+ void refreshInternally();
+
+private:
+ QList<Core::DocumentModel::Entry> m_editors;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // OPENDOCUMENTSFILTER_H
diff --git a/src/plugins/coreplugin/locator/settingspage.cpp b/src/plugins/coreplugin/locator/settingspage.cpp
new file mode 100644
index 0000000000..abdec8be5d
--- /dev/null
+++ b/src/plugins/coreplugin/locator/settingspage.cpp
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "settingspage.h"
+#include "locatorconstants.h"
+
+#include "locatorplugin.h"
+#include "ilocatorfilter.h"
+#include "directoryfilter.h"
+
+#include <coreplugin/coreconstants.h>
+#include <utils/qtcassert.h>
+
+#include <QCoreApplication>
+
+Q_DECLARE_METATYPE(Core::ILocatorFilter*)
+
+using namespace Core;
+using namespace Core::Internal;
+
+SettingsPage::SettingsPage(LocatorPlugin *plugin)
+ : m_plugin(plugin), m_widget(0)
+{
+ setId(Constants::FILTER_OPTIONS_PAGE);
+ setDisplayName(QCoreApplication::translate("Locator", Core::Constants::FILTER_OPTIONS_PAGE));
+ setCategory(Core::Constants::SETTINGS_CATEGORY_CORE);
+ setDisplayCategory(QCoreApplication::translate("Core", Core::Constants::SETTINGS_TR_CATEGORY_CORE));
+ setCategoryIcon(QLatin1String(Core::Constants::SETTINGS_CATEGORY_CORE_ICON));
+}
+
+QWidget *SettingsPage::widget()
+{
+ if (!m_widget) {
+ m_widget = new QWidget;
+ m_ui.setupUi(m_widget);
+ m_ui.refreshInterval->setToolTip(m_ui.refreshIntervalLabel->toolTip());
+ connect(m_ui.filterList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
+ this, SLOT(updateButtonStates()));
+ connect(m_ui.filterList, SIGNAL(itemActivated(QListWidgetItem*)),
+ this, SLOT(configureFilter(QListWidgetItem*)));
+ connect(m_ui.editButton, SIGNAL(clicked()),
+ this, SLOT(configureFilter()));
+ connect(m_ui.addButton, SIGNAL(clicked()),
+ this, SLOT(addCustomFilter()));
+ connect(m_ui.removeButton, SIGNAL(clicked()),
+ this, SLOT(removeCustomFilter()));
+
+ m_ui.refreshInterval->setValue(m_plugin->refreshInterval());
+ m_filters = m_plugin->filters();
+ m_customFilters = m_plugin->customFilters();
+ saveFilterStates();
+ updateFilterList();
+ }
+ return m_widget;
+}
+
+void SettingsPage::apply()
+{
+ // Delete removed filters and clear added filters
+ qDeleteAll(m_removedFilters);
+ m_removedFilters.clear();
+ m_addedFilters.clear();
+
+ // Pass the new configuration on to the plugin
+ m_plugin->setFilters(m_filters);
+ m_plugin->setCustomFilters(m_customFilters);
+ m_plugin->setRefreshInterval(m_ui.refreshInterval->value());
+ requestRefresh();
+ m_plugin->saveSettings();
+ saveFilterStates();
+}
+
+void SettingsPage::finish()
+{
+ // If settings were applied, this shouldn't change anything. Otherwise it
+ // makes sure the filter states aren't changed permanently.
+ restoreFilterStates();
+
+ // Delete added filters and clear removed filters
+ qDeleteAll(m_addedFilters);
+ m_addedFilters.clear();
+ m_removedFilters.clear();
+
+ // Further cleanup
+ m_filters.clear();
+ m_customFilters.clear();
+ m_refreshFilters.clear();
+ delete m_widget;
+}
+
+void SettingsPage::requestRefresh()
+{
+ if (!m_refreshFilters.isEmpty())
+ m_plugin->refresh(m_refreshFilters);
+}
+
+void SettingsPage::saveFilterStates()
+{
+ m_filterStates.clear();
+ foreach (ILocatorFilter *filter, m_filters)
+ m_filterStates.insert(filter, filter->saveState());
+}
+
+void SettingsPage::restoreFilterStates()
+{
+ foreach (ILocatorFilter *filter, m_filterStates.keys())
+ filter->restoreState(m_filterStates.value(filter));
+}
+
+void SettingsPage::updateFilterList()
+{
+ m_ui.filterList->clear();
+ foreach (ILocatorFilter *filter, m_filters) {
+ if (filter->isHidden())
+ continue;
+
+ QString title;
+ if (filter->isIncludedByDefault())
+ title = filter->displayName();
+ else
+ title = tr("%1 (prefix: %2)").arg(filter->displayName()).arg(filter->shortcutString());
+ QListWidgetItem *item = new QListWidgetItem(title);
+ item->setData(Qt::UserRole, qVariantFromValue(filter));
+ m_ui.filterList->addItem(item);
+ }
+ if (m_ui.filterList->count() > 0)
+ m_ui.filterList->setCurrentRow(0);
+}
+
+void SettingsPage::updateButtonStates()
+{
+ QListWidgetItem *item = m_ui.filterList->currentItem();
+ ILocatorFilter *filter = (item ? item->data(Qt::UserRole).value<ILocatorFilter *>() : 0);
+ m_ui.editButton->setEnabled(filter && filter->isConfigurable());
+ m_ui.removeButton->setEnabled(filter && m_customFilters.contains(filter));
+}
+
+void SettingsPage::configureFilter(QListWidgetItem *item)
+{
+ if (!item)
+ item = m_ui.filterList->currentItem();
+ QTC_ASSERT(item, return);
+ ILocatorFilter *filter = item->data(Qt::UserRole).value<ILocatorFilter *>();
+ QTC_ASSERT(filter, return);
+
+ if (!filter->isConfigurable())
+ return;
+ bool needsRefresh = false;
+ filter->openConfigDialog(m_widget, needsRefresh);
+ if (needsRefresh && !m_refreshFilters.contains(filter))
+ m_refreshFilters.append(filter);
+ updateFilterList();
+}
+
+void SettingsPage::addCustomFilter()
+{
+ ILocatorFilter *filter = new DirectoryFilter;
+ bool needsRefresh = false;
+ if (filter->openConfigDialog(m_widget, needsRefresh)) {
+ m_filters.append(filter);
+ m_addedFilters.append(filter);
+ m_customFilters.append(filter);
+ m_refreshFilters.append(filter);
+ updateFilterList();
+ }
+}
+
+void SettingsPage::removeCustomFilter()
+{
+ QListWidgetItem *item = m_ui.filterList->currentItem();
+ QTC_ASSERT(item, return);
+ ILocatorFilter *filter = item->data(Qt::UserRole).value<ILocatorFilter *>();
+ QTC_ASSERT(m_customFilters.contains(filter), return);
+ m_filters.removeAll(filter);
+ m_customFilters.removeAll(filter);
+ m_refreshFilters.removeAll(filter);
+ if (m_addedFilters.contains(filter)) {
+ m_addedFilters.removeAll(filter);
+ delete filter;
+ } else {
+ m_removedFilters.append(filter);
+ }
+ updateFilterList();
+}
diff --git a/src/plugins/coreplugin/locator/settingspage.h b/src/plugins/coreplugin/locator/settingspage.h
new file mode 100644
index 0000000000..86ddb9d1a5
--- /dev/null
+++ b/src/plugins/coreplugin/locator/settingspage.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef SETTINGSPAGE_H
+#define SETTINGSPAGE_H
+
+#include "ui_settingspage.h"
+
+#include <coreplugin/dialogs/ioptionspage.h>
+
+#include <QHash>
+#include <QPointer>
+
+QT_BEGIN_NAMESPACE
+class QListWidgetItem;
+QT_END_NAMESPACE
+
+namespace Core {
+
+class ILocatorFilter;
+
+namespace Internal {
+
+class LocatorPlugin;
+
+class SettingsPage : public Core::IOptionsPage
+{
+ Q_OBJECT
+
+public:
+ explicit SettingsPage(LocatorPlugin *plugin);
+
+ QWidget *widget();
+ void apply();
+ void finish();
+
+private slots:
+ void updateButtonStates();
+ void configureFilter(QListWidgetItem *item = 0);
+ void addCustomFilter();
+ void removeCustomFilter();
+
+private:
+ void updateFilterList();
+ void saveFilterStates();
+ void restoreFilterStates();
+ void requestRefresh();
+
+ Ui::LocatorSettingsWidget m_ui;
+ LocatorPlugin *m_plugin;
+ QPointer<QWidget> m_widget;
+ QList<ILocatorFilter *> m_filters;
+ QList<ILocatorFilter *> m_addedFilters;
+ QList<ILocatorFilter *> m_removedFilters;
+ QList<ILocatorFilter *> m_customFilters;
+ QList<ILocatorFilter *> m_refreshFilters;
+ QHash<ILocatorFilter *, QByteArray> m_filterStates;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SETTINGSPAGE_H
diff --git a/src/plugins/coreplugin/locator/settingspage.ui b/src/plugins/coreplugin/locator/settingspage.ui
new file mode 100644
index 0000000000..797b650269
--- /dev/null
+++ b/src/plugins/coreplugin/locator/settingspage.ui
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Core::Internal::LocatorSettingsWidget</class>
+ <widget class="QWidget" name="Core::Internal::LocatorSettingsWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>367</width>
+ <height>242</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Filters</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QListWidget" name="filterList">
+ <property name="font">
+ <font/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QPushButton" name="addButton">
+ <property name="text">
+ <string>Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="removeButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="editButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Edit</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel" name="refreshIntervalLabel">
+ <property name="toolTip">
+ <string>Locator filters that do not update their cached data immediately, such as the custom directory filters, update it after this time interval.</string>
+ </property>
+ <property name="text">
+ <string>Refresh interval:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="refreshInterval">
+ <property name="frame">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::PlusMinus</enum>
+ </property>
+ <property name="suffix">
+ <string> min</string>
+ </property>
+ <property name="maximum">
+ <number>320</number>
+ </property>
+ <property name="singleStep">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>60</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/coreplugin/progressmanager/progressmanager.cpp b/src/plugins/coreplugin/progressmanager/progressmanager.cpp
index 89818a6d31..5180a37411 100644
--- a/src/plugins/coreplugin/progressmanager/progressmanager.cpp
+++ b/src/plugins/coreplugin/progressmanager/progressmanager.cpp
@@ -129,7 +129,7 @@ using namespace Core::Internal;
\c QFuture object. This is what you want to give the
ProgressManager in the addTask() function.
- Have a look at e.g Locator::ILocatorFilter. Locator filters implement
+ Have a look at e.g Core::ILocatorFilter. Locator filters implement
a function \c refresh which takes a \c QFutureInterface object
as a parameter. These functions look something like:
\code