diff options
Diffstat (limited to 'src')
1312 files changed, 165975 insertions, 0 deletions
diff --git a/src/app/Info.plist b/src/app/Info.plist new file mode 100644 index 00000000000..dbd50d35ee1 --- /dev/null +++ b/src/app/Info.plist @@ -0,0 +1,189 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDocumentTypes</key> + <array> + <dict> + <key>CFBundleTypeRole</key> + <string>Editor</string> + <key>CFBundleTypeIconFile</key> + <string>qtcreator.icns</string> + <key>CFBundleTypeExtensions</key> + <array> + <string>pro</string> + </array> + <key>CFBundleTypeName</key> + <string>Qt Project File</string> + <key>LSHandlerRank</key> + <string>Default</string> + </dict> + <dict> + <key>CFBundleTypeRole</key> + <string>Editor</string> + <key>CFBundleTypeExtensions</key> + <array> + <string>pri</string> + </array> + <key>CFBundleTypeName</key> + <string>Qt Project Include File</string> + <key>LSHandlerRank</key> + <string>Default</string> + </dict> + <dict> + <key>CFBundleTypeRole</key> + <string>Editor</string> + <key>CFBundleTypeExtensions</key> + <array> + <string>qrc</string> + </array> + <key>CFBundleTypeName</key> + <string>Qt Resource File</string> + <key>LSHandlerRank</key> + <string>Default</string> + </dict> + <dict> + <key>CFBundleTypeRole</key> + <string>Editor</string> + <key>CFBundleTypeExtensions</key> + <array> + <string>pri</string> + </array> + <key>CFBundleTypeName</key> + <string>Qt UI File</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>h</string> + <string>hpp</string> + </array> + <key>CFBundleTypeName</key> + <string>Header File</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + <string>utxt</string> + </array> + <key>CFBundleTypeRole</key> + <string>Editor</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>cc</string> + <string>CC</string> + <string>cp</string> + <string>CP</string> + <string>cpp</string> + <string>CPP</string> + <string>cxx</string> + <string>CXX</string> + <string>c++</string> + <string>C++</string> + </array> + <key>CFBundleTypeName</key> + <string>C++ Source File</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + <string>utxt</string> + </array> + <key>CFBundleTypeRole</key> + <string>Editor</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>mm</string> + <string>MM</string> + </array> + <key>CFBundleTypeName</key> + <string>Objective-C++ Source File</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + <string>utxt</string> + </array> + <key>CFBundleTypeRole</key> + <string>Editor</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>m</string> + </array> + <key>CFBundleTypeName</key> + <string>Objective-C Source File</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + <string>utxt</string> + </array> + <key>CFBundleTypeRole</key> + <string>Editor</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>c</string> + <string>C</string> + </array> + <key>CFBundleTypeName</key> + <string>C Source File</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + <string>utxt</string> + </array> + <key>CFBundleTypeRole</key> + <string>Editor</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>txt</string> + <string>text</string> + </array> + <key>CFBundleTypeName</key> + <string>Text File</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>TEXT</string> + </array> + <key>CFBundleTypeRole</key> + <string>Editor</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>*</string> + </array> + <key>CFBundleTypeName</key> + <string>NSStringPboardType</string> + <key>CFBundleTypeOSTypes</key> + <array> + <string>****</string> + </array> + <key>CFBundleTypeRole</key> + <string>Editor</string> + </dict> + </array> + <key>CFBundleGetInfoString</key> + <string>Qt Creator; Copyright Nokia Corporation</string> + <key>CFBundleIconFile</key> + <string>@ICON@</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleSignature</key> + <string>@TYPEINFO@</string> + <key>CFBundleExecutable</key> + <string>@EXECUTABLE@</string> + <key>CFBundleIdentifier</key> + <string>com.nokia.qtcreator</string> + <key>CFBundleVersion</key> + <string>0.9.1</string> + <key>CFBundleShortVersionString</key> + <string>0.9.1</string> +</dict> +</plist> diff --git a/src/app/app.pro b/src/app/app.pro new file mode 100644 index 00000000000..67bb37e4efb --- /dev/null +++ b/src/app/app.pro @@ -0,0 +1,82 @@ +IDE_BUILD_TREE = $$OUT_PWD/../../ + +include(../qworkbench.pri) +include(../../shared/qtsingleapplication/qtsingleapplication.pri) + +macx { + CONFIG(debug, debug|release):LIBS *= -lExtensionSystem_debug -lAggregation_debug + else:LIBS *= -lExtensionSystem -lAggregation +} +win32 { + CONFIG(debug, debug|release):LIBS *= -lExtensionSystemd -lAggregationd + else:LIBS *= -lExtensionSystem -lAggregation +} +linux-* { + LIBS *= -lExtensionSystem -lAggregation +} + +TEMPLATE = app +TARGET = $$IDE_APP_TARGET +DESTDIR = ../../bin + +SOURCES += main.cpp + +macx { + SNIPPETS.path = Contents/Resources + SNIPPETS.files = $$IDE_SOURCE_TREE/bin/snippets + TEMPLATES.path = Contents/Resources + TEMPLATES.files = $$IDE_SOURCE_TREE/bin/templates + DESIGNER.path = Contents/Resources + DESIGNER.files = $$IDE_SOURCE_TREE/bin/designer + SCHEMES.path = Contents/Resources + SCHEMES.files = $$IDE_SOURCE_TREE/bin/schemes + GDBDEBUGGER.path = Contents/Resources + GDBDEBUGGER.files = $$IDE_SOURCE_TREE/bin/gdbmacros + DOC.path = Contents/Resources/doc + DOC.files = $$IDE_SOURCE_TREE/doc/qtcreator.qch + LICENSE.path = Contents/Resources + LICENSE.files = $$IDE_SOURCE_TREE/bin/license.txt + RUNINTERMINAL.path = Contents/Resources + RUNINTERMINAL.files = $$IDE_SOURCE_TREE/bin/runInTerminal.command + QMAKE_BUNDLE_DATA += SNIPPETS TEMPLATES DESIGNER SCHEMES GDBDEBUGGER DOC LICENSE RUNINTERMINAL + QMAKE_INFO_PLIST = $$PWD/Info.plist +} +!macx { + # make sure the resources are in place + !exists($$OUT_PWD/app.pro) { + unix:SEPARATOR = ; + win32:SEPARATOR = & + # we are shadow build + COPYSRC = snippets \ + templates \ + designer \ + schemes \ + gdbmacros + COPYDEST = $${OUT_PWD}/../../bin + win32:COPYDEST ~= s|/+|\| + for(tmp,COPYSRC) { + REALSRC = $$IDE_SOURCE_TREE/bin/$$tmp + REALDEST = $$COPYDEST/$$tmp + win32:tmp ~= s|/+|\| + win32:REALSRC ~= s|/+|\| + win32:REALDEST ~= s|/+|\| + QMAKE_POST_LINK += $${QMAKE_COPY_DIR} $${REALSRC} $${REALDEST} $$SEPARATOR + } + } +} + +linux-* { + #do the rpath by hand since it's not possible to use ORIGIN in QMAKE_RPATHDIR + QMAKE_RPATHDIR += \$\$ORIGIN/../lib + IDE_PLUGIN_RPATH = $$join(QMAKE_RPATHDIR, ":") + QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,$${IDE_PLUGIN_RPATH}\' + QMAKE_RPATHDIR = +} + +win32 { + RC_FILE = qtcreator.rc +} + +macx { + ICON = qtcreator.icns +} diff --git a/src/app/main.cpp b/src/app/main.cpp new file mode 100644 index 00000000000..0247b1dde35 --- /dev/null +++ b/src/app/main.cpp @@ -0,0 +1,285 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "qtsingleapplication.h" + +#include <extensionsystem/pluginmanager.h> +#include <extensionsystem/pluginspec.h> +#include <extensionsystem/iplugin.h> + +#include <QtCore/QDir> +#include <QtCore/QTextStream> +#include <QtCore/QFileInfo> +#include <QtCore/QDebug> +#include <QtCore/QTimer> + +#include <QtGui/QMessageBox> +#include <QtGui/QApplication> +#include <QtGui/QMainWindow> + +#ifdef Q_OS_DARWIN +# include <sys/resource.h> +#endif + +enum { OptionIndent =4, DescriptionIndent = 24 }; + +static const char *appNameC = "Qt Creator"; +static const char *corePluginNameC = "Core"; +static const char *fixedOptionsC = +" [OPTION]... [FILE]...\n" +"Options:\n" +" -help Display this help\n" +" -version Display program version\n" +" -client Attempt to connect to already running instance\n"; + +static const char *HELP_OPTION1 = "-h"; +static const char *HELP_OPTION2 = "-help"; +static const char *HELP_OPTION3 = "/h"; +static const char *HELP_OPTION4 = "--help"; +static const char *VERSION_OPTION = "-version"; +static const char *CLIENT_OPTION = "-client"; + +typedef QSet<ExtensionSystem::PluginSpec *> PluginSpecSet; + +// Helpers for displaying messages. Note that there is no console on Windows. +#ifdef Q_WS_WIN +// Format as <pre> HTML +static inline void toHtml(QString &t) +{ + t.replace(QLatin1Char('&'), QLatin1String("&")); + t.replace(QLatin1Char('<'), QLatin1String("<")); + t.replace(QLatin1Char('>'), QLatin1String(">")); + t.insert(0, QLatin1String("<html><pre>")); + t.append(QLatin1String("</pre></html>")); +} + +static void displayHelpText(QString t) // No console on Windows. +{ + toHtml(t); + QMessageBox::information(0, QLatin1String(appNameC), t); +} + +static void displayError(const QString &t) // No console on Windows. +{ + QMessageBox::critical(0, QLatin1String(appNameC), t); +} + +#else + +static void displayHelpText(const QString &t) +{ + qWarning(t.toUtf8().constData()); +} + +static void displayError(const QString &t) +{ + qCritical(t.toUtf8().constData()); +} + +#endif + +static void printVersion(const ExtensionSystem::PluginSpec *coreplugin, + const ExtensionSystem::PluginManager &pm) +{ + QString version; + QTextStream str(&version); + str << '\n' << appNameC << ' ' << coreplugin->version()<< " based on Qt " << qVersion() << "\n\n"; + pm.formatPluginVersions(str); + str << '\n' << coreplugin->copyright() << '\n'; + displayHelpText(version); +} + +static void printHelp(const QString &a0, const ExtensionSystem::PluginManager &pm) +{ + QString help; + QTextStream str(&help); + str << "Usage: " << a0 << fixedOptionsC; + ExtensionSystem::PluginManager::formatOptions(str, OptionIndent, DescriptionIndent); + pm.formatPluginOptions(str, OptionIndent, DescriptionIndent); + displayHelpText(help); +} + +static inline QString msgCoreLoadFailure(const QString &why) +{ + return QCoreApplication::translate("Application", "Failed to load core: %1").arg(why); +} + +static inline QString msgSendArgumentFailed() +{ + return QCoreApplication::translate("Application", "Unable to send command line arguments to the already running instance. It appears to be not responding."); +} + +// Prepare a remote argument: If it is a relative file, add the current directory +// since the the central instance might be running in a different directory. + +static inline QString prepareRemoteArgument(const QString &a) +{ + QFileInfo fi(a); + if (!fi.exists()) + return a; + if (fi.isRelative()) + return fi.absoluteFilePath(); + return a; +} + +// Send the arguments to an already running instance of Qt Creator +static bool sendArguments(SharedTools::QtSingleApplication &app, const QStringList &arguments) +{ + if (!arguments.empty()) { + // Send off arguments + const QStringList::const_iterator acend = arguments.constEnd(); + for (QStringList::const_iterator it = arguments.constBegin(); it != acend; ++it) { + if (!app.sendMessage(prepareRemoteArgument(*it))) { + displayError(msgSendArgumentFailed()); + return false; + } + } + } + // Special empty argument means: Show and raise (the slot just needs to be triggered) + if (!app.sendMessage(QString())) { + displayError(msgSendArgumentFailed()); + return false; + } + return true; +} + +static inline QStringList getPluginPaths() +{ + QStringList rc; + // Figure out root: Up one from 'bin' + QDir rootDir = QApplication::applicationDirPath(); + rootDir.cdUp(); + const QString rootDirPath = rootDir.canonicalPath(); + // 1) "lib" dir + QString pluginPath = rootDirPath; + pluginPath += QDir::separator(); + pluginPath += QLatin1String("lib"); + rc.push_back(pluginPath); + // 2) "PlugIns" + pluginPath = rootDirPath; + pluginPath += QDir::separator(); + pluginPath += QLatin1String("PlugIns"); + rc.push_back(pluginPath); + return rc; +} + +int main(int argc, char **argv) +{ +#ifdef Q_OS_DARWIN + // increase the number of file that can be opened in Qt Creator. + struct rlimit rl; + getrlimit(RLIMIT_NOFILE, &rl); + rl.rlim_cur = rl.rlim_max; + setrlimit(RLIMIT_NOFILE, &rl); +#endif + + SharedTools::QtSingleApplication app((QLatin1String(appNameC)), argc, argv); + // Load + ExtensionSystem::PluginManager pluginManager; + pluginManager.setFileExtension(QLatin1String("pluginspec")); + + const QStringList pluginPaths = getPluginPaths(); + pluginManager.setPluginPaths(pluginPaths); + + const QStringList arguments = app.arguments(); + QMap<QString,QString> foundAppOptions; + if (arguments.size() > 1) { + QMap<QString,bool> appOptions; + appOptions.insert(QLatin1String(HELP_OPTION1), false); + appOptions.insert(QLatin1String(HELP_OPTION2), false); + appOptions.insert(QLatin1String(HELP_OPTION3), false); + appOptions.insert(QLatin1String(HELP_OPTION4), false); + appOptions.insert(QLatin1String(VERSION_OPTION), false); + appOptions.insert(QLatin1String(CLIENT_OPTION), false); + QString errorMessage; + if (!pluginManager.parseOptions(arguments, + appOptions, + &foundAppOptions, + &errorMessage)) { + displayError(errorMessage); + printHelp(QFileInfo(app.applicationFilePath()).baseName(), pluginManager); + return -1; + } + } + + const PluginSpecSet plugins = pluginManager.plugins(); + ExtensionSystem::PluginSpec *coreplugin = 0; + foreach (ExtensionSystem::PluginSpec *spec, plugins) { + if (spec->name() == QLatin1String(corePluginNameC)) { + coreplugin = spec; + break; + } + } + if (!coreplugin) { + const QString reason = QCoreApplication::translate("Application", "Couldn't find 'Core.pluginspec' in %1").arg(pluginPaths.join(QLatin1String(","))); + displayError(msgCoreLoadFailure(reason)); + return 1; + } + if (coreplugin->hasError()) { + displayError(msgCoreLoadFailure(coreplugin->errorString())); + return 1; + } + if (foundAppOptions.contains(QLatin1String(VERSION_OPTION))) { + printVersion(coreplugin, pluginManager); + return 0; + } + if (foundAppOptions.contains(QLatin1String(HELP_OPTION1)) + || foundAppOptions.contains(QLatin1String(HELP_OPTION2)) + || foundAppOptions.contains(QLatin1String(HELP_OPTION3)) + || foundAppOptions.contains(QLatin1String(HELP_OPTION4))) { + printHelp(QFileInfo(app.applicationFilePath()).baseName(), pluginManager); + return 0; + } + + const bool isFirstInstance = !app.isRunning(); + if (!isFirstInstance && foundAppOptions.contains(QLatin1String(CLIENT_OPTION))) + return sendArguments(app, pluginManager.arguments()) ? 0 : -1; + + pluginManager.loadPlugins(); + if (coreplugin->hasError()) { + displayError(msgCoreLoadFailure(coreplugin->errorString())); + return 1; + } + if (isFirstInstance) { + // Set up lock and remote arguments for the first instance only. + // Silently fallback to unconnected instances for any subsequent + // instances. + app.initialize(); + QObject::connect(&app, SIGNAL(messageReceived(QString)), coreplugin->plugin(), SLOT(remoteArgument(QString))); + } + QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), coreplugin->plugin(), SLOT(remoteArgument(QString))); + + // Do this after the event loop has started + QTimer::singleShot(100, &pluginManager, SLOT(startTests())); + return app.exec(); +} diff --git a/src/app/qtcreator.icns b/src/app/qtcreator.icns Binary files differnew file mode 100644 index 00000000000..9d309e683e8 --- /dev/null +++ b/src/app/qtcreator.icns diff --git a/src/app/qtcreator.ico b/src/app/qtcreator.ico Binary files differnew file mode 100644 index 00000000000..4aaeed08733 --- /dev/null +++ b/src/app/qtcreator.ico diff --git a/src/app/qtcreator.rc b/src/app/qtcreator.rc new file mode 100644 index 00000000000..5f337b2c577 --- /dev/null +++ b/src/app/qtcreator.rc @@ -0,0 +1 @@ + IDI_ICON1 ICON DISCARDABLE "qtcreator.ico" diff --git a/src/libs/aggregation/aggregate.cpp b/src/libs/aggregation/aggregate.cpp new file mode 100644 index 00000000000..bfd9e0f4dce --- /dev/null +++ b/src/libs/aggregation/aggregate.cpp @@ -0,0 +1,265 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "aggregate.h" + +#include <QtCore/QWriteLocker> + +/*! + \namespace Aggregation + \brief Contains support for bundling related components, such that + each component exposes the properties and behavior of the + other components to the outside. + + Components that are bundled to an Aggregate can be "cast" to each other + and have a coupled life cycle. See the documentation of Aggregate for + details and examples. +*/ + +/*! + \class Aggregation::Aggregate + \mainclass + \threadsafe + + \brief Defines a collection of related components that can be viewed as a unit. + + An Aggregate is a collection of components that are handled as a unit, + such that each component exposes the properties and behavior of the + other components in the Aggregate to the outside. + Specifically that means: + \list + \o They can be "cast" to each other (using query and query_all methods). + \o Their life cycle is coupled, i.e. whenever one is deleted all of them are. + \endlist + Components can be of any QObject derived type. + + You can use an Aggregate to simulate multiple inheritance by aggregation. Assume we have + \code + using namespace Aggregation; + class MyInterface : public QObject { ........ }; + class MyInterfaceEx : public QObject { ........ }; + [...] + MyInterface *object = new MyInterface; // this is single inheritance + \endcode + The query method works like a qobject_cast with normal objects: + \code + Q_ASSERT(query<MyInterface>(object) == object); + Q_ASSERT(query<MyInterfaceEx>(object) == 0); + \endcode + If we want 'object' to also implement the class MyInterfaceEx, + but don't want to or cannot use multiple inheritance, we can do it + at any point using an Aggregate: + \code + MyInterfaceEx *objectEx = new MyInterfaceEx; + Aggregate *aggregate = new Aggregate; + aggregate->add(object); + aggregate->add(objectEx); + \endcode + The Aggregate bundles the two objects together. + If we have any part of the collection we get all parts: + \code + Q_ASSERT(query<MyInterface>(object) == object); + Q_ASSERT(query<MyInterfaceEx>(object) == objectEx); + Q_ASSERT(query<MyInterface>(objectEx) == object); + Q_ASSERT(query<MyInterfaceEx>(objectEx) == objectEx); + \endcode + The following deletes all three: object, objectEx and aggregate: + \code + delete objectEx; + // or delete object; + // or delete aggregate; + \endcode + + Aggregation aware code never uses qobject_cast, but always uses + Aggregation::query which behaves like a qobject_cast as a fallback. +*/ + +/*! + \fn T *Aggregate::component() + + Template method that returns the component with the given type, if there is one. + If there are multiple components with that type a random one is returned. + + \sa Aggregate::components() + \sa Aggregate::add() +*/ + +/*! + \fn QList<T *> Aggregate::components() + + Template method that returns all components with the given type, if there are any. + + \sa Aggregate::component() + \sa Aggregate::add() +*/ + +/*! + \fn T *Aggregation::query<T *>(Aggregate *obj) + \internal +*/ + +/*! + \fn QList<T *> Aggregation::query_all<T *>(Aggregate *obj) + \internal +*/ + +/*! + \relates Aggregation::Aggregate + \fn T *Aggregation::query<T *>(QObject *obj) + + Performs a dynamic cast that is aware of a possible Aggregate that \a obj + might belong to. If \a obj itself is of the requested type then it is simply cast + and returned. Otherwise, if \a obj belongs to an Aggregate all its components are + checked, or if it doesn't belong to an Aggregate null is returned. + + \sa Aggregate::component() +*/ + +/*! + \relates Aggregation::Aggregate + \fn QList<T *> Aggregation::query_all<T *>(QObject *obj) + + If \a obj belongs to an Aggregate, all components that can be cast to the given + type are returned. Otherwise, \a obj is returned if it is of the requested type. + + \sa Aggregate::components() +*/ + +using namespace Aggregation; + +/*! + \fn Aggregate *Aggregate::parentAggregate(QObject *obj) + + Returns the Aggregate object of \a obj if there is one. Otherwise returns 0. +*/ +Aggregate *Aggregate::parentAggregate(QObject *obj) +{ + QReadLocker locker(&lock()); + return aggregateMap().value(obj); +} + +QHash<QObject *, Aggregate *> &Aggregate::aggregateMap() +{ + static QHash<QObject *, Aggregate *> map; + return map; +} + +/*! + \fn QReadWriteLock &Aggregate::lock() + \internal +*/ +QReadWriteLock &Aggregate::lock() +{ + static QReadWriteLock lock; + return lock; +} + +/*! + \fn Aggregate::Aggregate(QObject *parent) + + Creates a new Aggregate with the given \a parent. + The \a parent is passed directly passed to the QObject part + of the class and is not used beside that. +*/ +Aggregate::Aggregate(QObject *parent) + : QObject(parent) +{ + QWriteLocker locker(&lock()); + aggregateMap().insert(this, this); +} + +/*! + \fn Aggregate::~Aggregate() + + Deleting the aggregate automatically deletes all its components. +*/ +Aggregate::~Aggregate() +{ + QWriteLocker locker(&lock()); + foreach (QObject *component, m_components) { + disconnect(component, SIGNAL(destroyed(QObject*)), this, SLOT(deleteSelf(QObject*))); + aggregateMap().remove(component); + } + qDeleteAll(m_components); + m_components.clear(); + aggregateMap().remove(this); +} + +void Aggregate::deleteSelf(QObject *obj) +{ + { + QWriteLocker locker(&lock()); + aggregateMap().remove(obj); + m_components.removeAll(obj); + } + delete this; +} + +/*! + \fn void Aggregate::add(QObject *component) + + Adds the \a component to the aggregate. + + \sa Aggregate::remove() +*/ +void Aggregate::add(QObject *component) +{ + if (!component) + return; + QWriteLocker locker(&lock()); + Aggregate *parentAggregation = aggregateMap().value(component); + if (parentAggregation == this) + return; + if (parentAggregation) + parentAggregation->remove(component); + m_components.append(component); + connect(component, SIGNAL(destroyed(QObject*)), this, SLOT(deleteSelf(QObject*))); + aggregateMap().insert(component, this); +} + +/*! + \fn void Aggregate::remove(QObject *component) + + Removes the \a component from the aggregate. + + \sa Aggregate::add() +*/ +void Aggregate::remove(QObject *component) +{ + if (!component) + return; + QWriteLocker locker(&lock()); + aggregateMap().remove(component); + m_components.removeAll(component); + disconnect(component, SIGNAL(destroyed(QObject*)), this, SLOT(deleteSelf(QObject*))); +} + diff --git a/src/libs/aggregation/aggregate.h b/src/libs/aggregation/aggregate.h new file mode 100644 index 00000000000..5040aec8b2e --- /dev/null +++ b/src/libs/aggregation/aggregate.h @@ -0,0 +1,134 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QAGGREGATION_H +#define QAGGREGATION_H + +#include "aggregation_global.h" + +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QtCore/QHash> +#include <QtCore/QReadWriteLock> +#include <QtCore/QReadLocker> + +namespace Aggregation { + +class AGGREGATION_EXPORT Aggregate : public QObject +{ + Q_OBJECT + +public: + Aggregate(QObject *parent = 0); + virtual ~Aggregate(); + + void add(QObject *component); + void remove(QObject *component); + + template <typename T> T *component() { + QReadLocker(&lock()); + foreach (QObject *component, m_components) { + if (T *result = qobject_cast<T *>(component)) + return result; + } + return (T *)0; + } + + template <typename T> QList<T *> components() { + QReadLocker(&lock()); + QList<T *> results; + foreach (QObject *component, m_components) { + if (T *result = qobject_cast<T *>(component)) { + results << result; + } + } + return results; + } + + static Aggregate *parentAggregate(QObject *obj); + static QReadWriteLock &lock(); + +private slots: + void deleteSelf(QObject *obj); + +private: + static QHash<QObject *, Aggregate *> &aggregateMap(); + + QList<QObject *> m_components; +}; + +// get a component via global template function +template <typename T> T *query(Aggregate *obj) +{ + if (!obj) + return (T *)0; + return obj->template component<T>(); +} + +template <typename T> T *query(QObject *obj) +{ + if (!obj) + return (T *)0; + T *result = qobject_cast<T *>(obj); + if (!result) { + QReadLocker(&lock()); + Aggregate *parentAggregation = Aggregate::parentAggregate(obj); + result = (parentAggregation ? query<T>(parentAggregation) : 0); + } + return result; +} + +// get all components of a specific type via template function +template <typename T> QList<T *> query_all(Aggregate *obj) +{ + if (!obj) + return QList<T *>(); + return obj->template components<T>(); +} + +template <typename T> QList<T *> query_all(QObject *obj) +{ + if (!obj) + return QList<T *>(); + QReadLocker(&lock()); + Aggregate *parentAggregation = Aggregate::parentAggregate(obj); + QList<T *> results; + if (parentAggregation) + results = query_all<T>(parentAggregation); + else if (T *result = qobject_cast<T *>(obj)) + results.append(result); + return results; +} + +} // namespace Aggregation + +#endif // header guard diff --git a/src/libs/aggregation/aggregation.pri b/src/libs/aggregation/aggregation.pri new file mode 100644 index 00000000000..a6c48c59cbb --- /dev/null +++ b/src/libs/aggregation/aggregation.pri @@ -0,0 +1 @@ +LIBS *= -l$$qtLibraryTarget(Aggregation) diff --git a/src/libs/aggregation/aggregation.pro b/src/libs/aggregation/aggregation.pro new file mode 100644 index 00000000000..c970be0ed15 --- /dev/null +++ b/src/libs/aggregation/aggregation.pro @@ -0,0 +1,12 @@ +TEMPLATE = lib +TARGET = Aggregation + +include(../../qworkbenchlibrary.pri) + +DEFINES += AGGREGATION_LIBRARY + +HEADERS = aggregate.h \ + aggregation_global.h + +SOURCES = aggregate.cpp + diff --git a/src/libs/aggregation/aggregation_global.h b/src/libs/aggregation/aggregation_global.h new file mode 100644 index 00000000000..cae0917d651 --- /dev/null +++ b/src/libs/aggregation/aggregation_global.h @@ -0,0 +1,44 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef AGGREGATION_GLOBAL_H +#define AGGREGATION_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(AGGREGATION_LIBRARY) +# define AGGREGATION_EXPORT Q_DECL_EXPORT +#else +# define AGGREGATION_EXPORT Q_DECL_IMPORT +#endif + +#endif // header diff --git a/src/libs/aggregation/examples/examples.pro b/src/libs/aggregation/examples/examples.pro new file mode 100644 index 00000000000..7389255366d --- /dev/null +++ b/src/libs/aggregation/examples/examples.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS = text diff --git a/src/libs/aggregation/examples/text/main.cpp b/src/libs/aggregation/examples/text/main.cpp new file mode 100644 index 00000000000..1a0ecac8167 --- /dev/null +++ b/src/libs/aggregation/examples/text/main.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "main.h" + +#include <QtGui/QApplication> + +MyMain::MyMain(QWidget *parent, Qt::WFlags flags) + : QWidget(parent, flags) +{ + ui.setupUi(this); + connect(ui.comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(select(int))); +} + +void MyMain::add(IComboEntry *obj) +{ + m_entries.append(obj); + ui.comboBox->addItem(obj->title()); +} + +void MyMain::select(int index) +{ + IComboEntry *entry = m_entries.at(index); + // with multiple inheritance we would use qobject_cast here + // instead we use query, to get the components if they exist + IText1 *t1 = Aggregation::query<IText1>(entry); + IText2 *t2 = Aggregation::query<IText2>(entry); + IText3 *t3 = Aggregation::query<IText3>(entry); + // set the label texts and enable/disable, depending on whether + // the respective interface implementations exist + ui.text1->setText(t1 ? t1->text() : tr("N/A")); + ui.text2->setText(t2 ? t2->text() : tr("N/A")); + ui.text3->setText(t3 ? t3->text() : tr("N/A")); + ui.text1->setEnabled(t1); + ui.text2->setEnabled(t2); + ui.text3->setEnabled(t3); +} + +MyMain::~MyMain() +{ + // the following deletes all the Aggregate and IComboEntry and ITextX + // objects, as well as any other components we might have added + qDeleteAll(m_entries); +} + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + MyMain w; + // create and set up some objects + + // the first does only implement the required IComboEntry + // we don't need an aggregation for this + IComboEntry *obj1 = new IComboEntry("Entry without text"); + + // the second additionally provides an IText2 implementation + // adding a component to the aggregation is done by setting the aggregation as the parent of the component + Aggregation::Aggregate *obj2 = new Aggregation::Aggregate; + obj2->add(new IComboEntry("Entry with text 2")); + obj2->add(new IText2("This is a text for label 2")); + + // and so on... two more objects... + Aggregation::Aggregate *obj3 = new Aggregation::Aggregate; + obj3->add(new IComboEntry("Entry with text 1 and 2")); + obj3->add(new IText1("I love Qt!")); + obj3->add(new IText2("There are software companies...")); + Aggregation::Aggregate *obj4 = new Aggregation::Aggregate; + obj4->add(new IComboEntry("Entry with text 1 and 3")); + obj4->add(new IText1("Some text written here.")); + obj4->add(new IText3("I'm a troll.")); + + // the API takes IComboEntries, so we convert the them to it + // the MyMain object takes the ownership of the whole aggregations + w.add(Aggregation::query<IComboEntry>(obj1)); + w.add(Aggregation::query<IComboEntry>(obj2)); + w.add(Aggregation::query<IComboEntry>(obj3)); + w.add(Aggregation::query<IComboEntry>(obj4)); + w.show(); + return app.exec(); +} + diff --git a/src/libs/aggregation/examples/text/main.h b/src/libs/aggregation/examples/text/main.h new file mode 100644 index 00000000000..3a458923b42 --- /dev/null +++ b/src/libs/aggregation/examples/text/main.h @@ -0,0 +1,62 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef MAIN_H +#define MAIN_H + +#include "myinterfaces.h" + +#include <aggregate.h> + +#include <QtGui/QWidget> +#include "ui_main.h" + +class MyMain : public QWidget +{ + Q_OBJECT + +public: + MyMain(QWidget *parent = 0, Qt::WFlags flags = 0); + ~MyMain(); + + void add(IComboEntry *obj); + +private slots: + void select(int index); + +private: + Ui::mainClass ui; + + QList<IComboEntry *> m_entries; +}; + +#endif // MAIN_H diff --git a/src/libs/aggregation/examples/text/main.ui b/src/libs/aggregation/examples/text/main.ui new file mode 100644 index 00000000000..32d39511ee7 --- /dev/null +++ b/src/libs/aggregation/examples/text/main.ui @@ -0,0 +1,128 @@ +<ui version="4.0" > + <class>mainClass</class> + <widget class="QWidget" name="mainClass" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>399</width> + <height>176</height> + </rect> + </property> + <property name="windowTitle" > + <string>main</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QComboBox" name="comboBox" /> + </item> + <item> + <layout class="QVBoxLayout" > + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QLabel" name="label" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Text1:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="text1" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="MinimumExpanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>N/A</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Text2:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="text2" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="MinimumExpanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>N/A</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QLabel" name="label_5" > + <property name="text" > + <string>Text3:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="text3" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="MinimumExpanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>N/A</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <resources/> + <connections/> +</ui> diff --git a/src/libs/aggregation/examples/text/myinterfaces.h b/src/libs/aggregation/examples/text/myinterfaces.h new file mode 100644 index 00000000000..62dad429e2a --- /dev/null +++ b/src/libs/aggregation/examples/text/myinterfaces.h @@ -0,0 +1,87 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef MYINTERFACES_H +#define MYINTERFACES_H + +#include <aggregate.h> + +#include <QtCore/QString> + +class IComboEntry : public QObject +{ + Q_OBJECT + +public: + IComboEntry(QString title) : m_title(title) {} + virtual ~IComboEntry() {} + QString title() const { return m_title; } +private: + QString m_title; +}; + +class IText1 : public QObject +{ + Q_OBJECT + +public: + IText1(QString text) : m_text(text) {} + virtual ~IText1() {} + QString text() const { return m_text; } +private: + QString m_text; +}; + +class IText2 : public QObject +{ + Q_OBJECT + +public: + IText2(QString text) : m_text(text) {} + QString text() const { return m_text; } +private: + QString m_text; +}; + +class IText3 : public QObject +{ + Q_OBJECT + +public: + IText3(QString text) : m_text(text) {} + virtual ~IText3() {} + QString text() const { return m_text; } +private: + QString m_text; +}; + +#endif // MYINTERFACES_H diff --git a/src/libs/aggregation/examples/text/text.pro b/src/libs/aggregation/examples/text/text.pro new file mode 100644 index 00000000000..3893c5e95f6 --- /dev/null +++ b/src/libs/aggregation/examples/text/text.pro @@ -0,0 +1,14 @@ +TARGET = text +TEMPLATE = app +QT += core \ + gui +DEFINES += AGGREGATION_LIBRARY +INCLUDEPATH += ../../ +SOURCES += main.cpp \ + ../../aggregate.cpp +HEADERS += main.h \ + myinterfaces.h \ + ../../aggregate.h \ + ../../aggregation_global.h +FORMS += main.ui + diff --git a/src/libs/aggregation/test/test.pro b/src/libs/aggregation/test/test.pro new file mode 100644 index 00000000000..6143ca98050 --- /dev/null +++ b/src/libs/aggregation/test/test.pro @@ -0,0 +1,12 @@ +CONFIG += qtestlib +TEMPLATE = app +CONFIG -= app_bundle +DEFINES += AGGREGATION_LIBRARY + +INCLUDEPATH += ../ +# Input +SOURCES += tst_aggregate.cpp \ + ../aggregate.cpp +HEADERS += ../aggregate.h \ + ../aggregation_global.h + diff --git a/src/libs/aggregation/test/tst_aggregate.cpp b/src/libs/aggregation/test/tst_aggregate.cpp new file mode 100644 index 00000000000..098cc723af9 --- /dev/null +++ b/src/libs/aggregation/test/tst_aggregate.cpp @@ -0,0 +1,198 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtTest/QtTest> + +#include <aggregate.h> + +class tst_Aggregate : public QObject +{ + Q_OBJECT + +private slots: + void deleteAggregation(); + void queryAggregation(); + void queryAll(); + void parentAggregate(); +}; + +class Interface1 : public QObject +{ + Q_OBJECT +}; + +class Interface11 : public Interface1 +{ + Q_OBJECT +}; + +class Interface2 : public QObject +{ + Q_OBJECT +}; + +class Interface3 : public QObject +{ + Q_OBJECT +}; + +void tst_Aggregate::deleteAggregation() +{ + QPointer<Aggregation::Aggregate> aggregation; + QPointer<QObject> component1; + QPointer<QObject> component2; + + aggregation = new Aggregation::Aggregate; + component1 = new Interface1; + component2 = new Interface2; + aggregation->add(component1); + aggregation->add(component2); + delete aggregation; + QVERIFY(aggregation == 0); + QVERIFY(component1 == 0); + QVERIFY(component2 == 0); + + aggregation = new Aggregation::Aggregate; + component1 = new Interface1; + component2 = new Interface2; + aggregation->add(component1); + aggregation->add(component2); + delete component1; + QVERIFY(aggregation == 0); + QVERIFY(component1 == 0); + QVERIFY(component2 == 0); + + aggregation = new Aggregation::Aggregate; + component1 = new Interface1; + component2 = new Interface2; + aggregation->add(component1); + aggregation->add(component2); + delete component2; + QVERIFY(aggregation == 0); + QVERIFY(component1 == 0); + QVERIFY(component2 == 0); + + // if a component doesn't belong to an aggregation, it should simply delete itself + component1 = new Interface1; + delete component1; + QVERIFY(component1 == 0); +} + +void tst_Aggregate::queryAggregation() +{ + Aggregation::Aggregate aggregation; + QObject *aggObject = &aggregation; + QObject *component1 = new Interface11; + QObject *component2 = new Interface2; + aggregation.add(component1); + aggregation.add(component2); + QCOMPARE(Aggregation::query<Interface1>(&aggregation), component1); + QCOMPARE(Aggregation::query<Interface2>(&aggregation), component2); + QCOMPARE(Aggregation::query<Interface11>(&aggregation), component1); + QCOMPARE(Aggregation::query<Interface3>(&aggregation), (Interface3 *)0); + + QCOMPARE(Aggregation::query<Interface1>(aggObject), component1); + QCOMPARE(Aggregation::query<Interface2>(aggObject), component2); + QCOMPARE(Aggregation::query<Interface11>(aggObject), component1); + QCOMPARE(Aggregation::query<Interface3>(aggObject), (Interface3 *)0); + + QCOMPARE(Aggregation::query<Interface1>(component1), component1); + QCOMPARE(Aggregation::query<Interface2>(component1), component2); + QCOMPARE(Aggregation::query<Interface11>(component1), component1); + QCOMPARE(Aggregation::query<Interface3>(component1), (Interface3 *)0); + + QCOMPARE(Aggregation::query<Interface1>(component2), component1); + QCOMPARE(Aggregation::query<Interface2>(component2), component2); + QCOMPARE(Aggregation::query<Interface11>(component2), component1); + QCOMPARE(Aggregation::query<Interface3>(component2), (Interface3 *)0); + + // components that don't belong to an aggregation should be query-able to itself only + QObject *component3 = new Interface3; + QCOMPARE(Aggregation::query<Interface1>(component3), (Interface1 *)0); + QCOMPARE(Aggregation::query<Interface2>(component3), (Interface2 *)0); + QCOMPARE(Aggregation::query<Interface11>(component3), (Interface11 *)0); + QCOMPARE(Aggregation::query<Interface3>(component3), component3); + delete component3; +} + +void tst_Aggregate::queryAll() +{ + Aggregation::Aggregate aggregation; + QObject *aggObject = &aggregation; + Interface1 *component1 = new Interface1; + Interface11 *component11 = new Interface11; + Interface2 *component2 = new Interface2; + aggregation.add(component1); + aggregation.add(component11); + aggregation.add(component2); + QCOMPARE(Aggregation::query_all<Interface1>(&aggregation), QList<Interface1 *>() << component1 << component11); + QCOMPARE(Aggregation::query_all<Interface11>(&aggregation), QList<Interface11 *>() << component11); + QCOMPARE(Aggregation::query_all<Interface2>(&aggregation), QList<Interface2 *>() << component2); + QCOMPARE(Aggregation::query_all<Interface3>(&aggregation), QList<Interface3 *>()); + + QCOMPARE(Aggregation::query_all<Interface1>(aggObject), QList<Interface1 *>() << component1 << component11); + QCOMPARE(Aggregation::query_all<Interface11>(aggObject), QList<Interface11 *>() << component11); + QCOMPARE(Aggregation::query_all<Interface2>(aggObject), QList<Interface2 *>() << component2); + QCOMPARE(Aggregation::query_all<Interface3>(aggObject), QList<Interface3 *>()); + + QCOMPARE(Aggregation::query_all<Interface1>(component1), QList<Interface1 *>() << component1 << component11); + QCOMPARE(Aggregation::query_all<Interface11>(component1), QList<Interface11 *>() << component11); + QCOMPARE(Aggregation::query_all<Interface2>(component1), QList<Interface2 *>() << component2); + QCOMPARE(Aggregation::query_all<Interface3>(component1), QList<Interface3 *>()); + + QCOMPARE(Aggregation::query_all<Interface1>(component11), QList<Interface1 *>() << component1 << component11); + QCOMPARE(Aggregation::query_all<Interface11>(component11), QList<Interface11 *>() << component11); + QCOMPARE(Aggregation::query_all<Interface2>(component11), QList<Interface2 *>() << component2); + QCOMPARE(Aggregation::query_all<Interface3>(component11), QList<Interface3 *>()); + + QCOMPARE(Aggregation::query_all<Interface1>(component2), QList<Interface1 *>() << component1 << component11); + QCOMPARE(Aggregation::query_all<Interface11>(component2), QList<Interface11 *>() << component11); + QCOMPARE(Aggregation::query_all<Interface2>(component2), QList<Interface2 *>() << component2); + QCOMPARE(Aggregation::query_all<Interface3>(component2), QList<Interface3 *>()); +} + +void tst_Aggregate::parentAggregate() +{ + Aggregation::Aggregate aggregation; + Interface1 *component1 = new Interface1; + Interface11 *component11 = new Interface11; + QObject *component2 = new QObject; + aggregation.add(component1); + aggregation.add(component11); + QCOMPARE(Aggregation::Aggregate::parentAggregate(&aggregation), &aggregation); + QCOMPARE(Aggregation::Aggregate::parentAggregate(component1), &aggregation); + QCOMPARE(Aggregation::Aggregate::parentAggregate(component11), &aggregation); + QCOMPARE(Aggregation::Aggregate::parentAggregate(component2), (Aggregation::Aggregate *)0); +} + +QTEST_MAIN(tst_Aggregate) +#include "tst_aggregate.moc" diff --git a/src/libs/cplusplus/CppDocument.cpp b/src/libs/cplusplus/CppDocument.cpp new file mode 100644 index 00000000000..b2e0ca4be14 --- /dev/null +++ b/src/libs/cplusplus/CppDocument.cpp @@ -0,0 +1,242 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "CppDocument.h" +#include <Control.h> +#include <TranslationUnit.h> +#include <DiagnosticClient.h> +#include <Semantic.h> +#include <Literals.h> +#include <Symbols.h> +#include <AST.h> +#include <Scope.h> +#include <QByteArray> +#include <QFile> +#include <QtDebug> + +using namespace CPlusPlus; + +namespace { + class DocumentDiagnosticClient: public DiagnosticClient + { + enum { MAX_MESSAGE_COUNT = 10 }; + + public: + DocumentDiagnosticClient(Document *doc, QList<Document::DiagnosticMessage> *messages) + : doc(doc), + messages(messages) + { } + + virtual void report(int level, + StringLiteral *fileId, + unsigned line, unsigned column, + const char *format, va_list ap) + { + if (messages->count() == MAX_MESSAGE_COUNT) + return; + + const QString fileName = QString::fromUtf8(fileId->chars(), fileId->size()); + + if (fileName != doc->fileName()) + return; + + QString message; + message.vsprintf(format, ap); + + Document::DiagnosticMessage m(convertLevel(level), doc->fileName(), + line, column, message); + messages->append(m); + } + + static int convertLevel(int level) { + switch (level) { + case Warning: return Document::DiagnosticMessage::Warning; + case Error: return Document::DiagnosticMessage::Error; + case Fatal: return Document::DiagnosticMessage::Fatal; + default: return Document::DiagnosticMessage::Error; + } + } + + Document *doc; + QList<Document::DiagnosticMessage> *messages; + }; +} // end of anonymous namespace + +Document::Document(const QString &fileName) + : _fileName(fileName), + _globalNamespace(0) +{ + _control = new Control(); + + _control->setDiagnosticClient(new DocumentDiagnosticClient(this, &_diagnosticMessages)); + + const QByteArray localFileName = fileName.toUtf8(); + StringLiteral *fileId = _control->findOrInsertFileName(localFileName.constData(), + localFileName.size()); + _translationUnit = new TranslationUnit(_control, fileId); + _translationUnit->setQtMocRunEnabled(true); + (void) _control->switchTranslationUnit(_translationUnit); +} + +Document::~Document() +{ + delete _translationUnit; + delete _control->diagnosticClient(); + delete _control; +} + +Control *Document::control() const +{ return _control; } + +QString Document::fileName() const +{ return _fileName; } + +QStringList Document::includedFiles() const +{ return _includedFiles; } + +void Document::addIncludeFile(const QString &fileName) +{ _includedFiles.append(fileName); } + +QByteArray Document::definedMacros() const +{ return _definedMacros; } + +void Document::appendMacro(const QByteArray ¯oName, const QByteArray &text) +{ + int index = macroName.indexOf('('); + if (index == -1) + _macroNames.insert(macroName); + else + _macroNames.insert(macroName.left(index)); + _definedMacros += text; +} + +TranslationUnit *Document::translationUnit() const +{ return _translationUnit; } + +bool Document::skipFunctionBody() const +{ return _translationUnit->skipFunctionBody(); } + +void Document::setSkipFunctionBody(bool skipFunctionBody) +{ _translationUnit->setSkipFunctionBody(skipFunctionBody); } + +unsigned Document::globalSymbolCount() const +{ + if (! _globalNamespace) + return 0; + + return _globalNamespace->memberCount(); +} + +Symbol *Document::globalSymbolAt(unsigned index) const +{ return _globalNamespace->memberAt(index); } + +Scope *Document::globalSymbols() const +{ + if (! _globalNamespace) + return 0; + + return _globalNamespace->members(); +} + +Namespace *Document::globalNamespace() const +{ return _globalNamespace; } + +Symbol *Document::findSymbolAt(unsigned line, unsigned column) const +{ return findSymbolAt(line, column, globalSymbols()); } + +Symbol *Document::findSymbolAt(unsigned line, unsigned column, Scope *scope) const +{ + Symbol *previousSymbol = 0; + + for (unsigned i = 0; i < scope->symbolCount(); ++i) { + Symbol *symbol = scope->symbolAt(i); + if (symbol->line() > line) + break; + + previousSymbol = symbol; + } + + if (previousSymbol) { + if (ScopedSymbol *scoped = previousSymbol->asScopedSymbol()) { + if (Symbol *member = findSymbolAt(line, column, scoped->members())) + return member; + } + } + + return previousSymbol; +} + +Document::Ptr Document::create(const QString &fileName) +{ + Document::Ptr doc(new Document(fileName)); + return doc; +} + +void Document::setSource(const QByteArray &source) +{ _translationUnit->setSource(source.constBegin(), source.size()); } + +void Document::startSkippingBlocks(unsigned start) +{ _skippedBlocks.append(Block(start, 0)); } + +void Document::stopSkippingBlocks(unsigned stop) +{ + unsigned start = _skippedBlocks.back().begin(); + if (start > stop) + _skippedBlocks.removeLast(); // Ignore this block, it's invalid. + else + _skippedBlocks.back() = Block(start, stop); +} + +QSet<QByteArray> Document::macroNames() const +{ return _macroNames; } + +void Document::parse() +{ _translationUnit->parse(); } + +void Document::check() +{ + Q_ASSERT(! _globalNamespace); + + Semantic semantic(_control); + + _globalNamespace = _control->newNamespace(0); + Scope *globals = _globalNamespace->members(); + if (TranslationUnitAST *ast = _translationUnit->ast()) { + for (DeclarationAST *decl = ast->declarations; decl; decl = decl->next) { + semantic.check(decl, globals); + } + } +} + +void Document::releaseTranslationUnit() +{ _translationUnit->release(); } diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h new file mode 100644 index 00000000000..47f2366ddd0 --- /dev/null +++ b/src/libs/cplusplus/CppDocument.h @@ -0,0 +1,187 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPDOCUMENT_H +#define CPPDOCUMENT_H + +#include <CPlusPlusForwardDeclarations.h> +#include <QByteArray> +#include <QSharedPointer> +#include <QString> +#include <QStringList> +#include <QList> +#include <QSet> + +namespace CPlusPlus { + +class CPLUSPLUS_EXPORT Document +{ + Document(const Document &other); + void operator =(const Document &other); + + Document(const QString &fileName); + +public: + typedef QSharedPointer<Document> Ptr; + +public: + ~Document(); + + QString fileName() const; + + QStringList includedFiles() const; + void addIncludeFile(const QString &fileName); + + QByteArray definedMacros() const; + QSet<QByteArray> macroNames() const; + + void appendMacro(const QByteArray ¯oName, const QByteArray &text); + + Control *control() const; + TranslationUnit *translationUnit() const; + + bool skipFunctionBody() const; + void setSkipFunctionBody(bool skipFunctionBody); + + unsigned globalSymbolCount() const; + Symbol *globalSymbolAt(unsigned index) const; + Scope *globalSymbols() const; // ### deprecate? + Namespace *globalNamespace() const; + + Symbol *findSymbolAt(unsigned line, unsigned column) const; + + void setSource(const QByteArray &source); + void startSkippingBlocks(unsigned offset); + void stopSkippingBlocks(unsigned offset); + + void parse(); // ### remove + void check(); + void releaseTranslationUnit(); + + static Ptr create(const QString &fileName); + + class DiagnosticMessage + { + public: + enum Level { + Warning, + Error, + Fatal + }; + + public: + DiagnosticMessage(int level, const QString &fileName, + int line, int column, + const QString &text) + : _level(level), + _fileName(fileName), + _line(line), + _column(column), + _text(text) + { } + + int level() const + { return _level; } + + bool isWarning() const + { return _level == Warning; } + + bool isError() const + { return _level == Error; } + + bool isFatal() const + { return _level == Fatal; } + + QString fileName() const + { return _fileName; } + + int line() const + { return _line; } + + int column() const + { return _column; } + + QString text() const + { return _text; } + + private: + int _level; + QString _fileName; + int _line; + int _column; + QString _text; + }; + + void addDiagnosticMessage(const DiagnosticMessage &d) + { _diagnosticMessages.append(d); } + + QList<DiagnosticMessage> diagnosticMessages() const + { return _diagnosticMessages; } + + class Block + { + unsigned _begin; + unsigned _end; + + public: + inline Block(unsigned begin = 0, unsigned end = 0) + : _begin(begin), _end(end) + { } + + inline unsigned begin() const + { return _begin; } + + inline unsigned end() const + { return _end; } + }; + + QList<Block> skippedBlocks() const + { return _skippedBlocks; } + +private: + Symbol *findSymbolAt(unsigned line, unsigned column, Scope *scope) const; + +private: + QString _fileName; + QStringList _includedFiles; + Control *_control; + TranslationUnit *_translationUnit; + Namespace *_globalNamespace; + QList<DiagnosticMessage> _diagnosticMessages; + QByteArray _definedMacros; + QSet<QByteArray> _macroNames; + QList<Block> _skippedBlocks; +}; + +} // end of namespace CPlusPlus + +#endif // CPPDOCUMENT_H diff --git a/src/libs/cplusplus/ExpressionUnderCursor.cpp b/src/libs/cplusplus/ExpressionUnderCursor.cpp new file mode 100644 index 00000000000..92d2cd8893f --- /dev/null +++ b/src/libs/cplusplus/ExpressionUnderCursor.cpp @@ -0,0 +1,242 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "ExpressionUnderCursor.h" +#include "SimpleLexer.h" +#include <Token.h> + +#include <QTextCursor> +#include <QTextBlock> + +using namespace CPlusPlus; + +ExpressionUnderCursor::ExpressionUnderCursor() +{ } + +ExpressionUnderCursor::~ExpressionUnderCursor() +{ } + +int ExpressionUnderCursor::startOfMatchingBrace(const QList<SimpleToken> &tk, int index) +{ + if (tk[index - 1].is(T_RPAREN)) { + int i = index - 1; + int count = 0; + do { + if (tk[i].is(T_LPAREN)) { + if (! ++count) + return i; + } else if (tk[i].is(T_RPAREN)) + --count; + --i; + } while (count != 0 && i > -1); + } else if (tk[index - 1].is(T_RBRACKET)) { + int i = index - 1; + int count = 0; + do { + if (tk[i].is(T_LBRACKET)) { + if (! ++count) + return i; + } else if (tk[i].is(T_RBRACKET)) + --count; + --i; + } while (count != 0 && i > -1); + } else if (tk[index - 1].is(T_GREATER)) { + int i = index - 1; + int count = 0; + do { + if (tk[i].is(T_LESS)) { + if (! ++count) + return i; + } else if (tk[i].is(T_GREATER)) + --count; + --i; + } while (count != 0 && i > -1); + } + + return index; +} + +int ExpressionUnderCursor::startOfExpression(const QList<SimpleToken> &tk, int index) +{ + // tk is a reference to a const QList. So, don't worry about [] access. + // ### TODO implement multiline support. It should be pretty easy. + if (tk[index - 1].isLiteral()) { + return index - 1; + } else if (tk[index - 1].is(T_THIS)) { + return index - 1; + } else if (tk[index - 1].is(T_TYPEID)) { + return index - 1; + } else if (tk[index - 1].is(T_SIGNAL)) { + if (tk[index - 2].is(T_COMMA) && !_jumpedComma) { + _jumpedComma = true; + return startOfExpression(tk, index - 2); + } + return index - 1; + } else if (tk[index - 1].is(T_SLOT)) { + if (tk[index - 2].is(T_COMMA) && !_jumpedComma) { + _jumpedComma = true; + return startOfExpression(tk, index - 2); + } + return index - 1; + } else if (tk[index - 1].is(T_IDENTIFIER)) { + if (tk[index - 2].is(T_TILDE)) { + if (tk[index - 3].is(T_COLON_COLON)) { + return startOfExpression(tk, index - 3); + } else if (tk[index - 3].is(T_DOT) || tk[index - 3].is(T_ARROW)) { + return startOfExpression(tk, index - 3); + } + return index - 2; + } else if (tk[index - 2].is(T_COLON_COLON)) { + return startOfExpression(tk, index - 1); + } else if (tk[index - 2].is(T_DOT) || tk[index - 2].is(T_ARROW)) { + return startOfExpression(tk, index - 2); + } else if (tk[index - 2].is(T_DOT_STAR) || tk[index - 2].is(T_ARROW_STAR)) { + return startOfExpression(tk, index - 2); + } + return index - 1; + } else if (tk[index - 1].is(T_RPAREN)) { + int rparenIndex = startOfMatchingBrace(tk, index); + if (rparenIndex != index) { + if (tk[rparenIndex - 1].is(T_GREATER)) { + int lessIndex = startOfMatchingBrace(tk, rparenIndex); + if (lessIndex != rparenIndex - 1) { + if (tk[lessIndex - 1].is(T_DYNAMIC_CAST) || + tk[lessIndex - 1].is(T_STATIC_CAST) || + tk[lessIndex - 1].is(T_CONST_CAST) || + tk[lessIndex - 1].is(T_REINTERPRET_CAST)) + return lessIndex - 1; + else if (tk[lessIndex - 1].is(T_IDENTIFIER)) + return startOfExpression(tk, lessIndex); + else if (tk[lessIndex - 1].is(T_SIGNAL)) + return startOfExpression(tk, lessIndex); + else if (tk[lessIndex - 1].is(T_SLOT)) + return startOfExpression(tk, lessIndex); + } + } + return startOfExpression(tk, rparenIndex); + } + return index; + } else if (tk[index - 1].is(T_RBRACKET)) { + int rbracketIndex = startOfMatchingBrace(tk, index); + if (rbracketIndex != index) + return startOfExpression(tk, rbracketIndex); + return index; + } else if (tk[index - 1].is(T_COLON_COLON)) { + if (tk[index - 2].is(T_GREATER)) { // ### not exactly + int lessIndex = startOfMatchingBrace(tk, index - 1); + if (lessIndex != index - 1) + return startOfExpression(tk, lessIndex); + return index - 1; + } else if (tk[index - 2].is(T_IDENTIFIER)) { + return startOfExpression(tk, index - 1); + } + return index - 1; + } else if (tk[index - 1].is(T_DOT) || tk[index - 1].is(T_ARROW)) { + return startOfExpression(tk, index - 1); + } else if (tk[index - 1].is(T_DOT_STAR) || tk[index - 1].is(T_ARROW_STAR)) { + return startOfExpression(tk, index - 1); + } + + return index; +} + +bool ExpressionUnderCursor::isAccessToken(const SimpleToken &tk) +{ + switch (tk.kind()) { + case T_COLON_COLON: + case T_DOT: case T_ARROW: + case T_DOT_STAR: case T_ARROW_STAR: + return true; + default: + return false; + } // switch +} + +int ExpressionUnderCursor::previousBlockState(const QTextBlock &block) +{ + const QTextBlock prevBlock = block.previous(); + if (prevBlock.isValid()) { + int state = prevBlock.userState(); + + if (state != -1) + return state; + } + return 0; +} + +QString ExpressionUnderCursor::operator()(const QTextCursor &cursor) +{ + enum { MAX_BLOCK_COUNT = 5 }; + + QTextBlock block = cursor.block(); + QTextBlock initialBlock = block; + for (int i = 0; i < MAX_BLOCK_COUNT; ++i) { + if (! initialBlock.previous().isValid()) + break; + + initialBlock = initialBlock.previous(); + } + + QString text; + + QTextBlock it = initialBlock; + for (; it.isValid(); it = it.next()) { + QString textBlock = it.text(); + + if (it == block) + textBlock = textBlock.left(cursor.position() - cursor.block().position()); + + text += textBlock; + + if (it == block) + break; + + text += QLatin1Char('\n'); + } + + SimpleLexer tokenize; + tokenize.setSkipComments(true); + QList<SimpleToken> tokens = tokenize(text, previousBlockState(initialBlock)); + tokens.prepend(SimpleToken()); // sentinel + + _jumpedComma = false; + + const int i = startOfExpression(tokens, tokens.size()); + if (i == tokens.size()) + return QString(); + + return text.mid(tokens.at(i).position(), + tokens.last().position() + tokens.last().length() + - tokens.at(i).position()); +} + diff --git a/src/libs/cplusplus/ExpressionUnderCursor.h b/src/libs/cplusplus/ExpressionUnderCursor.h new file mode 100644 index 00000000000..e3fa442326e --- /dev/null +++ b/src/libs/cplusplus/ExpressionUnderCursor.h @@ -0,0 +1,68 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef EXPRESSIONUNDERCURSOR_H +#define EXPRESSIONUNDERCURSOR_H + +#include "CPlusPlusForwardDeclarations.h" +#include <QList> + +QT_BEGIN_NAMESPACE +class QString; +class QTextCursor; +class QTextBlock; +QT_END_NAMESPACE + +namespace CPlusPlus { + +class SimpleToken; + +class CPLUSPLUS_EXPORT ExpressionUnderCursor +{ +public: + ExpressionUnderCursor(); + ~ExpressionUnderCursor(); + + QString operator()(const QTextCursor &cursor); + +private: + int startOfMatchingBrace(const QList<SimpleToken> &tk, int index); + int startOfExpression(const QList<SimpleToken> &tk, int index); + int previousBlockState(const QTextBlock &block); + bool isAccessToken(const SimpleToken &tk); + + bool _jumpedComma; +}; + +} // namespace CPlusPlus + +#endif // EXPRESSIONUNDERCURSOR_H diff --git a/src/libs/cplusplus/Icons.cpp b/src/libs/cplusplus/Icons.cpp new file mode 100644 index 00000000000..70c42142590 --- /dev/null +++ b/src/libs/cplusplus/Icons.cpp @@ -0,0 +1,122 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "Icons.h" + +#include <FullySpecifiedType.h> +#include <Scope.h> +#include <Symbols.h> +#include <Type.h> + +using namespace CPlusPlus; +using CPlusPlus::Icons; + +Icons::Icons() + : _classIcon(QLatin1String(":/codemodel/images/class.png")), + _enumIcon(QLatin1String(":/codemodel/images/enum.png")), + _enumeratorIcon(QLatin1String(":/codemodel/images/enumerator.png")), + _funcPublicIcon(QLatin1String(":/codemodel/images/func.png")), + _funcProtectedIcon(QLatin1String(":/codemodel/images/func_prot.png")), + _funcPrivateIcon(QLatin1String(":/codemodel/images/func_priv.png")), + _namespaceIcon(QLatin1String(":/codemodel/images/namespace.png")), + _varPublicIcon(QLatin1String(":/codemodel/images/var.png")), + _varProtectedIcon(QLatin1String(":/codemodel/images/var_prot.png")), + _varPrivateIcon(QLatin1String(":/codemodel/images/var_priv.png")), + _signalIcon(QLatin1String(":/codemodel/images/signal.png")), + _slotPublicIcon(QLatin1String(":/codemodel/images/slot.png")), + _slotProtectedIcon(QLatin1String(":/codemodel/images/slot_prot.png")), + _slotPrivateIcon(QLatin1String(":/codemodel/images/slot_priv.png")), + _keywordIcon(QLatin1String(":/codemodel/images/keyword.png")), + _macroIcon(QLatin1String(":/codemodel/images/macro.png")) +{ +} + +QIcon Icons::iconForSymbol(Symbol *symbol) const +{ + if (symbol->isFunction() || (symbol->isDeclaration() && symbol->type()->isFunction())) + { + Function *function = symbol->asFunction(); + if (!function) + function = symbol->type()->asFunction(); + + if (function->isSlot()) { + if (function->isPublic()) { + return _slotPublicIcon; + } else if (function->isProtected()) { + return _slotProtectedIcon; + } else if (function->isPrivate()) { + return _slotPrivateIcon; + } + } else if (function->isSignal()) { + return _signalIcon; + } else if (symbol->isPublic()) { + return _funcPublicIcon; + } else if (symbol->isProtected()) { + return _funcProtectedIcon; + } else if (symbol->isPrivate()) { + return _funcPrivateIcon; + } + } else if (symbol->scope()->isEnumScope()) { + return _enumeratorIcon; + } else if (symbol->isDeclaration() || symbol->isArgument()) { + if (symbol->isPublic()) { + return _varPublicIcon; + } else if (symbol->isProtected()) { + return _varProtectedIcon; + } else if (symbol->isPrivate()) { + return _varPrivateIcon; + } + } else if (symbol->isEnum()) { + return _enumIcon; + } else if (symbol->isClass()) { + return _classIcon; + } else if (symbol->isNamespace()) { + return _namespaceIcon; + } else if (symbol->isUsingNamespaceDirective() || + symbol->isUsingDeclaration()) { + // TODO: Might be nice to have a different icons for these things + return _namespaceIcon; + } + + return QIcon(); +} + +QIcon Icons::keywordIcon() const +{ + return _keywordIcon; +} + +QIcon Icons::macroIcon() const +{ + return _macroIcon; +} diff --git a/src/libs/cplusplus/Icons.h b/src/libs/cplusplus/Icons.h new file mode 100644 index 00000000000..c549c1c4296 --- /dev/null +++ b/src/libs/cplusplus/Icons.h @@ -0,0 +1,75 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPLUSPLUS_ICONS_H +#define CPLUSPLUS_ICONS_H + +#include "CPlusPlusForwardDeclarations.h" + +#include <QIcon> + +namespace CPlusPlus { + +class Symbol; + +class CPLUSPLUS_EXPORT Icons +{ +public: + Icons(); + + QIcon iconForSymbol(Symbol *symbol) const; + + QIcon keywordIcon() const; + QIcon macroIcon() const; + +private: + QIcon _classIcon; + QIcon _enumIcon; + QIcon _enumeratorIcon; + QIcon _funcPublicIcon; + QIcon _funcProtectedIcon; + QIcon _funcPrivateIcon; + QIcon _namespaceIcon; + QIcon _varPublicIcon; + QIcon _varProtectedIcon; + QIcon _varPrivateIcon; + QIcon _signalIcon; + QIcon _slotPublicIcon; + QIcon _slotProtectedIcon; + QIcon _slotPrivateIcon; + QIcon _keywordIcon; + QIcon _macroIcon; +}; + +} // namespace CPlusPlus + +#endif // CPLUSPLUS_ICONS_H diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp new file mode 100644 index 00000000000..027cfb577d6 --- /dev/null +++ b/src/libs/cplusplus/LookupContext.cpp @@ -0,0 +1,402 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "LookupContext.h" +#include <CoreTypes.h> +#include <Symbols.h> +#include <Literals.h> +#include <Names.h> +#include <Scope.h> +#include <Control.h> +#include <cplusplus/Overview.h> +#include <QFile> +#include <QtDebug> + +using namespace CPlusPlus; + +///////////////////////////////////////////////////////////////////// +// LookupUtils +///////////////////////////////////////////////////////////////////// +bool LookupUtils::isNameCompatibleWithIdentifier(Name *name, Identifier *id) +{ + if (! name) { + return false; + } else if (NameId *nameId = name->asNameId()) { + Identifier *identifier = nameId->identifier(); + return identifier->isEqualTo(id); + } else if (DestructorNameId *nameId = name->asDestructorNameId()) { + Identifier *identifier = nameId->identifier(); + return identifier->isEqualTo(id); + } else if (TemplateNameId *templNameId = name->asTemplateNameId()) { + Identifier *identifier = templNameId->identifier(); + return identifier->isEqualTo(id); + } + + return false; +} + +///////////////////////////////////////////////////////////////////// +// LookupContext +///////////////////////////////////////////////////////////////////// +LookupContext::LookupContext(Control *control) + : _control(control), + _symbol(0) +{ } + +LookupContext::LookupContext(Symbol *symbol, + Document::Ptr expressionDocument, + Document::Ptr thisDocument, + const QMap<QString, Document::Ptr> &documents) + : _symbol(symbol), + _expressionDocument(expressionDocument), + _thisDocument(thisDocument), + _documents(documents) +{ + _control = _expressionDocument->control(); + _visibleScopes = buildVisibleScopes(); +} + +LookupContext::LookupContext(Symbol *symbol, + const LookupContext &context) + : _control(context._control), + _symbol(symbol), + _expressionDocument(context._expressionDocument), + _documents(context._documents) +{ + const QString fn = QString::fromUtf8(symbol->fileName(), symbol->fileNameLength()); + _thisDocument = _documents.value(fn); + _visibleScopes = buildVisibleScopes(); +} + +LookupContext::LookupContext(Symbol *symbol, + Document::Ptr thisDocument, + const LookupContext &context) + : _control(context._control), + _symbol(symbol), + _expressionDocument(context._expressionDocument), + _thisDocument(thisDocument), + _documents(context._documents) +{ + _visibleScopes = buildVisibleScopes(); +} + +bool LookupContext::isValid() const +{ return _control != 0; } + +LookupContext::operator bool() const +{ return _control != 0; } + +Control *LookupContext::control() const +{ return _control; } + +Symbol *LookupContext::symbol() const +{ return _symbol; } + +Document::Ptr LookupContext::expressionDocument() const +{ return _expressionDocument; } + +Document::Ptr LookupContext::thisDocument() const +{ return _thisDocument; } + +Document::Ptr LookupContext::document(const QString &fileName) const +{ return _documents.value(fileName); } + +Identifier *LookupContext::identifier(Name *name) const +{ + if (NameId *nameId = name->asNameId()) + return nameId->identifier(); + else if (TemplateNameId *templId = name->asTemplateNameId()) + return templId->identifier(); + else if (DestructorNameId *dtorId = name->asDestructorNameId()) + return dtorId->identifier(); + else if (QualifiedNameId *q = name->asQualifiedNameId()) + return identifier(q->unqualifiedNameId()); + return 0; +} + +QList<Symbol *> LookupContext::resolve(Name *name, const QList<Scope *> &visibleScopes, + ResolveMode mode) const +{ + if (QualifiedNameId *q = name->asQualifiedNameId()) { + QList<Scope *> scopes = visibleScopes; + for (unsigned i = 0; i < q->nameCount(); ++i) { + Name *name = q->nameAt(i); + + QList<Symbol *> candidates; + if (i + 1 == q->nameCount()) + candidates = resolve(name, scopes, mode); + else + candidates = resolveClassOrNamespace(name, scopes); + + if (candidates.isEmpty() || i + 1 == q->nameCount()) + return candidates; + + scopes.clear(); + foreach (Symbol *candidate, candidates) { + if (ScopedSymbol *scoped = candidate->asScopedSymbol()) { + expand(scoped->members(), visibleScopes, &scopes); + } + } + } + + return QList<Symbol *>(); + } + + QList<Symbol *> candidates; + if (Identifier *id = identifier(name)) { + for (int scopeIndex = 0; scopeIndex < visibleScopes.size(); ++scopeIndex) { + Scope *scope = visibleScopes.at(scopeIndex); + for (Symbol *symbol = scope->lookat(id); symbol; symbol = symbol->next()) { + if (! symbol->name()) + continue; + else if (symbol->name()->isQualifiedNameId()) + continue; + else if (! isNameCompatibleWithIdentifier(symbol->name(), id)) + continue; + else if (symbol->name()->isDestructorNameId() != name->isDestructorNameId()) + continue; + else if ((((mode & ResolveNamespace) && symbol->isNamespace()) || + ((mode & ResolveClass) && symbol->isClass()) || + (mode & ResolveSymbol)) && ! candidates.contains(symbol)) { + candidates.append(symbol); + } + } + } + } else if (OperatorNameId *opId = name->asOperatorNameId()) { + for (int scopeIndex = 0; scopeIndex < visibleScopes.size(); ++scopeIndex) { + Scope *scope = visibleScopes.at(scopeIndex); + for (Symbol *symbol = scope->lookat(opId->kind()); symbol; symbol = symbol->next()) { + if (! opId->isEqualTo(symbol->name())) + continue; + else if (! candidates.contains(symbol)) + candidates.append(symbol); + } + } + } + + return candidates; +} + +QList<Scope *> LookupContext::buildVisibleScopes() +{ + QList<Scope *> scopes; + + if (_symbol) { + for (Scope *scope = _symbol->scope(); scope; scope = scope->enclosingScope()) { + scopes.append(scope); + } + } + + QSet<QString> processed; + processed.insert(_thisDocument->fileName()); + + QList<QString> todo = _thisDocument->includedFiles(); + while (! todo.isEmpty()) { + QString fn = todo.last(); + todo.removeLast(); + + if (processed.contains(fn)) + continue; + + processed.insert(fn); + if (Document::Ptr doc = document(fn)) { + scopes.append(doc->globalNamespace()->members()); + todo += doc->includedFiles(); + } + } + + while (true) { + QList<Scope *> expandedScopes; + expand(scopes, &expandedScopes); + + if (expandedScopes.size() == scopes.size()) + return expandedScopes; + + scopes = expandedScopes; + } + + return scopes; +} + +QList<Scope *> LookupContext::visibleScopes(const QPair<FullySpecifiedType, Symbol *> &result) const +{ + Symbol *symbol = result.second; + QList<Scope *> scopes; + for (Scope *scope = symbol->scope(); scope; scope = scope->enclosingScope()) + scopes.append(scope); + scopes += visibleScopes(); + scopes = expand(scopes); + return scopes; +} + +QList<Scope *> LookupContext::expand(const QList<Scope *> &scopes) const +{ + QList<Scope *> expanded; + expand(scopes, &expanded); + return expanded; +} + +void LookupContext::expand(const QList<Scope *> &scopes, QList<Scope *> *expandedScopes) const +{ + for (int i = 0; i < scopes.size(); ++i) { + expand(scopes.at(i), scopes, expandedScopes); + } +} + +void LookupContext::expand(Scope *scope, + const QList<Scope *> &visibleScopes, + QList<Scope *> *expandedScopes) const +{ + Overview overview; + + if (expandedScopes->contains(scope)) { + //qDebug() << "skipped:" << overview.prettyName(scope->owner()->name()); + return; + } + + expandedScopes->append(scope); + + if (scope->isNamespaceScope()) { + Namespace *ns = scope->owner()->asNamespace(); + Name *nsName = ns->name(); + if (nsName) { + QList<Symbol *> namespaceList = resolveNamespace(nsName, visibleScopes); + foreach (Symbol *otherNs, namespaceList) { + if (otherNs == ns) + continue; + expand(otherNs->asNamespace()->members(), visibleScopes, expandedScopes); + } + //qDebug() << "*** found:" << namespaceList.count() << "namespace aliases"; + } + //qDebug() << "namespace scope" << overview.prettyName(ns->name()) + //<< ns->fileName() << ns->line(); + for (unsigned i = 0; i < scope->symbolCount(); ++i) { // ### make me fast + Symbol *symbol = scope->symbolAt(i); + if (Namespace *ns = symbol->asNamespace()) { + if (! ns->name()) { + expand(ns->members(), visibleScopes, expandedScopes); + } + } else if (UsingNamespaceDirective *u = symbol->asUsingNamespaceDirective()) { + QList<Symbol *> candidates = resolveNamespace(u->name(), visibleScopes); + //qDebug() << "found:" << candidates.count() << "namespaces to import for:" + //<< overview.prettyName(u->name()); + for (int j = 0; j < candidates.size(); ++j) { + expand(candidates.at(j)->asNamespace()->members(), + visibleScopes, expandedScopes); + } + } else if (Enum *e = symbol->asEnum()) { + expand(e->members(), visibleScopes, expandedScopes); + } + } + } else if (scope->isClassScope()) { + Class *klass = scope->owner()->asClass(); + for (unsigned i = 0; i < scope->symbolCount(); ++i) { + Symbol *symbol = scope->symbolAt(i); + if (Class *nestedClass = symbol->asClass()) { + if (! nestedClass->name()) { + expand(nestedClass->members(), visibleScopes, expandedScopes); + } + } else if (Enum *e = symbol->asEnum()) { + expand(e->members(), visibleScopes, expandedScopes); + } + } + + if (klass->baseClassCount()) { + QList<Scope *> classVisibleScopes = visibleScopes; + for (Scope *scope = klass->scope(); scope; scope = scope->enclosingScope()) { + if (scope->isNamespaceScope()) { + Namespace *enclosingNamespace = scope->owner()->asNamespace(); + if (enclosingNamespace->name()) { + QList<Symbol *> nsList = resolveNamespace(enclosingNamespace->name(), + visibleScopes); + foreach (Symbol *ns, nsList) { + expand(ns->asNamespace()->members(), classVisibleScopes, &classVisibleScopes); + } + } + } + } + + for (unsigned i = 0; i < klass->baseClassCount(); ++i) { + BaseClass *baseClass = klass->baseClassAt(i); + Name *baseClassName = baseClass->name(); + QList<Symbol *> baseClassCandidates = resolveClass(baseClassName, classVisibleScopes); + if (baseClassCandidates.isEmpty()) { + Overview overview; + qDebug() << "unresolved base class:" << overview.prettyName(baseClassName); + } + for (int j = 0; j < baseClassCandidates.size(); ++j) { + Class *baseClassSymbol = baseClassCandidates.at(j)->asClass(); + expand(baseClassSymbol->members(), visibleScopes, expandedScopes); + } + } + } + } else if (scope->isBlockScope()) { + //qDebug() << "block scope" << overview.prettyName(scope->owner()->name()); + for (unsigned i = 0; i < scope->symbolCount(); ++i) { + Symbol *symbol = scope->symbolAt(i); + if (UsingNamespaceDirective *u = symbol->asUsingNamespaceDirective()) { + QList<Symbol *> candidates = resolveNamespace(u->name(), visibleScopes); + //qDebug() << "found:" << candidates.count() << "namespaces to import for:" + //<< overview.prettyName(u->name()); + for (int j = 0; j < candidates.size(); ++j) { + expand(candidates.at(j)->asNamespace()->members(), + visibleScopes, expandedScopes); + } + } + + } + } else if (scope->isFunctionScope()) { + Function *function = scope->owner()->asFunction(); + //qDebug() << "function scope" << overview.prettyName(function->name()); + if (! expandedScopes->contains(function->arguments())) + expandedScopes->append(function->arguments()); + if (QualifiedNameId *q = function->name()->asQualifiedNameId()) { + //qDebug() << "**** here:" << overview.prettyName(function->name()); + Name *nestedNameSpec = 0; + if (q->nameCount() == 1 && q->isGlobal()) + nestedNameSpec = q->nameAt(0); + else + nestedNameSpec = control()->qualifiedNameId(q->names(), q->nameCount() - 1, + q->isGlobal()); + QList<Symbol *> candidates = resolveClassOrNamespace(nestedNameSpec, visibleScopes); + //qDebug() << "**** found:" << candidates.count() << "class or namespace for:" + //<< overview.prettyName(nestedNameSpec); + for (int j = 0; j < candidates.size(); ++j) { + expand(candidates.at(j)->asScopedSymbol()->members(), + visibleScopes, expandedScopes); + } + } + } else if (scope->isPrototypeScope()) { + //qDebug() << "prototype scope" << overview.prettyName(scope->owner()->name()); + } +} diff --git a/src/libs/cplusplus/LookupContext.h b/src/libs/cplusplus/LookupContext.h new file mode 100644 index 00000000000..d2fe8b7beff --- /dev/null +++ b/src/libs/cplusplus/LookupContext.h @@ -0,0 +1,151 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPLUSPLUS_LOOKUPCONTEXT_H +#define CPLUSPLUS_LOOKUPCONTEXT_H + +#include <SymbolVisitor.h> +#include <cplusplus/CppDocument.h> + +#include <QList> +#include <QSet> +#include <QMap> + +namespace CPlusPlus { + +class CPLUSPLUS_EXPORT LookupUtils +{ +public: + static bool isNameCompatibleWithIdentifier(Name *name, + Identifier *id); +}; + +class CPLUSPLUS_EXPORT LookupContext: LookupUtils +{ +public: + LookupContext(Control *control = 0); + + LookupContext(Symbol *symbol, + Document::Ptr expressionDocument, + Document::Ptr thisDocument, + const QMap<QString, Document::Ptr> &documents); + + LookupContext(Symbol *symbol, + const LookupContext &context); + + LookupContext(Symbol *symbol, + Document::Ptr thisDocument, + const LookupContext &context); + + bool isValid() const; + operator bool() const; + + Control *control() const; + Symbol *symbol() const; + Document::Ptr expressionDocument() const; + Document::Ptr thisDocument() const; + Document::Ptr document(const QString &fileName) const; + + QList<Symbol *> resolve(Name *name) const + { return resolve(name, visibleScopes()); } + + QList<Symbol *> resolveNamespace(Name *name) const + { return resolveNamespace(name, visibleScopes()); } + + QList<Symbol *> resolveClass(Name *name) const + { return resolveClass(name, visibleScopes()); } + + QList<Symbol *> resolveClassOrNamespace(Name *name) const + { return resolveClassOrNamespace(name, visibleScopes()); } + + QMap<QString, Document::Ptr> documents() const + { return _documents; } + + enum ResolveMode { + ResolveSymbol = 0x01, + ResolveClass = 0x02, + ResolveNamespace = 0x04, + ResolveClassOrNamespace = ResolveClass | ResolveNamespace, + ResolveAll = ResolveSymbol | ResolveClassOrNamespace + }; + + Identifier *identifier(Name *name) const; + + QList<Symbol *> resolve(Name *name, const QList<Scope *> &visibleScopes, + ResolveMode mode = ResolveAll) const; + + QList<Symbol *> resolveNamespace(Name *name, const QList<Scope *> &visibleScopes) const + { return resolve(name, visibleScopes, ResolveNamespace); } + + QList<Symbol *> resolveClass(Name *name, const QList<Scope *> &visibleScopes) const + { return resolve(name, visibleScopes, ResolveClass); } + + QList<Symbol *> resolveClassOrNamespace(Name *name, const QList<Scope *> &visibleScopes) const + { return resolve(name, visibleScopes, ResolveClassOrNamespace); } + + QList<Scope *> visibleScopes() const + { return _visibleScopes; } + + QList<Scope *> visibleScopes(const QPair<FullySpecifiedType, Symbol *> &result) const; + + QList<Scope *> expand(const QList<Scope *> &scopes) const; + + void expand(const QList<Scope *> &scopes, QList<Scope *> *expandedScopes) const; + + void expand(Scope *scope, const QList<Scope *> &visibleScopes, + QList<Scope *> *expandedScopes) const; + +private: + QList<Scope *> buildVisibleScopes(); + +private: + Control *_control; + + // The current symbol. + Symbol *_symbol; + + // The current expression. + Document::Ptr _expressionDocument; + + // The current document. + Document::Ptr _thisDocument; + + // All documents. + QMap<QString, Document::Ptr> _documents; + + // Visible scopes. + QList<Scope *> _visibleScopes; +}; + +} // end of namespace CPlusPlus + +#endif // CPLUSPLUS_LOOKUPCONTEXT_H diff --git a/src/libs/cplusplus/NameOfExpression.cpp b/src/libs/cplusplus/NameOfExpression.cpp new file mode 100644 index 00000000000..d54e7f55216 --- /dev/null +++ b/src/libs/cplusplus/NameOfExpression.cpp @@ -0,0 +1,438 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "NameOfExpression.h" +#include "LookupContext.h" + +#include <cplusplus/Overview.h> +#include <Control.h> +#include <AST.h> +#include <Scope.h> +#include <Names.h> +#include <Symbols.h> +#include <Literals.h> +#include <CoreTypes.h> +#include <TypeVisitor.h> +#include <NameVisitor.h> +#include <QList> +#include <QtDebug> + +using namespace CPlusPlus; + +NameOfExpression::NameOfExpression(const LookupContext &context) + : ASTVisitor(context.expressionDocument()->control()), + _context(context), + sem(_context.control()) +{ } + +NameOfExpression::~NameOfExpression() +{ } + +QList<FullySpecifiedType> ResolveExpression::operator()(ExpressionAST *ast) +{ + QList<FullySpecifiedType> previousResolvedSymbols = switchResolvedSymbols(QList<FullySpecifiedType>()); + accept(ast); + return switchResolvedSymbols(previousResolvedSymbols); +} + +QList<FullySpecifiedType> ResolveExpression::switchResolvedSymbols(const QList<FullySpecifiedType> &symbols) +{ + QList<FullySpecifiedType> previousResolvedSymbols = _resolvedSymbols; + _resolvedSymbols = symbols; + return previousResolvedSymbols; +} + +bool ResolveExpression::visit(ExpressionListAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(BinaryExpressionAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(CastExpressionAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(ConditionAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(ConditionalExpressionAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(CppCastExpressionAST *) +{ + // ### resolve ast->type_id + return false; +} + +bool ResolveExpression::visit(DeleteExpressionAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(ArrayInitializerAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(NewExpressionAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(TypeidExpressionAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(TypenameCallExpressionAST *) +{ + // nothing to do + return false; +} + +bool ResolveExpression::visit(TypeConstructorCallAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(PostfixExpressionAST *ast) +{ + accept(ast->base_expression); + + for (PostfixAST *fx = ast->postfix_expressions; fx; fx = fx->next) { + accept(fx); + } + + return false; +} + +bool ResolveExpression::visit(SizeofExpressionAST *) +{ + FullySpecifiedType ty(control()->integerType(IntegerType::Int)); + ty.setUnsigned(true); + _resolvedSymbols.append(ty); + return false; +} + +bool ResolveExpression::visit(NumericLiteralAST *) +{ + _resolvedSymbols.append(control()->integerType(IntegerType::Int)); // ### handle short, long, floats, ... + return false; +} + +bool ResolveExpression::visit(BoolLiteralAST *) +{ + _resolvedSymbols.append(control()->integerType(IntegerType::Bool)); + return false; +} + +bool ResolveExpression::visit(ThisExpressionAST *) +{ + if (! _context.symbol()) + return false; + + Scope *scope = _context.symbol()->scope(); + for (; scope; scope = scope->enclosingScope()) { + if (scope->isFunctionScope()) { + Function *fun = scope->owner()->asFunction(); + if (Scope *cscope = scope->enclosingClassScope()) { + Class *klass = cscope->owner()->asClass(); + FullySpecifiedType classTy(control()->namedType(klass->name())); + FullySpecifiedType ptrTy(control()->pointerType(classTy)); + _resolvedSymbols.append(ptrTy); + break; + } else if (QualifiedNameId *q = fun->name()->asQualifiedNameId()) { + Name *nestedNameSpecifier = 0; + if (q->nameCount() == 2) + nestedNameSpecifier = q->nameAt(0); + else + nestedNameSpecifier = control()->qualifiedNameId(&q->names()[0], q->nameCount() - 1); + FullySpecifiedType classTy(control()->namedType(nestedNameSpecifier)); + FullySpecifiedType ptrTy(control()->pointerType(classTy)); + _resolvedSymbols.append(ptrTy); + break; + } + } + } + return false; +} + +bool ResolveExpression::visit(NestedExpressionAST *ast) +{ + accept(ast->expression); + return false; +} + +bool ResolveExpression::visit(StringLiteralAST *) +{ + FullySpecifiedType charTy = control()->integerType(IntegerType::Char); + charTy.setConst(true); + FullySpecifiedType ty(control()->pointerType(charTy)); + _resolvedSymbols.append(ty); + return false; +} + +bool ResolveExpression::visit(ThrowExpressionAST *) +{ + return false; +} + +bool ResolveExpression::visit(TypeIdAST *) +{ + return false; +} + +bool ResolveExpression::visit(UnaryExpressionAST *ast) +{ + accept(ast->expression); + unsigned unaryOp = tokenKind(ast->unary_op_token); + if (unaryOp == T_AMPER) { + QMutableListIterator<FullySpecifiedType> it(_resolvedSymbols); + while (it.hasNext()) { + FullySpecifiedType ty = it.next(); + ty.setType(control()->pointerType(ty)); + it.setValue(ty); + } + } else if (unaryOp == T_STAR) { + QMutableListIterator<FullySpecifiedType> it(_resolvedSymbols); + while (it.hasNext()) { + FullySpecifiedType ty = it.next(); + if (PointerType *ptrTy = ty->asPointerType()) { + it.setValue(ptrTy->elementType()); + } else { + it.remove(); + } + } + } + return false; +} + +bool ResolveExpression::visit(QualifiedNameAST *ast) +{ + Scope dummy; + Name *name = sem.check(ast, &dummy); + + QList<Symbol *> symbols = _context.resolve(name); + foreach (Symbol *symbol, symbols) { + if (symbol->isTypedef()) { + if (NamedType *namedTy = symbol->type()->asNamedType()) { + LookupContext symbolContext(symbol, _context); + QList<Symbol *> resolvedClasses = symbolContext.resolveClass(namedTy->name()); + if (resolvedClasses.count()) { + foreach (Symbol *s, resolvedClasses) { + _resolvedSymbols.append(s->type()); + } + continue; + } + } + } + _resolvedSymbols.append(symbol->type()); + } + return false; +} + +bool ResolveExpression::visit(OperatorFunctionIdAST *) +{ + return false; +} + +bool ResolveExpression::visit(ConversionFunctionIdAST *) +{ + return false; +} + +bool ResolveExpression::visit(SimpleNameAST *ast) +{ + Scope dummy; + Name *name = sem.check(ast, &dummy); + + QList<Symbol *> symbols = _context.resolve(name); + foreach (Symbol *symbol, symbols) + _resolvedSymbols.append(symbol->type()); + + return false; +} + +bool ResolveExpression::visit(DestructorNameAST *) +{ + FullySpecifiedType ty(control()->voidType()); + _resolvedSymbols.append(ty); + return false; +} + +bool ResolveExpression::visit(TemplateIdAST *ast) +{ + Scope dummy; + Name *name = sem.check(ast, &dummy); + + QList<Symbol *> symbols = _context.resolve(name); + foreach (Symbol *symbol, symbols) + _resolvedSymbols.append(symbol->type()); + + return false; +} + +bool ResolveExpression::visit(CallAST *) +{ + QMutableListIterator<FullySpecifiedType> it(_resolvedSymbols); + while (it.hasNext()) { + FullySpecifiedType ty = it.next(); + if (Function *funTy = ty->asFunction()) { + it.setValue(funTy->returnType()); + } else { + it.remove(); + } + } + return false; +} + +bool ResolveExpression::visit(ArrayAccessAST * /* ast */) +{ + QMutableListIterator<FullySpecifiedType> it(_resolvedSymbols); + while (it.hasNext()) { + FullySpecifiedType ty = it.next(); + if (PointerType *ptrTy = ty->asPointerType()) { + it.setValue(ptrTy->elementType()); + } else { + it.remove(); + } + } + return false; +} + +bool ResolveExpression::visit(MemberAccessAST *ast) +{ + Scope dummy; + Name *memberName = sem.check(ast->member_name, &dummy); + unsigned accessOp = tokenKind(ast->access_token); + + Overview overview; + + QList<FullySpecifiedType> candidates = _resolvedSymbols; + _resolvedSymbols.clear(); + + foreach (FullySpecifiedType ty, candidates) { + NamedType *namedTy = 0; + + if (accessOp == T_ARROW) { + if (PointerType *ptrTy = ty->asPointerType()) + namedTy = ptrTy->elementType()->asNamedType(); + } else if (accessOp == T_DOT) { + if (ReferenceType *refTy = ty->asReferenceType()) + ty = refTy->elementType(); + namedTy = ty->asNamedType(); + if (! namedTy) { + Function *fun = ty->asFunction(); + if (fun && (fun->scope()->isBlockScope() || fun->scope()->isNamespaceScope())) + namedTy = fun->returnType()->asNamedType(); + } + } + + if (namedTy) { + QList<Symbol *> symbols = _context.resolveClass(namedTy->name()); + if (symbols.isEmpty()) + return false; + + Class *klass = symbols.first()->asClass(); + QList<Scope *> allScopes; + QSet<Class *> processed; + QList<Class *> todo; + todo.append(klass); + + while (! todo.isEmpty()) { + Class *klass = todo.last(); + todo.removeLast(); + + if (processed.contains(klass)) + continue; + + processed.insert(klass); + allScopes.append(klass->members()); + + for (unsigned i = 0; i < klass->baseClassCount(); ++i) { + BaseClass *baseClass = klass->baseClassAt(i); + Name *baseClassName = baseClass->name(); + QList<Symbol *> baseClasses = _context.resolveClass(baseClassName/*, allScopes*/); + if (baseClasses.isEmpty()) + qWarning() << "unresolved base class:" << overview.prettyName(baseClassName); + foreach (Symbol *symbol, baseClasses) { + todo.append(symbol->asClass()); + } + } + } + + QList<Symbol *> candidates = _context.resolve(memberName, allScopes); + foreach (Symbol *candidate, candidates) { + FullySpecifiedType ty = candidate->type(); + if (TemplateNameId *templId = namedTy->name()->asTemplateNameId()) { + Substitution subst; + for (unsigned i = 0; i < templId->templateArgumentCount(); ++i) { + FullySpecifiedType templArgTy = templId->templateArgumentAt(i); + if (i < klass->templateParameterCount()) { + subst.append(qMakePair(klass->templateParameterAt(i)->name(), + templArgTy)); + } + } + Instantiation inst(control(), subst); + ty = inst(ty); + } + _resolvedSymbols.append(ty); + } + } + } + return false; +} + +bool ResolveExpression::visit(PostIncrDecrAST *) +{ + return false; +} diff --git a/src/libs/cplusplus/NameOfExpression.h b/src/libs/cplusplus/NameOfExpression.h new file mode 100644 index 00000000000..f68795cea0d --- /dev/null +++ b/src/libs/cplusplus/NameOfExpression.h @@ -0,0 +1,99 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPLUSPLUS_NAMEOFEXPRESSION_H +#define CPLUSPLUS_NAMEOFEXPRESSION_H + +#include "LookupContext.h" +#include <ASTVisitor.h> +#include <Semantic.h> +#include <FullySpecifiedType.h> + +namespace CPlusPlus { + +class CPLUSPLUS_EXPORT NameOfExpression : protected ASTVisitor +{ +public: + NameOfExpression(const LookupContext &context); + virtual ~NameOfExpression(); + + Name* operator()(ExpressionAST *ast); + +protected: + QList<FullySpecifiedType> switchResolvedSymbols(const QList<FullySpecifiedType> &symbols); + + virtual bool visit(ExpressionListAST *ast); + virtual bool visit(BinaryExpressionAST *ast); + virtual bool visit(CastExpressionAST *ast); + virtual bool visit(ConditionAST *ast); + virtual bool visit(ConditionalExpressionAST *ast); + virtual bool visit(CppCastExpressionAST *ast); + virtual bool visit(DeleteExpressionAST *ast); + virtual bool visit(ArrayInitializerAST *ast); + virtual bool visit(NewExpressionAST *ast); + virtual bool visit(TypeidExpressionAST *ast); + virtual bool visit(TypenameCallExpressionAST *ast); + virtual bool visit(TypeConstructorCallAST *ast); + virtual bool visit(PostfixExpressionAST *ast); + virtual bool visit(SizeofExpressionAST *ast); + virtual bool visit(NumericLiteralAST *ast); + virtual bool visit(BoolLiteralAST *ast); + virtual bool visit(ThisExpressionAST *ast); + virtual bool visit(NestedExpressionAST *ast); + virtual bool visit(StringLiteralAST *ast); + virtual bool visit(ThrowExpressionAST *ast); + virtual bool visit(TypeIdAST *ast); + virtual bool visit(UnaryExpressionAST *ast); + + //names + virtual bool visit(QualifiedNameAST *ast); + virtual bool visit(OperatorFunctionIdAST *ast); + virtual bool visit(ConversionFunctionIdAST *ast); + virtual bool visit(SimpleNameAST *ast); + virtual bool visit(DestructorNameAST *ast); + virtual bool visit(TemplateIdAST *ast); + + // postfix expressions + virtual bool visit(CallAST *ast); + virtual bool visit(ArrayAccessAST *ast); + virtual bool visit(PostIncrDecrAST *ast); + virtual bool visit(MemberAccessAST *ast); + +private: + LookupContext _context; + Semantic sem; + QList<FullySpecifiedType> _resolvedSymbols; +}; + +} // end of namespace CPlusPlus + +#endif // CPLUSPLUS_NAMEOFEXPRESSION_H diff --git a/src/libs/cplusplus/NamePrettyPrinter.cpp b/src/libs/cplusplus/NamePrettyPrinter.cpp new file mode 100644 index 00000000000..5d6fddfb63a --- /dev/null +++ b/src/libs/cplusplus/NamePrettyPrinter.cpp @@ -0,0 +1,255 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "NamePrettyPrinter.h" +#include <Names.h> +#include <Overview.h> +#include <NameVisitor.h> +#include <Literals.h> + +using namespace CPlusPlus; + +NamePrettyPrinter::NamePrettyPrinter(const Overview *overview) + : _overview(overview) +{ } + +NamePrettyPrinter::~NamePrettyPrinter() +{ } + +const Overview *NamePrettyPrinter::overview() const +{ return _overview; } + +QString NamePrettyPrinter::operator()(Name *name) +{ + QString previousName = switchName(); + accept(name); + return switchName(previousName); +} + +QString NamePrettyPrinter::switchName(const QString &name) +{ + QString previousName = _name; + _name = name; + return previousName; +} + +void NamePrettyPrinter::visit(NameId *name) +{ + Identifier *id = name->identifier(); + if (id) + _name = QString::fromLatin1(id->chars(), id->size()); + else + _name = QLatin1String("anonymous"); +} + +void NamePrettyPrinter::visit(TemplateNameId *name) +{ + Identifier *id = name->identifier(); + if (id) + _name = QString::fromLatin1(id->chars(), id->size()); + else + _name = QLatin1String("anonymous"); + _name += QLatin1Char('<'); + for (unsigned index = 0; index < name->templateArgumentCount(); ++index) { + if (index != 0) + _name += QLatin1String(", "); + + FullySpecifiedType argTy = name->templateArgumentAt(index); + QString arg = overview()->prettyType(argTy); + if (arg.isEmpty()) + _name += QString::fromLatin1("_Tp%1").arg(index + 1); + else + _name += arg; + } + _name += QLatin1Char('>'); +} + +void NamePrettyPrinter::visit(DestructorNameId *name) +{ + Identifier *id = name->identifier(); + _name += QLatin1Char('~'); + _name += QString::fromLatin1(id->chars(), id->size()); +} + +void NamePrettyPrinter::visit(OperatorNameId *name) +{ + _name += QLatin1String("operator "); + switch (name->kind()) { // ### i should probably do this in OperatorNameId + case OperatorNameId::InvalidOp: + _name += QLatin1String("<invalid>"); + break; + case OperatorNameId::NewOp: + _name += QLatin1String("new"); + break; + case OperatorNameId::DeleteOp: + _name += QLatin1String("delete"); + break; + case OperatorNameId::NewArrayOp: + _name += QLatin1String("new[]"); + break; + case OperatorNameId::DeleteArrayOp: + _name += QLatin1String("delete[]"); + break; + case OperatorNameId::PlusOp: + _name += QLatin1String("+"); + break; + case OperatorNameId::MinusOp: + _name += QLatin1String("-"); + break; + case OperatorNameId::StarOp: + _name += QLatin1String("*"); + break; + case OperatorNameId::SlashOp: + _name += QLatin1String("/"); + break; + case OperatorNameId::PercentOp: + _name += QLatin1String("%"); + break; + case OperatorNameId::CaretOp: + _name += QLatin1String("^"); + break; + case OperatorNameId::AmpOp: + _name += QLatin1String("&"); + break; + case OperatorNameId::PipeOp: + _name += QLatin1String("|"); + break; + case OperatorNameId::TildeOp: + _name += QLatin1String("~"); + break; + case OperatorNameId::ExclaimOp: + _name += QLatin1String("!"); + break; + case OperatorNameId::EqualOp: + _name += QLatin1String("="); + break; + case OperatorNameId::LessOp: + _name += QLatin1String("<"); + break; + case OperatorNameId::GreaterOp: + _name += QLatin1String(">"); + break; + case OperatorNameId::PlusEqualOp: + _name += QLatin1String("+="); + break; + case OperatorNameId::MinusEqualOp: + _name += QLatin1String("-="); + break; + case OperatorNameId::StarEqualOp: + _name += QLatin1String("*="); + break; + case OperatorNameId::SlashEqualOp: + _name += QLatin1String("/="); + break; + case OperatorNameId::PercentEqualOp: + _name += QLatin1String("%="); + break; + case OperatorNameId::CaretEqualOp: + _name += QLatin1String("^="); + break; + case OperatorNameId::AmpEqualOp: + _name += QLatin1String("&="); + break; + case OperatorNameId::PipeEqualOp: + _name += QLatin1String("|="); + break; + case OperatorNameId::LessLessOp: + _name += QLatin1String("<<"); + break; + case OperatorNameId::GreaterGreaterOp: + _name += QLatin1String(">>"); + break; + case OperatorNameId::LessLessEqualOp: + _name += QLatin1String("<<="); + break; + case OperatorNameId::GreaterGreaterEqualOp: + _name += QLatin1String(">>="); + break; + case OperatorNameId::EqualEqualOp: + _name += QLatin1String("=="); + break; + case OperatorNameId::ExclaimEqualOp: + _name += QLatin1String("!="); + break; + case OperatorNameId::LessEqualOp: + _name += QLatin1String("<="); + break; + case OperatorNameId::GreaterEqualOp: + _name += QLatin1String(">="); + break; + case OperatorNameId::AmpAmpOp: + _name += QLatin1String("&&"); + break; + case OperatorNameId::PipePipeOp: + _name += QLatin1String("||"); + break; + case OperatorNameId::PlusPlusOp: + _name += QLatin1String("++"); + break; + case OperatorNameId::MinusMinusOp: + _name += QLatin1String("--"); + break; + case OperatorNameId::CommaOp: + _name += QLatin1String(","); + break; + case OperatorNameId::ArrowStarOp: + _name += QLatin1String("->*"); + break; + case OperatorNameId::ArrowOp: + _name += QLatin1String("->"); + break; + case OperatorNameId::FunctionCallOp: + _name += QLatin1String("()"); + break; + case OperatorNameId::ArrayAccessOp: + _name += QLatin1String("[]"); + break; + } // switch +} + +void NamePrettyPrinter::visit(ConversionNameId *name) +{ + _name += QLatin1String("operator "); + _name += overview()->prettyType(name->type()); +} + +void NamePrettyPrinter::visit(QualifiedNameId *name) +{ + if (name->isGlobal()) + _name += QLatin1String("::"); + + for (unsigned index = 0; index < name->nameCount(); ++index) { + if (index != 0) + _name += QLatin1String("::"); + _name += operator()(name->nameAt(index)); + } +} diff --git a/src/libs/cplusplus/NamePrettyPrinter.h b/src/libs/cplusplus/NamePrettyPrinter.h new file mode 100644 index 00000000000..38684326507 --- /dev/null +++ b/src/libs/cplusplus/NamePrettyPrinter.h @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPLUSPLUS_NAMEPRETTYPRINTER_H +#define CPLUSPLUS_NAMEPRETTYPRINTER_H + +#include <NameVisitor.h> +#include <QString> + +namespace CPlusPlus { + +class Overview; + +class CPLUSPLUS_EXPORT NamePrettyPrinter: protected NameVisitor +{ +public: + NamePrettyPrinter(const Overview *overview); + virtual ~NamePrettyPrinter(); + + const Overview *overview() const; + QString operator()(Name *name); + +protected: + QString switchName(const QString &name = QString()); + + virtual void visit(NameId *name); + virtual void visit(TemplateNameId *name); + virtual void visit(DestructorNameId *name); + virtual void visit(OperatorNameId *name); + virtual void visit(ConversionNameId *name); + virtual void visit(QualifiedNameId *name); + +private: + const Overview *_overview; + QString _name; +}; + +} // end of namespace CPlusPlus + +#endif // CPLUSPLUS_NAMEPRETTYPRINTER_H diff --git a/src/libs/cplusplus/Overview.cpp b/src/libs/cplusplus/Overview.cpp new file mode 100644 index 00000000000..ec40f75b515 --- /dev/null +++ b/src/libs/cplusplus/Overview.cpp @@ -0,0 +1,91 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "Overview.h" +#include "NamePrettyPrinter.h" +#include "TypePrettyPrinter.h" +#include <FullySpecifiedType.h> + +using namespace CPlusPlus; + +Overview::Overview() + : _markArgument(0), + _showArgumentNames(false), + _showReturnTypes(false), + _showFunctionSignatures(true) +{ } + +Overview::~Overview() +{ } + +bool Overview::showArgumentNames() const +{ return _showArgumentNames; } + +void Overview::setShowArgumentNames(bool showArgumentNames) +{ _showArgumentNames = showArgumentNames; } + +void Overview::setShowReturnTypes(bool showReturnTypes) +{ _showReturnTypes = showReturnTypes; } + +bool Overview::showReturnTypes() const +{ return _showReturnTypes; } + +void Overview::setMarkArgument(unsigned position) +{ _markArgument = position; } + +bool Overview::showFunctionSignatures() const +{ return _showFunctionSignatures; } + +void Overview::setShowFunctionSignatures(bool showFunctionSignatures) +{ _showFunctionSignatures = showFunctionSignatures; } + +QString Overview::prettyName(Name *name) const +{ + NamePrettyPrinter pp(this); + return pp(name); +} + +QString Overview::prettyType(const FullySpecifiedType &ty, + Name *name) const +{ return prettyType(ty, prettyName(name)); } + +QString Overview::prettyType(const FullySpecifiedType &ty, + const QString &name) const +{ + TypePrettyPrinter pp(this); + pp.setMarkArgument(_markArgument); + pp.setShowArgumentNames(_showArgumentNames); + pp.setShowReturnTypes(_showReturnTypes); + pp.setShowFunctionSignatures(_showFunctionSignatures); + return pp(ty, name); +} diff --git a/src/libs/cplusplus/Overview.h b/src/libs/cplusplus/Overview.h new file mode 100644 index 00000000000..6b84a4b53f2 --- /dev/null +++ b/src/libs/cplusplus/Overview.h @@ -0,0 +1,80 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef OVERVIEW_H +#define OVERVIEW_H + +#include <CPlusPlusForwardDeclarations.h> +#include <QString> + +namespace CPlusPlus { + +class CPLUSPLUS_EXPORT Overview +{ + Overview(const Overview &other); + void operator =(const Overview &other); + +public: + Overview(); + ~Overview(); + + bool showArgumentNames() const; + void setShowArgumentNames(bool showArgumentNames); + + bool showReturnTypes() const; + void setShowReturnTypes(bool showReturnTypes); + + bool showFunctionSignatures() const; + void setShowFunctionSignatures(bool showFunctionSignatures); + + void setMarkArgument(unsigned position); // 1-based + + QString operator()(Name *name) const + { return prettyName(name); } + + QString operator()(const FullySpecifiedType &type, Name *name = 0) const + { return prettyType(type, name); } + + QString prettyName(Name *name) const; + QString prettyType(const FullySpecifiedType &type, Name *name = 0) const; + QString prettyType(const FullySpecifiedType &type, const QString &name) const; + +private: + unsigned _markArgument; + bool _showArgumentNames: 1; + bool _showReturnTypes: 1; + bool _showFunctionSignatures: 1; +}; + +} // end of namespace CPlusPlus + +#endif // OVERVIEW_H diff --git a/src/libs/cplusplus/OverviewModel.cpp b/src/libs/cplusplus/OverviewModel.cpp new file mode 100644 index 00000000000..028811e6ae3 --- /dev/null +++ b/src/libs/cplusplus/OverviewModel.cpp @@ -0,0 +1,183 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "OverviewModel.h" +#include "Overview.h" +#include <Scope.h> +#include <Semantic.h> +#include <Literals.h> +#include <Symbols.h> +#include <QFile> +#include <QtDebug> + +using namespace CPlusPlus; + +OverviewModel::OverviewModel(QObject *parent) + : QAbstractItemModel(parent) +{ } + +OverviewModel::~OverviewModel() +{ } + +bool OverviewModel::hasDocument() const +{ return _cppDocument; } + +Document::Ptr OverviewModel::document() const +{ return _cppDocument; } + +unsigned OverviewModel::globalSymbolCount() const +{ + unsigned count = 0; + if (_cppDocument) + count += _cppDocument->globalSymbolCount(); + return count; +} + +Symbol *OverviewModel::globalSymbolAt(unsigned index) const +{ return _cppDocument->globalSymbolAt(index); } + +QModelIndex OverviewModel::index(int row, int column, const QModelIndex &parent) const +{ + if (! hasDocument()) { + return QModelIndex(); + } else if (! parent.isValid()) { + Symbol *symbol = globalSymbolAt(row); + return createIndex(row, column, symbol); + } else { + Symbol *parentSymbol = static_cast<Symbol *>(parent.internalPointer()); + Q_ASSERT(parentSymbol != 0); + + ScopedSymbol *scopedSymbol = parentSymbol->asScopedSymbol(); + Q_ASSERT(scopedSymbol != 0); + + Scope *scope = scopedSymbol->members(); + Q_ASSERT(scope != 0); + + return createIndex(row, 0, scope->symbolAt(row)); + } +} + +QModelIndex OverviewModel::parent(const QModelIndex &child) const +{ + Symbol *symbol = static_cast<Symbol *>(child.internalPointer()); + Q_ASSERT(symbol != 0); + + if (Scope *scope = symbol->scope()) { + Symbol *parentSymbol = scope->owner(); + if (parentSymbol && parentSymbol->scope()) + return createIndex(parentSymbol->index(), 0, parentSymbol); + } + + return QModelIndex(); +} + +int OverviewModel::rowCount(const QModelIndex &parent) const +{ + if (hasDocument()) { + if (! parent.isValid()) { + return globalSymbolCount(); + } else { + Symbol *parentSymbol = static_cast<Symbol *>(parent.internalPointer()); + Q_ASSERT(parentSymbol != 0); + + if (ScopedSymbol *scopedSymbol = parentSymbol->asScopedSymbol()) { + if (! scopedSymbol->isFunction()) { + Scope *parentScope = scopedSymbol->members(); + Q_ASSERT(parentScope != 0); + + return parentScope->symbolCount(); + } + } + } + } + return 0; +} + +int OverviewModel::columnCount(const QModelIndex &) const +{ return 1; } + +QVariant OverviewModel::data(const QModelIndex &index, int role) const +{ + switch (role) { + case Qt::DisplayRole: { + Symbol *symbol = static_cast<Symbol *>(index.internalPointer()); + QString name = _overview.prettyName(symbol->name()); + if (name.isEmpty()) + name = QLatin1String("anonymous"); + if (! symbol->isScopedSymbol() || symbol->isFunction()) { + QString type = _overview.prettyType(symbol->type()); + if (! type.isEmpty()) { + if (! symbol->type()->isFunction()) + name += QLatin1String(": "); + name += type; + } + } + return name; + } + + case Qt::EditRole: { + Symbol *symbol = static_cast<Symbol *>(index.internalPointer()); + QString name = _overview.prettyName(symbol->name()); + if (name.isEmpty()) + name = QLatin1String("anonymous"); + return name; + } + + case Qt::DecorationRole: { + Symbol *symbol = static_cast<Symbol *>(index.internalPointer()); + return _icons.iconForSymbol(symbol); + } break; + + case FileNameRole: { + Symbol *symbol = static_cast<Symbol *>(index.internalPointer()); + return QString::fromUtf8(symbol->fileName(), symbol->fileNameLength()); + } + + case LineNumberRole: { + Symbol *symbol = static_cast<Symbol *>(index.internalPointer()); + return symbol->line(); + } + + default: + return QVariant(); + } // switch +} + +Symbol *OverviewModel::symbolFromIndex(const QModelIndex &index) const +{ return static_cast<Symbol *>(index.internalPointer()); } + +void OverviewModel::rebuild(Document::Ptr doc) +{ + _cppDocument = doc; + reset(); +} diff --git a/src/libs/cplusplus/OverviewModel.h b/src/libs/cplusplus/OverviewModel.h new file mode 100644 index 00000000000..9ff920487ba --- /dev/null +++ b/src/libs/cplusplus/OverviewModel.h @@ -0,0 +1,84 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPLUSPLUS_OVERVIEWMODEL_H +#define CPLUSPLUS_OVERVIEWMODEL_H + +#include "CppDocument.h" +#include "Overview.h" +#include "Icons.h" + +#include <QAbstractItemModel> +#include <QIcon> + +namespace CPlusPlus { + +class CPLUSPLUS_EXPORT OverviewModel: public QAbstractItemModel +{ + Q_OBJECT + +public: + enum Role { + FileNameRole = Qt::UserRole + 1, + LineNumberRole + }; + +public: + OverviewModel(QObject *parent = 0); + virtual ~OverviewModel(); + + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &child) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + Document::Ptr document() const; + Symbol *symbolFromIndex(const QModelIndex &index) const; + +public Q_SLOTS: + void rebuild(CPlusPlus::Document::Ptr doc); + +private: + bool hasDocument() const; + unsigned globalSymbolCount() const; + Symbol *globalSymbolAt(unsigned index) const; + +private: + Document::Ptr _cppDocument; + Overview _overview; + Icons _icons; +}; + +} // end of namespace CPlusPlus + +#endif // CPLUSPLUS_OVERVIEWMODEL_H diff --git a/src/libs/cplusplus/ResolveExpression.cpp b/src/libs/cplusplus/ResolveExpression.cpp new file mode 100644 index 00000000000..6071f235c43 --- /dev/null +++ b/src/libs/cplusplus/ResolveExpression.cpp @@ -0,0 +1,793 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "ResolveExpression.h" +#include "LookupContext.h" +#include "Overview.h" + +#include <Control.h> +#include <AST.h> +#include <Scope.h> +#include <Names.h> +#include <Symbols.h> +#include <Literals.h> +#include <CoreTypes.h> +#include <TypeVisitor.h> +#include <NameVisitor.h> +#include <QList> +#include <QtDebug> + +using namespace CPlusPlus; + +namespace { + +typedef QList< QPair<Name *, FullySpecifiedType> > Substitution; + +class Instantiation: protected TypeVisitor, protected NameVisitor +{ + Control *_control; + FullySpecifiedType _type; + const Substitution _substitution; + +public: + Instantiation(Control *control, const Substitution &substitution) + : _control(control), + _substitution(substitution) + { } + + FullySpecifiedType operator()(const FullySpecifiedType &ty) + { return subst(ty); } + +protected: + FullySpecifiedType subst(Name *name) + { + for (int i = 0; i < _substitution.size(); ++i) { + const QPair<Name *, FullySpecifiedType> s = _substitution.at(i); + if (name->isEqualTo(s.first)) + return s.second; + } + + return _control->namedType(name); + } + + FullySpecifiedType subst(const FullySpecifiedType &ty) + { + FullySpecifiedType previousType = switchType(ty); + TypeVisitor::accept(ty.type()); + return switchType(previousType); + } + + FullySpecifiedType switchType(const FullySpecifiedType &type) + { + FullySpecifiedType previousType = _type; + _type = type; + return previousType; + } + + // types + virtual void visit(PointerToMemberType * /*ty*/) + { + Q_ASSERT(0); + } + + virtual void visit(PointerType *ty) + { + FullySpecifiedType elementType = subst(ty->elementType()); + _type.setType(_control->pointerType(elementType)); + } + + virtual void visit(ReferenceType *ty) + { + FullySpecifiedType elementType = subst(ty->elementType()); + _type.setType(_control->referenceType(elementType)); + } + + virtual void visit(ArrayType *ty) + { + FullySpecifiedType elementType = subst(ty->elementType()); + _type.setType(_control->arrayType(elementType, ty->size())); + } + + virtual void visit(NamedType *ty) + { _type.setType(subst(ty->name()).type()); } // ### merge the specifiers + + virtual void visit(Function *ty) + { + Name *name = ty->name(); + FullySpecifiedType returnType = subst(ty->returnType()); + + Function *fun = _control->newFunction(0, name); + fun->setScope(ty->scope()); + fun->setReturnType(returnType); + for (unsigned i = 0; i < ty->argumentCount(); ++i) { + Symbol *arg = ty->argumentAt(i); + FullySpecifiedType argTy = subst(arg->type()); + Argument *newArg = _control->newArgument(0, arg->name()); + newArg->setType(argTy); + fun->arguments()->enterSymbol(newArg); + } + _type.setType(fun); + } + + virtual void visit(VoidType *) + { /* nothing to do*/ } + + virtual void visit(IntegerType *) + { /* nothing to do*/ } + + virtual void visit(FloatType *) + { /* nothing to do*/ } + + virtual void visit(Namespace *) + { Q_ASSERT(0); } + + virtual void visit(Class *) + { Q_ASSERT(0); } + + virtual void visit(Enum *) + { Q_ASSERT(0); } + + // names + virtual void visit(NameId *) + { Q_ASSERT(0); } + + virtual void visit(TemplateNameId *) + { Q_ASSERT(0); } + + virtual void visit(DestructorNameId *) + { Q_ASSERT(0); } + + virtual void visit(OperatorNameId *) + { Q_ASSERT(0); } + + virtual void visit(ConversionNameId *) + { Q_ASSERT(0); } + + virtual void visit(QualifiedNameId *) + { Q_ASSERT(0); } +}; + +} // end of anonymous namespace + +///////////////////////////////////////////////////////////////////// +// ResolveExpression +///////////////////////////////////////////////////////////////////// +ResolveExpression::ResolveExpression(const LookupContext &context) + : ASTVisitor(context.expressionDocument()->control()), + _context(context), + sem(_context.control()) +{ } + +ResolveExpression::~ResolveExpression() +{ } + +QList<ResolveExpression::Result> ResolveExpression::operator()(ExpressionAST *ast) +{ + const QList<Result> previousResults = switchResults(QList<Result>()); + accept(ast); + return switchResults(previousResults); +} + +QList<ResolveExpression::Result> +ResolveExpression::switchResults(const QList<ResolveExpression::Result> &results) +{ + const QList<Result> previousResults = _results; + _results = results; + return previousResults; +} + +void ResolveExpression::addResults(const QList<Result> &results) +{ + foreach (const Result r, results) + addResult(r); +} + +void ResolveExpression::addResult(const FullySpecifiedType &ty, Symbol *symbol) +{ return addResult(Result(ty, symbol)); } + +void ResolveExpression::addResult(const Result &r) +{ + Result p = r; + if (! p.second) + p.second = _context.symbol(); + + if (! _results.contains(p)) + _results.append(p); +} + +QList<Scope *> ResolveExpression::visibleScopes(const Result &result) const +{ return _context.visibleScopes(result); } + +bool ResolveExpression::visit(ExpressionListAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(BinaryExpressionAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(CastExpressionAST *ast) +{ + Scope dummy; + addResult(sem.check(ast->type_id, &dummy)); + return false; +} + +bool ResolveExpression::visit(ConditionAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(ConditionalExpressionAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(CppCastExpressionAST *ast) +{ + Scope dummy; + addResult(sem.check(ast->type_id, &dummy)); + return false; +} + +bool ResolveExpression::visit(DeleteExpressionAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(ArrayInitializerAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(NewExpressionAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(TypeidExpressionAST *) +{ + Name *std_type_info[2]; + std_type_info[0] = control()->nameId(control()->findOrInsertIdentifier("std")); + std_type_info[1] = control()->nameId(control()->findOrInsertIdentifier("type_info")); + + Name *q = control()->qualifiedNameId(std_type_info, 2, /*global=*/ true); + FullySpecifiedType ty(control()->namedType(q)); + addResult(ty); + + return false; +} + +bool ResolveExpression::visit(TypenameCallExpressionAST *) +{ + // nothing to do + return false; +} + +bool ResolveExpression::visit(TypeConstructorCallAST *) +{ + // nothing to do. + return false; +} + +bool ResolveExpression::visit(PostfixExpressionAST *ast) +{ + accept(ast->base_expression); + + for (PostfixAST *fx = ast->postfix_expressions; fx; fx = fx->next) { + accept(fx); + } + + return false; +} + +bool ResolveExpression::visit(SizeofExpressionAST *) +{ + FullySpecifiedType ty(control()->integerType(IntegerType::Int)); + ty.setUnsigned(true); + addResult(ty); + return false; +} + +bool ResolveExpression::visit(NumericLiteralAST *) +{ + FullySpecifiedType ty(control()->integerType(IntegerType::Int)); + addResult(ty); + return false; +} + +bool ResolveExpression::visit(BoolLiteralAST *) +{ + FullySpecifiedType ty(control()->integerType(IntegerType::Bool)); + addResult(ty); + return false; +} + +bool ResolveExpression::visit(ThisExpressionAST *) +{ + if (! _context.symbol()) + return false; + + Scope *scope = _context.symbol()->scope(); + for (; scope; scope = scope->enclosingScope()) { + if (scope->isFunctionScope()) { + Function *fun = scope->owner()->asFunction(); + if (Scope *cscope = scope->enclosingClassScope()) { + Class *klass = cscope->owner()->asClass(); + FullySpecifiedType classTy(control()->namedType(klass->name())); + FullySpecifiedType ptrTy(control()->pointerType(classTy)); + addResult(ptrTy, fun); + break; + } else if (QualifiedNameId *q = fun->name()->asQualifiedNameId()) { + Name *nestedNameSpecifier = 0; + if (q->nameCount() == 1 && q->isGlobal()) + nestedNameSpecifier = q->nameAt(0); + else + nestedNameSpecifier = control()->qualifiedNameId(q->names(), q->nameCount() - 1); + FullySpecifiedType classTy(control()->namedType(nestedNameSpecifier)); + FullySpecifiedType ptrTy(control()->pointerType(classTy)); + addResult(ptrTy, fun); + break; + } + } + } + return false; +} + +bool ResolveExpression::visit(NestedExpressionAST *ast) +{ + accept(ast->expression); + return false; +} + +bool ResolveExpression::visit(StringLiteralAST *) +{ + FullySpecifiedType charTy = control()->integerType(IntegerType::Char); + charTy.setConst(true); + FullySpecifiedType ty(control()->pointerType(charTy)); + addResult(ty); + return false; +} + +bool ResolveExpression::visit(ThrowExpressionAST *) +{ + return false; +} + +bool ResolveExpression::visit(TypeIdAST *) +{ + return false; +} + +bool ResolveExpression::visit(UnaryExpressionAST *ast) +{ + accept(ast->expression); + unsigned unaryOp = tokenKind(ast->unary_op_token); + if (unaryOp == T_AMPER) { + QMutableListIterator<Result > it(_results); + while (it.hasNext()) { + Result p = it.next(); + p.first.setType(control()->pointerType(p.first)); + it.setValue(p); + } + } else if (unaryOp == T_STAR) { + QMutableListIterator<Result > it(_results); + while (it.hasNext()) { + Result p = it.next(); + if (PointerType *ptrTy = p.first->asPointerType()) { + p.first = ptrTy->elementType(); + it.setValue(p); + } else { + it.remove(); + } + } + } + return false; +} + +bool ResolveExpression::visit(QualifiedNameAST *ast) +{ + Scope dummy; + Name *name = sem.check(ast, &dummy); + + QList<Symbol *> symbols = _context.resolve(name); + foreach (Symbol *symbol, symbols) { + if (symbol->isTypedef()) { + if (NamedType *namedTy = symbol->type()->asNamedType()) { + LookupContext symbolContext(symbol, _context); + QList<Symbol *> resolvedClasses = symbolContext.resolveClass(namedTy->name()); + if (resolvedClasses.count()) { + foreach (Symbol *s, resolvedClasses) { + addResult(s->type(), s); + } + continue; + } + } + } + addResult(symbol->type(), symbol); + } + return false; +} + +bool ResolveExpression::visit(OperatorFunctionIdAST *) +{ + return false; +} + +bool ResolveExpression::visit(ConversionFunctionIdAST *) +{ + return false; +} + +bool ResolveExpression::visit(SimpleNameAST *ast) +{ + Scope dummy; + Name *name = sem.check(ast, &dummy); + + QList<Symbol *> symbols = _context.resolve(name); + foreach (Symbol *symbol, symbols) + addResult(symbol->type(), symbol); + + return false; +} + +bool ResolveExpression::visit(DestructorNameAST *) +{ + FullySpecifiedType ty(control()->voidType()); + addResult(ty); + return false; +} + +bool ResolveExpression::visit(TemplateIdAST *ast) +{ + Scope dummy; + Name *name = sem.check(ast, &dummy); + + QList<Symbol *> symbols = _context.resolve(name); + foreach (Symbol *symbol, symbols) + addResult(symbol->type(), symbol); + + return false; +} + +bool ResolveExpression::visit(CallAST *ast) +{ + // Compute the types of the actual arguments. + QList< QList<Result> > arguments; + for (ExpressionListAST *exprIt = ast->expression_list; exprIt; + exprIt = exprIt->next) { + arguments.append(operator()(exprIt->expression)); + } + + QList<Result> baseResults = _results; + _results.clear(); + + foreach (Result p, baseResults) { + if (Function *funTy = p.first->asFunction()) { + unsigned minNumberArguments = 0; + for (; minNumberArguments < funTy->argumentCount(); ++minNumberArguments) { + Argument *arg = funTy->argumentAt(minNumberArguments)->asArgument(); + if (arg->hasInitializer()) + break; + } + const unsigned actualArgumentCount = arguments.count(); + if (actualArgumentCount < minNumberArguments) { + // not enough arguments. + } else if (! funTy->isVariadic() && actualArgumentCount > funTy->argumentCount()) { + // too many arguments. + } else { + p.first = funTy->returnType(); + addResult(p); + } + } else if (Class *classTy = p.first->asClass()) { + // Constructor call + p.first = control()->namedType(classTy->name()); + addResult(p); + } + } + + return false; +} + +bool ResolveExpression::visit(ArrayAccessAST *ast) +{ + const QList<Result> baseResults = _results; + _results.clear(); + + const QList<Result> indexResults = operator()(ast->expression); + + foreach (Result p, baseResults) { + FullySpecifiedType ty = p.first; + Symbol *contextSymbol = p.second; + + if (ReferenceType *refTy = ty->asReferenceType()) + ty = refTy->elementType(); + + if (PointerType *ptrTy = ty->asPointerType()) { + addResult(ptrTy->elementType(), contextSymbol); + } else if (ArrayType *arrTy = ty->asArrayType()) { + addResult(arrTy->elementType(), contextSymbol); + } else if (NamedType *namedTy = ty->asNamedType()) { + Name *className = namedTy->name(); + const QList<Scope *> scopes = visibleScopes(p); + const QList<Symbol *> classObjectCandidates = _context.resolveClass(className, scopes); + + foreach (Symbol *classObject, classObjectCandidates) { + const QList<Result> overloads = resolveArrayOperator(p, namedTy, + classObject->asClass()); + foreach (Result r, overloads) { + FullySpecifiedType ty = r.first; + Function *funTy = ty->asFunction(); + if (! funTy) + continue; + + ty = funTy->returnType(); + addResult(ty, funTy); + } + } + } + } + return false; +} + +bool ResolveExpression::visit(MemberAccessAST *ast) +{ + // The candidate types for the base expression are stored in + // _results. + QList<Result> baseResults = _results; + + // Evaluate the expression-id that follows the access operator. + Scope dummy; + Name *memberName = sem.check(ast->member_name, &dummy); + + // Remember the access operator. + const unsigned accessOp = tokenKind(ast->access_token); + + _results = resolveMemberExpression(baseResults, accessOp, memberName); + + return false; +} + +QList<ResolveExpression::Result> +ResolveExpression::resolveMemberExpression(const QList<Result> &baseResults, + unsigned accessOp, + Name *memberName) const +{ + QList<Result> results; + + if (accessOp == T_ARROW) { + foreach (Result p, baseResults) { + FullySpecifiedType ty = p.first; + + if (ReferenceType *refTy = ty->asReferenceType()) + ty = refTy->elementType(); + + if (NamedType *namedTy = ty->asNamedType()) { + Name *className = namedTy->name(); + const QList<Scope *> scopes = visibleScopes(p); + const QList<Symbol *> classObjectCandidates = _context.resolveClass(className, scopes); + + foreach (Symbol *classObject, classObjectCandidates) { + const QList<Result> overloads = resolveArrowOperator(p, namedTy, + classObject->asClass()); + foreach (Result r, overloads) { + FullySpecifiedType ty = r.first; + Function *funTy = ty->asFunction(); + if (! funTy) + continue; + + ty = funTy->returnType(); + + if (ReferenceType *refTy = ty->asReferenceType()) + ty = refTy->elementType(); + + if (PointerType *ptrTy = ty->asPointerType()) { + if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) + results += resolveMember(r, memberName, namedTy); + } + } + } + } else if (PointerType *ptrTy = ty->asPointerType()) { + if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) + results += resolveMember(p, memberName, namedTy); + } + } + } else if (accessOp == T_DOT) { + // The base expression shall be a "class object" of a complete type. + foreach (Result p, baseResults) { + FullySpecifiedType ty = p.first; + + if (ReferenceType *refTy = ty->asReferenceType()) + ty = refTy->elementType(); + + if (NamedType *namedTy = ty->asNamedType()) + results += resolveMember(p, memberName, namedTy); + else if (Function *fun = ty->asFunction()) { + if (fun->scope()->isBlockScope() || fun->scope()->isNamespaceScope()) { + ty = fun->returnType(); + + if (ReferenceType *refTy = ty->asReferenceType()) + ty = refTy->elementType(); + + if (NamedType *namedTy = ty->asNamedType()) + results += resolveMember(p, memberName, namedTy); + } + } + + } + } + + return results; +} + +QList<ResolveExpression::Result> +ResolveExpression::resolveMember(const Result &p, + Name *memberName, + NamedType *namedTy) const +{ + QList<Result> results; + Name *className = namedTy->name(); + const QList<Scope *> scopes = visibleScopes(p); + const QList<Symbol *> classObjectCandidates = _context.resolveClass(className, scopes); + foreach (Symbol *classObject, classObjectCandidates) { + results += resolveMember(p, memberName, namedTy, classObject->asClass()); + } + return results; +} + +QList<ResolveExpression::Result> +ResolveExpression::resolveMember(const Result &, + Name *memberName, + NamedType *namedTy, + Class *klass) const +{ + QList<Scope *> scopes; + _context.expand(klass->members(), _context.visibleScopes(), &scopes); + QList<Result> results; + + QList<Symbol *> candidates = _context.resolve(memberName, scopes); + foreach (Symbol *candidate, candidates) { + FullySpecifiedType ty = candidate->type(); + Name *unqualifiedNameId = namedTy->name(); + if (QualifiedNameId *q = namedTy->name()->asQualifiedNameId()) + unqualifiedNameId = q->unqualifiedNameId(); + if (TemplateNameId *templId = unqualifiedNameId->asTemplateNameId()) { + Substitution subst; + for (unsigned i = 0; i < templId->templateArgumentCount(); ++i) { + FullySpecifiedType templArgTy = templId->templateArgumentAt(i); + if (i < klass->templateParameterCount()) { + subst.append(qMakePair(klass->templateParameterAt(i)->name(), + templArgTy)); + } + } + Instantiation inst(control(), subst); + ty = inst(ty); + } + + const Result result(ty, candidate); + if (! results.contains(result)) + results.append(result); + } + + return results; +} + +QList<ResolveExpression::Result> +ResolveExpression::resolveArrowOperator(const Result &, + NamedType *namedTy, + Class *klass) const +{ + QList<Scope *> scopes; + _context.expand(klass->members(), _context.visibleScopes(), &scopes); + QList<Result> results; + + Name *memberName = control()->operatorNameId(OperatorNameId::ArrowOp); + QList<Symbol *> candidates = _context.resolve(memberName, scopes); + foreach (Symbol *candidate, candidates) { + FullySpecifiedType ty = candidate->type(); + Name *unqualifiedNameId = namedTy->name(); + if (QualifiedNameId *q = namedTy->name()->asQualifiedNameId()) + unqualifiedNameId = q->unqualifiedNameId(); + if (TemplateNameId *templId = unqualifiedNameId->asTemplateNameId()) { + Substitution subst; + for (unsigned i = 0; i < templId->templateArgumentCount(); ++i) { + FullySpecifiedType templArgTy = templId->templateArgumentAt(i); + if (i < klass->templateParameterCount()) { + subst.append(qMakePair(klass->templateParameterAt(i)->name(), + templArgTy)); + } + } + Instantiation inst(control(), subst); + ty = inst(ty); + } + + const Result result(ty, candidate); + if (! results.contains(result)) + results.append(result); + } + + return results; +} + +QList<ResolveExpression::Result> +ResolveExpression::resolveArrayOperator(const Result &, + NamedType *namedTy, + Class *klass) const +{ + // ### todo handle index expressions. + + QList<Scope *> scopes; + _context.expand(klass->members(), _context.visibleScopes(), &scopes); + QList<Result> results; + + Name *memberName = control()->operatorNameId(OperatorNameId::ArrayAccessOp); + QList<Symbol *> candidates = _context.resolve(memberName, scopes); + foreach (Symbol *candidate, candidates) { + FullySpecifiedType ty = candidate->type(); + Name *unqualifiedNameId = namedTy->name(); + if (QualifiedNameId *q = namedTy->name()->asQualifiedNameId()) + unqualifiedNameId = q->unqualifiedNameId(); + if (TemplateNameId *templId = unqualifiedNameId->asTemplateNameId()) { + Substitution subst; + for (unsigned i = 0; i < templId->templateArgumentCount(); ++i) { + FullySpecifiedType templArgTy = templId->templateArgumentAt(i); + if (i < klass->templateParameterCount()) { + subst.append(qMakePair(klass->templateParameterAt(i)->name(), + templArgTy)); + } + } + Instantiation inst(control(), subst); + ty = inst(ty); + } + + const Result result(ty, candidate); + if (! results.contains(result)) + results.append(result); + } + + return results; +} + +bool ResolveExpression::visit(PostIncrDecrAST *) +{ + return false; +} diff --git a/src/libs/cplusplus/ResolveExpression.h b/src/libs/cplusplus/ResolveExpression.h new file mode 100644 index 00000000000..75963bb2fee --- /dev/null +++ b/src/libs/cplusplus/ResolveExpression.h @@ -0,0 +1,131 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPLUSPLUS_RESOLVEEXPRESSION_H +#define CPLUSPLUS_RESOLVEEXPRESSION_H + +#include "LookupContext.h" +#include <ASTVisitor.h> +#include <Semantic.h> +#include <FullySpecifiedType.h> + +namespace CPlusPlus { + +class CPLUSPLUS_EXPORT ResolveExpression: protected ASTVisitor +{ +public: + typedef QPair<FullySpecifiedType, Symbol *> Result; + +public: + ResolveExpression(const LookupContext &context); + virtual ~ResolveExpression(); + + QList<Result> operator()(ExpressionAST *ast); + + QList<Result> resolveMemberExpression(const QList<Result> &baseResults, + unsigned accessOp, + Name *memberName) const; + + QList<Result> resolveMember(const Result &result, + Name *memberName, + NamedType *namedTy) const; + + QList<Result> resolveMember(const Result &result, + Name *memberName, + NamedType *namedTy, + Class *klass) const; + + QList<Result> resolveArrowOperator(const Result &result, + NamedType *namedTy, + Class *klass) const; + + QList<Result> resolveArrayOperator(const Result &result, + NamedType *namedTy, + Class *klass) const; + +protected: + QList<Result> switchResults(const QList<Result> &symbols); + + void addResult(const FullySpecifiedType &ty, Symbol *symbol = 0); + void addResult(const Result &result); + void addResults(const QList<Result> &results); + + using ASTVisitor::visit; + + virtual bool visit(ExpressionListAST *ast); + virtual bool visit(BinaryExpressionAST *ast); + virtual bool visit(CastExpressionAST *ast); + virtual bool visit(ConditionAST *ast); + virtual bool visit(ConditionalExpressionAST *ast); + virtual bool visit(CppCastExpressionAST *ast); + virtual bool visit(DeleteExpressionAST *ast); + virtual bool visit(ArrayInitializerAST *ast); + virtual bool visit(NewExpressionAST *ast); + virtual bool visit(TypeidExpressionAST *ast); + virtual bool visit(TypenameCallExpressionAST *ast); + virtual bool visit(TypeConstructorCallAST *ast); + virtual bool visit(PostfixExpressionAST *ast); + virtual bool visit(SizeofExpressionAST *ast); + virtual bool visit(NumericLiteralAST *ast); + virtual bool visit(BoolLiteralAST *ast); + virtual bool visit(ThisExpressionAST *ast); + virtual bool visit(NestedExpressionAST *ast); + virtual bool visit(StringLiteralAST *ast); + virtual bool visit(ThrowExpressionAST *ast); + virtual bool visit(TypeIdAST *ast); + virtual bool visit(UnaryExpressionAST *ast); + + //names + virtual bool visit(QualifiedNameAST *ast); + virtual bool visit(OperatorFunctionIdAST *ast); + virtual bool visit(ConversionFunctionIdAST *ast); + virtual bool visit(SimpleNameAST *ast); + virtual bool visit(DestructorNameAST *ast); + virtual bool visit(TemplateIdAST *ast); + + // postfix expressions + virtual bool visit(CallAST *ast); + virtual bool visit(ArrayAccessAST *ast); + virtual bool visit(PostIncrDecrAST *ast); + virtual bool visit(MemberAccessAST *ast); + + QList<Scope *> visibleScopes(const Result &result) const; + +private: + LookupContext _context; + Semantic sem; + QList<Result> _results; +}; + +} // end of namespace CPlusPlus + +#endif // CPLUSPLUS_RESOLVEEXPRESSION_H diff --git a/src/libs/cplusplus/SimpleLexer.cpp b/src/libs/cplusplus/SimpleLexer.cpp new file mode 100644 index 00000000000..f047d540c94 --- /dev/null +++ b/src/libs/cplusplus/SimpleLexer.cpp @@ -0,0 +1,117 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "SimpleLexer.h" +#include <Lexer.h> +#include <Token.h> +#include <QtDebug> + +using namespace CPlusPlus; + +bool SimpleToken::isLiteral() const +{ return _kind >= T_FIRST_LITERAL && _kind <= T_LAST_LITERAL; } + +bool SimpleToken::isOperator() const +{ return _kind >= T_FIRST_OPERATOR && _kind <= T_LAST_OPERATOR; } + +bool SimpleToken::isKeyword() const +{ return _kind >= T_FIRST_KEYWORD && _kind < T_FIRST_QT_KEYWORD; } + +SimpleLexer::SimpleLexer() + : _lastState(0), + _skipComments(false), + _qtMocRunEnabled(true) +{ } + +SimpleLexer::~SimpleLexer() +{ } + +bool SimpleLexer::qtMocRunEnabled() const +{ return _qtMocRunEnabled; } + +void SimpleLexer::setQtMocRunEnabled(bool enabled) +{ _qtMocRunEnabled = enabled; } + +bool SimpleLexer::skipComments() const +{ return _skipComments; } + +void SimpleLexer::setSkipComments(bool skipComments) +{ _skipComments = skipComments; } + +QList<SimpleToken> SimpleLexer::operator()(const QString &text, int state) +{ + QList<SimpleToken> tokens; + + const QByteArray bytes = text.toLatin1(); + const char *firstChar = bytes.constData(); + const char *lastChar = firstChar + bytes.size(); + + Lexer lex(firstChar, lastChar); + lex.setQtMocRunEnabled(_qtMocRunEnabled); + + if (! _skipComments) + lex.setScanCommentTokens(true); + + if (state != -1) + lex.setState(state & 0xff); + + bool inPreproc = false; + + for (;;) { + Token tk; + lex(&tk); + if (tk.is(T_EOF_SYMBOL)) + break; + + SimpleToken simpleTk; + simpleTk._kind = int(tk.kind); + simpleTk._position = int(lex.tokenOffset()); + simpleTk._length = int(lex.tokenLength()); + simpleTk._text = text.midRef(simpleTk._position, simpleTk._length); + + lex.setScanAngleStringLiteralTokens(false); + + if (tk.newline && tk.is(T_POUND)) + inPreproc = true; + else if (inPreproc && tokens.size() == 1 && simpleTk.is(T_IDENTIFIER) && + simpleTk.text() == QLatin1String("include")) + lex.setScanAngleStringLiteralTokens(true); + + tokens.append(simpleTk); + } + + _lastState = lex.state(); + return tokens; +} + + diff --git a/src/libs/cplusplus/SimpleLexer.h b/src/libs/cplusplus/SimpleLexer.h new file mode 100644 index 00000000000..9bbba419502 --- /dev/null +++ b/src/libs/cplusplus/SimpleLexer.h @@ -0,0 +1,106 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SIMPLELEXER_H +#define SIMPLELEXER_H + +#include <CPlusPlusForwardDeclarations.h> +#include <QString> +#include <QList> + +namespace CPlusPlus { + +class SimpleLexer; + +class CPLUSPLUS_EXPORT SimpleToken +{ +public: + SimpleToken() + : _kind(0), + _position(0), + _length(0) + { } + + inline int kind() const + { return _kind; } + + inline int position() const + { return _position; } + + inline int length() const + { return _length; } + + inline QStringRef text() const + { return _text; } + + inline bool is(int k) const { return _kind == k; } + inline bool isNot(int k) const { return _kind != k; } + + bool isLiteral() const; + bool isOperator() const; + bool isKeyword() const; + +public: + int _kind; + int _position; + int _length; + QStringRef _text; + + friend class SimpleLexer; +}; + +class CPLUSPLUS_EXPORT SimpleLexer +{ +public: + SimpleLexer(); + ~SimpleLexer(); + + bool skipComments() const; + void setSkipComments(bool skipComments); + + bool qtMocRunEnabled() const; + void setQtMocRunEnabled(bool enabled); + + QList<SimpleToken> operator()(const QString &text, int state = 0); + + int state() const + { return _lastState; } + +private: + int _lastState; + bool _skipComments: 1; + bool _qtMocRunEnabled: 1; +}; + +} // end of namespace CPlusPlus + +#endif // SIMPLELEXER_H diff --git a/src/libs/cplusplus/TokenUnderCursor.cpp b/src/libs/cplusplus/TokenUnderCursor.cpp new file mode 100644 index 00000000000..84a19922441 --- /dev/null +++ b/src/libs/cplusplus/TokenUnderCursor.cpp @@ -0,0 +1,75 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "TokenUnderCursor.h" +#include <Token.h> + +#include <QTextCursor> +#include <QTextBlock> +#include <climits> + +using namespace CPlusPlus; + +TokenUnderCursor::TokenUnderCursor() +{ } + +TokenUnderCursor::~TokenUnderCursor() +{ } + +SimpleToken TokenUnderCursor::operator()(const QTextCursor &cursor) const +{ + SimpleLexer tokenize; + QTextBlock block = cursor.block(); + int column = cursor.columnNumber(); + + QList<SimpleToken> tokens = tokenize(block.text(), previousBlockState(block)); + for (int index = tokens.size() - 1; index != -1; --index) { + const SimpleToken &tk = tokens.at(index); + if (tk.position() < column) + return tk; + } + + return SimpleToken(); +} + +int TokenUnderCursor::previousBlockState(const QTextBlock &block) const +{ + const QTextBlock prevBlock = block.previous(); + if (prevBlock.isValid()) { + int state = prevBlock.userState(); + + if (state != -1) + return state; + } + return 0; +} diff --git a/src/libs/cplusplus/TokenUnderCursor.h b/src/libs/cplusplus/TokenUnderCursor.h new file mode 100644 index 00000000000..cd08833d219 --- /dev/null +++ b/src/libs/cplusplus/TokenUnderCursor.h @@ -0,0 +1,63 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef TOKENUNDERCURSOR_H +#define TOKENUNDERCURSOR_H + +#include "SimpleLexer.h" +#include <QList> + +QT_BEGIN_NAMESPACE +class QString; +class QTextCursor; +class QTextBlock; +QT_END_NAMESPACE + +namespace CPlusPlus { + +class SimpleToken; + +class CPLUSPLUS_EXPORT TokenUnderCursor +{ +public: + TokenUnderCursor(); + ~TokenUnderCursor(); + + SimpleToken operator()(const QTextCursor &cursor) const; + +private: + int previousBlockState(const QTextBlock &block) const; +}; + +} // end of namespace CPlusPlus + +#endif // TOKENUNDERCURSOR_H diff --git a/src/libs/cplusplus/TypeOfExpression.cpp b/src/libs/cplusplus/TypeOfExpression.cpp new file mode 100644 index 00000000000..f6ad3bdc53d --- /dev/null +++ b/src/libs/cplusplus/TypeOfExpression.cpp @@ -0,0 +1,114 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "TypeOfExpression.h" + +#include <AST.h> +#include <TranslationUnit.h> +#include <cplusplus/LookupContext.h> +#include <cplusplus/ResolveExpression.h> + +using namespace CPlusPlus; + +TypeOfExpression::TypeOfExpression(): + m_ast(0) +{ +} + +void TypeOfExpression::setDocuments(const QMap<QString, Document::Ptr> &documents) +{ + m_documents = documents; +} + +QList<TypeOfExpression::Result> TypeOfExpression::operator()(const QString &expression, + Document::Ptr document, + Symbol *lastVisibleSymbol) +{ + Document::Ptr expressionDoc = documentForExpression(expression); + m_ast = extractExpressionAST(expressionDoc); + + m_lookupContext = LookupContext(lastVisibleSymbol, expressionDoc, + document, m_documents); + + ResolveExpression resolveExpression(m_lookupContext); + return resolveExpression(m_ast); +} + +ExpressionAST *TypeOfExpression::ast() const +{ + return m_ast; +} + +const LookupContext &TypeOfExpression::lookupContext() const +{ + return m_lookupContext; +} + +ExpressionAST *TypeOfExpression::expressionAST() const +{ + return extractExpressionAST(m_lookupContext.expressionDocument()); +} + +ExpressionAST *TypeOfExpression::extractExpressionAST(Document::Ptr doc) const +{ + TranslationUnitAST *translationUnitAST = doc->translationUnit()->ast(); + + // ### evaluate the expression + ExpressionAST *expressionAST = 0; + if (translationUnitAST) { + DeclarationAST *declaration = translationUnitAST->declarations; + SimpleDeclarationAST *simpleDecl = 0; + if (declaration) + simpleDecl = declaration->asSimpleDeclaration(); + if (simpleDecl && simpleDecl->decl_specifier_seq) { + if (TypeofSpecifierAST *typeOfSpec = simpleDecl->decl_specifier_seq->asTypeofSpecifier()) + expressionAST = typeOfSpec->expression; + } + } + return expressionAST; +} + +Document::Ptr TypeOfExpression::documentForExpression(const QString &expression) const +{ + // create a __typeof__ specifier + QByteArray declaration; + declaration += "__typeof__ "; + declaration += expression.toLatin1(); // C++ code needs to be in latin1 + declaration += ";"; + + // create the expression's AST. + Document::Ptr doc = Document::create(QLatin1String("<completion>")); + doc->setSource(declaration); + doc->parse(); + return doc; +} diff --git a/src/libs/cplusplus/TypeOfExpression.h b/src/libs/cplusplus/TypeOfExpression.h new file mode 100644 index 00000000000..87df28f1ba9 --- /dev/null +++ b/src/libs/cplusplus/TypeOfExpression.h @@ -0,0 +1,98 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPLUSPLUS_TYPEOFEXPRESSION_H +#define CPLUSPLUS_TYPEOFEXPRESSION_H + +#include <ASTfwd.h> +#include <cplusplus/CppDocument.h> +#include <cplusplus/LookupContext.h> + +#include <QtCore/QMap> +#include <QtCore/QObject> +#include <QtCore/QString> + +namespace CPlusPlus { + +class CPLUSPLUS_EXPORT TypeOfExpression +{ +public: + typedef QPair<FullySpecifiedType, Symbol *> Result; + +public: + TypeOfExpression(); + + /** + * Sets the documents used to evaluate expressions. Should be set before + * calling this functor. + */ + void setDocuments(const QMap<QString, Document::Ptr> &documents); + + /** + * Returns a list of possible fully specified types associated with the + * given expression. + * + * NOTE: The fully specified types only stay valid for as long as this + * expression evaluator instance still exists, and no new call to evaluate + * has been made! + * + * @param expression The expression to evaluate. + * @param document The document the expression is part of. + * @param lastVisibleSymbol The last visible symbol in the document. + */ + QList<Result> operator()(const QString &expression, Document::Ptr document, + Symbol *lastVisibleSymbol); + + /** + * Returns the AST of the last evaluated expression. + */ + ExpressionAST *ast() const; + + /** + * Returns the lookup context of the last evaluated expression. + */ + const LookupContext &lookupContext() const; + + ExpressionAST *expressionAST() const; + +private: + ExpressionAST *extractExpressionAST(Document::Ptr doc) const; + Document::Ptr documentForExpression(const QString &expression) const; + + QMap<QString, Document::Ptr> m_documents; + ExpressionAST *m_ast; + LookupContext m_lookupContext; +}; + +} // namespace CPlusPlus + +#endif // CPLUSPLUS_TYPEOFEXPRESSION_H diff --git a/src/libs/cplusplus/TypePrettyPrinter.cpp b/src/libs/cplusplus/TypePrettyPrinter.cpp new file mode 100644 index 00000000000..3301a8c8d10 --- /dev/null +++ b/src/libs/cplusplus/TypePrettyPrinter.cpp @@ -0,0 +1,309 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "Overview.h" +#include "TypePrettyPrinter.h" +#include <FullySpecifiedType.h> +#include <CoreTypes.h> +#include <Symbols.h> +#include <Scope.h> + +using namespace CPlusPlus; + +TypePrettyPrinter::TypePrettyPrinter(const Overview *overview) + : _overview(overview), + _name(0), + _markArgument(0), + _showArgumentNames(false), + _showReturnTypes(false), + _showFunctionSignatures(true) +{ } + +TypePrettyPrinter::~TypePrettyPrinter() +{ } + +bool TypePrettyPrinter::showArgumentNames() const +{ return _showArgumentNames; } + +void TypePrettyPrinter::setShowArgumentNames(bool showArgumentNames) +{ _showArgumentNames = showArgumentNames; } + +bool TypePrettyPrinter::showReturnTypes() const +{ return _showReturnTypes; } + +void TypePrettyPrinter::setShowReturnTypes(bool showReturnTypes) +{ _showReturnTypes = showReturnTypes; } + +bool TypePrettyPrinter::showFunctionSignatures() const +{ return _showFunctionSignatures; } + +void TypePrettyPrinter::setShowFunctionSignatures(bool showFunctionSignatures) +{ _showFunctionSignatures = showFunctionSignatures; } + +void TypePrettyPrinter::setMarkArgument(unsigned position) +{ _markArgument = position; } + +const Overview *TypePrettyPrinter::overview() const +{ return _overview; } + +QString TypePrettyPrinter::operator()(const FullySpecifiedType &ty) +{ + QString previousName = switchText(); + acceptType(ty); + return switchText(previousName).trimmed(); +} + +QString TypePrettyPrinter::operator()(const FullySpecifiedType &type, const QString &name) +{ + QString previousName = switchName(name); + QString text = operator()(type); + if (! _name.isEmpty() && ! text.isEmpty()) { + QChar ch = text.at(text.size() - 1); + if (ch.isLetterOrNumber() || ch == QLatin1Char('_')) + text += QLatin1Char(' '); + text += _name; + } + (void) switchName(previousName); + return text; +} + + +void TypePrettyPrinter::acceptType(const FullySpecifiedType &ty) +{ + if (ty.isConst()) + _text += QLatin1String("const "); + if (ty.isVolatile()) + _text += QLatin1String("volatile "); + if (ty.isSigned()) + _text += QLatin1String("signed "); + if (ty.isUnsigned()) + _text += QLatin1String("unsigned "); + accept(ty.type()); +} + +QString TypePrettyPrinter::switchName(const QString &name) +{ + const QString previousName = _name; + _name = name; + return previousName; +} + +QString TypePrettyPrinter::switchText(const QString &name) +{ + QString previousName = _text; + _text = name; + return previousName; +} + +QList<Type *> TypePrettyPrinter::switchPtrOperators(const QList<Type *> &ptrOperators) +{ + QList<Type *> previousPtrOperators = _ptrOperators; + _ptrOperators = ptrOperators; + return previousPtrOperators; +} + +void TypePrettyPrinter::applyPtrOperators(bool wantSpace) +{ + for (int i = _ptrOperators.size() - 1; i != -1; --i) { + Type *op = _ptrOperators.at(i); + + if (i == 0 && wantSpace) + _text += QLatin1Char(' '); + + if (PointerType *ptrTy = op->asPointerType()) { + _text += QLatin1Char('*'); + if (ptrTy->elementType().isConst()) + _text += " const"; + if (ptrTy->elementType().isVolatile()) + _text += " volatile"; + } else if (op->isReferenceType()) { + _text += QLatin1Char('&'); + } else if (PointerToMemberType *memPtrTy = op->asPointerToMemberType()) { + _text += QLatin1Char(' '); + _text += _overview->prettyName(memPtrTy->memberName()); + _text += QLatin1Char('*'); + } + } +} + +void TypePrettyPrinter::visit(VoidType *) +{ + _text += QLatin1String("void"); + + applyPtrOperators(); +} + +void TypePrettyPrinter::visit(IntegerType *type) +{ + switch (type->kind()) { + case IntegerType::Char: + _text += QLatin1String("char"); + break; + case IntegerType::WideChar: + _text += QLatin1String("wchar_t"); + break; + case IntegerType::Bool: + _text += QLatin1String("bool"); + break; + case IntegerType::Short: + _text += QLatin1String("short"); + break; + case IntegerType::Int: + _text += QLatin1String("int"); + break; + case IntegerType::Long: + _text += QLatin1String("long"); + break; + case IntegerType::LongLong: + _text += QLatin1String("long long"); + break; + } + + applyPtrOperators(); +} + +void TypePrettyPrinter::visit(FloatType *type) +{ + switch (type->kind()) { + case FloatType::Float: + _text += QLatin1String("float"); + break; + case FloatType::Double: + _text += QLatin1String("double"); + break; + case FloatType::LongDouble: + _text += QLatin1String("long double"); + break; + } + + applyPtrOperators(); +} + +void TypePrettyPrinter::visit(PointerToMemberType *type) +{ + _ptrOperators.append(type); + acceptType(type->elementType()); +} + +void TypePrettyPrinter::visit(PointerType *type) +{ + _ptrOperators.append(type); + acceptType(type->elementType()); +} + +void TypePrettyPrinter::visit(ReferenceType *type) +{ + _ptrOperators.append(type); + acceptType(type->elementType()); +} + +void TypePrettyPrinter::visit(ArrayType *type) +{ + _text += overview()->prettyType(type->elementType()); + if (! _ptrOperators.isEmpty()) { + _text += QLatin1Char('('); + applyPtrOperators(false); + if (! _name.isEmpty()) { + _text += _name; + _name.clear(); + } + _text += QLatin1Char(')'); + } + _text += QLatin1String("[]"); +} + +void TypePrettyPrinter::visit(NamedType *type) +{ + _text += overview()->prettyName(type->name()); + applyPtrOperators(); +} + +void TypePrettyPrinter::visit(Function *type) +{ + if (_showReturnTypes) + _text += _overview->prettyType(type->returnType()); + + if (! _ptrOperators.isEmpty()) { + _text += QLatin1Char('('); + applyPtrOperators(false); + if (! _name.isEmpty()) { + _text += _name; + _name.clear(); + } + _text += QLatin1Char(')'); + } else if (! _name.isEmpty() && _showFunctionSignatures) { + _text += QLatin1Char(' '); // ### fixme + _text += _name; + _name.clear(); + } + + if (_showFunctionSignatures) { + Overview argumentText; + _text += QLatin1Char('('); + for (unsigned index = 0; index < type->argumentCount(); ++index) { + if (index != 0) + _text += QLatin1String(", "); + + if (Argument *arg = type->argumentAt(index)->asArgument()) { + if (index + 1 == _markArgument) + _text += QLatin1String("<b>"); + Name *name = 0; + if (_showArgumentNames) + name = arg->name(); + _text += argumentText(arg->type(), name); + if (index + 1 == _markArgument) + _text += QLatin1String("</b>"); + } + } + + if (type->isVariadic()) + _text += QLatin1String("..."); + + _text += QLatin1Char(')'); + + if (type->isConst()) + _text += QLatin1String(" const"); + + if (type->isVolatile()) + _text += QLatin1String(" volatile"); + } +} + +void TypePrettyPrinter::visit(Namespace *type) +{ _text += overview()->prettyName(type->name()); } + +void TypePrettyPrinter::visit(Class *type) +{ _text += overview()->prettyName(type->name()); } + +void TypePrettyPrinter::visit(Enum *type) +{ _text += overview()->prettyName(type->name()); } diff --git a/src/libs/cplusplus/TypePrettyPrinter.h b/src/libs/cplusplus/TypePrettyPrinter.h new file mode 100644 index 00000000000..fd003381844 --- /dev/null +++ b/src/libs/cplusplus/TypePrettyPrinter.h @@ -0,0 +1,101 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef TYPEPRETTYPRINTER_H +#define TYPEPRETTYPRINTER_H + +#include "TypeVisitor.h" +#include <QString> +#include <QList> + +namespace CPlusPlus { + +class Overview; +class FullySpecifiedType; + +class CPLUSPLUS_EXPORT TypePrettyPrinter: protected TypeVisitor +{ +public: + TypePrettyPrinter(const Overview *overview); + virtual ~TypePrettyPrinter(); + + const Overview *overview() const; + + bool showArgumentNames() const; + void setShowArgumentNames(bool showArgumentNames); + + bool showReturnTypes() const; + void setShowReturnTypes(bool showReturnTypes); + + bool showFunctionSignatures() const; + void setShowFunctionSignatures(bool showFunctionSignatures); + + void setMarkArgument(unsigned position); // 1-based + + QString operator()(const FullySpecifiedType &type); + QString operator()(const FullySpecifiedType &type, const QString &name); + +protected: + QString switchText(const QString &text = QString()); + QList<Type *> switchPtrOperators(const QList<Type *> &ptrOperators); + QString switchName(const QString &name); + + void applyPtrOperators(bool wantSpace = true); + void acceptType(const FullySpecifiedType &ty); + + virtual void visit(VoidType *type); + virtual void visit(IntegerType *type); + virtual void visit(FloatType *type); + virtual void visit(PointerToMemberType *type); + virtual void visit(PointerType *type); + virtual void visit(ReferenceType *type); + virtual void visit(ArrayType *type); + virtual void visit(NamedType *type); + virtual void visit(Function *type); + virtual void visit(Namespace *type); + virtual void visit(Class *type); + virtual void visit(Enum *type); + +private: + const Overview *_overview; + QString _name; + QString _text; + QList<Type *> _ptrOperators; + unsigned _markArgument; + bool _showArgumentNames: 1; + bool _showReturnTypes: 1; + bool _showFunctionSignatures: 1; +}; + +} // end of namespace CPlusPlus + +#endif // TYPEPRETTYPRINTER_H diff --git a/src/libs/cplusplus/cplusplus.pri b/src/libs/cplusplus/cplusplus.pri new file mode 100644 index 00000000000..e2d0f6f0339 --- /dev/null +++ b/src/libs/cplusplus/cplusplus.pri @@ -0,0 +1,3 @@ +INCLUDEPATH += $$PWD/../../../shared/cplusplus +DEFINES += HAVE_QT CPLUSPLUS_WITH_NAMESPACE +LIBS *= -l$$qtLibraryTarget(CPlusPlus) diff --git a/src/libs/cplusplus/cplusplus.pro b/src/libs/cplusplus/cplusplus.pro new file mode 100644 index 00000000000..1a2b5326623 --- /dev/null +++ b/src/libs/cplusplus/cplusplus.pro @@ -0,0 +1,40 @@ +TEMPLATE = lib + +TARGET = CPlusPlus + +DEFINES += HAVE_QT CPLUSPLUS_WITH_NAMESPACE CPLUSPLUS_BUILD_LIB +DEFINES += NDEBUG +unix:QMAKE_CXXFLAGS_DEBUG += -O3 + +include(../../qworkbenchlibrary.pri) +include(../../../shared/cplusplus/cplusplus.pri) + +HEADERS += \ + SimpleLexer.h \ + ExpressionUnderCursor.h \ + TokenUnderCursor.h \ + CppDocument.h \ + Icons.h \ + Overview.h \ + OverviewModel.h \ + NamePrettyPrinter.h \ + TypeOfExpression.h \ + TypePrettyPrinter.h \ + ResolveExpression.h \ + LookupContext.h + +SOURCES += \ + SimpleLexer.cpp \ + ExpressionUnderCursor.cpp \ + TokenUnderCursor.cpp \ + CppDocument.cpp \ + Icons.cpp \ + Overview.cpp \ + OverviewModel.cpp \ + NamePrettyPrinter.cpp \ + TypeOfExpression.cpp \ + TypePrettyPrinter.cpp \ + ResolveExpression.cpp \ + LookupContext.cpp + +RESOURCES += cplusplus.qrc diff --git a/src/libs/cplusplus/cplusplus.qrc b/src/libs/cplusplus/cplusplus.qrc new file mode 100644 index 00000000000..73d4c6395ec --- /dev/null +++ b/src/libs/cplusplus/cplusplus.qrc @@ -0,0 +1,20 @@ +<RCC> + <qresource prefix="/codemodel" > + <file>images/class.png</file> + <file>images/enum.png</file> + <file>images/enumerator.png</file> + <file>images/func.png</file> + <file>images/func_priv.png</file> + <file>images/func_prot.png</file> + <file>images/keyword.png</file> + <file>images/macro.png</file> + <file>images/namespace.png</file> + <file>images/signal.png</file> + <file>images/slot.png</file> + <file>images/slot_priv.png</file> + <file>images/slot_prot.png</file> + <file>images/var.png</file> + <file>images/var_priv.png</file> + <file>images/var_prot.png</file> + </qresource> +</RCC> diff --git a/src/libs/cplusplus/images/class.png b/src/libs/cplusplus/images/class.png Binary files differnew file mode 100644 index 00000000000..88432d2cb10 --- /dev/null +++ b/src/libs/cplusplus/images/class.png diff --git a/src/libs/cplusplus/images/enum.png b/src/libs/cplusplus/images/enum.png Binary files differnew file mode 100644 index 00000000000..42a9e83bc76 --- /dev/null +++ b/src/libs/cplusplus/images/enum.png diff --git a/src/libs/cplusplus/images/enumerator.png b/src/libs/cplusplus/images/enumerator.png Binary files differnew file mode 100644 index 00000000000..25fc49c6598 --- /dev/null +++ b/src/libs/cplusplus/images/enumerator.png diff --git a/src/libs/cplusplus/images/func.png b/src/libs/cplusplus/images/func.png Binary files differnew file mode 100644 index 00000000000..e515e76e61f --- /dev/null +++ b/src/libs/cplusplus/images/func.png diff --git a/src/libs/cplusplus/images/func_priv.png b/src/libs/cplusplus/images/func_priv.png Binary files differnew file mode 100644 index 00000000000..49dda7dfea0 --- /dev/null +++ b/src/libs/cplusplus/images/func_priv.png diff --git a/src/libs/cplusplus/images/func_prot.png b/src/libs/cplusplus/images/func_prot.png Binary files differnew file mode 100644 index 00000000000..f8add65e073 --- /dev/null +++ b/src/libs/cplusplus/images/func_prot.png diff --git a/src/libs/cplusplus/images/keyword.png b/src/libs/cplusplus/images/keyword.png Binary files differnew file mode 100644 index 00000000000..e5a51858d9e --- /dev/null +++ b/src/libs/cplusplus/images/keyword.png diff --git a/src/libs/cplusplus/images/macro.png b/src/libs/cplusplus/images/macro.png Binary files differnew file mode 100644 index 00000000000..53e42af63d2 --- /dev/null +++ b/src/libs/cplusplus/images/macro.png diff --git a/src/libs/cplusplus/images/namespace.png b/src/libs/cplusplus/images/namespace.png Binary files differnew file mode 100644 index 00000000000..18d2941572e --- /dev/null +++ b/src/libs/cplusplus/images/namespace.png diff --git a/src/libs/cplusplus/images/signal.png b/src/libs/cplusplus/images/signal.png Binary files differnew file mode 100644 index 00000000000..a4de5dddfe5 --- /dev/null +++ b/src/libs/cplusplus/images/signal.png diff --git a/src/libs/cplusplus/images/slot.png b/src/libs/cplusplus/images/slot.png Binary files differnew file mode 100644 index 00000000000..5534bbfe087 --- /dev/null +++ b/src/libs/cplusplus/images/slot.png diff --git a/src/libs/cplusplus/images/slot_priv.png b/src/libs/cplusplus/images/slot_priv.png Binary files differnew file mode 100644 index 00000000000..8f585e875d1 --- /dev/null +++ b/src/libs/cplusplus/images/slot_priv.png diff --git a/src/libs/cplusplus/images/slot_prot.png b/src/libs/cplusplus/images/slot_prot.png Binary files differnew file mode 100644 index 00000000000..469e9c18d06 --- /dev/null +++ b/src/libs/cplusplus/images/slot_prot.png diff --git a/src/libs/cplusplus/images/var.png b/src/libs/cplusplus/images/var.png Binary files differnew file mode 100644 index 00000000000..089cfb45e5f --- /dev/null +++ b/src/libs/cplusplus/images/var.png diff --git a/src/libs/cplusplus/images/var_priv.png b/src/libs/cplusplus/images/var_priv.png Binary files differnew file mode 100644 index 00000000000..8c6cf64fe7b --- /dev/null +++ b/src/libs/cplusplus/images/var_priv.png diff --git a/src/libs/cplusplus/images/var_prot.png b/src/libs/cplusplus/images/var_prot.png Binary files differnew file mode 100644 index 00000000000..a7496aada00 --- /dev/null +++ b/src/libs/cplusplus/images/var_prot.png diff --git a/src/libs/extensionsystem/ExtensionSystemInterfaces b/src/libs/extensionsystem/ExtensionSystemInterfaces new file mode 100644 index 00000000000..527148af8e1 --- /dev/null +++ b/src/libs/extensionsystem/ExtensionSystemInterfaces @@ -0,0 +1,6 @@ +#include "extensionsystem/pluginmanager.h" +#include "extensionsystem/pluginspec.h" +#include "extensionsystem/iplugin.h" +#include "extensionsystem/pluginview.h" +#include "extensionsystem/pluginerrorview.h" +#include "extensionsystem/plugindetailsview.h" diff --git a/src/libs/extensionsystem/extensionsystem.pri b/src/libs/extensionsystem/extensionsystem.pri new file mode 100644 index 00000000000..43855eb1688 --- /dev/null +++ b/src/libs/extensionsystem/extensionsystem.pri @@ -0,0 +1,3 @@ +include(extensionsystem_dependencies.pri) + +LIBS *= -l$$qtLibraryTarget(ExtensionSystem) diff --git a/src/libs/extensionsystem/extensionsystem.pro b/src/libs/extensionsystem/extensionsystem.pro new file mode 100644 index 00000000000..fb26b9c59cc --- /dev/null +++ b/src/libs/extensionsystem/extensionsystem.pro @@ -0,0 +1,32 @@ +TEMPLATE = lib +TARGET = ExtensionSystem +QT += xml +DEFINES += EXTENSIONSYSTEM_LIBRARY +include(../../qworkbenchlibrary.pri) +include(extensionsystem_dependencies.pri) + +DEFINES += IDE_TEST_DIR=\\\"$$IDE_SOURCE_TREE\\\" + +HEADERS += pluginerrorview.h \ + plugindetailsview.h \ + iplugin.h \ + iplugin_p.h \ + extensionsystem_global.h \ + pluginmanager.h \ + pluginmanager_p.h \ + pluginspec.h \ + pluginspec_p.h \ + pluginview.h \ + pluginview_p.h \ + optionsparser.h +SOURCES += pluginerrorview.cpp \ + plugindetailsview.cpp \ + iplugin.cpp \ + pluginmanager.cpp \ + pluginspec.cpp \ + pluginview.cpp \ + optionsparser.cpp +FORMS += pluginview.ui \ + pluginerrorview.ui \ + plugindetailsview.ui +RESOURCES += pluginview.qrc diff --git a/src/libs/extensionsystem/extensionsystem_dependencies.pri b/src/libs/extensionsystem/extensionsystem_dependencies.pri new file mode 100644 index 00000000000..63b2e339a36 --- /dev/null +++ b/src/libs/extensionsystem/extensionsystem_dependencies.pri @@ -0,0 +1 @@ +include(../aggregation/aggregation.pri) diff --git a/src/libs/extensionsystem/extensionsystem_global.h b/src/libs/extensionsystem/extensionsystem_global.h new file mode 100644 index 00000000000..ebd1f34da25 --- /dev/null +++ b/src/libs/extensionsystem/extensionsystem_global.h @@ -0,0 +1,44 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef EXTENSIONSYSTEM_GLOBAL_H +#define EXTENSIONSYSTEM_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(EXTENSIONSYSTEM_LIBRARY) +# define EXTENSIONSYSTEM_EXPORT Q_DECL_EXPORT +#else +# define EXTENSIONSYSTEM_EXPORT Q_DECL_IMPORT +#endif + +#endif // header diff --git a/src/libs/extensionsystem/images/error.png b/src/libs/extensionsystem/images/error.png Binary files differnew file mode 100644 index 00000000000..e2f85d98eb6 --- /dev/null +++ b/src/libs/extensionsystem/images/error.png diff --git a/src/libs/extensionsystem/images/ok.png b/src/libs/extensionsystem/images/ok.png Binary files differnew file mode 100644 index 00000000000..15cd35d27ba --- /dev/null +++ b/src/libs/extensionsystem/images/ok.png diff --git a/src/libs/extensionsystem/iplugin.cpp b/src/libs/extensionsystem/iplugin.cpp new file mode 100644 index 00000000000..47cb702e16b --- /dev/null +++ b/src/libs/extensionsystem/iplugin.cpp @@ -0,0 +1,325 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "iplugin.h" +#include "iplugin_p.h" +#include "pluginmanager.h" +#include "pluginspec.h" + +/*! + \class ExtensionSystem::IPlugin + \brief Base class for all plugins. + + The IPlugin class is an abstract class that must be implemented + once for each plugin. + A plugin consists of two parts: A description file, and a library + that at least contains the IPlugin implementation. + + \tableofcontents + + \section1 Plugin Specification + The plugin specification file is an xml file that contains all + information that are necessary for loading the plugin's library, + plus some textual descriptions. The file must be located in + (a subdir of) one of the plugin manager's plugin search paths, + and must have the \c .xml extension. + + \section2 Main Tag + The root tag is \c plugin. It has mandatory attributes \c name + and \c version, and an optional \c compatVersion. + \table + \header + \o Tag + \o Meaning + \row + \o plugin + \o Root element in a plugin's xml file. + \endtable + \table + \header + \o Attribute + \o Meaning + \row + \o name + \o This is used as an identifier for the plugin and can e.g. + be referenced in other plugin's dependencies. It is + also used to construct the name of the plugin library + as \c lib[name].[dll|.so|.dylib]. + \row + \o version + \o Version string in the form \c {"x.y.z_n"}, used for identifying + the plugin. + \row + \o compatVersion + \o Compatibility version. Optional. If not given, it is implicitly + set to the same value as \c version. The compatibility version + is used to resolve dependencies on this plugin. See + \l {Dependencies}{Dependencies} for details. + \endtable + + \section2 Plugin-describing Tags + These are direct children of the \c plugin tag, and are solely used + for more detailed (user centric) description of the plugin. All of these + are optional. + \table + \header + \o Tag + \o Meaning + \row + \o vendor + \o String that describes the plugin creator/vendor, + like \c {MyCompany}. + \row + \o copyright + \o A short copyright notice, like \c {(C) 2007-2008 MyCompany}. + \row + \o license + \o Possibly multi-line license information about the plugin. + \row + \o description + \o Possibly multi-line description of what the plugin is supposed + to provide. + \row + \o url + \o Link to further information about the plugin, like + \c {http://www.mycompany-online.com/products/greatplugin}. + \endtable + + \section2 Dependencies + A plugin can have dependencies on other plugins. These are + specified in the plugin's xml file as well, to ensure that + these other plugins are loaded before this plugin. + Dependency information consists of the name of the required plugin + (lets denote that as \c {dependencyName}), + and the required version of the plugin (\c {dependencyVersion}). + A plugin with given \c name, \c version and \c compatVersion matches + the dependency if + \list + \o it's \c name matches \c dependencyName, and + \o \c {compatVersion <= dependencyVersion <= version}. + \endlist + + The xml element that describes dependencies is the \c dependency tag, + with required attributes \c name and \c version. It is an + optional direct child of the \c plugin tag and can appear multiple times. + \table + \header + \o Tag + \o Meaning + \row + \o dependency + \o Describes a dependency on another plugin. + \endtable + \table + \header + \o Attribute + \o Meaning + \row + \o name + \o The name of the plugin, on which this plugin relies. + \row + \o version + \o The version to which the plugin must be compatible to + fill the dependency, in the form \c {"x.y.z_n"}. + \endtable + + \section2 Example \c plugin.xml + \code + <plugin name="test" version="1.0.1" compatVersion="1.0.0"> + <vendor>MyCompany</vendor> + <copyright>(C) 2007 MyCompany</copyright> + <license> + This is a default license bla + blubbblubb + end of terms + </license> + <description> + This plugin is just a test. + it demonstrates the great use of the plugin spec. + </description> + <url>http://www.mycompany-online.com/products/greatplugin</url> + <dependencyList> + <dependency name="SomeOtherPlugin" version="2.3.0_2"/> + <dependency name="EvenOther" version="1.0.0"/> + </dependencyList> + </plugin> + \endcode + The first dependency could for example be matched by a plugin with + \code + <plugin name="SomeOtherPlugin" version="3.1.0" compatVersion="2.2.0"> + </plugin> + \endcode + since the name matches, and the version \c "2.3.0_2" given in the dependency tag + lies in the range of \c "2.2.0" and \c "3.1.0". + + \section2 A Note on Plugin Versions + Plugin versions are in the form \c "x.y.z_n" where, x, y, z and n are + non-negative integer numbers. You don't have to specify the version + in this full form - any left-out part will implicitly be set to zero. + So, \c "2.10_2" is equal to \c "2.10.0_2", and "1" is the same as "1.0.0_0". + + \section1 Plugin Implementation + Plugins must provide one implementation of the IPlugin class, located + in a library that matches the \c name attribute given in their + xml description. The IPlugin implementation must be exported and + made known to Qt's plugin system via the Q_EXPORT_PLUGIN macro, see the + Qt documentation for details on that. + + After the plugins' xml files have been read, and dependencies have been + found, the plugin loading is done in three phases: + \list 1 + \o All plugin libraries are loaded in 'root-to-leaf' order of the + dependency tree. + \o All plugins' initialize methods are called in 'root-to-leaf' order + of the dependency tree. This is a good place to put + objects in the plugin manager's object pool. + \o All plugins' extensionsInitialized methods are called in 'leaf-to-root' + order of the dependency tree. At this point, plugins can + be sure that all plugins that depend on this plugin have + been initialized completely (implying that they have put + objects in the object pool, if they want that during the + initialization sequence). + \endlist + If library loading or initialization of a plugin fails, all plugins + that depend on that plugin also fail. + + Plugins have access to the plugin manager + (and it's object pool) via the PluginManager::instance() + method. +*/ + +/*! + \fn bool IPlugin::initialize(const QStringList &arguments, QString *errorString) + Called after the plugin has been loaded and the IPlugin instance + has been created. The initialize methods of plugins that depend + on this plugin are called after the initialize method of this plugin + has been called. Plugins should initialize their internal state in this + method. Returns if initialization of successful. If it wasn't successful, + the \a errorString should be set to a user-readable message + describing the reason. + \sa extensionsInitialized() +*/ + +/*! + \fn void IPlugin::extensionsInitialized() + Called after the IPlugin::initialize() method has been called, + and after both the IPlugin::initialize() and IPlugin::extensionsInitialized() + methods of plugins that depend on this plugin have been called. + In this method, the plugin can assume that plugins that depend on + this plugin are fully 'up and running'. It is a good place to + look in the plugin manager's object pool for objects that have + been provided by dependent plugins. + \sa initialize() +*/ + +/*! + \fn void IPlugin::shutdown() + Called during a shutdown sequence in the same order as initialization + before the plugins get deleted in reverse order. + This method can be used to optimize the shutdown down, e.g. to + disconnect from the PluginManager::aboutToRemoveObject() signal + if getting the signal (and probably doing lots of stuff to update + the internal and visible state) doesn't make sense during shutdown. +*/ + +using namespace ExtensionSystem; + +/*! + \fn IPlugin::IPlugin() + \internal +*/ +IPlugin::IPlugin() + : d(new Internal::IPluginPrivate()) +{ +} + +/*! + \fn IPlugin::~IPlugin() + \internal +*/ +IPlugin::~IPlugin() +{ + PluginManager *pm = PluginManager::instance(); + foreach (QObject *obj, d->addedObjectsInReverseOrder) + pm->removeObject(obj); + qDeleteAll(d->addedObjectsInReverseOrder); + d->addedObjectsInReverseOrder.clear(); + delete d; + d = 0; +} + +/*! + \fn PluginSpec *IPlugin::pluginSpec() const + Returns the PluginSpec corresponding to this plugin. + This is not available in the constructor. +*/ +PluginSpec *IPlugin::pluginSpec() const +{ + return d->pluginSpec; +} + +/*! + \fn void IPlugin::addObject(QObject *obj) + Convenience method that registers \a obj in the plugin manager's + plugin pool by just calling PluginManager::addObject(). +*/ +void IPlugin::addObject(QObject *obj) +{ + PluginManager::instance()->addObject(obj); +} + +/*! + \fn void IPlugin::addAutoReleasedObject(QObject *obj) + Convenience method for registering \a obj in the plugin manager's + plugin pool. Usually, registered objects must be removed from + the object pool and deleted by hand. + Objects added to the pool via addAutoReleasedObject are automatically + removed and deleted in \i reverse order of registration when + the IPlugin instance is destroyed. + \sa PluginManager::addObject() +*/ +void IPlugin::addAutoReleasedObject(QObject *obj) +{ + d->addedObjectsInReverseOrder.prepend(obj); + PluginManager::instance()->addObject(obj); +} + +/*! + \fn void IPlugin::removeObject(QObject *obj) + Convenience method that unregisters \a obj from the plugin manager's + plugin pool by just calling PluginManager::removeObject(). +*/ +void IPlugin::removeObject(QObject *obj) +{ + PluginManager::instance()->removeObject(obj); +} + diff --git a/src/libs/extensionsystem/iplugin.h b/src/libs/extensionsystem/iplugin.h new file mode 100644 index 00000000000..b263e7b0d29 --- /dev/null +++ b/src/libs/extensionsystem/iplugin.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IPLUGIN_H +#define IPLUGIN_H + +#include "extensionsystem_global.h" + +#include <QtCore/QObject> + +namespace ExtensionSystem { + +namespace Internal { + class IPluginPrivate; + class PluginSpecPrivate; +} + +class PluginManager; +class PluginSpec; + +class EXTENSIONSYSTEM_EXPORT IPlugin : public QObject +{ + Q_OBJECT + +public: + IPlugin(); + virtual ~IPlugin(); + + virtual bool initialize(const QStringList &arguments, QString *errorString) = 0; + virtual void extensionsInitialized() = 0; + virtual void shutdown() { } + + PluginSpec *pluginSpec() const; + + void addObject(QObject *obj); + void addAutoReleasedObject(QObject *obj); + void removeObject(QObject *obj); + +private: + Internal::IPluginPrivate *d; + + friend class Internal::PluginSpecPrivate; +}; + +} // namespace ExtensionSystem + +#endif // IPLUGIN_H diff --git a/src/libs/extensionsystem/iplugin_p.h b/src/libs/extensionsystem/iplugin_p.h new file mode 100644 index 00000000000..461a903a398 --- /dev/null +++ b/src/libs/extensionsystem/iplugin_p.h @@ -0,0 +1,58 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IPLUGIN_P_H +#define IPLUGIN_P_H + +#include "iplugin.h" + +#include <QtCore/QString> + +namespace ExtensionSystem { + +class PluginManager; +class PluginSpec; + +namespace Internal { + +class IPluginPrivate +{ +public: + PluginSpec *pluginSpec; + + QList<QObject *> addedObjectsInReverseOrder; +}; + +} // namespace Internal +} // namespace ExtensionSystem + +#endif // header guard diff --git a/src/libs/extensionsystem/optionsparser.cpp b/src/libs/extensionsystem/optionsparser.cpp new file mode 100644 index 00000000000..77b6ed869e4 --- /dev/null +++ b/src/libs/extensionsystem/optionsparser.cpp @@ -0,0 +1,192 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "optionsparser.h" + +#include <QtCore/QCoreApplication> + +using namespace ExtensionSystem; +using namespace ExtensionSystem::Internal; + +static const char *END_OF_OPTIONS = "--"; +const char *OptionsParser::NO_LOAD_OPTION = "-noload"; +const char *OptionsParser::TEST_OPTION = "-test"; + +OptionsParser::OptionsParser(const QStringList &args, + const QMap<QString, bool> &appOptions, + QMap<QString, QString> *foundAppOptions, + QString *errorString, + PluginManagerPrivate *pmPrivate) + : m_args(args), m_appOptions(appOptions), + m_foundAppOptions(foundAppOptions), + m_errorString(errorString), + m_pmPrivate(pmPrivate), + m_it(m_args.constBegin()), + m_end(m_args.constEnd()), + m_isDependencyRefreshNeeded(false), + m_hasError(false) +{ + ++m_it; // jump over program name + if (m_errorString) + m_errorString->clear(); + if (m_foundAppOptions) + m_foundAppOptions->clear(); + m_pmPrivate->arguments.clear(); +} + +bool OptionsParser::parse() +{ + while (!m_hasError) { + if (!nextToken()) // move forward + break; + if (checkForEndOfOptions()) + break; + if (checkForNoLoadOption()) + continue; + if (checkForTestOption()) + continue; + if (checkForAppOption()) + continue; + if (checkForPluginOption()) + continue; + if (checkForUnknownOption()) + break; + // probably a file or something + m_pmPrivate->arguments << m_currentArg; + } + if (m_isDependencyRefreshNeeded) + m_pmPrivate->resolveDependencies(); + return !m_hasError; +} + +bool OptionsParser::checkForEndOfOptions() +{ + if (m_currentArg != QLatin1String(END_OF_OPTIONS)) + return false; + while (nextToken()) { + m_pmPrivate->arguments << m_currentArg; + } + return true; +} + +bool OptionsParser::checkForTestOption() +{ + if (m_currentArg != QLatin1String(TEST_OPTION)) + return false; + if (nextToken(RequiredToken)) { + PluginSpec *spec = m_pmPrivate->pluginByName(m_currentArg); + if (!spec) { + if (m_errorString) + *m_errorString = QCoreApplication::translate("PluginManager", + "The plugin '%1' does not exist.").arg(m_currentArg); + m_hasError = true; + } else { + m_pmPrivate->testSpecs.append(spec); + } + } + return true; +} + +bool OptionsParser::checkForNoLoadOption() +{ + if (m_currentArg != QLatin1String(NO_LOAD_OPTION)) + return false; + if (nextToken(RequiredToken)) { + PluginSpec *spec = m_pmPrivate->pluginByName(m_currentArg); + if (!spec) { + if (m_errorString) + *m_errorString = QCoreApplication::translate("PluginManager", + "The plugin '%1' does not exist.").arg(m_currentArg); + m_hasError = true; + } else { + m_pmPrivate->pluginSpecs.remove(spec); + delete spec; + m_isDependencyRefreshNeeded = true; + } + } + return true; +} + +bool OptionsParser::checkForAppOption() +{ + if (!m_appOptions.contains(m_currentArg)) + return false; + QString option = m_currentArg; + QString argument; + if (m_appOptions.value(m_currentArg) && nextToken(RequiredToken)) { + //argument required + argument = m_currentArg; + } + if (m_foundAppOptions) + m_foundAppOptions->insert(option, argument); + return true; +} + +bool OptionsParser::checkForPluginOption() +{ + bool requiresParameter; + PluginSpec *spec = m_pmPrivate->pluginForOption(m_currentArg, &requiresParameter); + if (!spec) + return false; + spec->addArgument(m_currentArg); + if (requiresParameter && nextToken(RequiredToken)) { + spec->addArgument(m_currentArg); + } + return true; +} + +bool OptionsParser::checkForUnknownOption() +{ + if (!m_currentArg.startsWith(QLatin1Char('-'))) + return false; + if (m_errorString) + *m_errorString = QCoreApplication::translate("PluginManager", + "Unknown option %1").arg(m_currentArg); + m_hasError = true; + return true; +} + +bool OptionsParser::nextToken(OptionsParser::TokenType type) +{ + if (m_it == m_end) { + if (type == OptionsParser::RequiredToken) { + m_hasError = true; + if (m_errorString) + *m_errorString = QCoreApplication::translate("PluginManager", + "The option %1 requires an argument.").arg(m_currentArg); + } + return false; + } + m_currentArg = *m_it; + ++m_it; + return true; +} diff --git a/src/libs/extensionsystem/optionsparser.h b/src/libs/extensionsystem/optionsparser.h new file mode 100644 index 00000000000..65ea70c9ba2 --- /dev/null +++ b/src/libs/extensionsystem/optionsparser.h @@ -0,0 +1,87 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef OPTIONSPARSER_H +#define OPTIONSPARSER_H + +#include "pluginmanager_p.h" + +#include <QtCore/QStringList> +#include <QtCore/QMap> + +namespace ExtensionSystem { +namespace Internal { + +class OptionsParser +{ +public: + OptionsParser(const QStringList &args, + const QMap<QString, bool> &appOptions, + QMap<QString, QString> *foundAppOptions, + QString *errorString, + PluginManagerPrivate *pmPrivate); + + bool parse(); + + static const char *NO_LOAD_OPTION; + static const char *TEST_OPTION; +private: + // return value indicates if the option was processed + // it doesn't indicate success (--> m_hasError) + bool checkForEndOfOptions(); + bool checkForNoLoadOption(); + bool checkForTestOption(); + bool checkForAppOption(); + bool checkForPluginOption(); + bool checkForUnknownOption(); + + enum TokenType { OptionalToken, RequiredToken }; + bool nextToken(TokenType type = OptionalToken); + + const QStringList &m_args; + const QMap<QString, bool> &m_appOptions; + QMap<QString, QString> *m_foundAppOptions; + QString *m_errorString; + PluginManagerPrivate *m_pmPrivate; + + // state + QString m_currentArg; + QStringList::const_iterator m_it; + QStringList::const_iterator m_end; + bool m_isDependencyRefreshNeeded; + bool m_hasError; +}; + +} // namespace Internal +} // namespace ExtensionSystem + +#endif // OPTIONSPARSER_H diff --git a/src/libs/extensionsystem/plugindetailsview.cpp b/src/libs/extensionsystem/plugindetailsview.cpp new file mode 100644 index 00000000000..349cc48a12a --- /dev/null +++ b/src/libs/extensionsystem/plugindetailsview.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "plugindetailsview.h" +#include "ui_plugindetailsview.h" + +/*! + \class ExtensionSystem::PluginDetailsView + \brief Widget that displays the contents of a PluginSpec. + + Can be used for integration in the application that + uses the plugin manager. + + \sa ExtensionSystem::PluginView +*/ + +using namespace ExtensionSystem; + +/*! + \fn PluginDetailsView::PluginDetailsView(QWidget *parent) + Constructs a new view with given \a parent widget. +*/ +PluginDetailsView::PluginDetailsView(QWidget *parent) + : QWidget(parent), + m_ui(new Internal::Ui::PluginDetailsView()) +{ + m_ui->setupUi(this); +} + +/*! + \fn PluginDetailsView::~PluginDetailsView() + \internal +*/ +PluginDetailsView::~PluginDetailsView() +{ + delete m_ui; +} + +/*! + \fn void PluginDetailsView::update(PluginSpec *spec) + Reads the given \a spec and displays its values + in this PluginDetailsView. +*/ +void PluginDetailsView::update(PluginSpec *spec) +{ + m_ui->name->setText(spec->name()); + m_ui->version->setText(spec->version()); + m_ui->compatVersion->setText(spec->compatVersion()); + m_ui->vendor->setText(spec->vendor()); + m_ui->url->setText(spec->url()); + m_ui->location->setText(spec->filePath()); + m_ui->description->setText(spec->description()); + m_ui->copyright->setText(spec->copyright()); + m_ui->license->setText(spec->license()); + QStringList depStrings; + foreach (PluginDependency dep, spec->dependencies()) { + depStrings << QString("%1 (%2)").arg(dep.name).arg(dep.version); + } + m_ui->dependencies->addItems(depStrings); +} diff --git a/src/libs/extensionsystem/plugindetailsview.h b/src/libs/extensionsystem/plugindetailsview.h new file mode 100644 index 00000000000..99386f29d0f --- /dev/null +++ b/src/libs/extensionsystem/plugindetailsview.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGINDETAILSVIEW_H_ +#define PLUGINDETAILSVIEW_H_ + +#include "extensionsystem_global.h" +#include "pluginspec.h" + +#include <QtGui/QWidget> + +namespace ExtensionSystem +{ + +namespace Internal { +namespace Ui { + class PluginDetailsView; +} // namespace Ui +} // namespace Internal + + +class EXTENSIONSYSTEM_EXPORT PluginDetailsView : public QWidget +{ + Q_OBJECT + +public: + PluginDetailsView(QWidget *parent = 0); + ~PluginDetailsView(); + + void update(PluginSpec *spec); + +private: + Internal::Ui::PluginDetailsView *m_ui; +}; + +} // namespace ExtensionSystem + +#endif diff --git a/src/libs/extensionsystem/plugindetailsview.ui b/src/libs/extensionsystem/plugindetailsview.ui new file mode 100644 index 00000000000..e73c9de39b0 --- /dev/null +++ b/src/libs/extensionsystem/plugindetailsview.ui @@ -0,0 +1,258 @@ +<ui version="4.0" > + <class>ExtensionSystem::Internal::PluginDetailsView</class> + <widget class="QWidget" name="ExtensionSystem::Internal::PluginDetailsView" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>674</width> + <height>505</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QGridLayout" > + <property name="leftMargin" > + <number>2</number> + </property> + <property name="topMargin" > + <number>2</number> + </property> + <property name="rightMargin" > + <number>2</number> + </property> + <property name="bottomMargin" > + <number>2</number> + </property> + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Name:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLabel" name="name" > + <property name="text" > + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Version:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLabel" name="version" > + <property name="text" > + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Compatibility Version:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLabel" name="compatVersion" > + <property name="text" > + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_4" > + <property name="text" > + <string>Vendor:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLabel" name="vendor" > + <property name="text" > + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="label_6" > + <property name="text" > + <string>Url:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="1" > + <widget class="QLabel" name="url" > + <property name="text" > + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="label_7" > + <property name="text" > + <string>Location:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="5" column="1" > + <widget class="QLabel" name="location" > + <property name="text" > + <string>TextLabel</string> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="6" column="0" > + <layout class="QVBoxLayout" > + <item> + <widget class="QLabel" name="label_8" > + <property name="text" > + <string>Description:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="6" column="1" > + <widget class="QTextEdit" name="description" > + <property name="tabChangesFocus" > + <bool>true</bool> + </property> + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="7" column="0" > + <widget class="QLabel" name="label_5" > + <property name="text" > + <string>Copyright:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="7" column="1" > + <widget class="QLabel" name="copyright" > + <property name="text" > + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="8" column="0" > + <layout class="QVBoxLayout" > + <item> + <widget class="QLabel" name="label_9" > + <property name="text" > + <string>License:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>17</width> + <height>13</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="8" column="1" > + <widget class="QTextEdit" name="license" > + <property name="tabChangesFocus" > + <bool>true</bool> + </property> + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="9" column="0" > + <layout class="QVBoxLayout" > + <item> + <widget class="QLabel" name="label_10" > + <property name="text" > + <string>Dependencies:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="9" column="1" > + <widget class="QListWidget" name="dependencies" /> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/libs/extensionsystem/pluginerrorview.cpp b/src/libs/extensionsystem/pluginerrorview.cpp new file mode 100644 index 00000000000..c1572d88299 --- /dev/null +++ b/src/libs/extensionsystem/pluginerrorview.cpp @@ -0,0 +1,114 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "pluginerrorview.h" +#include "ui_pluginerrorview.h" + +#include <QtCore/QString> + +/*! + \class ExtensionSystem::PluginErrorView + \brief Widget that displays the state and error message of a PluginSpec. + + Can be used for integration in the application that + uses the plugin manager. + + \sa ExtensionSystem::PluginView +*/ + +using namespace ExtensionSystem; + +/*! + \fn PluginErrorView::PluginErrorView(QWidget *parent) + Constructs a new error view with given \a parent widget. +*/ +PluginErrorView::PluginErrorView(QWidget *parent) + : QWidget(parent), + m_ui(new Internal::Ui::PluginErrorView()) +{ + m_ui->setupUi(this); +} + +/*! + \fn PluginErrorView::~PluginErrorView() + \internal +*/ +PluginErrorView::~PluginErrorView() +{ + delete m_ui; +} + +/*! + \fn void PluginErrorView::update(PluginSpec *spec) + Reads the given \a spec and displays its state and + error information in this PluginErrorView. +*/ +void PluginErrorView::update(PluginSpec *spec) +{ + QString text; + QString tooltip; + switch (spec->state()) { + case PluginSpec::Invalid: + text = tr("Invalid"); + tooltip = tr("Description file found, but error on read"); + break; + case PluginSpec::Read: + text = tr("Read"); + tooltip = tr("Description successfully read"); + break; + case PluginSpec::Resolved: + text = tr("Resolved"); + tooltip = tr("Dependencies are successfully resolved"); + break; + case PluginSpec::Loaded: + text = tr("Loaded"); + tooltip = tr("Library is loaded"); + break; + case PluginSpec::Initialized: + text = tr("Initialized"); + tooltip = tr("Plugin's initialization method succeeded"); + break; + case PluginSpec::Running: + text = tr("Running"); + tooltip = tr("Plugin successfully loaded and running"); + break; + case PluginSpec::Stopped: + text = tr("Stopped"); + tooltip = tr("Plugin was shut down"); + case PluginSpec::Deleted: + text = tr("Deleted"); + tooltip = tr("Plugin ended it's life cycle and was deleted"); + } + m_ui->state->setText(text); + m_ui->state->setToolTip(tooltip); + m_ui->errorString->setText(spec->errorString()); +} diff --git a/src/libs/extensionsystem/pluginerrorview.h b/src/libs/extensionsystem/pluginerrorview.h new file mode 100644 index 00000000000..87b4097d033 --- /dev/null +++ b/src/libs/extensionsystem/pluginerrorview.h @@ -0,0 +1,66 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGINERRORVIEW_H_ +#define PLUGINERRORVIEW_H_ + +#include "extensionsystem_global.h" +#include "pluginspec.h" + +#include <QtGui/QWidget> + +namespace ExtensionSystem +{ + +namespace Internal { +namespace Ui { + class PluginErrorView; +} // namespace Ui +} // namespace Internal + +class EXTENSIONSYSTEM_EXPORT PluginErrorView : public QWidget +{ + Q_OBJECT + +public: + PluginErrorView(QWidget *parent = 0); + ~PluginErrorView(); + + void update(PluginSpec *spec); + +private: + Internal::Ui::PluginErrorView *m_ui; +}; + +} // namespace ExtensionSystem + +#endif /*PLUGINERRORVIEW_H_*/ diff --git a/src/libs/extensionsystem/pluginerrorview.ui b/src/libs/extensionsystem/pluginerrorview.ui new file mode 100644 index 00000000000..69f49e53c66 --- /dev/null +++ b/src/libs/extensionsystem/pluginerrorview.ui @@ -0,0 +1,77 @@ +<ui version="4.0" > + <class>ExtensionSystem::Internal::PluginErrorView</class> + <widget class="QWidget" name="ExtensionSystem::Internal::PluginErrorView" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>579</width> + <height>342</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout" > + <property name="margin" > + <number>2</number> + </property> + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>State:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLabel" name="state" > + <property name="text" > + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <layout class="QVBoxLayout" name="verticalLayout" > + <item> + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Error Message:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </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" > + <widget class="QTextEdit" name="errorString" > + <property name="tabChangesFocus" > + <bool>true</bool> + </property> + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp new file mode 100644 index 00000000000..daf4169a80a --- /dev/null +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -0,0 +1,749 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "pluginmanager.h" +#include "pluginmanager_p.h" +#include "pluginspec.h" +#include "pluginspec_p.h" +#include "optionsparser.h" +#include "iplugin.h" + +#include <QtCore/QMetaProperty> +#include <QtCore/QPluginLoader> +#include <QtCore/QDir> +#include <QtCore/QTextStream> +#include <QtCore/QWriteLocker> +#include <QtDebug> +#ifdef WITH_TESTS +#include <QTest> +#endif + +typedef QSet<ExtensionSystem::PluginSpec *> PluginSpecSet; + +enum { debugLeaks = 0 }; + +/*! + \namespace ExtensionSystem + \brief Classes that belong to the core plugin system. + + The basic extension system contains of the plugin manager and its supporting classes, + and the IPlugin interface that must be implemented by plugin providers. +*/ + +/*! + \namespace ExtensionSystem::Internal + \internal +*/ + +/*! + \class ExtensionSystem::PluginManager + \mainclass + + \brief Core plugin system that manages the plugins, their life cycle and their registered objects. + + The plugin manager is used for the following tasks: + \list + \o Manage plugins and their state + \o Manipulate a 'common object pool' + \endlist + + \section1 Plugins + Plugins consist of an xml descriptor file, and of a library that contains a Qt plugin + (declared via Q_EXPORT_PLUGIN) that must derive from the IPlugin class. + The plugin manager is used to set a list of file system directories to search for + plugins, retrieve information about the state of these plugins, and to load them. + + Usually the application creates a PluginManager instance and initiates the loading. + \code + ExtensionSystem::PluginManager *manager = new ExtensionSystem::PluginManager(); + manager->setPluginPaths(QStringList() << "plugins"); // 'plugins' and subdirs will be searched for plugins + manager->loadPlugins(); // try to load all the plugins + \endcode + Additionally it is possible to directly access to the plugin specifications + (the information in the descriptor file), and the plugin instances (via PluginSpec), + and their state. + + \section1 Object Pool + Plugins (and everybody else) can add objects to a common 'pool' that is located in + the plugin manager. Objects in the pool must derive from QObject, there are no other + prerequisites. All objects of a specified type can be retrieved from the object pool + via the getObjects() and getObject() methods. They are aware of Aggregation::Aggregate, i.e. + these methods use the Aggregation::query methods instead of a qobject_cast to determine + the matching objects. + + Whenever the state of the object pool changes a corresponding signal is emitted by the plugin manager. + + A common usecase for the object pool is that a plugin (or the application) provides + an "extension point" for other plugins, which is a class / interface that can + be implemented and added to the object pool. The plugin that provides the + extension point looks for implementations of the class / interface in the object pool. + \code + // plugin A provides a "MimeTypeHandler" extension point + // in plugin B: + MyMimeTypeHandler *handler = new MyMimeTypeHandler(); + ExtensionSystem::PluginManager::instance()->addObject(handler); + // in plugin A: + QList<MimeTypeHandler *> mimeHandlers = + ExtensionSystem::PluginManager::instance()->getObjects<MimeTypeHandler>(); + \endcode + + \bold Note: The object pool manipulating functions are thread-safe. +*/ + +/*! + \fn void PluginManager::objectAdded(QObject *obj) + Signal that \a obj has been added to the object pool. +*/ + +/*! + \fn void PluginManager::aboutToRemoveObject(QObject *obj) + Signal that \a obj will be removed from the object pool. +*/ + +/*! + \fn void PluginManager::pluginsChanged() + Signal that the list of available plugins has changed. + + \sa plugins() +*/ + +/*! + \fn T *PluginManager::getObject() const + Retrieve the object of a given type from the object pool. + This method is aware of Aggregation::Aggregate, i.e. it uses + the Aggregation::query methods instead of qobject_cast to + determine the type of an object. + If there are more than one object of the given type in + the object pool, this method will choose an arbitrary one of them. + + \sa addObject() +*/ + +/*! + \fn QList<T *> PluginManager::getObjects() const + Retrieve all objects of a given type from the object pool. + This method is aware of Aggregation::Aggregate, i.e. it uses + the Aggregation::query methods instead of qobject_cast to + determine the type of an object. + + \sa addObject() +*/ + +using namespace ExtensionSystem; +using namespace ExtensionSystem::Internal; + +PluginManager *PluginManager::m_instance = 0; + +/*! + \fn PluginManager *PluginManager::instance() + Get the unique plugin manager instance. +*/ +PluginManager *PluginManager::instance() +{ + return m_instance; +} + +/*! + \fn PluginManager::PluginManager() + Create a plugin manager. Should be done only once per application. +*/ +PluginManager::PluginManager() + : d(new PluginManagerPrivate(this)) +{ + m_instance = this; +} + +/*! + \fn PluginManager::~PluginManager() + \internal +*/ +PluginManager::~PluginManager() +{ + delete d; + d = 0; +} + +/*! + \fn void PluginManager::addObject(QObject *obj) + Add the given object \a obj to the object pool, so it can be retrieved again from the pool by type. + The plugin manager does not do any memory management - added objects + must be removed from the pool and deleted manually by whoever is responsible for the object. + + Emits the objectAdded() signal. + + \sa PluginManager::removeObject() + \sa PluginManager::getObject() + \sa PluginManager::getObjects() +*/ +void PluginManager::addObject(QObject *obj) +{ + d->addObject(obj); +} + +/*! + \fn void PluginManager::removeObject(QObject *obj) + Emits aboutToRemoveObject() and removes the object \a obj from the object pool. + \sa PluginManager::addObject() +*/ +void PluginManager::removeObject(QObject *obj) +{ + d->removeObject(obj); +} + +/*! + \fn QList<QObject *> PluginManager::allObjects() const + Retrieve the list of all objects in the pool, unfiltered. + Usually clients do not need to call this. + \sa PluginManager::getObject() + \sa PluginManager::getObjects() +*/ +QList<QObject *> PluginManager::allObjects() const +{ + return d->allObjects; +} + +/*! + \fn void PluginManager::loadPlugins() + Tries to load all the plugins that were previously found when + setting the plugin search paths. The plugin specs of the plugins + can be used to retrieve error and state information about individual plugins. + + \sa setPluginPaths() + \sa plugins() +*/ +void PluginManager::loadPlugins() +{ + return d->loadPlugins(); +} + +/*! + \fn QStringList PluginManager::pluginPaths() const + The list of paths were the plugin manager searches for plugins. + + \sa setPluginPaths() +*/ +QStringList PluginManager::pluginPaths() const +{ + return d->pluginPaths; +} + +/*! + \fn void PluginManager::setPluginPaths(const QStringList &paths) + Sets the plugin search paths, i.e. the file system paths where the plugin manager + looks for plugin descriptions. All given \a paths and their sub directory trees + are searched for plugin xml description files. + + \sa pluginPaths() + \sa loadPlugins() +*/ +void PluginManager::setPluginPaths(const QStringList &paths) +{ + d->setPluginPaths(paths); +} + +/*! + \fn QString PluginManager::fileExtension() const + The file extension of plugin description files. + The default is "xml". + + \sa setFileExtension() +*/ +QString PluginManager::fileExtension() const +{ + return d->extension; +} + +/*! + \fn void PluginManager::setFileExtension(const QString &extension) + Sets the file extension of plugin description files. + The default is "xml". + At the moment this must be called before setPluginPaths() is called. + // ### TODO let this + setPluginPaths read the plugin specs lazyly whenever loadPlugins() or plugins() is called. +*/ +void PluginManager::setFileExtension(const QString &extension) +{ + d->extension = extension; +} + +/*! + \fn QStringList PluginManager::arguments() const + The arguments left over after parsing (Neither startup nor plugin + arguments). Typically, this will be the list of files to open. +*/ +QStringList PluginManager::arguments() const +{ + return d->arguments; +} + +/*! + \fn QSet<PluginSpec *> PluginManager::plugins() const + List of all plugin specifications that have been found in the plugin search paths. + This list is valid directly after the setPluginPaths() call. + The plugin specifications contain the information from the plugins' xml description files + and the current state of the plugins. If a plugin's library has been already successfully loaded, + the plugin specification has a reference to the created plugin instance as well. + + \sa setPluginPaths() +*/ +QSet<PluginSpec *> PluginManager::plugins() const +{ + return d->pluginSpecs; +} + +/*! + \fn bool PluginManager::parseOptions(const QStringList &args, const QMap<QString, bool> &appOptions, QMap<QString, QString> *foundAppOptions, QString *errorString) + Takes the list of command line options in \a args and parses them. + The plugin manager itself might process some options itself directly (-noload <plugin>), and + adds options that are registered by plugins to their plugin specs. + The caller (the application) may register itself for options via the \a appOptions list, containing pairs + of "option string" and a bool that indicates if the option requires an argument. + Application options always override any plugin's options. + + \a foundAppOptions is set to pairs of ("option string", "argument") for any application options that were found. + The command line options that were not processed can be retrieved via the arguments() method. + If an error occurred (like missing argument for an option that requires one), \a errorString contains + a descriptive message of the error. + + Returns if there was an error. + */ +bool PluginManager::parseOptions(const QStringList &args, + const QMap<QString, bool> &appOptions, + QMap<QString, QString> *foundAppOptions, + QString *errorString) +{ + OptionsParser options(args, appOptions, foundAppOptions, errorString, d); + return options.parse(); +} + + + +static inline void indent(QTextStream &str, int indent) +{ + const QChar blank = QLatin1Char(' '); + for (int i = 0 ; i < indent; i++) + str << blank; +} + +static inline void formatOption(QTextStream &str, + const QString &opt, const QString &parm, const QString &description, + int optionIndentation, int descriptionIndentation) +{ + int remainingIndent = descriptionIndentation - optionIndentation - opt.size(); + indent(str, optionIndentation); + str << opt; + if (!parm.isEmpty()) { + str << " <" << parm << '>'; + remainingIndent -= 3 + parm.size(); + } + indent(str, qMax(0, remainingIndent)); + str << description << '\n'; +} + +/*! + \fn static PluginManager::formatOptions(QTextStream &str, int optionIndentation, int descriptionIndentation) + + Format the startup options of the plugin manager for command line help. +*/ + +void PluginManager::formatOptions(QTextStream &str, int optionIndentation, int descriptionIndentation) +{ + formatOption(str, QLatin1String(OptionsParser::NO_LOAD_OPTION), + QLatin1String("plugin"), QLatin1String("Do not load <plugin>"), + optionIndentation, descriptionIndentation); +} + +/*! + \fn PluginManager::formatPluginOptions(QTextStream &str, int optionIndentation, int descriptionIndentation) const + + Format the plugin options of the plugin specs for command line help. +*/ + +void PluginManager::formatPluginOptions(QTextStream &str, int optionIndentation, int descriptionIndentation) const +{ + typedef PluginSpec::PluginArgumentDescriptions PluginArgumentDescriptions; + // Check plugins for options + const PluginSpecSet::const_iterator pcend = d->pluginSpecs.constEnd(); + for (PluginSpecSet::const_iterator pit = d->pluginSpecs.constBegin(); pit != pcend; ++pit) { + const PluginArgumentDescriptions pargs = (*pit)->argumentDescriptions(); + if (!pargs.empty()) { + str << "\nPlugin: " << (*pit)->name() << '\n'; + const PluginArgumentDescriptions::const_iterator acend = pargs.constEnd(); + for (PluginArgumentDescriptions::const_iterator ait =pargs.constBegin(); ait != acend; ++ait) + formatOption(str, ait->name, ait->parameter, ait->description, optionIndentation, descriptionIndentation); + } + } +} + +/*! + \fn PluginManager::formatPluginVersions(QTextStream &str) const + + Format the version of the plugin specs for command line help. +*/ + +void PluginManager::formatPluginVersions(QTextStream &str) const +{ + const PluginSpecSet::const_iterator cend = d->pluginSpecs.constEnd(); + for (PluginSpecSet::const_iterator it = d->pluginSpecs.constBegin(); it != cend; ++it) { + const PluginSpec *ps = *it; + str << " " << ps->name() << ' ' << ps->version() << ' ' << ps->description() << '\n'; + } +} + +void PluginManager::startTests() +{ +#ifdef WITH_TESTS + foreach(PluginSpec *pluginSpec, d->testSpecs) { + const QMetaObject *mo = pluginSpec->plugin()->metaObject(); + QStringList methods; + methods.append("arg0"); + // We only want slots starting with "test" + for(int i = mo->methodOffset(); i < mo->methodCount(); ++i) { + if (QByteArray(mo->method(i).signature()).startsWith("test")) { + QString method = QString::fromLatin1(mo->method(i).signature()); + methods.append(method.left(method.size()-2)); + } + } + QTest::qExec(pluginSpec->plugin(), methods); + + } +#endif +} + +bool PluginManager::runningTests() const +{ + return !d->testSpecs.isEmpty(); +} + +QString PluginManager::testDataDirectory() const +{ + QString s = QString::fromLocal8Bit(qgetenv("IDETESTDIR")); + if (s.isEmpty()) { + s = IDE_TEST_DIR; + s.append("/tests"); + } + s = QDir::cleanPath(s); + return s; +} + +//============PluginManagerPrivate=========== + +/*! + \fn PluginSpec *PluginManagerPrivate::createSpec() + \internal +*/ +PluginSpec *PluginManagerPrivate::createSpec() +{ + return new PluginSpec(); +} + +/*! + \fn PluginSpecPrivate *PluginManagerPrivate::privateSpec(PluginSpec *spec) + \internal +*/ +PluginSpecPrivate *PluginManagerPrivate::privateSpec(PluginSpec *spec) +{ + return spec->d; +} + +/*! + \fn PluginManagerPrivate::PluginManagerPrivate(PluginManager *pluginManager) + \internal +*/ +PluginManagerPrivate::PluginManagerPrivate(PluginManager *pluginManager) + : extension("xml"), q(pluginManager) +{ +} + +/*! + \fn PluginManagerPrivate::~PluginManagerPrivate() + \internal +*/ +PluginManagerPrivate::~PluginManagerPrivate() +{ + stopAll(); + qDeleteAll(pluginSpecs); + if (!allObjects.isEmpty()) { + qDebug() << "There are" << allObjects.size() << "objects left in the plugin manager pool: " << allObjects; + } +} + +void PluginManagerPrivate::stopAll() +{ + QList<PluginSpec *> queue = loadQueue(); + foreach (PluginSpec *spec, queue) { + loadPlugin(spec, PluginSpec::Stopped); + } + QListIterator<PluginSpec *> it(queue); + it.toBack(); + while (it.hasPrevious()) { + loadPlugin(it.previous(), PluginSpec::Deleted); + } +} + +/*! + \fn void PluginManagerPrivate::addObject(QObject *obj) + \internal +*/ +void PluginManagerPrivate::addObject(QObject *obj) +{ + { + QWriteLocker lock(&(q->m_lock)); + if (obj == 0) { + qWarning() << "PluginManagerPrivate::addObject(): trying to add null object"; + return; + } + if (allObjects.contains(obj)) { + qWarning() << "PluginManagerPrivate::addObject(): trying to add duplicate object"; + return; + } + + if (debugLeaks) + qDebug() << "PluginManagerPrivate::addObject" << obj << obj->objectName(); + + allObjects.append(obj); + } + emit q->objectAdded(obj); +} + +/*! + \fn void PluginManagerPrivate::removeObject(QObject *obj) + \internal +*/ +void PluginManagerPrivate::removeObject(QObject *obj) +{ + if (obj == 0) { + qWarning() << "PluginManagerPrivate::removeObject(): trying to remove null object"; + return; + } + + if (!allObjects.contains(obj)) { + qWarning() << "PluginManagerPrivate::removeObject(): object not in list:" + << obj << obj->objectName(); + return; + } + if (debugLeaks) + qDebug() << "PluginManagerPrivate::removeObject" << obj << obj->objectName(); + + emit q->aboutToRemoveObject(obj); + QWriteLocker lock(&(q->m_lock)); + allObjects.removeAll(obj); +} + +/*! + \fn void PluginManagerPrivate::loadPlugins() + \internal +*/ +void PluginManagerPrivate::loadPlugins() +{ + QList<PluginSpec *> queue = loadQueue(); + foreach (PluginSpec *spec, queue) { + loadPlugin(spec, PluginSpec::Loaded); + } + foreach (PluginSpec *spec, queue) { + loadPlugin(spec, PluginSpec::Initialized); + } + QListIterator<PluginSpec *> it(queue); + it.toBack(); + while (it.hasPrevious()) { + loadPlugin(it.previous(), PluginSpec::Running); + } + emit q->pluginsChanged(); +} + +/*! + \fn void PluginManagerPrivate::loadQueue() + \internal +*/ +QList<PluginSpec *> PluginManagerPrivate::loadQueue() +{ + QList<PluginSpec *> queue; + foreach (PluginSpec *spec, pluginSpecs) { + QList<PluginSpec *> circularityCheckQueue; + loadQueue(spec, queue, circularityCheckQueue); + } + return queue; +} + +/*! + \fn bool PluginManagerPrivate::loadQueue(PluginSpec *spec, QList<PluginSpec *> &queue, QList<PluginSpec *> &circularityCheckQueue) + \internal +*/ +bool PluginManagerPrivate::loadQueue(PluginSpec *spec, QList<PluginSpec *> &queue, + QList<PluginSpec *> &circularityCheckQueue) +{ + if (queue.contains(spec)) + return true; + // check for circular dependencies + if (circularityCheckQueue.contains(spec)) { + spec->d->hasError = true; + spec->d->errorString = q->tr("Circular dependency detected:\n"); + int index = circularityCheckQueue.indexOf(spec); + for (int i = index; i < circularityCheckQueue.size(); ++i) { + spec->d->errorString.append(q->tr("%1(%2) depends on\n") + .arg(circularityCheckQueue.at(i)->name()).arg(circularityCheckQueue.at(i)->version())); + } + spec->d->errorString.append(q->tr("%1(%2)").arg(spec->name()).arg(spec->version())); + return false; + } + circularityCheckQueue.append(spec); + // check if we have the dependencies + if (spec->state() == PluginSpec::Invalid || spec->state() == PluginSpec::Read) { + spec->d->hasError = true; + spec->d->errorString += "\n"; + spec->d->errorString += q->tr("Cannot load plugin because dependencies are not resolved"); + return false; + } + // add dependencies + foreach (PluginSpec *depSpec, spec->dependencySpecs()) { + if (!loadQueue(depSpec, queue, circularityCheckQueue)) { + spec->d->hasError = true; + spec->d->errorString = + q->tr("Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3") + .arg(depSpec->name()).arg(depSpec->version()).arg(depSpec->errorString()); + return false; + } + } + // add self + queue.append(spec); + return true; +} + +/*! + \fn void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destState) + \internal +*/ +void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destState) +{ + if (spec->hasError()) + return; + if (destState == PluginSpec::Running) { + spec->d->initializeExtensions(); + return; + } else if (destState == PluginSpec::Deleted) { + spec->d->kill(); + return; + } + foreach (PluginSpec *depSpec, spec->dependencySpecs()) { + if (depSpec->state() != destState) { + spec->d->hasError = true; + spec->d->errorString = + q->tr("Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3") + .arg(depSpec->name()).arg(depSpec->version()).arg(depSpec->errorString()); + return; + } + } + if (destState == PluginSpec::Loaded) + spec->d->loadLibrary(); + else if (destState == PluginSpec::Initialized) + spec->d->initializePlugin(); + else if (destState == PluginSpec::Stopped) + spec->d->stop(); +} + +/*! + \fn void PluginManagerPrivate::setPluginPaths(const QStringList &paths) + \internal +*/ +void PluginManagerPrivate::setPluginPaths(const QStringList &paths) +{ + pluginPaths = paths; + readPluginPaths(); +} + +/*! + \fn void PluginManagerPrivate::readPluginPaths() + \internal +*/ +void PluginManagerPrivate::readPluginPaths() +{ + qDeleteAll(pluginSpecs); + pluginSpecs.clear(); + + QStringList specFiles; + QStringList searchPaths = pluginPaths; + while (!searchPaths.isEmpty()) { + const QDir dir(searchPaths.takeFirst()); + const QFileInfoList files = dir.entryInfoList(QStringList() << QString("*.%1").arg(extension), QDir::Files); + foreach (const QFileInfo &file, files) + specFiles << file.absoluteFilePath(); + const QFileInfoList dirs = dir.entryInfoList(QDir::Dirs|QDir::NoDotAndDotDot); + foreach (const QFileInfo &subdir, dirs) + searchPaths << subdir.absoluteFilePath(); + } + foreach (const QString &specFile, specFiles) { + PluginSpec *spec = new PluginSpec; + spec->d->read(specFile); + pluginSpecs.insert(spec); + } + resolveDependencies(); + emit q->pluginsChanged(); +} + +void PluginManagerPrivate::resolveDependencies() +{ + foreach (PluginSpec *spec, pluginSpecs) { + spec->d->resolveDependencies(pluginSpecs); + } +} + + // Look in argument descriptions of the specs for the option. +PluginSpec *PluginManagerPrivate::pluginForOption(const QString &option, bool *requiresArgument) const +{ + // Look in the plugins for an option + typedef PluginSpec::PluginArgumentDescriptions PluginArgumentDescriptions; + + *requiresArgument = false; + const PluginSpecSet::const_iterator pcend = pluginSpecs.constEnd(); + for (PluginSpecSet::const_iterator pit = pluginSpecs.constBegin(); pit != pcend; ++pit) { + PluginSpec *ps = *pit; + const PluginArgumentDescriptions pargs = ps->argumentDescriptions(); + if (!pargs.empty()) { + const PluginArgumentDescriptions::const_iterator acend = pargs.constEnd(); + for (PluginArgumentDescriptions::const_iterator ait = pargs.constBegin(); ait != acend; ++ait) { + if (ait->name == option) { + *requiresArgument = !ait->parameter.isEmpty(); + return ps; + } + } + } + } + return 0; +} + +PluginSpec *PluginManagerPrivate::pluginByName(const QString &name) const +{ + foreach (PluginSpec *spec, pluginSpecs) + if (spec->name() == name) + return spec; + return 0; +} + diff --git a/src/libs/extensionsystem/pluginmanager.h b/src/libs/extensionsystem/pluginmanager.h new file mode 100644 index 00000000000..7f003b4f899 --- /dev/null +++ b/src/libs/extensionsystem/pluginmanager.h @@ -0,0 +1,138 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef EXTENSIONSYSTEM_PLUGINMANAGER_H +#define EXTENSIONSYSTEM_PLUGINMANAGER_H + +#include "extensionsystem_global.h" +#include <aggregation/aggregate.h> + +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QtCore/QSet> +#include <QtCore/QStringList> +#include <QtCore/QWriteLocker> +#include <QtCore/QReadWriteLock> + +QT_BEGIN_NAMESPACE +class QTextStream; +QT_END_NAMESPACE + +namespace ExtensionSystem { + +namespace Internal { + class PluginManagerPrivate; +} + +class IPlugin; +class PluginSpec; + +class EXTENSIONSYSTEM_EXPORT PluginManager : public QObject +{ + Q_DISABLE_COPY(PluginManager) + Q_OBJECT + +public: + static PluginManager *instance(); + + PluginManager(); + virtual ~PluginManager(); + + // Object pool operations + void addObject(QObject *obj); + void removeObject(QObject *obj); + QList<QObject *> allObjects() const; + template <typename T> QList<T *> getObjects() const + { + QReadLocker lock(&m_lock); + QList<T *> results; + QList<QObject *> all = allObjects(); + QList<T *> result; + foreach (QObject *obj, all) { + result = Aggregation::query_all<T>(obj); + if (!result.isEmpty()) + results += result; + } + return results; + } + template <typename T> T *getObject() const + { + QReadLocker lock(&m_lock); + QList<QObject *> all = allObjects(); + T *result = 0; + foreach (QObject *obj, all) { + if ((result = Aggregation::query<T>(obj)) != 0) + break; + } + return result; + } + + // Plugin operations + void loadPlugins(); + QStringList pluginPaths() const; + void setPluginPaths(const QStringList &paths); + QSet<PluginSpec *> plugins() const; + void setFileExtension(const QString &extension); + QString fileExtension() const; + + // command line arguments + QStringList arguments() const; + bool parseOptions(const QStringList &args, + const QMap<QString, bool> &appOptions, + QMap<QString, QString> *foundAppOptions, + QString *errorString); + static void formatOptions(QTextStream &str, int optionIndentation, int descriptionIndentation); + void formatPluginOptions(QTextStream &str, int optionIndentation, int descriptionIndentation) const; + void formatPluginVersions(QTextStream &str) const; + + bool runningTests() const; + QString testDataDirectory() const; + +signals: + void objectAdded(QObject *obj); + void aboutToRemoveObject(QObject *obj); + + void pluginsChanged(); +private slots: + void startTests(); + +private: + Internal::PluginManagerPrivate *d; + static PluginManager *m_instance; + mutable QReadWriteLock m_lock; + + friend class Internal::PluginManagerPrivate; +}; + +} //namespace + +#endif // PLUGINMANAGER_H diff --git a/src/libs/extensionsystem/pluginmanager_p.h b/src/libs/extensionsystem/pluginmanager_p.h new file mode 100644 index 00000000000..6b8df8d2bee --- /dev/null +++ b/src/libs/extensionsystem/pluginmanager_p.h @@ -0,0 +1,96 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGINMANAGER_P_H +#define PLUGINMANAGER_P_H + +#include "pluginspec.h" + +#include <QtCore/QList> +#include <QtCore/QSet> +#include <QtCore/QStringList> +#include <QtCore/QObject> + +namespace ExtensionSystem { + +class PluginManager; + +namespace Internal { + +class PluginSpecPrivate; + +class EXTENSIONSYSTEM_EXPORT PluginManagerPrivate +{ +public: + PluginManagerPrivate(PluginManager *pluginManager); + virtual ~PluginManagerPrivate(); + + // Object pool operations + void addObject(QObject *obj); + void removeObject(QObject *obj); + + // Plugin operations + void loadPlugins(); + void setPluginPaths(const QStringList &paths); + QList<PluginSpec *> loadQueue(); + void loadPlugin(PluginSpec *spec, PluginSpec::State destState); + void resolveDependencies(); + + QSet<PluginSpec *> pluginSpecs; + QList<PluginSpec *> testSpecs; + QStringList pluginPaths; + QString extension; + QList<QObject *> allObjects; // ### make this a QList<QPointer<QObject> > > ? + + QStringList arguments; + + // Look in argument descriptions of the specs for the option. + PluginSpec *pluginForOption(const QString &option, bool *requiresArgument) const; + PluginSpec *pluginByName(const QString &name) const; + + // used by tests + static PluginSpec *createSpec(); + static PluginSpecPrivate *privateSpec(PluginSpec *spec); +private: + PluginManager *q; + + void readPluginPaths(); + bool loadQueue(PluginSpec *spec, + QList<PluginSpec *> &queue, + QList<PluginSpec *> &circularityCheckQueue); + void stopAll(); +}; + +} // namespace Internal +} // namespace ExtensionSystem + +#endif // PLUGINMANAGER_P_H diff --git a/src/libs/extensionsystem/pluginspec.cpp b/src/libs/extensionsystem/pluginspec.cpp new file mode 100644 index 00000000000..dfa7bdbe7c0 --- /dev/null +++ b/src/libs/extensionsystem/pluginspec.cpp @@ -0,0 +1,871 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "pluginspec.h" +#include "pluginspec.h" +#include "pluginspec_p.h" +#include "iplugin.h" +#include "iplugin_p.h" +#include "pluginmanager.h" + +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QXmlStreamReader> +#include <QtCore/QRegExp> +#include <QtCore/QPluginLoader> +#include <QtCore/QCoreApplication> +#include <QtDebug> + +/*! + \class ExtensionSystem::PluginDependency + \brief Struct that contains the name and required compatible version number of a plugin's dependency. + + This reflects the data of a dependency tag in the plugin's xml description file. + The name and version are used to resolve the dependency, i.e. a plugin with the given name and + plugin \c {compatibility version <= dependency version <= plugin version} is searched for. + + See also ExtensionSystem::IPlugin for more information about plugin dependencies and + version matching. +*/ + +/*! + \variable ExtensionSystem::PluginDependency::name + String identifier of the plugin. +*/ + +/*! + \variable ExtensionSystem::PluginDependency::version + Version string that a plugin must match to fill this dependency. +*/ + +/*! + \class ExtensionSystem::PluginSpec + \brief Contains the information of the plugins xml description file and + information about the plugin's current state. + + The plugin spec is also filled with more information as the plugin + goes through it's loading process (see PluginSpec::State). + If an error occurs, the plugin spec is the place to look for the + error details. +*/ + +/*! + \enum ExtensionSystem::PluginSpec::State + + The plugin goes through several steps while being loaded. + The state gives a hint on what went wrong in case of an error. + + \value Invalid + Starting point: Even the xml description file was not read. + \value Read + The xml description file has been successfully read, and it's + information is available via the PluginSpec. + \value Resolved + The dependencies given in the description file have been + successfully found, and are available via the dependencySpecs() method. + \value Loaded + The plugin's library is loaded and the plugin instance created + (available through plugin()). + \value Initialized + The plugin instance's IPlugin::initialize() method has been called + and returned a success value. + \value Running + The plugin's dependencies are successfully initialized and + extensionsInitialized has been called. The loading process is + complete. + \value Stopped + The plugin has been shut down, i.e. the plugin's IPlugin::shutdown() method has been called. + \value Deleted + The plugin instance has been deleted. +*/ +using namespace ExtensionSystem; +using namespace ExtensionSystem::Internal; + +/*! + \fn bool PluginDependency::operator==(const PluginDependency &other) + \internal +*/ +bool PluginDependency::operator==(const PluginDependency &other) +{ + return name == other.name && version == other.version; +} + +/*! + \fn PluginSpec::PluginSpec() + \internal +*/ +PluginSpec::PluginSpec() + : d(new PluginSpecPrivate(this)) +{ +} + +/*! + \fn PluginSpec::~PluginSpec() + \internal +*/ +PluginSpec::~PluginSpec() +{ + delete d; + d = 0; +} + +/*! + \fn QString PluginSpec::name() const + The plugin name. This is valid after the PluginSpec::Read state is reached. +*/ +QString PluginSpec::name() const +{ + return d->name; +} + +/*! + \fn QString PluginSpec::version() const + The plugin version. This is valid after the PluginSpec::Read state is reached. +*/ +QString PluginSpec::version() const +{ + return d->version; +} + +/*! + \fn QString PluginSpec::compatVersion() const + The plugin compatibility version. This is valid after the PluginSpec::Read state is reached. +*/ +QString PluginSpec::compatVersion() const +{ + return d->compatVersion; +} + +/*! + \fn QString PluginSpec::vendor() const + The plugin vendor. This is valid after the PluginSpec::Read state is reached. +*/ +QString PluginSpec::vendor() const +{ + return d->vendor; +} + +/*! + \fn QString PluginSpec::copyright() const + The plugin copyright. This is valid after the PluginSpec::Read state is reached. +*/ +QString PluginSpec::copyright() const +{ + return d->copyright; +} + +/*! + \fn QString PluginSpec::license() const + The plugin license. This is valid after the PluginSpec::Read state is reached. +*/ +QString PluginSpec::license() const +{ + return d->license; +} + +/*! + \fn QString PluginSpec::description() const + The plugin description. This is valid after the PluginSpec::Read state is reached. +*/ +QString PluginSpec::description() const +{ + return d->description; +} + +/*! + \fn QString PluginSpec::url() const + The plugin url where you can find more information about the plugin. This is valid after the PluginSpec::Read state is reached. +*/ +QString PluginSpec::url() const +{ + return d->url; +} + +/*! + \fn QList<PluginDependency> PluginSpec::dependencies() const + The plugin dependencies. This is valid after the PluginSpec::Read state is reached. +*/ +QList<PluginDependency> PluginSpec::dependencies() const +{ + return d->dependencies; +} + +/*! + \fn PluginOptionDescriptions optionDescriptions() const + Returns a list of descriptions of command line arguments the plugin processes. +*/ + +PluginSpec::PluginArgumentDescriptions PluginSpec::argumentDescriptions() const +{ + return d->argumentDescriptions; +} + +/*! + \fn QString PluginSpec::location() const + The absolute path to the directory containing the plugin xml description file + this PluginSpec corresponds to. +*/ +QString PluginSpec::location() const +{ + return d->location; +} + +/*! + \fn QString PluginSpec::filePath() const + The absolute path to the plugin xml description file (including the file name) + this PluginSpec corresponds to. +*/ +QString PluginSpec::filePath() const +{ + return d->filePath; +} + +/*! + \fn QStringList PluginSpec::arguments() const + Command line arguments specific to that plugin. Set at startup +*/ + +QStringList PluginSpec::arguments() const +{ + return d->arguments; +} + +/*! + \fn void PluginSpec::setArguments(const QStringList &arguments) + Set the command line arguments specific to that plugin to \a arguments. +*/ + +void PluginSpec::setArguments(const QStringList &arguments) +{ + d->arguments = arguments; +} + +/*! + \fn PluginSpec::addArgument(const QString &argument) + Adds \a argument to the command line arguments specific to that plugin. +*/ + +void PluginSpec::addArgument(const QString &argument) +{ + d->arguments.push_back(argument); +} + + +/*! + \fn PluginSpec::State PluginSpec::state() const + The state in which the plugin currently is. + See the description of the PluginSpec::State enum for details. +*/ +PluginSpec::State PluginSpec::state() const +{ + return d->state; +} + +/*! + \fn bool PluginSpec::hasError() const + Returns whether an error occurred while reading/starting the plugin. +*/ +bool PluginSpec::hasError() const +{ + return d->hasError; +} + +/*! + \fn QString PluginSpec::errorString() const + Detailed, possibly multi-line, error description in case of an error. +*/ +QString PluginSpec::errorString() const +{ + return d->errorString; +} + +/*! + \fn bool PluginSpec::provides(const QString &pluginName, const QString &version) const + Returns if this plugin can be used to fill in a dependency of the given + \a pluginName and \a version. + + \sa PluginSpec::dependencies() +*/ +bool PluginSpec::provides(const QString &pluginName, const QString &version) const +{ + return d->provides(pluginName, version); +} + +/*! + \fn IPlugin *PluginSpec::plugin() const + The corresponding IPlugin instance, if the plugin library has already been successfully loaded, + i.e. the PluginSpec::Loaded state is reached. +*/ +IPlugin *PluginSpec::plugin() const +{ + return d->plugin; +} + +/*! + \fn QList<PluginSpec *> PluginSpec::dependencySpecs() const + Returns the list of dependencies, already resolved to existing plugin specs. + Valid if PluginSpec::Resolved state is reached. + + \sa PluginSpec::dependencies() +*/ +QList<PluginSpec *> PluginSpec::dependencySpecs() const +{ + return d->dependencySpecs; +} + +//==========PluginSpecPrivate================== + +namespace { + const char * const PLUGIN = "plugin"; + const char * const PLUGIN_NAME = "name"; + const char * const PLUGIN_VERSION = "version"; + const char * const PLUGIN_COMPATVERSION = "compatVersion"; + const char * const VENDOR = "vendor"; + const char * const COPYRIGHT = "copyright"; + const char * const LICENSE = "license"; + const char * const DESCRIPTION = "description"; + const char * const URL = "url"; + const char * const DEPENDENCYLIST = "dependencyList"; + const char * const DEPENDENCY = "dependency"; + const char * const DEPENDENCY_NAME = "name"; + const char * const DEPENDENCY_VERSION = "version"; + const char * const ARGUMENTLIST = "argumentList"; + const char * const ARGUMENT = "argument"; + const char * const ARGUMENT_NAME = "name"; + const char * const ARGUMENT_PARAMETER = "parameter"; +} +/*! + \fn PluginSpecPrivate::PluginSpecPrivate(PluginSpec *spec) + \internal +*/ +PluginSpecPrivate::PluginSpecPrivate(PluginSpec *spec) + : plugin(0), + state(PluginSpec::Invalid), + hasError(false), + q(spec) +{ +} + +/*! + \fn bool PluginSpecPrivate::read(const QString &fileName) + \internal +*/ +bool PluginSpecPrivate::read(const QString &fileName) +{ + name + = version + = compatVersion + = vendor + = copyright + = license + = description + = url + = location + = ""; + state = PluginSpec::Invalid; + hasError = false; + errorString = ""; + dependencies.clear(); + QFile file(fileName); + if (!file.exists()) + return reportError(tr("File does not exist: %1").arg(file.fileName())); + if (!file.open(QIODevice::ReadOnly)) + return reportError(tr("Could not open file for read: %1").arg(file.fileName())); + QFileInfo fileInfo(file); + location = fileInfo.absolutePath(); + filePath = fileInfo.absoluteFilePath(); + QXmlStreamReader reader(&file); + while (!reader.atEnd()) { + reader.readNext(); + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + readPluginSpec(reader); + break; + default: + break; + } + } + if (reader.hasError()) + return reportError(tr("Error parsing file %1: %2, at line %3, column %4") + .arg(file.fileName()) + .arg(reader.errorString()) + .arg(reader.lineNumber()) + .arg(reader.columnNumber())); + state = PluginSpec::Read; + return true; +} + +/*! + \fn bool PluginSpecPrivate::reportError(const QString &err) + \internal +*/ +bool PluginSpecPrivate::reportError(const QString &err) +{ + errorString = err; + hasError = true; + return false; +} + +static inline QString msgAttributeMissing(const char *elt, const char *attribute) +{ + return QCoreApplication::translate("PluginSpec", "'%1' misses attribute '%2'").arg(QLatin1String(elt), QLatin1String(attribute)); +} + +static inline QString msgInvalidFormat(const char *content) +{ + return QCoreApplication::translate("PluginSpec", "'%1' has invalid format").arg(content); +} + +static inline QString msgInvalidElement(const QString &name) +{ + return QCoreApplication::translate("PluginSpec", "Invalid element '%1'").arg(name); +} + +static inline QString msgUnexpectedClosing(const QString &name) +{ + return QCoreApplication::translate("PluginSpec", "Unexpected closing element '%1'").arg(name); +} + +static inline QString msgUnexpectedToken() +{ + return QCoreApplication::translate("PluginSpec", "Unexpected token"); +} + +/*! + \fn void PluginSpecPrivate::readPluginSpec(QXmlStreamReader &reader) + \internal +*/ +void PluginSpecPrivate::readPluginSpec(QXmlStreamReader &reader) +{ + QString element = reader.name().toString(); + if (element != QString(PLUGIN)) { + reader.raiseError(QCoreApplication::translate("PluginSpec", "Expected element '%1' as top level element").arg(PLUGIN)); + return; + } + name = reader.attributes().value(PLUGIN_NAME).toString(); + if (name.isEmpty()) { + reader.raiseError(msgAttributeMissing(PLUGIN, PLUGIN_NAME)); + return; + } + version = reader.attributes().value(PLUGIN_VERSION).toString(); + if (version.isEmpty()) { + reader.raiseError(msgAttributeMissing(PLUGIN, PLUGIN_VERSION)); + return; + } + if (!isValidVersion(version)) { + reader.raiseError(msgInvalidFormat(PLUGIN_VERSION)); + return; + } + compatVersion = reader.attributes().value(PLUGIN_COMPATVERSION).toString(); + if (!compatVersion.isEmpty() && !isValidVersion(compatVersion)) { + reader.raiseError(msgInvalidFormat(PLUGIN_COMPATVERSION)); + return; + } else if (compatVersion.isEmpty()) { + compatVersion = version; + } + while (!reader.atEnd()) { + reader.readNext(); + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + element = reader.name().toString(); + if (element == VENDOR) + vendor = reader.readElementText().trimmed(); + else if (element == COPYRIGHT) + copyright = reader.readElementText().trimmed(); + else if (element == LICENSE) + license = reader.readElementText().trimmed(); + else if (element == DESCRIPTION) + description = reader.readElementText().trimmed(); + else if (element == URL) + url = reader.readElementText().trimmed(); + else if (element == DEPENDENCYLIST) + readDependencies(reader); + else if (element == ARGUMENTLIST) + readArgumentDescriptions(reader); + else + reader.raiseError(msgInvalidElement(name)); + break; + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Comment: + case QXmlStreamReader::EndElement: + case QXmlStreamReader::Characters: + break; + default: + reader.raiseError(msgUnexpectedToken()); + break; + } + } +} + +/*! + \fn void PluginSpecPrivate::readArgumentDescriptions(QXmlStreamReader &reader) + \internal +*/ + +void PluginSpecPrivate::readArgumentDescriptions(QXmlStreamReader &reader) +{ + QString element; + while (!reader.atEnd()) { + reader.readNext(); + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + element = reader.name().toString(); + if (element == ARGUMENT) { + readArgumentDescription(reader); + } else { + reader.raiseError(msgInvalidElement(name)); + } + break; + case QXmlStreamReader::Comment: + case QXmlStreamReader::Characters: + break; + case QXmlStreamReader::EndElement: + element = reader.name().toString(); + if (element == ARGUMENTLIST) + return; + reader.raiseError(msgUnexpectedClosing(element)); + break; + default: + reader.raiseError(msgUnexpectedToken()); + break; + } + } +} + +/*! + \fn void PluginSpecPrivate::readArgumentDescription(QXmlStreamReader &reader) + \internal +*/ +void PluginSpecPrivate::readArgumentDescription(QXmlStreamReader &reader) +{ + PluginArgumentDescription arg; + arg.name = reader.attributes().value(ARGUMENT_NAME).toString(); + if (arg.name.isEmpty()) { + reader.raiseError(msgAttributeMissing(ARGUMENT, ARGUMENT_NAME)); + return; + } + arg.parameter = reader.attributes().value(ARGUMENT_PARAMETER).toString(); + arg.description = reader.readElementText(); + if (reader.tokenType() != QXmlStreamReader::EndElement) + reader.raiseError(msgUnexpectedToken()); + argumentDescriptions.push_back(arg); +} + +/*! + \fn void PluginSpecPrivate::readDependencies(QXmlStreamReader &reader) + \internal +*/ +void PluginSpecPrivate::readDependencies(QXmlStreamReader &reader) +{ + QString element; + while (!reader.atEnd()) { + reader.readNext(); + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + element = reader.name().toString(); + if (element == DEPENDENCY) { + readDependencyEntry(reader); + } else { + reader.raiseError(msgInvalidElement(name)); + } + break; + case QXmlStreamReader::Comment: + case QXmlStreamReader::Characters: + break; + case QXmlStreamReader::EndElement: + element = reader.name().toString(); + if (element == DEPENDENCYLIST) + return; + reader.raiseError(msgUnexpectedClosing(element)); + break; + default: + reader.raiseError(msgUnexpectedToken()); + break; + } + } +} + +/*! + \fn void PluginSpecPrivate::readDependencyEntry(QXmlStreamReader &reader) + \internal +*/ +void PluginSpecPrivate::readDependencyEntry(QXmlStreamReader &reader) +{ + PluginDependency dep; + dep.name = reader.attributes().value(DEPENDENCY_NAME).toString(); + if (dep.name.isEmpty()) { + reader.raiseError(msgAttributeMissing(DEPENDENCY, DEPENDENCY_NAME)); + return; + } + dep.version = reader.attributes().value(DEPENDENCY_VERSION).toString(); + if (!dep.version.isEmpty() && !isValidVersion(dep.version)) { + reader.raiseError(msgInvalidFormat(DEPENDENCY_VERSION)); + return; + } + dependencies.append(dep); + reader.readNext(); + if (reader.tokenType() != QXmlStreamReader::EndElement) + reader.raiseError(msgUnexpectedToken()); +} + +/*! + \fn bool PluginSpecPrivate::provides(const QString &pluginName, const QString &pluginVersion) const + \internal +*/ +bool PluginSpecPrivate::provides(const QString &pluginName, const QString &pluginVersion) const +{ + if (QString::compare(pluginName, name, Qt::CaseInsensitive) != 0) + return false; + return (versionCompare(version, pluginVersion) >= 0) && (versionCompare(compatVersion, pluginVersion) <= 0); +} + +/*! + \fn QRegExp &PluginSpecPrivate::versionRegExp() + \internal +*/ +QRegExp &PluginSpecPrivate::versionRegExp() +{ + static QRegExp reg("([0-9]+)(?:[.]([0-9]+))?(?:[.]([0-9]+))?(?:_([0-9]+))?"); + return reg; +} +/*! + \fn bool PluginSpecPrivate::isValidVersion(const QString &version) + \internal +*/ +bool PluginSpecPrivate::isValidVersion(const QString &version) +{ + return versionRegExp().exactMatch(version); +} + +/*! + \fn int PluginSpecPrivate::versionCompare(const QString &version1, const QString &version2) + \internal +*/ +int PluginSpecPrivate::versionCompare(const QString &version1, const QString &version2) +{ + QRegExp reg1 = versionRegExp(); + QRegExp reg2 = versionRegExp(); + if (!reg1.exactMatch(version1)) + return 0; + if (!reg2.exactMatch(version2)) + return 0; + int number1; + int number2; + for (int i = 0; i < 4; ++i) { + number1 = reg1.cap(i+1).toInt(); + number2 = reg2.cap(i+1).toInt(); + if (number1 < number2) + return -1; + if (number1 > number2) + return 1; + } + return 0; +} + +/*! + \fn bool PluginSpecPrivate::resolveDependencies(const QSet<PluginSpec *> &specs) + \internal +*/ +bool PluginSpecPrivate::resolveDependencies(const QSet<PluginSpec *> &specs) +{ + if (hasError) + return false; + if (state == PluginSpec::Resolved) + state = PluginSpec::Read; // Go back, so we just re-resolve the dependencies. + if (state != PluginSpec::Read) { + errorString = QCoreApplication::translate("PluginSpec", "Resolving dependencies failed because state != Read"); + hasError = true; + return false; + } + QList<PluginSpec *> resolvedDependencies; + foreach (const PluginDependency &dependency, dependencies) { + PluginSpec *found = 0; + foreach (PluginSpec *spec, specs) { + if (spec->provides(dependency.name, dependency.version)) { + found = spec; + break; + } + } + if (!found) { + hasError = true; + if (!errorString.isEmpty()) + errorString.append("\n"); + errorString.append(QCoreApplication::translate("PluginSpec", "Could not resolve dependency '%1(%2)'") + .arg(dependency.name).arg(dependency.version)); + continue; + } + resolvedDependencies.append(found); + } + if (hasError) + return false; + dependencySpecs = resolvedDependencies; + state = PluginSpec::Resolved; + return true; +} + +/*! + \fn bool PluginSpecPrivate::loadLibrary() + \internal +*/ +bool PluginSpecPrivate::loadLibrary() +{ + if (hasError) + return false; + if (state != PluginSpec::Resolved) { + if (state == PluginSpec::Loaded) + return true; + errorString = QCoreApplication::translate("PluginSpec", "Loading the library failed because state != Resolved"); + hasError = true; + return false; + } +#ifdef QT_NO_DEBUG + +#ifdef Q_OS_WIN + QString libName = QString("%1/%2.dll").arg(location).arg(name); +#elif defined(Q_OS_MAC) + QString libName = QString("%1/lib%2.dylib").arg(location).arg(name); +#else + QString libName = QString("%1/lib%2.so").arg(location).arg(name); +#endif + +#else //Q_NO_DEBUG + +#ifdef Q_OS_WIN + QString libName = QString("%1/%2d.dll").arg(location).arg(name); +#elif defined(Q_OS_MAC) + QString libName = QString("%1/lib%2_debug.dylib").arg(location).arg(name); +#else + QString libName = QString("%1/lib%2.so").arg(location).arg(name); +#endif + +#endif + + QPluginLoader loader(libName); + if (!loader.load()) { + hasError = true; + errorString = loader.errorString(); + errorString.append(QCoreApplication::translate("PluginSpec", "\nLibrary base name: %1").arg(libName)); + return false; + } + IPlugin *pluginObject = qobject_cast<IPlugin*>(loader.instance()); + if (!pluginObject) { + hasError = true; + errorString = QCoreApplication::translate("PluginSpec", "Plugin is not valid (doesn't derive from IPlugin)"); + loader.unload(); + return false; + } + state = PluginSpec::Loaded; + plugin = pluginObject; + plugin->d->pluginSpec = q; + return true; +} + +/*! + \fn bool PluginSpecPrivate::initializePlugin() + \internal +*/ +bool PluginSpecPrivate::initializePlugin() +{ + if (hasError) + return false; + if (state != PluginSpec::Loaded) { + if (state == PluginSpec::Initialized) + return true; + errorString = QCoreApplication::translate("PluginSpec", "Initializing the plugin failed because state != Loaded"); + hasError = true; + return false; + } + if (!plugin) { + errorString = QCoreApplication::translate("PluginSpec", "Internal error: have no plugin instance to initialize"); + hasError = true; + return false; + } + QString err; + if (!plugin->initialize(arguments, &err)) { + errorString = QCoreApplication::translate("PluginSpec", "Plugin initialization failed: %1").arg(err); + hasError = true; + return false; + } + state = PluginSpec::Initialized; + return true; +} + +/*! + \fn bool PluginSpecPrivate::initializeExtensions() + \internal +*/ +bool PluginSpecPrivate::initializeExtensions() +{ + if (hasError) + return false; + if (state != PluginSpec::Initialized) { + if (state == PluginSpec::Running) + return true; + errorString = QCoreApplication::translate("PluginSpec", "Cannot perform extensionsInitialized because state != Initialized"); + hasError = true; + return false; + } + if (!plugin) { + errorString = QCoreApplication::translate("PluginSpec", "Internal error: have no plugin instance to perform extensionsInitialized"); + hasError = true; + return false; + } + plugin->extensionsInitialized(); + state = PluginSpec::Running; + return true; +} + +/*! + \fn bool PluginSpecPrivate::stop() + \internal +*/ +void PluginSpecPrivate::stop() +{ + if (!plugin) + return; + plugin->shutdown(); + state = PluginSpec::Stopped; +} + +/*! + \fn bool PluginSpecPrivate::kill() + \internal +*/ +void PluginSpecPrivate::kill() +{ + if (!plugin) + return; + delete plugin; + plugin = 0; + state = PluginSpec::Deleted; +} + diff --git a/src/libs/extensionsystem/pluginspec.h b/src/libs/extensionsystem/pluginspec.h new file mode 100644 index 00000000000..06a219fe2d4 --- /dev/null +++ b/src/libs/extensionsystem/pluginspec.h @@ -0,0 +1,120 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGINSPEC_H +#define PLUGINSPEC_H + +#include "extensionsystem_global.h" + +#include <QtCore/QString> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE +class QStringList; +QT_END_NAMESPACE + +namespace ExtensionSystem { + +namespace Internal { + class PluginSpecPrivate; + class PluginManagerPrivate; +} + +class IPlugin; + +struct EXTENSIONSYSTEM_EXPORT PluginDependency +{ + QString name; + QString version; + bool operator==(const PluginDependency &other); +}; + +struct EXTENSIONSYSTEM_EXPORT PluginArgumentDescription +{ + QString name; + QString parameter; + QString description; +}; + +class EXTENSIONSYSTEM_EXPORT PluginSpec +{ +public: + enum State { Invalid, Read, Resolved, Loaded, Initialized, Running, Stopped, Deleted}; + + ~PluginSpec(); + + // information from the xml file, valid after 'Read' state is reached + QString name() const; + QString version() const; + QString compatVersion() const; + QString vendor() const; + QString copyright() const; + QString license() const; + QString description() const; + QString url() const; + QList<PluginDependency> dependencies() const; + + typedef QList<PluginArgumentDescription> PluginArgumentDescriptions; + PluginArgumentDescriptions argumentDescriptions() const; + + // other information, valid after 'Read' state is reached + QString location() const; + QString filePath() const; + + QStringList arguments() const; + void setArguments(const QStringList &arguments); + void addArgument(const QString &argument); + + bool provides(const QString &pluginName, const QString &version) const; + + // dependency specs, valid after 'Resolved' state is reached + QList<PluginSpec *> dependencySpecs() const; + + // linked plugin instance, valid after 'Loaded' state is reached + IPlugin *plugin() const; + + // state + State state() const; + bool hasError() const; + QString errorString() const; + +private: + PluginSpec(); + + Internal::PluginSpecPrivate *d; + friend class Internal::PluginManagerPrivate; +}; + +} // namespace ExtensionSystem + +#endif // PLUGINSPEC_H + diff --git a/src/libs/extensionsystem/pluginspec_p.h b/src/libs/extensionsystem/pluginspec_p.h new file mode 100644 index 00000000000..a11167e85e2 --- /dev/null +++ b/src/libs/extensionsystem/pluginspec_p.h @@ -0,0 +1,106 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGINSPEC_P_H +#define PLUGINSPEC_P_H + +#include "pluginspec.h" + +#include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtCore/QXmlStreamReader> + +namespace ExtensionSystem { + +class IPlugin; +class PluginManager; + +namespace Internal { + +class EXTENSIONSYSTEM_EXPORT PluginSpecPrivate : public QObject +{ + Q_OBJECT + +public: + PluginSpecPrivate(PluginSpec *spec); + + bool read(const QString &fileName); + bool provides(const QString &pluginName, const QString &version) const; + bool resolveDependencies(const QSet<PluginSpec *> &specs); + bool loadLibrary(); + bool initializePlugin(); + bool initializeExtensions(); + void stop(); + void kill(); + + QString name; + QString version; + QString compatVersion; + QString vendor; + QString copyright; + QString license; + QString description; + QString url; + QList<PluginDependency> dependencies; + + QString location; + QString filePath; + QStringList arguments; + + QList<PluginSpec *> dependencySpecs; + PluginSpec::PluginArgumentDescriptions argumentDescriptions; + IPlugin *plugin; + + PluginSpec::State state; + bool hasError; + QString errorString; + + static bool isValidVersion(const QString &version); + static int versionCompare(const QString &version1, const QString &version2); + +private: + PluginSpec *q; + + bool reportError(const QString &err); + void readPluginSpec(QXmlStreamReader &reader); + void readDependencies(QXmlStreamReader &reader); + void readDependencyEntry(QXmlStreamReader &reader); + void readArgumentDescriptions(QXmlStreamReader &reader); + void readArgumentDescription(QXmlStreamReader &reader); + + static QRegExp &versionRegExp(); +}; + +} // namespace Internal +} // namespace ExtensionSystem + +#endif // header guard diff --git a/src/libs/extensionsystem/pluginview.cpp b/src/libs/extensionsystem/pluginview.cpp new file mode 100644 index 00000000000..d75911ea37d --- /dev/null +++ b/src/libs/extensionsystem/pluginview.cpp @@ -0,0 +1,159 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "pluginview.h" +#include "pluginview_p.h" +#include "pluginmanager.h" +#include "pluginspec.h" +#include "ui_pluginview.h" + +#include <QtGui/QHeaderView> +#include <QtGui/QTreeWidgetItem> +#include <QtDebug> + +/*! + \class ExtensionSystem::PluginView + \brief Widget that shows a list of all plugins and their state. + + This can be embedded e.g. in a dialog in the application that + uses the plugin manager. + The class also provides notifications for interactions with the list. + + \sa ExtensionSystem::PluginDetailsView + \sa ExtensionSystem::PluginErrorView +*/ + +/*! + \fn void PluginView::currentPluginChanged(ExtensionSystem::PluginSpec *spec) + The current selection in the plugin list has changed to the + plugin corresponding to \a spec. +*/ + +/*! + \fn void PluginView::pluginActivated(ExtensionSystem::PluginSpec *spec) + The plugin list entry corresponding to \a spec has been activated, + e.g. by a double-click. +*/ + +using namespace ExtensionSystem; + +Q_DECLARE_METATYPE(ExtensionSystem::PluginSpec*); + +/*! + \fn PluginView::PluginView(PluginManager *manager, QWidget *parent) + Constructs a PluginView that gets the list of plugins from the + given plugin \a manager with a given \a parent widget. +*/ +PluginView::PluginView(PluginManager *manager, QWidget *parent) + : QWidget(parent), + m_ui(new Internal::Ui::PluginView), + p(new Internal::PluginViewPrivate) +{ + m_ui->setupUi(this); + QHeaderView *header = m_ui->pluginWidget->header(); + header->setResizeMode(0, QHeaderView::ResizeToContents); + header->setResizeMode(1, QHeaderView::ResizeToContents); + header->setResizeMode(2, QHeaderView::ResizeToContents); + m_ui->pluginWidget->sortItems(1, Qt::AscendingOrder); + p->manager = manager; + connect(p->manager, SIGNAL(pluginsChanged()), this, SLOT(updateList())); + connect(m_ui->pluginWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), + this, SLOT(selectPlugin(QTreeWidgetItem*))); + connect(m_ui->pluginWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), + this, SLOT(activatePlugin(QTreeWidgetItem*))); + updateList(); +} + +/*! + \fn PluginView::~PluginView() + \internal +*/ +PluginView::~PluginView() +{ + delete p; + delete m_ui; +} + +/*! + \fn PluginSpec *PluginView::currentPlugin() const + Returns the current selection in the list of plugins. +*/ +PluginSpec *PluginView::currentPlugin() const +{ + if (!m_ui->pluginWidget->currentItem()) + return 0; + return m_ui->pluginWidget->currentItem()->data(0, Qt::UserRole).value<PluginSpec *>(); +} + +void PluginView::updateList() +{ + static QIcon okIcon(":/extensionsystem/images/ok.png"); + static QIcon errorIcon(":/extensionsystem/images/error.png"); + QList<QTreeWidgetItem *> items; + QTreeWidgetItem *currentItem = 0; + PluginSpec *currPlugin = currentPlugin(); + foreach (PluginSpec *spec, p->manager->plugins()) { + QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() + << "" + << spec->name() + << QString("%1 (%2)").arg(spec->version()).arg(spec->compatVersion()) + << spec->vendor() + << spec->filePath()); + item->setToolTip(4, spec->filePath()); + item->setIcon(0, spec->hasError() ? errorIcon : okIcon); + item->setData(0, Qt::UserRole, qVariantFromValue(spec)); + items.append(item); + if (currPlugin == spec) + currentItem = item; + } + m_ui->pluginWidget->clear(); + if (!items.isEmpty()) + m_ui->pluginWidget->addTopLevelItems(items); + if (currentItem) + m_ui->pluginWidget->setCurrentItem(currentItem); + else if (!items.isEmpty()) + m_ui->pluginWidget->setCurrentItem(items.first()); +} + +void PluginView::selectPlugin(QTreeWidgetItem *current) +{ + if (!current) + emit currentPluginChanged(0); + else + emit currentPluginChanged(current->data(0, Qt::UserRole).value<PluginSpec *>()); +} + +void PluginView::activatePlugin(QTreeWidgetItem *item) +{ + emit pluginActivated(item->data(0, Qt::UserRole).value<PluginSpec *>()); +} + diff --git a/src/libs/extensionsystem/pluginview.h b/src/libs/extensionsystem/pluginview.h new file mode 100644 index 00000000000..eacf38edaa5 --- /dev/null +++ b/src/libs/extensionsystem/pluginview.h @@ -0,0 +1,82 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGINVIEW_H +#define PLUGINVIEW_H + +#include "extensionsystem_global.h" + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE +class QTreeWidgetItem; +QT_END_NAMESPACE + +namespace ExtensionSystem { + +class PluginManager; +class PluginSpec; + +namespace Internal { + class PluginViewPrivate; +namespace Ui { + class PluginView; +} // namespace Ui +} // namespace Internal + +class EXTENSIONSYSTEM_EXPORT PluginView : public QWidget +{ + Q_OBJECT + +public: + PluginView(PluginManager *manager, QWidget *parent = 0); + ~PluginView(); + + PluginSpec *currentPlugin() const; + +signals: + void currentPluginChanged(ExtensionSystem::PluginSpec *spec); + void pluginActivated(ExtensionSystem::PluginSpec *spec); + +private slots: + void updateList(); + void selectPlugin(QTreeWidgetItem *current); + void activatePlugin(QTreeWidgetItem *item); + +private: + Internal::Ui::PluginView *m_ui; + Internal::PluginViewPrivate *p; +}; + +} // namespae ExtensionSystem + +#endif diff --git a/src/libs/extensionsystem/pluginview.qrc b/src/libs/extensionsystem/pluginview.qrc new file mode 100644 index 00000000000..7b78566568f --- /dev/null +++ b/src/libs/extensionsystem/pluginview.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/extensionsystem" > + <file>images/ok.png</file> + <file>images/error.png</file> + </qresource> +</RCC> diff --git a/src/libs/extensionsystem/pluginview.ui b/src/libs/extensionsystem/pluginview.ui new file mode 100644 index 00000000000..8d9123dfbf4 --- /dev/null +++ b/src/libs/extensionsystem/pluginview.ui @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ExtensionSystem::Internal::PluginView</class> + <widget class="QWidget" name="ExtensionSystem::Internal::PluginView"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>773</width> + <height>304</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <property name="margin"> + <number>2</number> + </property> + <item row="0" column="0"> + <widget class="QTreeWidget" name="pluginWidget"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="indentation"> + <number>0</number> + </property> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <property name="uniformRowHeights"> + <bool>true</bool> + </property> + <property name="itemsExpandable"> + <bool>false</bool> + </property> + <property name="sortingEnabled"> + <bool>true</bool> + </property> + <property name="columnCount"> + <number>5</number> + </property> + <column> + <property name="text"> + <string>State</string> + </property> + </column> + <column> + <property name="text"> + <string>Name</string> + </property> + </column> + <column> + <property name="text"> + <string>Version</string> + </property> + </column> + <column> + <property name="text"> + <string>Vendor</string> + </property> + </column> + <column> + <property name="text"> + <string>Location</string> + </property> + </column> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/libs/extensionsystem/pluginview_p.h b/src/libs/extensionsystem/pluginview_p.h new file mode 100644 index 00000000000..48d63ba3593 --- /dev/null +++ b/src/libs/extensionsystem/pluginview_p.h @@ -0,0 +1,51 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGINVIEW_P_H +#define PLUGINVIEW_P_H + +namespace ExtensionSystem { + +class PluginManager; + +namespace Internal { + +class PluginViewPrivate +{ +public: + PluginManager *manager; +}; + +} // namespace +} // namespace + +#endif diff --git a/src/libs/extensionsystem/test/auto/auto.pro b/src/libs/extensionsystem/test/auto/auto.pro new file mode 100644 index 00000000000..2dd64002127 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/auto.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = pluginmanager pluginspec + diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/circularplugins.pro b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/circularplugins.pro new file mode 100644 index 00000000000..21f257cf083 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/circularplugins.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = plugin1 plugin2 plugin3 diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin1/plugin.xml b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin1/plugin.xml new file mode 100644 index 00000000000..db201f34c39 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin1/plugin.xml @@ -0,0 +1,6 @@ +<plugin name="plugin1" version="1.0.0" compatVersion="1.0.0"> + <dependencyList> + <dependency name="plugin2" version="1.0.0"/> + <dependency name="plugin3" version="1.0.0"/> + </dependencyList> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin1/plugin1.cpp b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin1/plugin1.cpp new file mode 100644 index 00000000000..69b9821b3b1 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin1/plugin1.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "plugin1.h" + +#include <QtCore/qplugin.h> + +using namespace Plugin1; + +MyPlugin1::MyPlugin1() +{ +} + +bool MyPlugin1::initialize(const QStringList & /*arguments*/, QString *errorString) +{ + return true; +} + +void MyPlugin1::extensionsInitialized() +{ +} + +Q_EXPORT_PLUGIN(MyPlugin1) + diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin1/plugin1.h b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin1/plugin1.h new file mode 100644 index 00000000000..d3c493aebaa --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin1/plugin1.h @@ -0,0 +1,55 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGIN1_H +#define PLUGIN1_H + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> + +namespace Plugin1 { + +class MyPlugin1 : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + MyPlugin1(); + + bool initialize(const QStringList &arguments, QString *errorString); + void extensionsInitialized(); +}; + +} // namespace + +#endif // header guard diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin1/plugin1.pro b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin1/plugin1.pro new file mode 100644 index 00000000000..4181188287f --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin1/plugin1.pro @@ -0,0 +1,8 @@ +TEMPLATE = lib +TARGET = plugin1 + +SOURCES += plugin1.cpp +HEADERS += plugin1.h + +include(../../../../extensionsystem_test.pri) + diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin2/plugin.xml b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin2/plugin.xml new file mode 100644 index 00000000000..5436967a80b --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin2/plugin.xml @@ -0,0 +1,2 @@ +<plugin name="plugin2" version="1.0.0" compatVersion="1.0.0"> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin2/plugin2.cpp b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin2/plugin2.cpp new file mode 100644 index 00000000000..c5af5f39a35 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin2/plugin2.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "plugin2.h" + +#include <QtCore/qplugin.h> + +using namespace Plugin2; + +MyPlugin2::MyPlugin2() +{ +} + +bool MyPlugin2::initialize(const QStringList &, QString *) +{ + return true; +} + +void MyPlugin2::extensionsInitialized() +{ +} + +Q_EXPORT_PLUGIN(MyPlugin2) + diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin2/plugin2.h b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin2/plugin2.h new file mode 100644 index 00000000000..b1cd95c7627 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin2/plugin2.h @@ -0,0 +1,55 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGIN2_H +#define PLUGIN2_H + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> + +namespace Plugin2 { + +class MyPlugin2 : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + MyPlugin2(); + + bool initialize(const QStringList &arguments, QString *errorString); + void extensionsInitialized(); +}; + +} // namespace + +#endif // header guard diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin2/plugin2.pro b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin2/plugin2.pro new file mode 100644 index 00000000000..58798b54f14 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin2/plugin2.pro @@ -0,0 +1,8 @@ +TEMPLATE = lib +TARGET = plugin2 + +SOURCES += plugin2.cpp +HEADERS += plugin2.h + +include(../../../../extensionsystem_test.pri) + diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin3/plugin.xml b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin3/plugin.xml new file mode 100644 index 00000000000..f7e90978ba6 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin3/plugin.xml @@ -0,0 +1,5 @@ +<plugin name="plugin3" version="1.0.0" compatVersion="1.0.0"> + <dependencyList> + <dependency name="plugin1" version="1.0.0"/> + </dependencyList> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin3/plugin3.cpp b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin3/plugin3.cpp new file mode 100644 index 00000000000..9b38565c35c --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin3/plugin3.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "plugin3.h" + +#include <QtCore/qplugin.h> + +using namespace Plugin3; + +MyPlugin3::MyPlugin3() +{ +} + +bool MyPlugin3::initialize(const QStringList &, QString *) +{ + return true; +} + +void MyPlugin3::extensionsInitialized() +{ +} + +Q_EXPORT_PLUGIN(MyPlugin3) + diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin3/plugin3.h b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin3/plugin3.h new file mode 100644 index 00000000000..7514d63bd71 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin3/plugin3.h @@ -0,0 +1,55 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGIN3_H +#define PLUGIN3_H + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> + +namespace Plugin3 { + +class MyPlugin3 : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + MyPlugin3(); + + bool initialize(const QStringList &arguments, QString *errorString); + void extensionsInitialized(); +}; + +} // namespace + +#endif // header guard diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin3/plugin3.pro b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin3/plugin3.pro new file mode 100644 index 00000000000..f601f06162c --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/circularplugins/plugin3/plugin3.pro @@ -0,0 +1,8 @@ +TEMPLATE = lib +TARGET = plugin3 + +SOURCES += plugin3.cpp +HEADERS += plugin3.h + +include(../../../../extensionsystem_test.pri) + diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/correctplugins1.pro b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/correctplugins1.pro new file mode 100644 index 00000000000..f0d76950e86 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/correctplugins1.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS = plugin2 plugin3 plugin1 diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin1/plugin.spec b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin1/plugin.spec new file mode 100644 index 00000000000..db201f34c39 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin1/plugin.spec @@ -0,0 +1,6 @@ +<plugin name="plugin1" version="1.0.0" compatVersion="1.0.0"> + <dependencyList> + <dependency name="plugin2" version="1.0.0"/> + <dependency name="plugin3" version="1.0.0"/> + </dependencyList> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin1/plugin1.cpp b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin1/plugin1.cpp new file mode 100644 index 00000000000..8149d046d21 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin1/plugin1.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "plugin1.h" + +#include <extensionsystem/pluginmanager.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QObject> + +using namespace Plugin1; + +MyPlugin1::MyPlugin1() + : initializeCalled(false) +{ +} + +bool MyPlugin1::initialize(const QStringList & /*arguments*/, QString *errorString) +{ + initializeCalled = true; + QObject *obj = new QObject; + obj->setObjectName("MyPlugin1"); + addAutoReleasedObject(obj); + + bool found2 = false; + bool found3 = false; + foreach (QObject *object, ExtensionSystem::PluginManager::instance()->allObjects()) { + if (object->objectName() == "MyPlugin2") + found2 = true; + else if (object->objectName() == "MyPlugin3") + found3 = true; + } + if (found2 && found3) + return true; + if (errorString) { + QString error = "object(s) missing from plugin(s):"; + if (!found2) + error.append(" plugin2"); + if (!found3) + error.append(" plugin3"); + *errorString = error; + } + return false; +} + +void MyPlugin1::extensionsInitialized() +{ + if (!initializeCalled) + return; + // don't do this at home, it's just done here for the test + QObject *obj = new QObject; + obj->setObjectName("MyPlugin1_running"); + addAutoReleasedObject(obj); +} + +Q_EXPORT_PLUGIN(MyPlugin1) + diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin1/plugin1.h b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin1/plugin1.h new file mode 100644 index 00000000000..4be9f9bbf7f --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin1/plugin1.h @@ -0,0 +1,58 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGIN1_H +#define PLUGIN1_H + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> + +namespace Plugin1 { + +class MyPlugin1 : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + MyPlugin1(); + + bool initialize(const QStringList &arguments, QString *errorString); + void extensionsInitialized(); + +private: + bool initializeCalled; +}; + +} // namespace + +#endif // header guard diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin1/plugin1.pro b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin1/plugin1.pro new file mode 100644 index 00000000000..9101770f9ac --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin1/plugin1.pro @@ -0,0 +1,15 @@ +TEMPLATE = lib +TARGET = plugin1 + +SOURCES += plugin1.cpp +HEADERS += plugin1.h + +include(../../../../extensionsystem_test.pri) + +LIBS += -L$${PWD}/../plugin2 -L$${PWD}/../plugin3 -lplugin2 -lplugin3 + +macx { +} else:unix { + QMAKE_RPATHDIR += $${PWD}/../plugin2 + QMAKE_RPATHDIR += $${PWD}/../plugin3 +} diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin2/plugin.spec b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin2/plugin.spec new file mode 100644 index 00000000000..5436967a80b --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin2/plugin.spec @@ -0,0 +1,2 @@ +<plugin name="plugin2" version="1.0.0" compatVersion="1.0.0"> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin2/plugin2.cpp b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin2/plugin2.cpp new file mode 100644 index 00000000000..99f192766a4 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin2/plugin2.cpp @@ -0,0 +1,68 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "plugin2.h" + +#include <extensionsystem/pluginmanager.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QObject> + +using namespace Plugin2; + +MyPlugin2::MyPlugin2() + : initializeCalled(false) +{ +} + +bool MyPlugin2::initialize(const QStringList &, QString *) +{ + initializeCalled = true; + QObject *obj = new QObject; + obj->setObjectName("MyPlugin2"); + addAutoReleasedObject(obj); + + return true; +} + +void MyPlugin2::extensionsInitialized() +{ + if (!initializeCalled) + return; + // don't do this at home, it's just done here for the test + QObject *obj = new QObject; + obj->setObjectName("MyPlugin2_running"); + addAutoReleasedObject(obj); +} + +Q_EXPORT_PLUGIN(MyPlugin2) + diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin2/plugin2.h b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin2/plugin2.h new file mode 100644 index 00000000000..08d77a38c70 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin2/plugin2.h @@ -0,0 +1,58 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGIN2_H +#define PLUGIN2_H + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> + +namespace Plugin2 { + +class MyPlugin2 : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + MyPlugin2(); + + bool initialize(const QStringList &arguments, QString *errorString); + void extensionsInitialized(); + +private: + bool initializeCalled; +}; + +} // namespace + +#endif // header guard diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin2/plugin2.pro b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin2/plugin2.pro new file mode 100644 index 00000000000..a80f4a5c765 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin2/plugin2.pro @@ -0,0 +1,12 @@ +TEMPLATE = lib +TARGET = plugin2 + +SOURCES += plugin2.cpp +HEADERS += plugin2.h + +include(../../../../extensionsystem_test.pri) + +macx { + QMAKE_LFLAGS_SONAME = -Wl,-install_name,$${PWD}/ +} + diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin3/plugin.spec b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin3/plugin.spec new file mode 100644 index 00000000000..234bf56ea2d --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin3/plugin.spec @@ -0,0 +1,5 @@ +<plugin name="plugin3" version="1.0.0" compatVersion="1.0.0"> + <dependencyList> + <dependency name="plugin2" version="1.0.0"/> + </dependencyList> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin3/plugin3.cpp b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin3/plugin3.cpp new file mode 100644 index 00000000000..4410c345113 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin3/plugin3.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "plugin3.h" + +#include <extensionsystem/pluginmanager.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QObject> + +using namespace Plugin3; + +MyPlugin3::MyPlugin3() + : initializeCalled(false) +{ +} + +bool MyPlugin3::initialize(const QStringList & /*arguments*/, QString *errorString) +{ + initializeCalled = true; + QObject *obj = new QObject; + obj->setObjectName("MyPlugin3"); + addAutoReleasedObject(obj); + + bool found2 = false; + foreach (QObject *object, ExtensionSystem::PluginManager::instance()->allObjects()) { + if (object->objectName() == "MyPlugin2") + found2 = true; + } + if (found2) + return true; + if (errorString) + *errorString = "object from plugin2 could not be found"; + return false; +} + +void MyPlugin3::extensionsInitialized() +{ + if (!initializeCalled) + return; + // don't do this at home, it's just done here for the test + QObject *obj = new QObject; + obj->setObjectName("MyPlugin3_running"); + addAutoReleasedObject(obj); +} + +Q_EXPORT_PLUGIN(MyPlugin3) + diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin3/plugin3.h b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin3/plugin3.h new file mode 100644 index 00000000000..0991b7a1526 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin3/plugin3.h @@ -0,0 +1,58 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGIN3_H +#define PLUGIN3_H + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> + +namespace Plugin3 { + +class MyPlugin3 : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + MyPlugin3(); + + bool initialize(const QStringList &arguments, QString *errorString); + void extensionsInitialized(); + +private: + bool initializeCalled; +}; + +} // namespace + +#endif // header guard diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin3/plugin3.pro b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin3/plugin3.pro new file mode 100644 index 00000000000..c5ff581b1ba --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/correctplugins1/plugin3/plugin3.pro @@ -0,0 +1,15 @@ +TEMPLATE = lib +TARGET = plugin3 + +SOURCES += plugin3.cpp +HEADERS += plugin3.h + +include(../../../../extensionsystem_test.pri) + +LIBS += -L$${PWD}/../plugin2 -lplugin2 + +macx { + QMAKE_LFLAGS_SONAME = -Wl,-install_name,$${PWD}/ +} else:unix { + QMAKE_RPATHDIR += $${PWD}/../plugin2 +} diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/pluginmanager.pro b/src/libs/extensionsystem/test/auto/pluginmanager/pluginmanager.pro new file mode 100644 index 00000000000..57b026f5fb8 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/pluginmanager.pro @@ -0,0 +1,6 @@ +TEMPLATE = subdirs + +SUBDIRS = test.pro \ + circularplugins \ + correctplugins1 + diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/plugins/myplug/myplug.xml b/src/libs/extensionsystem/test/auto/pluginmanager/plugins/myplug/myplug.xml new file mode 100644 index 00000000000..c79b29780d1 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/plugins/myplug/myplug.xml @@ -0,0 +1,2 @@ +<plugin name="dummyPlugin" version="1.1.1" compatVersion="1.0.0"> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/plugins/otherplugin.xml b/src/libs/extensionsystem/test/auto/pluginmanager/plugins/otherplugin.xml new file mode 100644 index 00000000000..6f0483f0f82 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/plugins/otherplugin.xml @@ -0,0 +1,2 @@ +<plugin name="helloworld" version="1.0.0"> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/plugins/plugin1.xml b/src/libs/extensionsystem/test/auto/pluginmanager/plugins/plugin1.xml new file mode 100644 index 00000000000..6bd573957b7 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/plugins/plugin1.xml @@ -0,0 +1,2 @@ +<plugin name="MyPlugin" version="2.2.3_9" compatVersion="2.0.0"> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/test.pro b/src/libs/extensionsystem/test/auto/pluginmanager/test.pro new file mode 100644 index 00000000000..c5934b1331c --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/test.pro @@ -0,0 +1,14 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Fr Jul 27 23:12:52 2007 +###################################################################### + +CONFIG += qtestlib +TEMPLATE = app +CONFIG -= app_bundle + +# Input + +include(../../extensionsystem_test.pri) + +SOURCES += tst_pluginmanager.cpp + diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/test.sh b/src/libs/extensionsystem/test/auto/pluginmanager/test.sh new file mode 100755 index 00000000000..426901ea74d --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/test.sh @@ -0,0 +1,5 @@ +# -- run the plugin test from this directory. + +export LD_LIBRARY_PATH=../../../../../../lib:$LD_LIBRARY_PATH +export DYLD_LIBRARY_PATH=../../../../../../bin/QtCreator.app/Contents/PlugIns:$DYLD_LIBRARY_PATH # mac +exec ./test diff --git a/src/libs/extensionsystem/test/auto/pluginmanager/tst_pluginmanager.cpp b/src/libs/extensionsystem/test/auto/pluginmanager/tst_pluginmanager.cpp new file mode 100644 index 00000000000..f54b978e9af --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginmanager/tst_pluginmanager.cpp @@ -0,0 +1,267 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtTest/QtTest> + +#include <QtCore/QObject> + +#include <extensionsystem/pluginmanager.h> +#include <extensionsystem/pluginspec.h> +#include <extensionsystem/iplugin.h> + +using namespace ExtensionSystem; + +class SignalReceiver; + +class tst_PluginManager : public QObject +{ + Q_OBJECT + +private slots: + void init(); + void cleanup(); + void addRemoveObjects(); + void getObject(); + void getObjects(); + void plugins(); + void circularPlugins(); + void correctPlugins1(); + +private: + PluginManager *m_pm; + SignalReceiver *m_sr; +}; + +class SignalReceiver : public QObject +{ + Q_OBJECT + +public: + SignalReceiver() : + objectAddedCount(0), + aboutToRemoveObjectCount(0), + pluginsChangedCount(0), + objectAddedObj(0), + aboutToRemoveObjectObj(0) + { } + int objectAddedCount; + int aboutToRemoveObjectCount; + int pluginsChangedCount; + QObject *objectAddedObj; + QObject *aboutToRemoveObjectObj; +public slots: + void objectAdded(QObject *obj) { objectAddedCount++; objectAddedObj = obj; } + void aboutToRemoveObject(QObject *obj) { aboutToRemoveObjectCount++; aboutToRemoveObjectObj = obj; } + void pluginsChanged() { pluginsChangedCount++; } +}; + +class MyClass1 : public QObject +{ + Q_OBJECT +}; + +class MyClass2 : public QObject +{ + Q_OBJECT +}; + +class MyClass11 : public MyClass1 +{ + Q_OBJECT +}; + +void tst_PluginManager::init() +{ + m_pm = new PluginManager; + m_sr = new SignalReceiver; + connect(m_pm, SIGNAL(objectAdded(QObject*)), m_sr, SLOT(objectAdded(QObject*))); + connect(m_pm, SIGNAL(aboutToRemoveObject(QObject*)), m_sr, SLOT(aboutToRemoveObject(QObject*))); + connect(m_pm, SIGNAL(pluginsChanged()), m_sr, SLOT(pluginsChanged())); +} + +void tst_PluginManager::cleanup() +{ + delete m_pm; + delete m_sr; +} + +void tst_PluginManager::addRemoveObjects() +{ + QObject *object1 = new QObject; + QObject *object2 = new QObject; + QCOMPARE(m_pm->allObjects().size(), 0); + m_pm->addObject(object1); + QCOMPARE(m_sr->objectAddedCount, 1); + QCOMPARE(m_sr->objectAddedObj, object1); + QCOMPARE(m_sr->aboutToRemoveObjectCount, 0); + QVERIFY(m_pm->allObjects().contains(object1)); + QVERIFY(!m_pm->allObjects().contains(object2)); + QCOMPARE(m_pm->allObjects().size(), 1); + m_pm->addObject(object2); + QCOMPARE(m_sr->objectAddedCount, 2); + QCOMPARE(m_sr->objectAddedObj, object2); + QCOMPARE(m_sr->aboutToRemoveObjectCount, 0); + QVERIFY(m_pm->allObjects().contains(object1)); + QVERIFY(m_pm->allObjects().contains(object2)); + QCOMPARE(m_pm->allObjects().size(), 2); + m_pm->removeObject(object1); + QCOMPARE(m_sr->objectAddedCount, 2); + QCOMPARE(m_sr->aboutToRemoveObjectCount, 1); + QCOMPARE(m_sr->aboutToRemoveObjectObj, object1); + QVERIFY(!m_pm->allObjects().contains(object1)); + QVERIFY(m_pm->allObjects().contains(object2)); + QCOMPARE(m_pm->allObjects().size(), 1); + m_pm->removeObject(object2); + QCOMPARE(m_sr->objectAddedCount, 2); + QCOMPARE(m_sr->aboutToRemoveObjectCount, 2); + QCOMPARE(m_sr->aboutToRemoveObjectObj, object2); + QVERIFY(!m_pm->allObjects().contains(object1)); + QVERIFY(!m_pm->allObjects().contains(object2)); + QCOMPARE(m_pm->allObjects().size(), 0); + delete object1; + delete object2; +} + +void tst_PluginManager::getObject() +{ + MyClass2 *object2 = new MyClass2; + MyClass11 *object11 = new MyClass11; + m_pm->addObject(object2); + QCOMPARE(m_pm->getObject<MyClass11>(), (MyClass11*)0); + QCOMPARE(m_pm->getObject<MyClass1>(), (MyClass1*)0); + QCOMPARE(m_pm->getObject<MyClass2>(), object2); + m_pm->addObject(object11); + QCOMPARE(m_pm->getObject<MyClass11>(), object11); + QCOMPARE(m_pm->getObject<MyClass1>(), qobject_cast<MyClass1*>(object11)); + QCOMPARE(m_pm->getObject<MyClass2>(), object2); + m_pm->removeObject(object2); + m_pm->removeObject(object11); + delete object2; + delete object11; +} + +void tst_PluginManager::getObjects() +{ + MyClass1 *object1 = new MyClass1; + MyClass2 *object2 = new MyClass2; + MyClass11 *object11 = new MyClass11; + m_pm->addObject(object2); + QCOMPARE(m_pm->getObjects<MyClass11>(), QList<MyClass11*>()); + QCOMPARE(m_pm->getObjects<MyClass1>(), QList<MyClass1*>()); + QCOMPARE(m_pm->getObjects<MyClass2>(), QList<MyClass2*>() << object2); + QCOMPARE(m_pm->allObjects(), QList<QObject*>() << object2); + m_pm->addObject(object11); + QCOMPARE(m_pm->getObjects<MyClass11>(), QList<MyClass11*>() << object11); + QCOMPARE(m_pm->getObjects<MyClass1>(), QList<MyClass1*>() << object11); + QCOMPARE(m_pm->getObjects<MyClass2>(), QList<MyClass2*>() << object2); + QCOMPARE(m_pm->allObjects(), QList<QObject*>() << object2 << object11); + m_pm->addObject(object1); + QCOMPARE(m_pm->getObjects<MyClass11>(), QList<MyClass11*>() << object11); + QCOMPARE(m_pm->getObjects<MyClass1>(), QList<MyClass1*>() << object11 << object1); + QCOMPARE(m_pm->getObjects<MyClass2>(), QList<MyClass2*>() << object2); + QCOMPARE(m_pm->allObjects(), QList<QObject*>() << object2 << object11 << object1); + m_pm->removeObject(object2); + m_pm->removeObject(object11); + m_pm->removeObject(object1); + delete object1; + delete object2; + delete object11; +} + +void tst_PluginManager::plugins() +{ + m_pm->setPluginPaths(QStringList() << "plugins"); + QCOMPARE(m_sr->pluginsChangedCount, 1); + QSet<PluginSpec *> plugins = m_pm->plugins(); + QCOMPARE(plugins.count(), 3); + foreach (const QString &expected, QStringList() << "helloworld" << "MyPlugin" << "dummyPlugin") { + bool found = false; + foreach (PluginSpec *spec, plugins) { + if (spec->name() == expected) { + found = true; + break; + } + } + QVERIFY2(found, QString("plugin '%1' not found").arg(expected).toLocal8Bit().constData()); + } +} + +void tst_PluginManager::circularPlugins() +{ + m_pm->setPluginPaths(QStringList() << "circularplugins"); + m_pm->loadPlugins(); + foreach (PluginSpec *spec, m_pm->plugins()) { + if (spec->name() == "plugin1") { + QVERIFY(spec->hasError()); + QCOMPARE(spec->state(), PluginSpec::Resolved); + QCOMPARE(spec->plugin(), (IPlugin*)0); + } else if (spec->name() == "plugin2") { + QVERIFY(!spec->hasError()); + QCOMPARE(spec->state(), PluginSpec::Running); + } else if (spec->name() == "plugin3") { + QVERIFY(spec->hasError()); + QCOMPARE(spec->state(), PluginSpec::Resolved); + QCOMPARE(spec->plugin(), (IPlugin*)0); + } + } +} + +void tst_PluginManager::correctPlugins1() +{ + m_pm->setFileExtension("spec"); + m_pm->setPluginPaths(QStringList() << "correctplugins1"); + m_pm->loadPlugins(); + foreach (PluginSpec *spec, m_pm->plugins()) { + if (spec->hasError()) + qDebug() << spec->errorString(); + QVERIFY(!spec->hasError()); + QCOMPARE(spec->state(), PluginSpec::Running); + } + bool plugin1running = false; + bool plugin2running = false; + bool plugin3running = false; + foreach (QObject *obj, m_pm->allObjects()) { + if (obj->objectName() == "MyPlugin1_running") + plugin1running = true; + else if (obj->objectName() == "MyPlugin2_running") + plugin2running = true; + else if (obj->objectName() == "MyPlugin3_running") + plugin3running = true; + } + QVERIFY(plugin1running); + QVERIFY(plugin2running); + QVERIFY(plugin3running); +} + +QTEST_MAIN(tst_PluginManager) +#include "tst_pluginmanager.moc" + diff --git a/src/libs/extensionsystem/test/auto/pluginspec/pluginspec.pro b/src/libs/extensionsystem/test/auto/pluginspec/pluginspec.pro new file mode 100644 index 00000000000..d4b941b2320 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/pluginspec.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +CONFIG += ordered + +SUBDIRS = testplugin test.pro diff --git a/src/libs/extensionsystem/test/auto/pluginspec/test.pro b/src/libs/extensionsystem/test/auto/pluginspec/test.pro new file mode 100644 index 00000000000..d8fda889176 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/test.pro @@ -0,0 +1,14 @@ +CONFIG += qtestlib +TEMPLATE = app +CONFIG -= app_bundle +DESTDIR = $${PWD} +# Input +SOURCES += tst_pluginspec.cpp + +include(../../extensionsystem_test.pri) + +LIBS += -L$${PWD}/testplugin -ltest +macx { +} else:unix { + QMAKE_RPATHDIR += $${PWD}/testplugin +} diff --git a/src/libs/extensionsystem/test/auto/pluginspec/test.sh b/src/libs/extensionsystem/test/auto/pluginspec/test.sh new file mode 100755 index 00000000000..426901ea74d --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/test.sh @@ -0,0 +1,5 @@ +# -- run the plugin test from this directory. + +export LD_LIBRARY_PATH=../../../../../../lib:$LD_LIBRARY_PATH +export DYLD_LIBRARY_PATH=../../../../../../bin/QtCreator.app/Contents/PlugIns:$DYLD_LIBRARY_PATH # mac +exec ./test diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec1.xml b/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec1.xml new file mode 100644 index 00000000000..137e1b494cf --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec1.xml @@ -0,0 +1,6 @@ +<plugin name="plugin1" version="1.0.1" compatVersion="1.0.0"> + <dependencyList> + <dependency name="plugin2" version="2.3.0_2"/> + <dependency name="plugin3" version="1.0.0"/> + </dependencyList> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec2.xml b/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec2.xml new file mode 100644 index 00000000000..451f854185f --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec2.xml @@ -0,0 +1,2 @@ +<plugin name="plugin2" version="2.4.1" compatVersion="2.3.0"> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec3.xml b/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec3.xml new file mode 100644 index 00000000000..9fa01a442f5 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec3.xml @@ -0,0 +1,2 @@ +<plugin name="plugin3" version="1.0.0" compatVersion="1.0.0"> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec4.xml b/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec4.xml new file mode 100644 index 00000000000..b06bab2fa0e --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec4.xml @@ -0,0 +1,5 @@ +<plugin name="plugin4" version="1.0.1" compatVersion="1.0.0"> + <dependencyList> + <dependency name="plugin5" version="2.3.0_2"/> + </dependencyList> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec5.xml b/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec5.xml new file mode 100644 index 00000000000..aab8f424c32 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testdependencies/spec5.xml @@ -0,0 +1,2 @@ +<plugin name="plugin5" version="1.0.1" compatVersion="1.0.0"> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testdir/spec.xml b/src/libs/extensionsystem/test/auto/pluginspec/testdir/spec.xml new file mode 100644 index 00000000000..6bd573957b7 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testdir/spec.xml @@ -0,0 +1,2 @@ +<plugin name="MyPlugin" version="2.2.3_9" compatVersion="2.0.0"> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin.cpp b/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin.cpp new file mode 100644 index 00000000000..8c3db6c1895 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin.cpp @@ -0,0 +1,56 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "testplugin.h" + +#include <QtCore/qplugin.h> + +using namespace MyPlugin; + +MyPluginImpl::MyPluginImpl() + : m_isInitialized(false), m_isExtensionsInitialized(false) +{ +} + +bool MyPluginImpl::initialize(const QStringList &, QString *) +{ + m_isInitialized = true; + return true; +} + +void MyPluginImpl::extensionsInitialized() +{ + m_isExtensionsInitialized = true; +} + +Q_EXPORT_PLUGIN(MyPluginImpl) + diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin.h b/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin.h new file mode 100644 index 00000000000..10fc0acf508 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin.h @@ -0,0 +1,62 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef TESTPLUGIN_H +#define TESTPLUGIN_H + +#include "testplugin_global.h" +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> + +namespace MyPlugin { + +class MYPLUGIN_EXPORT MyPluginImpl : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + MyPluginImpl(); + + bool initialize(const QStringList &arguments, QString *errorString); + void extensionsInitialized(); + + bool isInitialized() { return m_isInitialized; } + bool isExtensionsInitialized() { return m_isExtensionsInitialized; } +private: + bool m_isInitialized; + bool m_isExtensionsInitialized; +}; + +} // namespace + +#endif // header guard diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin.pro b/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin.pro new file mode 100644 index 00000000000..8b4d81a82ad --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin.pro @@ -0,0 +1,13 @@ +TEMPLATE = lib +TARGET = test +DESTDIR = $${PWD} +DEFINES += MYPLUGIN_LIBRARY +SOURCES += testplugin.cpp +HEADERS += testplugin.h testplugin_global.h + +include(../../../extensionsystem_test.pri) + +macx { + QMAKE_LFLAGS_SONAME = -Wl,-install_name,$${PWD}/ +} + diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin.xml b/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin.xml new file mode 100644 index 00000000000..f8ab3f7a39e --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin.xml @@ -0,0 +1,2 @@ +<plugin name="test" version="1.0.0"> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin_global.h b/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin_global.h new file mode 100644 index 00000000000..ae40ac8ea8b --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testplugin/testplugin_global.h @@ -0,0 +1,44 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef TESTPLUGIN_GLOBAL_H +#define TESTPLUGIN_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(MYPLUGIN_LIBRARY) +# define MYPLUGIN_EXPORT Q_DECL_EXPORT +#else +# define MYPLUGIN_EXPORT Q_DECL_IMPORT +#endif + +#endif // header diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testspecs/simplespec.xml b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/simplespec.xml new file mode 100644 index 00000000000..6bd573957b7 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/simplespec.xml @@ -0,0 +1,2 @@ +<plugin name="MyPlugin" version="2.2.3_9" compatVersion="2.0.0"> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec1.xml b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec1.xml new file mode 100644 index 00000000000..31cebf44146 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec1.xml @@ -0,0 +1,18 @@ +<plugin name="test" version="1.0.1" compatVersion="1.0.0"> + <vendor>Trolltech</vendor> + <copyright>(C) 2007 Trolltech ASA</copyright> + <license> +This is a default license bla +blubbblubb +end of terms + </license> + <description> +This plugin is just a test. + it demonstrates the great use of the plugin spec. + </description> + <url>http://www.trolltech.com</url> + <dependencyList> + <dependency name="SomeOtherPlugin" version="2.3.0_2"/> + <dependency name="EvenOther" version="1.0.0"/> + </dependencyList> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec2.xml b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec2.xml new file mode 100644 index 00000000000..454f58cb755 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec2.xml @@ -0,0 +1,2 @@ +<plugin name="test" version="3.1.4_10"> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong1.xml b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong1.xml new file mode 100644 index 00000000000..e6fe956c98e --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong1.xml @@ -0,0 +1,18 @@ +<something name="test" version="1.0.1" compatVersion="1.0.0"> + <vendor>Trolltech</vendor> + <copyright>(C) 2007 Trolltech ASA</copyright> + <license> +This is a default license bla +blubbblubb +end of terms + </license> + <description> +This plugin is just a test. + it demonstrates the great use of the plugin spec. + </description> + <url>http://www.trolltech.com</url> + <dependencyList> + <dependency name="SomeOtherPlugin" version="2.3.0_2"/> + <dependency name="EvenOther" version="1.0.0"/> + </dependencyList> +</something> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong2.xml b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong2.xml new file mode 100644 index 00000000000..200a1fd1ac1 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong2.xml @@ -0,0 +1,18 @@ +<plugin version="1.0.1" compatVersion="1.0.0"> + <vendor>Trolltech</vendor> + <copyright>(C) 2007 Trolltech ASA</copyright> + <license> +This is a default license bla +blubbblubb +end of terms + </license> + <description> +This plugin is just a test. + it demonstrates the great use of the plugin spec. + </description> + <url>http://www.trolltech.com</url> + <dependencyList> + <dependency name="SomeOtherPlugin" version="2.3.0_2"/> + <dependency name="EvenOther" version="1.0.0"/> + </dependencyList> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong3.xml b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong3.xml new file mode 100644 index 00000000000..13bbdb09a8d --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong3.xml @@ -0,0 +1,18 @@ +<plugin name="test" compatVersion="1.0.0"> + <vendor>Trolltech</vendor> + <copyright>(C) 2007 Trolltech ASA</copyright> + <license> +This is a default license bla +blubbblubb +end of terms + </license> + <description> +This plugin is just a test. + it demonstrates the great use of the plugin spec. + </description> + <url>http://www.trolltech.com</url> + <dependencyList> + <dependency name="SomeOtherPlugin" version="2.3.0_2"/> + <dependency name="EvenOther" version="1.0.0"/> + </dependencyList> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong4.xml b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong4.xml new file mode 100644 index 00000000000..b904f901387 --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong4.xml @@ -0,0 +1,18 @@ +<plugin name="test" version="1.0.1" compatVersion="1.0.0"> + <vendor>Trolltech</vendor> + <copyright>(C) 2007 Trolltech ASA</copyright> + <license> +This is a default license bla +blubbblubb +end of terms + </license> + <description> +This plugin is just a test. + it demonstrates the great use of the plugin spec. + </description> + <url>http://www.trolltech.com</url> + <dependencyList> + <dependency version="2.3.0_2"/> + <dependency name="EvenOther" version="1.0.0"/> + </dependencyList> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong5.xml b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong5.xml new file mode 100644 index 00000000000..1c110d1eeae --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/testspecs/spec_wrong5.xml @@ -0,0 +1,18 @@ +<plugin name="test" version="1.0.1" compatVersion="1.0.0"> + <vendor>Trolltech</vendor> + <copyright>(C) 2007 Trolltech ASA</copyright> + <license> +This is a default license bla +blubbblubb +end of terms + </license> + <description> +This plugin is just a test. + it demonstrates the great use of the plugin spec. + </description> + <url>http://www.trolltech.com</url> + <dependencyList> + <dependency name="SomeOtherPlugin" version="2.3aa.0_2"/> + <dependency name="EvenOther" version="1.0.0"/> + </dependencyList> +</plugin> diff --git a/src/libs/extensionsystem/test/auto/pluginspec/tst_pluginspec.cpp b/src/libs/extensionsystem/test/auto/pluginspec/tst_pluginspec.cpp new file mode 100644 index 00000000000..f3471edcaac --- /dev/null +++ b/src/libs/extensionsystem/test/auto/pluginspec/tst_pluginspec.cpp @@ -0,0 +1,278 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtTest/QtTest> + +#include "testplugin/testplugin.h" + +#include <extensionsystem/pluginspec.h> +#include <extensionsystem/pluginspec_p.h> +#include <extensionsystem/pluginmanager_p.h> +#include <extensionsystem/pluginmanager.h> + +#include <QtCore/QObject> + +using namespace ExtensionSystem; + +class tst_PluginSpec : public QObject +{ + Q_OBJECT + +private slots: + void read(); + void readError(); + void isValidVersion(); + void versionCompare(); + void provides(); + void locationAndPath(); + void resolveDependencies(); + void loadLibrary(); + void initializePlugin(); + void initializeExtensions(); +}; + +void tst_PluginSpec::read() +{ + Internal::PluginSpecPrivate spec(0); + QCOMPARE(spec.state, PluginSpec::Invalid); + QVERIFY(spec.read("testspecs/spec1.xml")); + QCOMPARE(spec.state, PluginSpec::Read); + QVERIFY(!spec.hasError); + QVERIFY(spec.errorString.isEmpty()); + QCOMPARE(spec.name, QString("test")); + QCOMPARE(spec.version, QString("1.0.1")); + QCOMPARE(spec.compatVersion, QString("1.0.0")); + QCOMPARE(spec.vendor, QString("Trolltech")); + QCOMPARE(spec.copyright, QString("(C) 2007 Trolltech ASA")); + QCOMPARE(spec.license, QString("This is a default license bla\nblubbblubb\nend of terms")); + QCOMPARE(spec.description, QString("This plugin is just a test.\n it demonstrates the great use of the plugin spec.")); + QCOMPARE(spec.url, QString("http://www.trolltech.com")); + PluginDependency dep1; + dep1.name = QString("SomeOtherPlugin"); + dep1.version = QString("2.3.0_2"); + PluginDependency dep2; + dep2.name = QString("EvenOther"); + dep2.version = QString("1.0.0"); + QCOMPARE(spec.dependencies, QList<PluginDependency>() << dep1 << dep2); + + // test missing compatVersion behavior + QVERIFY(spec.read("testspecs/spec2.xml")); + QCOMPARE(spec.version, QString("3.1.4_10")); + QCOMPARE(spec.compatVersion, QString("3.1.4_10")); +} + +void tst_PluginSpec::readError() +{ + Internal::PluginSpecPrivate spec(0); + QCOMPARE(spec.state, PluginSpec::Invalid); + QVERIFY(!spec.read("non-existing-file.xml")); + QCOMPARE(spec.state, PluginSpec::Invalid); + QVERIFY(spec.hasError); + QVERIFY(!spec.errorString.isEmpty()); + QVERIFY(!spec.read("testspecs/spec_wrong1.xml")); + QCOMPARE(spec.state, PluginSpec::Invalid); + QVERIFY(spec.hasError); + QVERIFY(!spec.errorString.isEmpty()); + QVERIFY(!spec.read("testspecs/spec_wrong2.xml")); + QCOMPARE(spec.state, PluginSpec::Invalid); + QVERIFY(spec.hasError); + QVERIFY(!spec.errorString.isEmpty()); + QVERIFY(!spec.read("testspecs/spec_wrong3.xml")); + QCOMPARE(spec.state, PluginSpec::Invalid); + QVERIFY(spec.hasError); + QVERIFY(!spec.errorString.isEmpty()); + QVERIFY(!spec.read("testspecs/spec_wrong4.xml")); + QCOMPARE(spec.state, PluginSpec::Invalid); + QVERIFY(spec.hasError); + QVERIFY(!spec.errorString.isEmpty()); + QVERIFY(!spec.read("testspecs/spec_wrong5.xml")); + QCOMPARE(spec.state, PluginSpec::Invalid); + QVERIFY(spec.hasError); + QVERIFY(!spec.errorString.isEmpty()); +} + +void tst_PluginSpec::isValidVersion() +{ + QVERIFY(Internal::PluginSpecPrivate::isValidVersion("2")); + QVERIFY(Internal::PluginSpecPrivate::isValidVersion("53")); + QVERIFY(Internal::PluginSpecPrivate::isValidVersion("52_1")); + QVERIFY(Internal::PluginSpecPrivate::isValidVersion("3.12")); + QVERIFY(Internal::PluginSpecPrivate::isValidVersion("31.1_12")); + QVERIFY(Internal::PluginSpecPrivate::isValidVersion("31.1.0")); + QVERIFY(Internal::PluginSpecPrivate::isValidVersion("1.0.2_1")); + + QVERIFY(!Internal::PluginSpecPrivate::isValidVersion("")); + QVERIFY(!Internal::PluginSpecPrivate::isValidVersion("1..0")); + QVERIFY(!Internal::PluginSpecPrivate::isValidVersion("1.0_")); + QVERIFY(!Internal::PluginSpecPrivate::isValidVersion("1.0.0.0")); +} + +void tst_PluginSpec::versionCompare() +{ + QVERIFY(Internal::PluginSpecPrivate::versionCompare("3", "3") == 0); + QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.0.0", "3") == 0); + QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.0", "3") == 0); + QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.0.0_1", "3_1") == 0); + QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.0_21", "3_21") == 0); + + QVERIFY(Internal::PluginSpecPrivate::versionCompare("3", "1") > 0); + QVERIFY(Internal::PluginSpecPrivate::versionCompare("3", "1.0_12") > 0); + QVERIFY(Internal::PluginSpecPrivate::versionCompare("3_1", "3") > 0); + QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.1.0_23", "3.1") > 0); + QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.1_23", "3.1_12") > 0); + + QVERIFY(Internal::PluginSpecPrivate::versionCompare("1", "3") < 0); + QVERIFY(Internal::PluginSpecPrivate::versionCompare("1.0_12", "3") < 0); + QVERIFY(Internal::PluginSpecPrivate::versionCompare("3", "3_1") < 0); + QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.1", "3.1.0_23") < 0); + QVERIFY(Internal::PluginSpecPrivate::versionCompare("3.1_12", "3.1_23") < 0); +} + +void tst_PluginSpec::provides() +{ + Internal::PluginSpecPrivate spec(0); + QVERIFY(spec.read("testspecs/simplespec.xml")); + QVERIFY(!spec.provides("SomeOtherPlugin", "2.2.3_9")); + QVERIFY(!spec.provides("MyPlugin", "2.2.3_10")); + QVERIFY(!spec.provides("MyPlugin", "2.2.4")); + QVERIFY(!spec.provides("MyPlugin", "2.3.11_1")); + QVERIFY(!spec.provides("MyPlugin", "2.3")); + QVERIFY(!spec.provides("MyPlugin", "3.0")); + QVERIFY(!spec.provides("MyPlugin", "1.9.9_99")); + QVERIFY(!spec.provides("MyPlugin", "1.9")); + QVERIFY(!spec.provides("MyPlugin", "0.9")); + QVERIFY(!spec.provides("MyPlugin", "1")); + + QVERIFY(spec.provides("myplugin", "2.2.3_9")); + QVERIFY(spec.provides("MyPlugin", "2.2.3_9")); + QVERIFY(spec.provides("MyPlugin", "2.2.3_8")); + QVERIFY(spec.provides("MyPlugin", "2.2.3")); + QVERIFY(spec.provides("MyPlugin", "2.2.2")); + QVERIFY(spec.provides("MyPlugin", "2.1.2_10")); + QVERIFY(spec.provides("MyPlugin", "2.0_10")); + QVERIFY(spec.provides("MyPlugin", "2")); +} + +void tst_PluginSpec::locationAndPath() +{ + Internal::PluginSpecPrivate spec(0); + QVERIFY(spec.read("testspecs/simplespec.xml")); + QCOMPARE(spec.location, QDir::currentPath()+"/testspecs"); + QCOMPARE(spec.filePath, QDir::currentPath()+"/testspecs/simplespec.xml"); + QVERIFY(spec.read("testdir/../testspecs/simplespec.xml")); + QCOMPARE(spec.location, QDir::currentPath()+"/testspecs"); + QCOMPARE(spec.filePath, QDir::currentPath()+"/testspecs/simplespec.xml"); + QVERIFY(spec.read("testdir/spec.xml")); + QCOMPARE(spec.location, QDir::currentPath()+"/testdir"); + QCOMPARE(spec.filePath, QDir::currentPath()+"/testdir/spec.xml"); +} + +void tst_PluginSpec::resolveDependencies() +{ + QSet<PluginSpec *> specs; + PluginSpec *spec1 = Internal::PluginManagerPrivate::createSpec(); + specs.insert(spec1); + Internal::PluginSpecPrivate *spec1Priv = Internal::PluginManagerPrivate::privateSpec(spec1); + spec1Priv->read("testdependencies/spec1.xml"); + PluginSpec *spec2 = Internal::PluginManagerPrivate::createSpec(); + specs.insert(spec2); + Internal::PluginManagerPrivate::privateSpec(spec2)->read("testdependencies/spec2.xml"); + PluginSpec *spec3 = Internal::PluginManagerPrivate::createSpec(); + specs.insert(spec3); + Internal::PluginManagerPrivate::privateSpec(spec3)->read("testdependencies/spec3.xml"); + PluginSpec *spec4 = Internal::PluginManagerPrivate::createSpec(); + specs.insert(spec4); + Internal::PluginSpecPrivate *spec4Priv = Internal::PluginManagerPrivate::privateSpec(spec4); + spec4Priv->read("testdependencies/spec4.xml"); + PluginSpec *spec5 = Internal::PluginManagerPrivate::createSpec(); + specs.insert(spec5); + Internal::PluginManagerPrivate::privateSpec(spec5)->read("testdependencies/spec5.xml"); + QVERIFY(spec1Priv->resolveDependencies(specs)); + QCOMPARE(spec1Priv->dependencySpecs.size(), 2); + QVERIFY(spec1Priv->dependencySpecs.contains(spec2)); + QVERIFY(spec1Priv->dependencySpecs.contains(spec3)); + QCOMPARE(spec1Priv->state, PluginSpec::Resolved); + QVERIFY(!spec4Priv->resolveDependencies(specs)); + QVERIFY(spec4Priv->hasError); + QCOMPARE(spec4Priv->state, PluginSpec::Read); +} + +void tst_PluginSpec::loadLibrary() +{ + PluginSpec *ps = Internal::PluginManagerPrivate::createSpec(); + Internal::PluginSpecPrivate *spec = Internal::PluginManagerPrivate::privateSpec(ps); + PluginManager *manager = new PluginManager(); + QVERIFY(spec->read("testplugin/testplugin.xml")); + QVERIFY(spec->resolveDependencies(QSet<PluginSpec *>())); + QVERIFY(spec->loadLibrary()); + QVERIFY(qobject_cast<MyPlugin::MyPluginImpl*>(spec->plugin) != 0); + QCOMPARE(spec->state, PluginSpec::Loaded); + QVERIFY(!spec->hasError); + QCOMPARE(spec->plugin->pluginSpec(), ps); + delete manager; + delete ps; +} + +void tst_PluginSpec::initializePlugin() +{ + Internal::PluginSpecPrivate spec(0); + MyPlugin::MyPluginImpl *impl; + QVERIFY(spec.read("testplugin/testplugin.xml")); + QVERIFY(spec.resolveDependencies(QSet<PluginSpec *>())); + QVERIFY(spec.loadLibrary()); + impl = qobject_cast<MyPlugin::MyPluginImpl*>(spec.plugin); + QVERIFY(impl != 0); + QVERIFY(!impl->isInitialized()); + QVERIFY(spec.initializePlugin()); + QCOMPARE(spec.state, PluginSpec::Initialized); + QVERIFY(!spec.hasError); + QVERIFY(impl->isInitialized()); +} + +void tst_PluginSpec::initializeExtensions() +{ + Internal::PluginSpecPrivate spec(0); + MyPlugin::MyPluginImpl *impl; + QVERIFY(spec.read("testplugin/testplugin.xml")); + QVERIFY(spec.resolveDependencies(QSet<PluginSpec *>())); + QVERIFY(spec.loadLibrary()); + impl = qobject_cast<MyPlugin::MyPluginImpl*>(spec.plugin); + QVERIFY(impl != 0); + QVERIFY(spec.initializePlugin()); + QVERIFY(spec.initializeExtensions()); + QCOMPARE(spec.state, PluginSpec::Running); + QVERIFY(!spec.hasError); + QVERIFY(impl->isExtensionsInitialized()); +} + +QTEST_MAIN(tst_PluginSpec) +#include "tst_pluginspec.moc" diff --git a/src/libs/extensionsystem/test/extensionsystem_test.pri b/src/libs/extensionsystem/test/extensionsystem_test.pri new file mode 100644 index 00000000000..6ad874add3e --- /dev/null +++ b/src/libs/extensionsystem/test/extensionsystem_test.pri @@ -0,0 +1,12 @@ + +INCLUDEPATH *= $$PWD/../.. +macx { + LIBPATH*= $$PWD/../../../../bin/QtCreator.app/Contents/PlugIns +} +else { + LIBPATH*= $$PWD/../../../../lib +} + +include(../extensionsystem.pri) + +QT *= xml diff --git a/src/libs/extensionsystem/test/manual/manual.pro b/src/libs/extensionsystem/test/manual/manual.pro new file mode 100644 index 00000000000..829f2fce579 --- /dev/null +++ b/src/libs/extensionsystem/test/manual/manual.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = pluginview + diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugindialog.cpp b/src/libs/extensionsystem/test/manual/pluginview/plugindialog.cpp new file mode 100644 index 00000000000..3282d9862e7 --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugindialog.cpp @@ -0,0 +1,142 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "plugindialog.h" +#include <extensionsystem/plugindetailsview.h> +#include <extensionsystem/pluginerrorview.h> +#include <extensionsystem/pluginspec.h> + +#include <QtGui/QVBoxLayout> +#include <QtGui/QHBoxLayout> +#include <QtGui/QDialog> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QApplication> +#include <QtDebug> + +PluginDialog::PluginDialog(ExtensionSystem::PluginManager *manager) + : m_view(new ExtensionSystem::PluginView(manager, this)) +{ + QVBoxLayout *vl = new QVBoxLayout(this); + vl->setMargin(0); + vl->setSpacing(0); + vl->addWidget(m_view); + + QHBoxLayout *hl = new QHBoxLayout; + vl->addLayout(hl); + hl->setMargin(0); + hl->setSpacing(6); + m_detailsButton = new QPushButton(tr("Details"), this); + m_errorDetailsButton = new QPushButton(tr("Error Details"), this); + m_detailsButton->setEnabled(false); + m_errorDetailsButton->setEnabled(false); + hl->addWidget(m_detailsButton); + hl->addWidget(m_errorDetailsButton); + hl->addStretch(5); + resize(650, 300); + setWindowTitle(tr("Installed Plugins")); + + connect(m_view, SIGNAL(currentPluginChanged(ExtensionSystem::PluginSpec*)), + this, SLOT(updateButtons())); + connect(m_view, SIGNAL(pluginActivated(ExtensionSystem::PluginSpec*)), + this, SLOT(openDetails(ExtensionSystem::PluginSpec*))); + connect(m_detailsButton, SIGNAL(clicked()), this, SLOT(openDetails())); + connect(m_errorDetailsButton, SIGNAL(clicked()), this, SLOT(openErrorDetails())); +} + +void PluginDialog::updateButtons() +{ + ExtensionSystem::PluginSpec *selectedSpec = m_view->currentPlugin(); + if (selectedSpec) { + m_detailsButton->setEnabled(true); + m_errorDetailsButton->setEnabled(selectedSpec->hasError()); + } else { + m_detailsButton->setEnabled(false); + m_errorDetailsButton->setEnabled(false); + } +} + + +void PluginDialog::openDetails() +{ + openDetails(m_view->currentPlugin()); +} + +void PluginDialog::openDetails(ExtensionSystem::PluginSpec *spec) +{ + if (!spec) + return; + QDialog dialog(this); + dialog.setWindowTitle(tr("Plugin Details of %1").arg(spec->name())); + QVBoxLayout *layout = new QVBoxLayout; + dialog.setLayout(layout); + ExtensionSystem::PluginDetailsView *details = new ExtensionSystem::PluginDetailsView(&dialog); + layout->addWidget(details); + details->update(spec); + QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, &dialog); + layout->addWidget(buttons); + connect(buttons, SIGNAL(accepted()), &dialog, SLOT(accept())); + connect(buttons, SIGNAL(rejected()), &dialog, SLOT(reject())); + dialog.resize(400, 500); + dialog.exec(); +} + +void PluginDialog::openErrorDetails() +{ + ExtensionSystem::PluginSpec *spec = m_view->currentPlugin(); + if (!spec) + return; + QDialog dialog(this); + dialog.setWindowTitle(tr("Plugin Errors of %1").arg(spec->name())); + QVBoxLayout *layout = new QVBoxLayout; + dialog.setLayout(layout); + ExtensionSystem::PluginErrorView *errors = new ExtensionSystem::PluginErrorView(&dialog); + layout->addWidget(errors); + errors->update(spec); + QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, &dialog); + layout->addWidget(buttons); + connect(buttons, SIGNAL(accepted()), &dialog, SLOT(accept())); + connect(buttons, SIGNAL(rejected()), &dialog, SLOT(reject())); + dialog.resize(500, 300); + dialog.exec(); +} + +int main(int argc, char *argv[]) +{ + ExtensionSystem::PluginManager manager; + QApplication app(argc, argv); + PluginDialog dialog(&manager); + manager.setPluginPaths(QStringList() << "plugins"); + manager.loadPlugins(); + dialog.show(); + app.exec(); +} + diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugindialog.h b/src/libs/extensionsystem/test/manual/pluginview/plugindialog.h new file mode 100644 index 00000000000..8987230681a --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugindialog.h @@ -0,0 +1,61 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGINDIALOG_H +#define PLUGINDIALOG_H + +#include <extensionsystem/pluginview.h> +#include <extensionsystem/pluginmanager.h> +#include <QtGui/QWidget> +#include <QtGui/QPushButton> + +class PluginDialog : public QWidget +{ + Q_OBJECT + +public: + PluginDialog(ExtensionSystem::PluginManager *manager); + +private slots: + void updateButtons(); + void openDetails(); + void openDetails(ExtensionSystem::PluginSpec *spec); + void openErrorDetails(); + +private: + ExtensionSystem::PluginView *m_view; + + QPushButton *m_detailsButton; + QPushButton *m_errorDetailsButton; +}; + +#endif diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin1/plugin.xml b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin1/plugin.xml new file mode 100644 index 00000000000..63faecdf52d --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin1/plugin.xml @@ -0,0 +1,18 @@ +<plugin name="plugin1" version="2.1.0" compatVersion="1.0.0"> + <vendor>Blablubb Corp</vendor> + <copyright>(C) 2023 Blubb</copyright> + <license> +This is a default license bla +blubbblubb +end of terms + </license> + <description> +This plugin is just a test. + it demonstrates the great use of the plugin spec. + </description> + <url>http://www.blablubb-corp.com/greatplugin</url> + <dependencyList> + <dependency name="plugin2" version="1.0.0"/> + <dependency name="plugin3" version="1.0.0"/> + </dependencyList> +</plugin> diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin1/plugin1.cpp b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin1/plugin1.cpp new file mode 100644 index 00000000000..32d3bdf0701 --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin1/plugin1.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "plugin1.h" + +#include <extensionsystem/pluginmanager.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QObject> + +using namespace Plugin1; + +MyPlugin1::MyPlugin1() + : initializeCalled(false) +{ +} + +bool MyPlugin1::initialize(const QStringList & /*arguments*/, QString *errorString) +{ + initializeCalled = true; + QObject *obj = new QObject(this); + obj->setObjectName("MyPlugin1"); + addAutoReleasedObject(obj); + + bool found2 = false; + bool found3 = false; + foreach (QObject *object, ExtensionSystem::PluginManager::instance()->allObjects()) { + if (object->objectName() == "MyPlugin2") + found2 = true; + else if (object->objectName() == "MyPlugin3") + found3 = true; + } + if (found2 && found3) + return true; + if (errorString) { + QString error = "object(s) missing from plugin(s):"; + if (!found2) + error.append(" plugin2"); + if (!found3) + error.append(" plugin3"); + *errorString = error; + } + return false; +} + +void MyPlugin1::extensionsInitialized() +{ + if (!initializeCalled) + return; + // don't do this at home, it's just done here for the test + QObject *obj = new QObject(this); + obj->setObjectName("MyPlugin1_running"); + addAutoReleasedObject(obj); +} + +Q_EXPORT_PLUGIN(MyPlugin1) + diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin1/plugin1.h b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin1/plugin1.h new file mode 100644 index 00000000000..f993b25c9f2 --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin1/plugin1.h @@ -0,0 +1,59 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGIN1_H +#define PLUGIN1_H + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> +#include <QtCore/QString> + +namespace Plugin1 { + +class MyPlugin1 : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + MyPlugin1(); + + bool initialize(const QStringList &arguments, QString *errorString); + void extensionsInitialized(); + +private: + bool initializeCalled; +}; + +} // namespace + +#endif // header guard diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin1/plugin1.pro b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin1/plugin1.pro new file mode 100644 index 00000000000..9101770f9ac --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin1/plugin1.pro @@ -0,0 +1,15 @@ +TEMPLATE = lib +TARGET = plugin1 + +SOURCES += plugin1.cpp +HEADERS += plugin1.h + +include(../../../../extensionsystem_test.pri) + +LIBS += -L$${PWD}/../plugin2 -L$${PWD}/../plugin3 -lplugin2 -lplugin3 + +macx { +} else:unix { + QMAKE_RPATHDIR += $${PWD}/../plugin2 + QMAKE_RPATHDIR += $${PWD}/../plugin3 +} diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin2/plugin.xml b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin2/plugin.xml new file mode 100644 index 00000000000..1a230ad38d9 --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin2/plugin.xml @@ -0,0 +1,4 @@ +<plugin name="plugin2" version="1.0.0" compatVersion="1.0.0"> + <vendor>1 < 2 GmbH</vendor> + <url>http://www.somewhereintheweb.com/dodo.php?q=p</url> +</plugin> diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin2/plugin2.cpp b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin2/plugin2.cpp new file mode 100644 index 00000000000..3ce6f258fa8 --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin2/plugin2.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "plugin2.h" + +#include <extensionsystem/pluginmanager.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QObject> + +using namespace Plugin2; + +MyPlugin2::MyPlugin2() + : initializeCalled(false) +{ +} + +bool MyPlugin2::initialize(const QStringList & /*arguments*/, QString *errorString) +{ + Q_UNUSED(errorString); + initializeCalled = true; + QObject *obj = new QObject(this); + obj->setObjectName("MyPlugin2"); + addAutoReleasedObject(obj); + + return true; +} + +void MyPlugin2::extensionsInitialized() +{ + if (!initializeCalled) + return; + // don't do this at home, it's just done here for the test + QObject *obj = new QObject(this); + obj->setObjectName("MyPlugin2_running"); + addAutoReleasedObject(obj); +} + +Q_EXPORT_PLUGIN(MyPlugin2) + diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin2/plugin2.h b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin2/plugin2.h new file mode 100644 index 00000000000..51200387546 --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin2/plugin2.h @@ -0,0 +1,59 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGIN2_H +#define PLUGIN2_H + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> +#include <QtCore/QString> + +namespace Plugin2 { + +class MyPlugin2 : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + MyPlugin2(); + + bool initialize(const QStringList &arguments, QString *errorString); + void extensionsInitialized(); + +private: + bool initializeCalled; +}; + +} // namespace + +#endif // header guard diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin2/plugin2.pro b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin2/plugin2.pro new file mode 100644 index 00000000000..a80f4a5c765 --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin2/plugin2.pro @@ -0,0 +1,12 @@ +TEMPLATE = lib +TARGET = plugin2 + +SOURCES += plugin2.cpp +HEADERS += plugin2.h + +include(../../../../extensionsystem_test.pri) + +macx { + QMAKE_LFLAGS_SONAME = -Wl,-install_name,$${PWD}/ +} + diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin3/plugin.xml b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin3/plugin.xml new file mode 100644 index 00000000000..fd2279824e2 --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin3/plugin.xml @@ -0,0 +1,6 @@ +<plugin name="plugin3" version="1.0.0" compatVersion="1.0.0"> + <vendor>Günöl AG</vendor> + <dependencyList> + <dependency name="plugin2" version="1.0.0"/> + </dependencyList> +</plugin> diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin3/plugin3.cpp b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin3/plugin3.cpp new file mode 100644 index 00000000000..ff37d06096d --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin3/plugin3.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "plugin3.h" + +#include <extensionsystem/pluginmanager.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QObject> + +using namespace Plugin3; + +MyPlugin3::MyPlugin3() + : initializeCalled(false) +{ +} + +bool MyPlugin3::initialize(const QStringList & /*arguments*/, QString *errorString) +{ + initializeCalled = true; + QObject *obj = new QObject(this); + obj->setObjectName("MyPlugin3"); + addAutoReleasedObject(obj); + + bool found2 = false; + foreach (QObject *object, ExtensionSystem::PluginManager::instance()->allObjects()) { + if (object->objectName() == "MyPlugin2") + found2 = true; + } + if (found2) + return true; + if (errorString) + *errorString = "object from plugin2 could not be found"; + return false; +} + +void MyPlugin3::extensionsInitialized() +{ + if (!initializeCalled) + return; + // don't do this at home, it's just done here for the test + QObject *obj = new QObject(this); + obj->setObjectName("MyPlugin3_running"); + addAutoReleasedObject(obj); +} + +Q_EXPORT_PLUGIN(MyPlugin3) + diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin3/plugin3.h b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin3/plugin3.h new file mode 100644 index 00000000000..00bd6429125 --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin3/plugin3.h @@ -0,0 +1,59 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGIN3_H +#define PLUGIN3_H + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> +#include <QtCore/QString> + +namespace Plugin3 { + +class MyPlugin3 : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + MyPlugin3(); + + bool initialize(const QStringList &arguments, QString *errorString); + void extensionsInitialized(); + +private: + bool initializeCalled; +}; + +} // namespace + +#endif // header guard diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin3/plugin3.pro b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin3/plugin3.pro new file mode 100644 index 00000000000..c5ff581b1ba --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin3/plugin3.pro @@ -0,0 +1,15 @@ +TEMPLATE = lib +TARGET = plugin3 + +SOURCES += plugin3.cpp +HEADERS += plugin3.h + +include(../../../../extensionsystem_test.pri) + +LIBS += -L$${PWD}/../plugin2 -lplugin2 + +macx { + QMAKE_LFLAGS_SONAME = -Wl,-install_name,$${PWD}/ +} else:unix { + QMAKE_RPATHDIR += $${PWD}/../plugin2 +} diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin4/plugin.xml b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin4/plugin.xml new file mode 100644 index 00000000000..61f37bd706c --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugin4/plugin.xml @@ -0,0 +1,5 @@ +<plugin name="plugin4" version="1.0.0" compatVersion="1.0.0"> + <dependencyList> + <dependency name="plugin2" version="1.0.0"/> + </dependencyList> +</plugin> diff --git a/src/libs/extensionsystem/test/manual/pluginview/plugins/plugins.pro b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugins.pro new file mode 100644 index 00000000000..f0d76950e86 --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/plugins/plugins.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS = plugin2 plugin3 plugin1 diff --git a/src/libs/extensionsystem/test/manual/pluginview/pluginview.pro b/src/libs/extensionsystem/test/manual/pluginview/pluginview.pro new file mode 100644 index 00000000000..8527b82c47c --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/pluginview.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = test.pro \ + plugins diff --git a/src/libs/extensionsystem/test/manual/pluginview/test.pro b/src/libs/extensionsystem/test/manual/pluginview/test.pro new file mode 100644 index 00000000000..4b9cd21d8ad --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/test.pro @@ -0,0 +1,9 @@ +TEMPLATE = app + +macx:CONFIG-=app_bundle + +# Input +HEADERS += plugindialog.h +SOURCES += plugindialog.cpp + +include(../../extensionsystem_test.pri) diff --git a/src/libs/extensionsystem/test/manual/pluginview/test.sh b/src/libs/extensionsystem/test/manual/pluginview/test.sh new file mode 100755 index 00000000000..426901ea74d --- /dev/null +++ b/src/libs/extensionsystem/test/manual/pluginview/test.sh @@ -0,0 +1,5 @@ +# -- run the plugin test from this directory. + +export LD_LIBRARY_PATH=../../../../../../lib:$LD_LIBRARY_PATH +export DYLD_LIBRARY_PATH=../../../../../../bin/QtCreator.app/Contents/PlugIns:$DYLD_LIBRARY_PATH # mac +exec ./test diff --git a/src/libs/extensionsystem/test/test.pro b/src/libs/extensionsystem/test/test.pro new file mode 100644 index 00000000000..471ea892f1f --- /dev/null +++ b/src/libs/extensionsystem/test/test.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +SUBDIRS = auto manual + diff --git a/src/libs/libs.pro b/src/libs/libs.pro new file mode 100644 index 00000000000..0bb942edada --- /dev/null +++ b/src/libs/libs.pro @@ -0,0 +1,9 @@ +TEMPLATE = subdirs +CONFIG += ordered + +SUBDIRS = \ + qtconcurrent \ + aggregation \ + extensionsystem \ + utils \ + cplusplus diff --git a/src/libs/qtconcurrent/QtConcurrentTools b/src/libs/qtconcurrent/QtConcurrentTools new file mode 100644 index 00000000000..ffd0cb10a7e --- /dev/null +++ b/src/libs/qtconcurrent/QtConcurrentTools @@ -0,0 +1,2 @@ +#include "qtconcurrent/multitask.h" +#include "qtconcurrent/runextensions.h" diff --git a/src/libs/qtconcurrent/multitask.h b/src/libs/qtconcurrent/multitask.h new file mode 100644 index 00000000000..fc66de1715e --- /dev/null +++ b/src/libs/qtconcurrent/multitask.h @@ -0,0 +1,198 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef MULTITASK_H +#define MULTITASK_H + +#include "qtconcurrent_global.h" +#include "runextensions.h" +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QtCore/QEventLoop> +#include <QtCore/QFutureWatcher> +#include <QtCore/QtConcurrentRun> +#include <QtCore/QThreadPool> + +#include <QtDebug> + +QT_BEGIN_NAMESPACE + +namespace QtConcurrent { + +class QTCONCURRENT_EXPORT MultiTaskBase : public QObject, public QRunnable +{ + Q_OBJECT +protected slots: + virtual void cancelSelf() = 0; + virtual void setFinished() = 0; + virtual void setProgressRange(int min, int max) = 0; + virtual void setProgressValue(int value) = 0; + virtual void setProgressText(QString value) = 0; +}; + +template <typename Class, typename R> +class MultiTask : public MultiTaskBase +{ +public: + MultiTask(void (Class::*fn)(QFutureInterface<R> &), const QList<Class *> &objects) + : fn(fn), + objects(objects) + { + maxProgress = 100*objects.size(); + } + + QFuture<R> future() + { + futureInterface.reportStarted(); + return futureInterface.future(); + } + + void run() + { + QThreadPool::globalInstance()->releaseThread(); + futureInterface.setProgressRange(0, maxProgress); + foreach (Class *object, objects) { + QFutureWatcher<R> *watcher = new QFutureWatcher<R>(); + watchers.insert(object, watcher); + finished.insert(watcher, false); + connect(watcher, SIGNAL(finished()), this, SLOT(setFinished())); + connect(watcher, SIGNAL(progressRangeChanged(int,int)), this, SLOT(setProgressRange(int,int))); + connect(watcher, SIGNAL(progressValueChanged(int)), this, SLOT(setProgressValue(int))); + connect(watcher, SIGNAL(progressTextChanged(QString)), this, SLOT(setProgressText(QString))); + watcher->setFuture(QtConcurrent::run(fn, object)); + } + selfWatcher = new QFutureWatcher<R>(); + connect(selfWatcher, SIGNAL(canceled()), this, SLOT(cancelSelf())); + selfWatcher->setFuture(futureInterface.future()); + loop = new QEventLoop; + loop->exec(); + futureInterface.reportFinished(); + QThreadPool::globalInstance()->reserveThread(); + qDeleteAll(watchers.values()); + delete selfWatcher; + delete loop; + } +protected: + void cancelSelf() + { + foreach (QFutureWatcher<R> *watcher, watchers) + watcher->future().cancel(); + } + + void setFinished() + { + updateProgress(); + QFutureWatcher<R> *watcher = static_cast<QFutureWatcher<R> *>(sender()); + if (finished.contains(watcher)) + finished[watcher] = true; + bool allFinished = true; + const QList<bool> finishedValues = finished.values(); + foreach (bool isFinished, finishedValues) { + if (!isFinished) { + allFinished = false; + break; + } + } + if (allFinished) + loop->quit(); + } + + void setProgressRange(int min, int max) + { + Q_UNUSED(min); + Q_UNUSED(max); + updateProgress(); + } + + void setProgressValue(int value) + { + Q_UNUSED(value); + updateProgress(); + } + + void setProgressText(QString value) + { + Q_UNUSED(value); + updateProgressText(); + } +private: + void updateProgress() + { + int progressSum = 0; + const QList<QFutureWatcher<R> *> watchersValues = watchers.values(); + foreach (QFutureWatcher<R> *watcher, watchersValues) { + if (watcher->progressMinimum() == watcher->progressMaximum()) { + if (watcher->future().isFinished() && !watcher->future().isCanceled()) + progressSum += 100; + } else { + progressSum += 100*(watcher->progressValue()-watcher->progressMinimum())/(watcher->progressMaximum()-watcher->progressMinimum()); + } + } + futureInterface.setProgressValue(progressSum); + } + + void updateProgressText() + { + QString text; + const QList<QFutureWatcher<R> *> watchersValues = watchers.values(); + foreach (QFutureWatcher<R> *watcher, watchersValues) { + if (!watcher->progressText().isEmpty()) + text += watcher->progressText() + "\n"; + } + text = text.trimmed(); + futureInterface.setProgressValueAndText(futureInterface.progressValue(), text); + } + + QFutureInterface<R> futureInterface; + void (Class::*fn)(QFutureInterface<R> &); + QList<Class *> objects; + + QFutureWatcher<R> *selfWatcher; + QMap<Class *, QFutureWatcher<R> *> watchers; + QMap<QFutureWatcher<R> *, bool> finished; + QEventLoop *loop; + int maxProgress; +}; + +template <typename Class, typename T> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &), const QList<Class *> &objects, int priority = 0) { + MultiTask<Class, T> *task = new MultiTask<Class, T>(fn, objects); + QFuture<T> future = task->future(); + QThreadPool::globalInstance()->start(task, priority); + return future; +} + +} //namespace + +QT_END_NAMESPACE + +#endif // MULTITASK_H diff --git a/src/libs/qtconcurrent/qtconcurrent.pri b/src/libs/qtconcurrent/qtconcurrent.pri new file mode 100644 index 00000000000..57929a4cf11 --- /dev/null +++ b/src/libs/qtconcurrent/qtconcurrent.pri @@ -0,0 +1 @@ +LIBS *= -l$$qtLibraryTarget(QtConcurrent) diff --git a/src/libs/qtconcurrent/qtconcurrent.pro b/src/libs/qtconcurrent/qtconcurrent.pro new file mode 100644 index 00000000000..46302f11cde --- /dev/null +++ b/src/libs/qtconcurrent/qtconcurrent.pro @@ -0,0 +1,10 @@ +TEMPLATE = lib +TARGET = QtConcurrent +DEFINES += BUILD_QTCONCURRENT + +include(../../qworkbenchlibrary.pri) + +HEADERS += \ + qtconcurrent_global.h \ + multitask.h \ + runextensions.h diff --git a/src/libs/qtconcurrent/qtconcurrent_global.h b/src/libs/qtconcurrent/qtconcurrent_global.h new file mode 100644 index 00000000000..15d47af5f3a --- /dev/null +++ b/src/libs/qtconcurrent/qtconcurrent_global.h @@ -0,0 +1,44 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QTCONCURRENT_GLOBAL_H +#define QTCONCURRENT_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(BUILD_QTCONCURRENT) +# define QTCONCURRENT_EXPORT Q_DECL_EXPORT +#else +# define QTCONCURRENT_EXPORT Q_DECL_IMPORT +#endif + +#endif // QTCONCURRENT_GLOBAL_H diff --git a/src/libs/qtconcurrent/runextensions.h b/src/libs/qtconcurrent/runextensions.h new file mode 100644 index 00000000000..43707c14d5d --- /dev/null +++ b/src/libs/qtconcurrent/runextensions.h @@ -0,0 +1,397 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QTCONCURRENT_RUNEX_H +#define QTCONCURRENT_RUNEX_H + +#include <qrunnable.h> +#include <qfutureinterface.h> +#include <qthreadpool.h> + +QT_BEGIN_NAMESPACE + +namespace QtConcurrent { + +template <typename T, typename FunctionPointer> +class StoredInterfaceFunctionCall0 : public QRunnable +{ +public: + StoredInterfaceFunctionCall0(void (fn)(QFutureInterface<T> &)) + : fn(fn) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + +}; +template <typename T, typename FunctionPointer, typename Class> +class StoredInterfaceMemberFunctionCall0 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall0(void (Class::*fn)(QFutureInterface<T> &), Class *object) + : fn(fn), object(object) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + +}; + +template <typename T, typename FunctionPointer, typename Arg1> +class StoredInterfaceFunctionCall1 : public QRunnable +{ +public: + StoredInterfaceFunctionCall1(void (fn)(QFutureInterface<T> &, Arg1), Arg1 arg1) + : fn(fn), arg1(arg1) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1> +class StoredInterfaceMemberFunctionCall1 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall1(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, Arg1 arg1) + : fn(fn), object(object), arg1(arg1) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; +}; + +template <typename T, typename FunctionPointer, typename Arg1, typename Arg2> +class StoredInterfaceFunctionCall2 : public QRunnable +{ +public: + StoredInterfaceFunctionCall2(void (fn)(QFutureInterface<T> &, Arg1, Arg2), Arg1 arg1, Arg2 arg2) + : fn(fn), arg1(arg1), arg2(arg2) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1, arg2); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; Arg2 arg2; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2> +class StoredInterfaceMemberFunctionCall2 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall2(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, Arg1 arg1, Arg2 arg2) + : fn(fn), object(object), arg1(arg1), arg2(arg2) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1, arg2); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; Arg2 arg2; +}; + +template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3> +class StoredInterfaceFunctionCall3 : public QRunnable +{ +public: + StoredInterfaceFunctionCall3(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Arg1 arg1, Arg2 arg2, Arg3 arg3) + : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1, arg2, arg3); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; Arg2 arg2; Arg3 arg3; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3> +class StoredInterfaceMemberFunctionCall3 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall3(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, Arg1 arg1, Arg2 arg2, Arg3 arg3) + : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1, arg2, arg3); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; Arg2 arg2; Arg3 arg3; +}; + +template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +class StoredInterfaceFunctionCall4 : public QRunnable +{ +public: + StoredInterfaceFunctionCall4(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1, arg2, arg3, arg4); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +class StoredInterfaceMemberFunctionCall4 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall4(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1, arg2, arg3, arg4); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; +}; + +template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> +class StoredInterfaceFunctionCall5 : public QRunnable +{ +public: + StoredInterfaceFunctionCall5(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + fn(futureInterface, arg1, arg2, arg3, arg4, arg5); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5; +}; +template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> +class StoredInterfaceMemberFunctionCall5 : public QRunnable +{ +public: + StoredInterfaceMemberFunctionCall5(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { } + + QFuture<T> start() + { + futureInterface.reportStarted(); + QFuture<T> future = futureInterface.future(); + QThreadPool::globalInstance()->start(this); + return future; + } + + void run() + { + (object->*fn)(futureInterface, arg1, arg2, arg3, arg4, arg5); + futureInterface.reportFinished(); + } +private: + QFutureInterface<T> futureInterface; + FunctionPointer fn; + Class *object; + Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5; +}; + +template <typename T> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &)) +{ + return (new StoredInterfaceFunctionCall0<T, void (*)(QFutureInterface<T> &)>(functionPointer))->start(); +} +template <typename T, typename Arg1> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1), Arg1 arg1) +{ + return (new StoredInterfaceFunctionCall1<T, void (*)(QFutureInterface<T> &, Arg1), Arg1>(functionPointer, arg1))->start(); +} +template <typename T, typename Arg1, typename Arg2> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2), Arg1 arg1, Arg2 arg2) +{ + return (new StoredInterfaceFunctionCall2<T, void (*)(QFutureInterface<T> &, Arg1, Arg2), Arg1, Arg2>(functionPointer, arg1, arg2))->start(); +} +template <typename T, typename Arg1, typename Arg2, typename Arg3> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Arg1 arg1, Arg2 arg2, Arg3 arg3) +{ + return (new StoredInterfaceFunctionCall3<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Arg1, Arg2, Arg3>(functionPointer, arg1, arg2, arg3))->start(); +} +template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) +{ + return (new StoredInterfaceFunctionCall4<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Arg1, Arg2, Arg3, Arg4>(functionPointer, arg1, arg2, arg3, arg4))->start(); +} +template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> +QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) +{ + return (new StoredInterfaceFunctionCall5<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Arg1, Arg2, Arg3, Arg4, Arg5>(functionPointer, arg1, arg2, arg3, arg4, arg5))->start(); +} + +template <typename Class, typename T> +QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &), Class *object) +{ + return (new StoredInterfaceMemberFunctionCall0<T, void (Class::*)(QFutureInterface<T> &), Class>(fn, object))->start(); +} + +} //namespace QtConcurrent + +QT_END_NAMESPACE + +#endif diff --git a/src/libs/utils/basevalidatinglineedit.cpp b/src/libs/utils/basevalidatinglineedit.cpp new file mode 100644 index 00000000000..cb936e3c2d2 --- /dev/null +++ b/src/libs/utils/basevalidatinglineedit.cpp @@ -0,0 +1,157 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "basevalidatinglineedit.h" + +#include <QtCore/QDebug> + +enum { debug = 0 }; + +namespace Core { +namespace Utils { + +struct BaseValidatingLineEditPrivate { + explicit BaseValidatingLineEditPrivate(const QWidget *w); + + const QColor m_okTextColor; + QColor m_errorTextColor; + + BaseValidatingLineEdit::State m_state; + QString m_errorMessage; + QString m_initialText; + bool m_firstChange; +}; + +BaseValidatingLineEditPrivate::BaseValidatingLineEditPrivate(const QWidget *w) : + m_okTextColor(BaseValidatingLineEdit::textColor(w)), + m_errorTextColor(Qt::red), + m_state(BaseValidatingLineEdit::Invalid), + m_firstChange(true) +{ +} + +BaseValidatingLineEdit::BaseValidatingLineEdit(QWidget *parent) : + QLineEdit(parent), + m_bd(new BaseValidatingLineEditPrivate(this)) +{ + // Note that textChanged() is also triggered automagically by + // QLineEdit::setText(), no need to trigger manually. + connect(this, SIGNAL(textChanged(QString)), this, SLOT(slotChanged(QString))); +} + +BaseValidatingLineEdit::~BaseValidatingLineEdit() +{ + delete m_bd; +} + +QString BaseValidatingLineEdit::initialText() const +{ + return m_bd->m_initialText; +} + +void BaseValidatingLineEdit::setInitialText(const QString &t) +{ + if (m_bd->m_initialText != t) { + m_bd->m_initialText = t; + m_bd->m_firstChange = true; + setText(t); + } +} + +QColor BaseValidatingLineEdit::errorColor() const +{ + return m_bd->m_errorTextColor; +} + +void BaseValidatingLineEdit::setErrorColor(const QColor &c) +{ + m_bd->m_errorTextColor = c; +} + +QColor BaseValidatingLineEdit::textColor(const QWidget *w) +{ + return w->palette().color(QPalette::Active, QPalette::Text); +} + +void BaseValidatingLineEdit::setTextColor(QWidget *w, const QColor &c) +{ + QPalette palette = w->palette(); + palette.setColor(QPalette::Active, QPalette::Text, c); + w->setPalette(palette); +} + +BaseValidatingLineEdit::State BaseValidatingLineEdit::state() const +{ + return m_bd->m_state; +} + +bool BaseValidatingLineEdit::isValid() const +{ + return m_bd->m_state == Valid; +} + +QString BaseValidatingLineEdit::errorMessage() const +{ + return m_bd->m_errorMessage; +} + +void BaseValidatingLineEdit::slotChanged(const QString &t) +{ + m_bd->m_errorMessage.clear(); + // Are we displaying the initial text? + const bool isDisplayingInitialText = !m_bd->m_initialText.isEmpty() && t == m_bd->m_initialText; + const State newState = isDisplayingInitialText ? + DisplayingInitialText : + (validate(t, &m_bd->m_errorMessage) ? Valid : Invalid); + setToolTip(m_bd->m_errorMessage); + if (debug) + qDebug() << Q_FUNC_INFO << t << "State" << m_bd->m_state << "->" << newState << m_bd->m_errorMessage; + // Changed..figure out if valid changed. DisplayingInitialText is not valid, + // but should not show error color. Also trigger on the first change. + if (newState != m_bd->m_state || m_bd->m_firstChange) { + const bool validHasChanged = (m_bd->m_state == Valid) != (newState == Valid); + m_bd->m_state = newState; + m_bd->m_firstChange = false; + setTextColor(this, newState == Invalid ? m_bd->m_errorTextColor : m_bd->m_okTextColor); + if (validHasChanged) + emit validChanged(); + } +} + +void BaseValidatingLineEdit::slotReturnPressed() +{ + if (isValid()) + emit validReturnPressed(); +} + +} +} diff --git a/src/libs/utils/basevalidatinglineedit.h b/src/libs/utils/basevalidatinglineedit.h new file mode 100644 index 00000000000..34826870112 --- /dev/null +++ b/src/libs/utils/basevalidatinglineedit.h @@ -0,0 +1,100 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BASEVALIDATINGLINEEDIT_H +#define BASEVALIDATINGLINEEDIT_H + +#include "utils_global.h" + +#include <QtGui/QLineEdit> + +namespace Core { +namespace Utils { + +struct BaseValidatingLineEditPrivate; + +/* Base class for validating line edits that performs validation in a virtual + * validate() function to be implemented in derived classes. + * When invalid, the text color will turn red and a tooltip will + * contain the error message. This approach is less intrusive than a + * QValidator which will prevent the user from entering certain characters. + * + * The widget has a concept of an "initialText" which can be something like + * "<Enter name here>". This results in state 'DisplayingInitialText', which + * is not valid, but is not marked red. */ + +class QWORKBENCH_UTILS_EXPORT BaseValidatingLineEdit : public QLineEdit { + Q_OBJECT + Q_DISABLE_COPY(BaseValidatingLineEdit) + Q_PROPERTY(QString initialText READ initialText WRITE setInitialText DESIGNABLE true) + Q_PROPERTY(QColor errorColor READ errorColor WRITE setErrorColor DESIGNABLE true) + +public: + enum State { Invalid, DisplayingInitialText, Valid }; + + explicit BaseValidatingLineEdit(QWidget *parent = 0); + virtual ~BaseValidatingLineEdit(); + + + State state() const; + bool isValid() const; + QString errorMessage() const; + + QString initialText() const; + void setInitialText(const QString &); + + QColor errorColor() const; + void setErrorColor(const QColor &); + + static QColor textColor(const QWidget *w); + static void setTextColor(QWidget *w, const QColor &c); + +signals: + void validChanged(); + void validReturnPressed(); + +protected: + virtual bool validate(const QString &value, QString *errorMessage) const = 0; + +protected slots: + // Custom behaviour can be added here. The base implementation must + // be called. + virtual void slotReturnPressed(); + virtual void slotChanged(const QString &t); + +private: + BaseValidatingLineEditPrivate *m_bd; +}; + +} +} +#endif // BASEVALIDATINGLINEEDIT_H diff --git a/src/libs/utils/classnamevalidatinglineedit.cpp b/src/libs/utils/classnamevalidatinglineedit.cpp new file mode 100644 index 00000000000..b52758eda5f --- /dev/null +++ b/src/libs/utils/classnamevalidatinglineedit.cpp @@ -0,0 +1,137 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "classnamevalidatinglineedit.h" + +#include <QtCore/QDebug> +#include <QtCore/QRegExp> + +namespace Core { +namespace Utils { + +struct ClassNameValidatingLineEditPrivate { + ClassNameValidatingLineEditPrivate(); + + const QRegExp m_nameRegexp; + const QString m_namespaceDelimiter; + bool m_namespacesEnabled; +}; + +// Match something like "Namespace1::Namespace2::ClassName". +ClassNameValidatingLineEditPrivate:: ClassNameValidatingLineEditPrivate() : + m_nameRegexp(QLatin1String("[a-zA-Z_][a-zA-Z0-9_]*(::[a-zA-Z_][a-zA-Z0-9_]*)*")), + m_namespaceDelimiter(QLatin1String("::")), + m_namespacesEnabled(false) +{ + Q_ASSERT(m_nameRegexp.isValid()); +} + +// --------------------- ClassNameValidatingLineEdit +ClassNameValidatingLineEdit::ClassNameValidatingLineEdit(QWidget *parent) : + Core::Utils::BaseValidatingLineEdit(parent), + m_d(new ClassNameValidatingLineEditPrivate) +{ +} + +ClassNameValidatingLineEdit::~ClassNameValidatingLineEdit() +{ + delete m_d; +} + +bool ClassNameValidatingLineEdit::namespacesEnabled() const +{ + return m_d->m_namespacesEnabled; +} + +void ClassNameValidatingLineEdit::setNamespacesEnabled(bool b) +{ + m_d->m_namespacesEnabled = b; +} + +bool ClassNameValidatingLineEdit::validate(const QString &value, QString *errorMessage) const +{ + if (!m_d->m_namespacesEnabled && value.contains(QLatin1Char(':'))) { + if (errorMessage) + *errorMessage = tr("The class name must not contain namespace delimiters."); + return false; + } + if (!m_d->m_nameRegexp.exactMatch(value)) { + if (errorMessage) + *errorMessage = tr("The class name contains invalid characters."); + return false; + } + return true; +} + +void ClassNameValidatingLineEdit::slotChanged(const QString &t) +{ + Core::Utils::BaseValidatingLineEdit::slotChanged(t); + if (isValid()) { + // Suggest file names, strip namespaces + QString fileName = t.toLower(); + if (m_d->m_namespacesEnabled) { + const int namespaceIndex = fileName.lastIndexOf(m_d->m_namespaceDelimiter); + if (namespaceIndex != -1) + fileName.remove(0, namespaceIndex + m_d->m_namespaceDelimiter.size()); + } + emit updateFileName(fileName); + } +} + +QString ClassNameValidatingLineEdit::createClassName(const QString &name) +{ + // Remove spaces and convert the adjacent characters to uppercase + QString className = name; + QRegExp spaceMatcher(QLatin1String(" +(\\w)"), Qt::CaseSensitive, QRegExp::RegExp2); + Q_ASSERT(spaceMatcher.isValid()); + int pos; + while ((pos = spaceMatcher.indexIn(className)) != -1) { + className.replace(pos, spaceMatcher.matchedLength(), + spaceMatcher.cap(1).toUpper()); + } + + // Filter out any remaining invalid characters + className.remove(QRegExp(QLatin1String("[^a-zA-Z0-9_]"))); + + // If the first character is numeric, prefix the name with a "_" + if (className.at(0).isNumber()) { + className.prepend(QLatin1Char('_')); + } else { + // Convert the first character to uppercase + className.replace(0, 1, className.left(1).toUpper()); + } + + return className; +} + +} +} diff --git a/src/libs/utils/classnamevalidatinglineedit.h b/src/libs/utils/classnamevalidatinglineedit.h new file mode 100644 index 00000000000..c0a209d0d71 --- /dev/null +++ b/src/libs/utils/classnamevalidatinglineedit.h @@ -0,0 +1,79 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CLASSNAMEVALIDATINGLINEEDIT_H +#define CLASSNAMEVALIDATINGLINEEDIT_H + +#include "utils_global.h" +#include "basevalidatinglineedit.h" + +namespace Core { +namespace Utils { + +struct ClassNameValidatingLineEditPrivate; + +/* A Line edit that validates a C++ class name and emits a signal + * to derive suggested file names from it. */ + +class QWORKBENCH_UTILS_EXPORT ClassNameValidatingLineEdit : + public Core::Utils::BaseValidatingLineEdit { + Q_DISABLE_COPY(ClassNameValidatingLineEdit) + Q_PROPERTY(bool namespacesEnabled READ namespacesEnabled WRITE setNamespacesEnabled DESIGNABLE true) + Q_OBJECT + +public: + explicit ClassNameValidatingLineEdit(QWidget *parent = 0); + virtual ~ClassNameValidatingLineEdit(); + + bool namespacesEnabled() const; + void setNamespacesEnabled(bool b); + + // Clean an input string to get a valid class name. + static QString createClassName(const QString &name); + +signals: + // Will be emitted with a suggestion for a base name of the + // source/header file of the class. + void updateFileName(const QString &t); + +protected: + virtual bool validate(const QString &value, QString *errorMessage) const; + virtual void slotChanged(const QString &t); + +private: + ClassNameValidatingLineEditPrivate *m_d; +}; + +} +} + +#endif // CLASSNAMEVALIDATINGLINEEDIT_H diff --git a/src/libs/utils/codegeneration.cpp b/src/libs/utils/codegeneration.cpp new file mode 100644 index 00000000000..3af484935a7 --- /dev/null +++ b/src/libs/utils/codegeneration.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "codegeneration.h" + +#include <QtCore/QTextStream> +#include <QtCore/QStringList> +#include <QtCore/QFileInfo> + +namespace Core { +namespace Utils { + +QWORKBENCH_UTILS_EXPORT QString headerGuard(const QString &file) +{ + QString rc = QFileInfo(file).baseName().toUpper(); + rc += QLatin1String("_H"); + return rc; +} + +QWORKBENCH_UTILS_EXPORT +void writeIncludeFileDirective(const QString &file, bool globalInclude, + QTextStream &str) +{ + const QChar opening = globalInclude ? QLatin1Char('<') : QLatin1Char('"'); + const QChar closing = globalInclude ? QLatin1Char('>') : QLatin1Char('"'); + str << QLatin1String("#include ") << opening << file << closing << QLatin1Char('\n'); +} + +QWORKBENCH_UTILS_EXPORT +QString writeOpeningNameSpaces(const QStringList &l, const QString &indent, + QTextStream &str) +{ + const int count = l.size(); + QString rc; + if (count) { + str << '\n'; + for (int i = 0; i < count; i++) { + str << rc << "namespace " << l.at(i) << " {\n"; + rc += indent; + } + str << '\n'; + } + return rc; +} + +QWORKBENCH_UTILS_EXPORT +void writeClosingNameSpaces(const QStringList &l, const QString &indent, + QTextStream &str) +{ + if (!l.empty()) + str << '\n'; + for (int i = l.size() - 1; i >= 0; i--) { + if (i) + str << QString(indent.size() * i, QLatin1Char(' ')); + str << "} // namespace " << l.at(i) << '\n'; + } +} + +} // namespace Utils +} // namespace Core diff --git a/src/libs/utils/codegeneration.h b/src/libs/utils/codegeneration.h new file mode 100644 index 00000000000..1a20c76d083 --- /dev/null +++ b/src/libs/utils/codegeneration.h @@ -0,0 +1,70 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CODEGENERATION_H +#define CODEGENERATION_H + +#include "utils_global.h" +#include <QtCore/QString> + +QT_BEGIN_NAMESPACE +class QTextStream; +class QStringList; +QT_END_NAMESPACE + +namespace Core { +namespace Utils { + +QWORKBENCH_UTILS_EXPORT QString headerGuard(const QString &file); + +QWORKBENCH_UTILS_EXPORT +void writeIncludeFileDirective(const QString &file, + bool globalInclude, + QTextStream &str); + +// Write opening namespaces and return an indentation string to be used +// in the following code if there are any. +QWORKBENCH_UTILS_EXPORT +QString writeOpeningNameSpaces(const QStringList &namespaces, + const QString &indent, + QTextStream &str); + +// Close namespacesnamespaces +QWORKBENCH_UTILS_EXPORT +void writeClosingNameSpaces(const QStringList &namespaces, + const QString &indent, + QTextStream &str); + +} // namespace Utils +} // namespace Core + +#endif // CODEGENERATION_H diff --git a/src/libs/utils/fancylineedit.cpp b/src/libs/utils/fancylineedit.cpp new file mode 100644 index 00000000000..e7e930c544e --- /dev/null +++ b/src/libs/utils/fancylineedit.cpp @@ -0,0 +1,317 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "fancylineedit.h" + +#include <QtCore/QEvent> +#include <QtCore/QDebug> +#include <QtCore/QString> +#include <QtGui/QApplication> +#include <QtGui/QMenu> +#include <QtGui/QMouseEvent> +#include <QtGui/QLabel> + +enum { margin = 6 }; + +namespace Core { +namespace Utils { + +static inline QString sideToStyleSheetString(FancyLineEdit::Side side) +{ + return side == FancyLineEdit::Left ? QLatin1String("left") : QLatin1String("right"); +} + +// Format style sheet for the label containing the pixmap. It has a margin on +// the outer side of the whole FancyLineEdit. +static QString labelStyleSheet(FancyLineEdit::Side side) +{ + QString rc = QLatin1String("QLabel { margin-"); + rc += sideToStyleSheetString(side); + rc += QLatin1String(": "); + rc += QString::number(margin); + rc += QLatin1Char('}'); + return rc; +} + +// --------- FancyLineEditPrivate as QObject with label +// event filter + +class FancyLineEditPrivate : public QObject { +public: + explicit FancyLineEditPrivate(QLineEdit *parent); + + virtual bool eventFilter(QObject *obj, QEvent *event); + + const QString m_leftLabelStyleSheet; + const QString m_rightLabelStyleSheet; + + QLineEdit *m_lineEdit; + QPixmap m_pixmap; + QMenu *m_menu; + QLabel *m_menuLabel; + FancyLineEdit::Side m_side; + bool m_useLayoutDirection; + bool m_menuTabFocusTrigger; + QString m_hintText; + bool m_showingHintText; +}; + + +FancyLineEditPrivate::FancyLineEditPrivate(QLineEdit *parent) : + QObject(parent), + m_leftLabelStyleSheet(labelStyleSheet(FancyLineEdit::Left)), + m_rightLabelStyleSheet(labelStyleSheet(FancyLineEdit::Right)), + m_lineEdit(parent), + m_menu(0), + m_menuLabel(0), + m_side(FancyLineEdit::Left), + m_useLayoutDirection(false), + m_menuTabFocusTrigger(false), + m_showingHintText(false) +{ +} + +bool FancyLineEditPrivate::eventFilter(QObject *obj, QEvent *event) +{ + if (!m_menu || obj != m_menuLabel) + return QObject::eventFilter(obj, event); + + switch (event->type()) { + case QEvent::MouseButtonPress: { + const QMouseEvent *me = static_cast<QMouseEvent *>(event); + m_menu->exec(me->globalPos()); + return true; + } + case QEvent::FocusIn: + if (m_menuTabFocusTrigger) { + m_lineEdit->setFocus(); + m_menu->exec(m_menuLabel->mapToGlobal(m_menuLabel->rect().center())); + return true; + } + default: + break; + } + return QObject::eventFilter(obj, event); +} + +// --------- FancyLineEdit +FancyLineEdit::FancyLineEdit(QWidget *parent) : + QLineEdit(parent), + m_d(new FancyLineEditPrivate(this)) +{ + m_d->m_menuLabel = new QLabel(this); + m_d->m_menuLabel->installEventFilter(m_d); + updateMenuLabel(); + showHintText(); +} + +FancyLineEdit::~FancyLineEdit() +{ +} + +// Position the menu label left or right according to size. +// Called when switching side and from resizeEvent. +void FancyLineEdit::positionMenuLabel() +{ + switch (side()) { + case Left: + m_d->m_menuLabel->setGeometry(0, 0, m_d->m_pixmap.width()+margin, height()); + break; + case Right: + m_d->m_menuLabel->setGeometry(width() - m_d->m_pixmap.width() - margin, 0, + m_d->m_pixmap.width()+margin, height()); + break; + } +} + +void FancyLineEdit::updateStyleSheet(Side side) +{ + // Udate the LineEdit style sheet. Make room for the label on the + // respective side and set color according to whether we are showing the + // hint text + QString sheet = QLatin1String("QLineEdit{ padding-"); + sheet += sideToStyleSheetString(side); + sheet += QLatin1String(": "); + sheet += QString::number(m_d->m_pixmap.width() + margin); + sheet += QLatin1Char(';'); + if (m_d->m_showingHintText) + sheet += QLatin1String(" color: #BBBBBB;"); + sheet += QLatin1Char('}'); + setStyleSheet(sheet); +} + +void FancyLineEdit::updateMenuLabel() +{ + m_d->m_menuLabel->setPixmap(m_d->m_pixmap); + const Side s = side(); + switch (s) { + case Left: + m_d->m_menuLabel->setAlignment(Qt::AlignVCenter | Qt::AlignLeft); + m_d->m_menuLabel->setStyleSheet(m_d->m_leftLabelStyleSheet); + break; + case Right: + m_d->m_menuLabel->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + m_d->m_menuLabel->setStyleSheet(m_d->m_rightLabelStyleSheet); + break; + } + updateStyleSheet(s); + positionMenuLabel(); +} + +void FancyLineEdit::setSide(Side side) +{ + m_d->m_side = side; + updateMenuLabel(); +} + +FancyLineEdit::Side FancyLineEdit::side() const +{ + if (m_d->m_useLayoutDirection) + return qApp->layoutDirection() == Qt::LeftToRight ? Left : Right; + return m_d->m_side; +} + +void FancyLineEdit::resizeEvent(QResizeEvent *) +{ + positionMenuLabel(); +} + +void FancyLineEdit::setPixmap(const QPixmap &pixmap) +{ + m_d->m_pixmap = pixmap; + updateMenuLabel(); +} + +QPixmap FancyLineEdit::pixmap() const +{ + return m_d->m_pixmap; +} + +void FancyLineEdit::setMenu(QMenu *menu) +{ + m_d->m_menu = menu; +} + +QMenu *FancyLineEdit::menu() const +{ + return m_d->m_menu; +} + +bool FancyLineEdit::useLayoutDirection() const +{ + return m_d->m_useLayoutDirection; +} + +void FancyLineEdit::setUseLayoutDirection(bool v) +{ + m_d->m_useLayoutDirection = v; +} + +bool FancyLineEdit::isSideStored() const +{ + return !m_d->m_useLayoutDirection; +} + +bool FancyLineEdit::hasMenuTabFocusTrigger() const +{ + return m_d->m_menuTabFocusTrigger; +} + +void FancyLineEdit::setMenuTabFocusTrigger(bool v) +{ + if (m_d->m_menuTabFocusTrigger == v) + return; + + m_d->m_menuTabFocusTrigger = v; + m_d->m_menuLabel->setFocusPolicy(v ? Qt::TabFocus : Qt::NoFocus); +} + +QString FancyLineEdit::hintText() const +{ + return m_d->m_hintText; +} + +void FancyLineEdit::setHintText(const QString &ht) +{ + // Updating magic to make the property work in Designer. + if (ht == m_d->m_hintText) + return; + hideHintText(); + m_d->m_hintText = ht; + if (!hasFocus() && !ht.isEmpty()) + showHintText(); +} + +void FancyLineEdit::showHintText() +{ + if (!m_d->m_showingHintText && text().isEmpty() && !m_d->m_hintText.isEmpty()) { + m_d->m_showingHintText = true; + setText(m_d->m_hintText); + updateStyleSheet(side()); + } +} + +void FancyLineEdit::hideHintText() +{ + if (m_d->m_showingHintText && !m_d->m_hintText.isEmpty()) { + m_d->m_showingHintText = false; + setText(QString()); + updateStyleSheet(side()); + } +} + +void FancyLineEdit::focusInEvent(QFocusEvent *e) +{ + hideHintText(); + QLineEdit::focusInEvent(e); +} + +void FancyLineEdit::focusOutEvent(QFocusEvent *e) +{ + // Focus out: Switch to displaying the hint text unless + // there is user input + showHintText(); + QLineEdit::focusOutEvent(e); +} + +bool FancyLineEdit::isShowingHintText() const +{ + return m_d->m_showingHintText; +} + +QString FancyLineEdit::typedText() const +{ + return m_d->m_showingHintText ? QString() : text(); +} + +} // namespace Utils +} // namespace Core diff --git a/src/libs/utils/fancylineedit.h b/src/libs/utils/fancylineedit.h new file mode 100644 index 00000000000..24a109eada3 --- /dev/null +++ b/src/libs/utils/fancylineedit.h @@ -0,0 +1,115 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FANCYLINEEDIT_H +#define FANCYLINEEDIT_H + +#include "utils_global.h" + +#include <QtGui/QLineEdit> + +namespace Core { +namespace Utils { + +class FancyLineEditPrivate; + +/* A line edit with an embedded pixmap on one side that is connected to + * a menu. Additionally, it can display a grayed hintText (like "Type Here to") + * when not focussed and empty. When connecting to the changed signals and + * querying text, one has to be aware that the text is set to that hint + * text if isShowingHintText() returns true (that is, does not contain + * valid user input). + */ +class QWORKBENCH_UTILS_EXPORT FancyLineEdit : public QLineEdit +{ + Q_DISABLE_COPY(FancyLineEdit) + Q_OBJECT + Q_ENUMS(Side) + Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap DESIGNABLE true) + Q_PROPERTY(Side side READ side WRITE setSide DESIGNABLE isSideStored STORED isSideStored) + Q_PROPERTY(bool useLayoutDirection READ useLayoutDirection WRITE setUseLayoutDirection DESIGNABLE true) + Q_PROPERTY(bool menuTabFocusTrigger READ hasMenuTabFocusTrigger WRITE setMenuTabFocusTrigger DESIGNABLE true) + Q_PROPERTY(QString hintText READ hintText WRITE setHintText DESIGNABLE true) + +public: + enum Side {Left, Right}; + + explicit FancyLineEdit(QWidget *parent = 0); + ~FancyLineEdit(); + + QPixmap pixmap() const; + + void setMenu(QMenu *menu); + QMenu *menu() const; + + void setSide(Side side); + Side side() const; + + bool useLayoutDirection() const; + void setUseLayoutDirection(bool v); + + /* Set whether tabbing in will trigger the menu. */ + bool hasMenuTabFocusTrigger() const; + void setMenuTabFocusTrigger(bool v); + + /* Hint text that is displayed when no focus is set */ + QString hintText() const; + + bool isShowingHintText() const; + + // Convenience for accessing the text that returns "" in case of isShowingHintText(). + QString typedText() const; + +public slots: + void setPixmap(const QPixmap &pixmap); + void setHintText(const QString &ht); + void showHintText(); + void hideHintText(); + +protected: + virtual void resizeEvent(QResizeEvent *e); + virtual void focusInEvent(QFocusEvent *e); + virtual void focusOutEvent(QFocusEvent *e); + +private: + bool isSideStored() const; + void updateMenuLabel(); + void positionMenuLabel(); + void updateStyleSheet(Side side); + + FancyLineEditPrivate *m_d; +}; + +} // namespace Utils +} // namespace Core + +#endif // FANCYLINEEDIT_H diff --git a/src/libs/utils/filenamevalidatinglineedit.cpp b/src/libs/utils/filenamevalidatinglineedit.cpp new file mode 100644 index 00000000000..1beed717efd --- /dev/null +++ b/src/libs/utils/filenamevalidatinglineedit.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "filenamevalidatinglineedit.h" + +namespace Core { +namespace Utils { + +FileNameValidatingLineEdit::FileNameValidatingLineEdit(QWidget *parent) : + BaseValidatingLineEdit(parent) +{ + +} + +/* Validate a file base name, check for forbidden characters/strings. */ + +static const char *notAllowedChars = "/?:&\\*\"|#%<> "; +static const char *notAllowedSubStrings[] = {".."}; + +// Naming a file like a device name will break on Windows, even if it is +// "com1.txt". Since we are cross-platform, we generally disallow such file +// names. +static const char *notAllowedStrings[] = {"CON", "AUX", "PRN", "COM1", "COM2", "LPT1", "LPT2" }; + +bool FileNameValidatingLineEdit::validateFileName(const QString &name, QString *errorMessage /* = 0*/) +{ + if (name.isEmpty()) { + if (errorMessage) + *errorMessage = tr("The name must not be empty"); + return false; + } + // Characters + for (const char *c = notAllowedChars; *c; c++) + if (name.contains(QLatin1Char(*c))) { + if (errorMessage) + *errorMessage = tr("The name must not contain any of the characters '%1'.").arg(QLatin1String(notAllowedChars)); + return false; + } + // Substrings + const int notAllowedSubStringCount = sizeof(notAllowedSubStrings)/sizeof(const char *); + for (int s = 0; s < notAllowedSubStringCount; s++) { + const QLatin1String notAllowedSubString(notAllowedSubStrings[s]); + if (name.contains(notAllowedSubString)) { + if (errorMessage) + *errorMessage = tr("The name must not contain '%1'.").arg(QString(notAllowedSubString)); + return false; + } + } + // Strings + const int notAllowedStringCount = sizeof(notAllowedStrings)/sizeof(const char *); + for (int s = 0; s < notAllowedStringCount; s++) { + const QLatin1String notAllowedString(notAllowedStrings[s]); + if (name == notAllowedString) { + if (errorMessage) + *errorMessage = tr("The name must not be '%1'.").arg(QString(notAllowedString)); + return false; + } + } + return true; +} + +bool FileNameValidatingLineEdit::validate(const QString &value, QString *errorMessage) const +{ + return validateFileName(value, errorMessage); +} + +} +} diff --git a/src/libs/utils/filenamevalidatinglineedit.h b/src/libs/utils/filenamevalidatinglineedit.h new file mode 100644 index 00000000000..7535fc196de --- /dev/null +++ b/src/libs/utils/filenamevalidatinglineedit.h @@ -0,0 +1,56 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FILENAMEVALIDATINGLINEEDIT_H +#define FILENAMEVALIDATINGLINEEDIT_H + +#include "basevalidatinglineedit.h" + +namespace Core { +namespace Utils { + +class QWORKBENCH_UTILS_EXPORT FileNameValidatingLineEdit : public BaseValidatingLineEdit { + Q_OBJECT + Q_DISABLE_COPY(FileNameValidatingLineEdit) + +public: + explicit FileNameValidatingLineEdit(QWidget *parent = 0); + + static bool validateFileName(const QString &name, QString *errorMessage /* = 0*/); + +protected: + virtual bool validate(const QString &value, QString *errorMessage) const; +}; + +} +} +#endif // FILENAMEVALIDATINGLINEEDIT_H diff --git a/src/libs/utils/filesearch.cpp b/src/libs/utils/filesearch.cpp new file mode 100644 index 00000000000..9756984b4b0 --- /dev/null +++ b/src/libs/utils/filesearch.cpp @@ -0,0 +1,216 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "filesearch.h" + +#include <QtCore/QFile> +#include <QtCore/QDir> +#include <QtCore/QFutureInterface> +#include <QtCore/QtConcurrentRun> +#include <QtCore/QRegExp> +#include <QtGui/QApplication> + +#include <qtconcurrent/runextensions.h> + +using namespace Core::Utils; + +namespace { + void runFileSearch(QFutureInterface<FileSearchResult> &future, + QString searchTerm, + QStringList files, + QTextDocument::FindFlags flags) + { + future.setProgressRange(0, files.size()); + int numFilesSearched = 0; + int numMatches = 0; + + bool caseInsensitive = !(flags & QTextDocument::FindCaseSensitively); + bool wholeWord = (flags & QTextDocument::FindWholeWords); + + QByteArray sa = searchTerm.toUtf8(); + int scMaxIndex = sa.length()-1; + const char *sc = sa.constData(); + + QByteArray sal = searchTerm.toLower().toUtf8(); + const char *scl = sal.constData(); + + QByteArray sau = searchTerm.toUpper().toUtf8(); + const char *scu = sau.constData(); + + int chunkSize = qMax(100000, sa.length()); + + foreach (QString s, files) { + if (future.isPaused()) + future.waitForResume(); + if (future.isCanceled()) { + future.setProgressValueAndText(numFilesSearched, + qApp->translate("FileSearch", "%1: canceled. %2 occurrences found in %3 files."). + arg(searchTerm).arg(numMatches).arg(numFilesSearched)); + break; + } + QFile file(s); + if (!file.open(QIODevice::ReadOnly)) + continue; + int lineNr = 1; + const char *startOfLastLine = NULL; + + bool firstChunk = true; + while (!file.atEnd()) { + if (!firstChunk) + file.seek(file.pos()-sa.length()+1); + + const QByteArray chunk = file.read(chunkSize); + const char *chunkPtr = chunk.constData(); + startOfLastLine = chunkPtr; + for (const char *regionPtr = chunkPtr; regionPtr < chunkPtr + chunk.length()-scMaxIndex; ++regionPtr) { + const char *regionEnd = regionPtr + scMaxIndex; + + if (*regionPtr == '\n') { + startOfLastLine = regionPtr + 1; + ++lineNr; + } + else if ( + // case sensitive + (!caseInsensitive && *regionPtr == sc[0] && *regionEnd == sc[scMaxIndex]) + || + // case insensitive + (caseInsensitive && (*regionPtr == scl[0] || *regionPtr == scu[0]) + && (*regionEnd == scl[scMaxIndex] || *regionEnd == scu[scMaxIndex])) + ) { + const char *afterRegion = regionEnd + 1; + const char *beforeRegion = regionPtr - 1; + bool equal = true; + if (wholeWord && + ( ((*beforeRegion >= '0' && *beforeRegion <= '9') || *beforeRegion >= 'A') + || ((*afterRegion >= '0' && *afterRegion <= '9') || *afterRegion >= 'A'))) + { + equal = false; + } + + int regionIndex = 1; + for (const char *regionCursor = regionPtr + 1; regionCursor < regionEnd; ++regionCursor, ++regionIndex) { + if ( // case sensitive + (!caseInsensitive && equal && *regionCursor != sc[regionIndex]) + || + // case insensitive + (caseInsensitive && equal && *regionCursor != sc[regionIndex] && *regionCursor != scl[regionIndex] && *regionCursor != scu[regionIndex]) + ) { + equal = false; + } + } + if (equal) { + int textLength = chunk.length() - (startOfLastLine - chunkPtr); + if (textLength > 0) { + QByteArray res; + res.reserve(256); + int i = 0; + int n = 0; + while (startOfLastLine[i] != '\n' && startOfLastLine[i] != '\r' && i < textLength && n++ < 256) + res.append(startOfLastLine[i++]); + future.reportResult(FileSearchResult(QDir::toNativeSeparators(s), lineNr, QString(res), + regionPtr - startOfLastLine, sa.length())); + ++numMatches; + } + } + } + } + firstChunk = false; + } + ++numFilesSearched; + future.setProgressValueAndText(numFilesSearched, qApp->translate("FileSearch", "%1: %2 occurrences found in %3 of %4 files."). + arg(searchTerm).arg(numMatches).arg(numFilesSearched).arg(files.size())); + } + if (!future.isCanceled()) + future.setProgressValueAndText(numFilesSearched, qApp->translate("FileSearch", "%1: %2 occurrences found in %3 files."). + arg(searchTerm).arg(numMatches).arg(numFilesSearched)); + } + + void runFileSearchRegExp(QFutureInterface<FileSearchResult> &future, + QString searchTerm, + QStringList files, + QTextDocument::FindFlags flags) + { + future.setProgressRange(0, files.size()); + int numFilesSearched = 0; + int numMatches = 0; + if (flags & QTextDocument::FindWholeWords) + searchTerm = QString("\b%1\b").arg(searchTerm); + Qt::CaseSensitivity caseSensitivity = (flags & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive; + QRegExp expression(searchTerm, caseSensitivity); + + foreach (QString s, files) { + if (future.isPaused()) + future.waitForResume(); + if (future.isCanceled()) { + future.setProgressValueAndText(numFilesSearched, + qApp->translate("FileSearch", "%1: canceled. %2 occurrences found in %3 files."). + arg(searchTerm).arg(numMatches).arg(numFilesSearched)); + break; + } + QFile file(s); + if (!file.open(QIODevice::ReadOnly)) + continue; + QTextStream stream(&file); + int lineNr = 1; + QString line; + while (!stream.atEnd()) { + line = stream.readLine(); + int pos = 0; + while ((pos = expression.indexIn(line, pos)) != -1) { + future.reportResult(FileSearchResult(QDir::toNativeSeparators(s), lineNr, line, + pos, expression.matchedLength())); + pos += expression.matchedLength(); + } + ++lineNr; + } + ++numFilesSearched; + future.setProgressValueAndText(numFilesSearched, qApp->translate("FileSearch", "%1: %2 occurrences found in %3 of %4 files."). + arg(searchTerm).arg(numMatches).arg(numFilesSearched).arg(files.size())); + } + if (!future.isCanceled()) + future.setProgressValueAndText(numFilesSearched, qApp->translate("FileSearch", "%1: %2 occurrences found in %3 files."). + arg(searchTerm).arg(numMatches).arg(numFilesSearched)); + } +} // namespace + + +QFuture<FileSearchResult> Core::Utils::findInFiles(const QString &searchTerm, const QStringList &files, + QTextDocument::FindFlags flags) +{ + return QtConcurrent::run<FileSearchResult, QString, QStringList, QTextDocument::FindFlags>(runFileSearch, searchTerm, files, flags); +} + +QFuture<FileSearchResult> Core::Utils::findInFilesRegExp(const QString &searchTerm, const QStringList &files, + QTextDocument::FindFlags flags) +{ + return QtConcurrent::run<FileSearchResult, QString, QStringList, QTextDocument::FindFlags>(runFileSearchRegExp, searchTerm, files, flags); +} diff --git a/src/libs/utils/filesearch.h b/src/libs/utils/filesearch.h new file mode 100644 index 00000000000..3b747fb5489 --- /dev/null +++ b/src/libs/utils/filesearch.h @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FILESEARCH_H +#define FILESEARCH_H + +#include "utils_global.h" + +#include <QtCore/QStringList> +#include <QtCore/QFuture> +#include <QtGui/QTextDocument> + +namespace Core { +namespace Utils { + +class QWORKBENCH_UTILS_EXPORT FileSearchResult +{ +public: + FileSearchResult() {} + FileSearchResult(QString fileName, int lineNumber, QString matchingLine, int matchStart, int matchLength) + : fileName(fileName), lineNumber(lineNumber), matchingLine(matchingLine), matchStart(matchStart), matchLength(matchLength) + { + } + QString fileName; + int lineNumber; + QString matchingLine; + int matchStart; + int matchLength; +}; + +QWORKBENCH_UTILS_EXPORT QFuture<FileSearchResult> findInFiles(const QString &searchTerm, const QStringList &files, + QTextDocument::FindFlags flags); + +QWORKBENCH_UTILS_EXPORT QFuture<FileSearchResult> findInFilesRegExp(const QString &searchTerm, const QStringList &files, + QTextDocument::FindFlags flags); + +} //Utils +} //Core + +#endif diff --git a/src/libs/utils/filewizarddialog.cpp b/src/libs/utils/filewizarddialog.cpp new file mode 100644 index 00000000000..2843a304e5b --- /dev/null +++ b/src/libs/utils/filewizarddialog.cpp @@ -0,0 +1,73 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "filewizarddialog.h" +#include "filewizardpage.h" + +#include <QtGui/QAbstractButton> + +namespace Core { +namespace Utils { + +FileWizardDialog::FileWizardDialog(QWidget *parent) : + QWizard(parent), + m_filePage(new FileWizardPage) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setPixmap(QWizard::WatermarkPixmap, QPixmap(QLatin1String(":/qworkbench/images/qtwatermark.png"))); + addPage(m_filePage); + connect(m_filePage, SIGNAL(activated()), button(QWizard::FinishButton), SLOT(animateClick())); +} + +QString FileWizardDialog::name() const +{ + return m_filePage->name(); +} + +QString FileWizardDialog::path() const +{ + return m_filePage->path(); +} + +void FileWizardDialog::setPath(const QString &path) +{ + m_filePage->setPath(path); + +} + +void FileWizardDialog::setName(const QString &name) +{ + m_filePage->setName(name); +} + +} // namespace Utils +} // namespace Core diff --git a/src/libs/utils/filewizarddialog.h b/src/libs/utils/filewizarddialog.h new file mode 100644 index 00000000000..6a4a7d9ba6f --- /dev/null +++ b/src/libs/utils/filewizarddialog.h @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FILEWIZARDDIALOG_H +#define FILEWIZARDDIALOG_H + +#include "utils_global.h" + +#include <QtGui/QWizard> + +namespace Core { +namespace Utils { + +class FileWizardPage; + +/* + Standard wizard for a single file letting the user choose name + and path. Custom pages can be added via Core::IWizardExtension. +*/ + +class QWORKBENCH_UTILS_EXPORT FileWizardDialog : public QWizard { + Q_OBJECT + Q_DISABLE_COPY(FileWizardDialog) +public: + explicit FileWizardDialog(QWidget *parent = 0); + + QString name() const; + QString path() const; + +public slots: + void setPath(const QString &path); + void setName(const QString &name); + +private: + FileWizardPage *m_filePage; +}; + +} +} +#endif // FILEWIZARDDIALOG_H diff --git a/src/libs/utils/filewizardpage.cpp b/src/libs/utils/filewizardpage.cpp new file mode 100644 index 00000000000..a448ebe7395 --- /dev/null +++ b/src/libs/utils/filewizardpage.cpp @@ -0,0 +1,129 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "filewizardpage.h" +#include "ui_filewizardpage.h" + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtGui/QMessageBox> + +namespace Core { +namespace Utils { + +struct FileWizardPagePrivate +{ + FileWizardPagePrivate(); + Ui::WizardPage m_ui; + bool m_complete; +}; + +FileWizardPagePrivate::FileWizardPagePrivate() : + m_complete(false) +{ +} + +FileWizardPage::FileWizardPage(QWidget *parent) : + QWizardPage(parent), + m_d(new FileWizardPagePrivate) +{ + m_d->m_ui.setupUi(this); + connect(m_d->m_ui.pathChooser, SIGNAL(validChanged()), this, SLOT(slotValidChanged())); + connect(m_d->m_ui.nameLineEdit, SIGNAL(validChanged()), this, SLOT(slotValidChanged())); + + connect(m_d->m_ui.pathChooser, SIGNAL(returnPressed()), this, SLOT(slotActivated())); + connect(m_d->m_ui.nameLineEdit, SIGNAL(validReturnPressed()), this, SLOT(slotActivated())); +} + +FileWizardPage::~FileWizardPage() +{ + delete m_d; +} + +QString FileWizardPage::name() const +{ + return m_d->m_ui.nameLineEdit->text(); +} + +QString FileWizardPage::path() const +{ + return m_d->m_ui.pathChooser->path(); +} + +void FileWizardPage::setPath(const QString &path) +{ + m_d->m_ui.pathChooser->setPath(path); +} + +void FileWizardPage::setName(const QString &name) +{ + m_d->m_ui.nameLineEdit->setText(name); +} + +void FileWizardPage::changeEvent(QEvent *e) +{ + switch(e->type()) { + case QEvent::LanguageChange: + m_d->m_ui.retranslateUi(this); + break; + default: + break; + } +} + +bool FileWizardPage::isComplete() const +{ + return m_d->m_complete; +} + +void FileWizardPage::slotValidChanged() +{ + const bool newComplete = m_d->m_ui.pathChooser->isValid() && m_d->m_ui.nameLineEdit->isValid(); + if (newComplete != m_d->m_complete) { + m_d->m_complete = newComplete; + emit completeChanged(); + } +} + +void FileWizardPage::slotActivated() +{ + if (m_d->m_complete) + emit activated(); +} + +bool FileWizardPage::validateBaseName(const QString &name, QString *errorMessage /* = 0*/) +{ + return FileNameValidatingLineEdit::validateFileName(name, errorMessage); +} + +} // namespace Utils +} // namespace Core diff --git a/src/libs/utils/filewizardpage.h b/src/libs/utils/filewizardpage.h new file mode 100644 index 00000000000..b2ae28a9d0d --- /dev/null +++ b/src/libs/utils/filewizardpage.h @@ -0,0 +1,86 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FILEWIZARDPAGE_H +#define FILEWIZARDPAGE_H + +#include "utils_global.h" + +#include <QtGui/QWizardPage> + +namespace Core { +namespace Utils { + +struct FileWizardPagePrivate; + +/* Standard wizard page for a single file letting the user choose name + * and path. Sets the "FileNames" QWizard field. */ + +class QWORKBENCH_UTILS_EXPORT FileWizardPage : public QWizardPage { + Q_OBJECT + Q_DISABLE_COPY(FileWizardPage) + Q_PROPERTY(QString path READ path WRITE setPath DESIGNABLE true) + Q_PROPERTY(QString name READ name WRITE setName DESIGNABLE true) +public: + explicit FileWizardPage(QWidget *parent = 0); + virtual ~FileWizardPage(); + + QString name() const; + QString path() const; + + virtual bool isComplete() const; + + // Validate a base name entry field (potentially containing extension) + static bool validateBaseName(const QString &name, QString *errorMessage = 0); + +signals: + void activated(); + void pathChanged(); + +public slots: + void setPath(const QString &path); + void setName(const QString &name); + +private slots: + void slotValidChanged(); + void slotActivated(); + +protected: + virtual void changeEvent(QEvent *e); + +private: + FileWizardPagePrivate *m_d; +}; + +} +} +#endif // FILEWIZARDPAGE_H diff --git a/src/libs/utils/filewizardpage.ui b/src/libs/utils/filewizardpage.ui new file mode 100644 index 00000000000..2e614c6f552 --- /dev/null +++ b/src/libs/utils/filewizardpage.ui @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Core::Utils::WizardPage</class> + <widget class="QWizardPage" name="Core::Utils::WizardPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>WizardPage</string> + </property> + <property name="title"> + <string>Choose the location</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QWidget" name="widget" native="true"> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::AllNonFixedFieldsGrow</enum> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="nameLabel"> + <property name="text"> + <string>Name:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="Core::Utils::FileNameValidatingLineEdit" name="nameLineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="pathLabel"> + <property name="text"> + <string>Path:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="Core::Utils::PathChooser" name="pathChooser" native="true"/> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>201</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Core::Utils::PathChooser</class> + <extends>QWidget</extends> + <header>pathchooser.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>Core::Utils::FileNameValidatingLineEdit</class> + <extends>QLineEdit</extends> + <header>filenamevalidatinglineedit.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/libs/utils/linecolumnlabel.cpp b/src/libs/utils/linecolumnlabel.cpp new file mode 100644 index 00000000000..c6028ab13ff --- /dev/null +++ b/src/libs/utils/linecolumnlabel.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "linecolumnlabel.h" + +namespace Core { +namespace Utils { + +LineColumnLabel::LineColumnLabel(QWidget *parent) : + QLabel(parent), + m_unused(0) +{ +} + +LineColumnLabel::~LineColumnLabel() +{ +} + +void LineColumnLabel::setText(const QString &text, const QString &maxText) +{ + QLabel::setText(text); + m_maxText = maxText; +} +QSize LineColumnLabel::sizeHint() const +{ + return fontMetrics().boundingRect(m_maxText).size(); +} + +QString LineColumnLabel::maxText() const +{ + return m_maxText; +} + +void LineColumnLabel::setMaxText(const QString &maxText) +{ + m_maxText = maxText; +} + +} +} diff --git a/src/libs/utils/linecolumnlabel.h b/src/libs/utils/linecolumnlabel.h new file mode 100644 index 00000000000..d5dea084cc2 --- /dev/null +++ b/src/libs/utils/linecolumnlabel.h @@ -0,0 +1,68 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef LINECOLUMNLABEL_H +#define LINECOLUMNLABEL_H + +#include "utils_global.h" +#include <QtGui/QLabel> + +namespace Core { +namespace Utils { + +/* A label suitable for displaying cursor positions, etc. with a fixed + * with derived from a sample text. */ + +class QWORKBENCH_UTILS_EXPORT LineColumnLabel : public QLabel { + Q_DISABLE_COPY(LineColumnLabel) + Q_OBJECT + Q_PROPERTY(QString maxText READ maxText WRITE setMaxText DESIGNABLE true) + +public: + explicit LineColumnLabel(QWidget *parent = 0); + virtual ~LineColumnLabel(); + + void setText(const QString &text, const QString &maxText); + QSize sizeHint() const; + + QString maxText() const; + void setMaxText(const QString &maxText); + +private: + QString m_maxText; + void *m_unused; +}; + +} +} + +#endif // LINECOLUMNLABEL_H diff --git a/src/libs/utils/listutils.h b/src/libs/utils/listutils.h new file mode 100644 index 00000000000..3b688f336df --- /dev/null +++ b/src/libs/utils/listutils.h @@ -0,0 +1,54 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef LISTUTILS_H +#define LISTUTILS_H + +#include <QtCore/QList> + +namespace Core { +namespace Utils { + +template <class T1, class T2> +QList<T1> qwConvertList(const QList<T2> &list) +{ + QList<T1> convertedList; + foreach (T2 listEntry, list) { + convertedList << qobject_cast<T1>(listEntry); + } + return convertedList; +} + +} // Utils +} // Core + +#endif // LISTUTILS_H diff --git a/src/libs/utils/newclasswidget.cpp b/src/libs/utils/newclasswidget.cpp new file mode 100644 index 00000000000..67cd1f8691d --- /dev/null +++ b/src/libs/utils/newclasswidget.cpp @@ -0,0 +1,462 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "newclasswidget.h" +#include "ui_newclasswidget.h" + +#include <utils/filewizardpage.h> + +#include <QtGui/QFileDialog> +#include <QtCore/QFileInfo> +#include <QtCore/QStringList> +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtCore/QRegExp> + +enum { debugNewClassWidget = 0 }; + +namespace Core { +namespace Utils { + +struct NewClassWidgetPrivate { + NewClassWidgetPrivate(); + + Ui::NewClassWidget m_ui; + QString m_headerExtension; + QString m_sourceExtension; + QString m_formExtension; + bool m_valid; + bool m_classEdited; + // Store the "visible" values to prevent the READ accessors from being + // fooled by a temporarily hidden widget + bool m_baseClassInputVisible; + bool m_formInputVisible; + bool m_pathInputVisible; + bool m_formInputCheckable; +}; + +NewClassWidgetPrivate:: NewClassWidgetPrivate() : + m_headerExtension(QLatin1String("h")), + m_sourceExtension(QLatin1String("cpp")), + m_formExtension(QLatin1String("ui")), + m_valid(false), + m_classEdited(false), + m_baseClassInputVisible(true), + m_formInputVisible(true), + m_pathInputVisible(true), + m_formInputCheckable(false) +{ +} + +// --------------------- NewClassWidget +NewClassWidget::NewClassWidget(QWidget *parent) : + QWidget(parent), + m_d(new NewClassWidgetPrivate) +{ + m_d->m_ui.setupUi(this); + + m_d->m_ui.baseClassComboBox->setEditable(false); + + connect(m_d->m_ui.classLineEdit, SIGNAL(updateFileName(QString)), + this, SLOT(updateFileNames(QString))); + connect(m_d->m_ui.classLineEdit, SIGNAL(textEdited(QString)), + this, SLOT(classNameEdited())); + connect(m_d->m_ui.baseClassComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(suggestClassNameFromBase())); + connect(m_d->m_ui.baseClassComboBox, SIGNAL(editTextChanged(QString)), + this, SLOT(slotValidChanged())); + connect(m_d->m_ui.classLineEdit, SIGNAL(validChanged()), + this, SLOT(slotValidChanged())); + connect(m_d->m_ui.headerFileLineEdit, SIGNAL(validChanged()), + this, SLOT(slotValidChanged())); + connect(m_d->m_ui.sourceFileLineEdit, SIGNAL(validChanged()), + this, SLOT(slotValidChanged())); + connect(m_d->m_ui.formFileLineEdit, SIGNAL(validChanged()), + this, SLOT(slotValidChanged())); + connect(m_d->m_ui.pathChooser, SIGNAL(validChanged()), + this, SLOT(slotValidChanged())); + + connect(m_d->m_ui.classLineEdit, SIGNAL(validReturnPressed()), + this, SLOT(slotActivated())); + connect(m_d->m_ui.headerFileLineEdit, SIGNAL(validReturnPressed()), + this, SLOT(slotActivated())); + connect(m_d->m_ui.sourceFileLineEdit, SIGNAL(validReturnPressed()), + this, SLOT(slotActivated())); + connect(m_d->m_ui.formFileLineEdit, SIGNAL(validReturnPressed()), + this, SLOT(slotActivated())); + connect(m_d->m_ui.formFileLineEdit, SIGNAL(validReturnPressed()), + this, SLOT(slotActivated())); + connect(m_d->m_ui.pathChooser, SIGNAL(returnPressed()), + this, SLOT(slotActivated())); + + connect(m_d->m_ui.generateFormCheckBox, SIGNAL(stateChanged(int)), + this, SLOT(slotFormInputChecked())); + + m_d->m_ui.generateFormCheckBox->setChecked(true); + setFormInputCheckable(false, true); +} + +NewClassWidget::~NewClassWidget() +{ + delete m_d; +} + +void NewClassWidget::classNameEdited() +{ + if (debugNewClassWidget) + qDebug() << Q_FUNC_INFO << m_d->m_headerExtension << m_d->m_sourceExtension; + m_d->m_classEdited = true; +} + +void NewClassWidget::suggestClassNameFromBase() +{ + if (debugNewClassWidget) + qDebug() << Q_FUNC_INFO << m_d->m_headerExtension << m_d->m_sourceExtension; + if (m_d->m_classEdited) + return; + // Suggest a class unless edited ("QMainWindow"->"MainWindow") + QString base = baseClassName(); + if (base.startsWith(QLatin1Char('Q'))) { + base.remove(0, 1); + setClassName(base); + } +} + +QStringList NewClassWidget::baseClassChoices() const +{ + QStringList rc; + const int count = m_d->m_ui.baseClassComboBox->count(); + for (int i = 0; i < count; i++) + rc.push_back(m_d->m_ui.baseClassComboBox->itemText(i)); + return rc; +} + +void NewClassWidget::setBaseClassChoices(const QStringList &choices) +{ + m_d->m_ui.baseClassComboBox->clear(); + m_d->m_ui.baseClassComboBox->addItems(choices); +} + +void NewClassWidget::setBaseClassInputVisible(bool visible) +{ + m_d->m_baseClassInputVisible = visible; + m_d->m_ui.baseClassLabel->setVisible(visible); + m_d->m_ui.baseClassComboBox->setVisible(visible); +} + +void NewClassWidget::setBaseClassEditable(bool editable) +{ + m_d->m_ui.baseClassComboBox->setEditable(editable); +} + +bool NewClassWidget::isBaseClassInputVisible() const +{ + return m_d->m_baseClassInputVisible; +} + +bool NewClassWidget::isBaseClassEditable() const +{ + return m_d->m_ui.baseClassComboBox->isEditable(); +} + +void NewClassWidget::setFormInputVisible(bool visible) +{ + m_d->m_formInputVisible = visible; + m_d->m_ui.formLabel->setVisible(visible); + m_d->m_ui.formFileLineEdit->setVisible(visible); +} + +bool NewClassWidget::isFormInputVisible() const +{ + return m_d->m_formInputVisible; +} + +void NewClassWidget::setFormInputCheckable(bool checkable) +{ + setFormInputCheckable(checkable, false); +} + +void NewClassWidget::setFormInputCheckable(bool checkable, bool force) +{ + if (!force && checkable == m_d->m_formInputCheckable) + return; + m_d->m_formInputCheckable = checkable; + m_d->m_ui.generateFormLabel->setVisible(checkable); + m_d->m_ui.generateFormCheckBox->setVisible(checkable); +} + +void NewClassWidget::setFormInputChecked(bool v) +{ + m_d->m_ui.generateFormCheckBox->setChecked(v); +} + +bool NewClassWidget::formInputCheckable() const +{ + return m_d->m_formInputCheckable; +} + +bool NewClassWidget::formInputChecked() const +{ + return m_d->m_ui.generateFormCheckBox->isChecked(); +} + +void NewClassWidget::slotFormInputChecked() +{ + const bool checked = formInputChecked(); + m_d->m_ui.formLabel->setEnabled(checked); + m_d->m_ui.formFileLineEdit->setEnabled(checked); +} + +void NewClassWidget::setPathInputVisible(bool visible) +{ + m_d->m_pathInputVisible = visible; + m_d->m_ui.pathLabel->setVisible(visible); + m_d->m_ui.pathChooser->setVisible(visible); +} + +bool NewClassWidget::isPathInputVisible() const +{ + return m_d->m_pathInputVisible; +} + +void NewClassWidget::setClassName(const QString &suggestedName) +{ + if (debugNewClassWidget) + qDebug() << Q_FUNC_INFO << suggestedName << m_d->m_headerExtension << m_d->m_sourceExtension; + m_d->m_ui.classLineEdit->setText(ClassNameValidatingLineEdit::createClassName(suggestedName)); +} + +QString NewClassWidget::className() const +{ + return m_d->m_ui.classLineEdit->text(); +} + +QString NewClassWidget::baseClassName() const +{ + return m_d->m_ui.baseClassComboBox->currentText(); +} + +void NewClassWidget::setBaseClassName(const QString &c) +{ + const int index = m_d->m_ui.baseClassComboBox->findText(c); + if (index != -1) { + m_d->m_ui.baseClassComboBox->setCurrentIndex(index); + suggestClassNameFromBase(); + } +} + +QString NewClassWidget::sourceFileName() const +{ + return m_d->m_ui.sourceFileLineEdit->text(); +} + +QString NewClassWidget::headerFileName() const +{ + return m_d->m_ui.headerFileLineEdit->text(); +} + +QString NewClassWidget::formFileName() const +{ + return m_d->m_ui.formFileLineEdit->text(); +} + +QString NewClassWidget::path() const +{ + return m_d->m_ui.pathChooser->path(); +} + +void NewClassWidget::setPath(const QString &path) +{ + m_d->m_ui.pathChooser->setPath(path); +} + +bool NewClassWidget::namespacesEnabled() const +{ + return m_d->m_ui.classLineEdit->namespacesEnabled(); +} + +void NewClassWidget::setNamespacesEnabled(bool b) +{ + m_d->m_ui.classLineEdit->setNamespacesEnabled(b); +} + +QString NewClassWidget::sourceExtension() const +{ + return m_d->m_sourceExtension; +} + +void NewClassWidget::setSourceExtension(const QString &e) +{ + if (debugNewClassWidget) + qDebug() << Q_FUNC_INFO << e; + m_d->m_sourceExtension = fixSuffix(e); +} + +QString NewClassWidget::headerExtension() const +{ + return m_d->m_headerExtension; +} + +void NewClassWidget::setHeaderExtension(const QString &e) +{ + if (debugNewClassWidget) + qDebug() << Q_FUNC_INFO << e; + m_d->m_headerExtension = fixSuffix(e); +} + +QString NewClassWidget::formExtension() const +{ + return m_d->m_formExtension; +} + +void NewClassWidget::setFormExtension(const QString &e) +{ + if (debugNewClassWidget) + qDebug() << Q_FUNC_INFO << e; + m_d->m_formExtension = fixSuffix(e); +} + +void NewClassWidget::slotValidChanged() +{ + const bool newValid = isValid(); + if (newValid != m_d->m_valid) { + m_d->m_valid = newValid; + emit validChanged(); + } +} + +bool NewClassWidget::isValid(QString *error) const +{ + if (!m_d->m_ui.classLineEdit->isValid()) { + if (error) + *error = m_d->m_ui.classLineEdit->errorMessage(); + return false; + } + + if (isBaseClassInputVisible() && isBaseClassEditable()) { + // TODO: Should this be a ClassNameValidatingComboBox? + QRegExp classNameValidator(QLatin1String("[a-zA-Z_][a-zA-Z0-9_]*(::[a-zA-Z_][a-zA-Z0-9_]*)*")); + const QString baseClass = m_d->m_ui.baseClassComboBox->currentText().trimmed(); + if (!baseClass.isEmpty() && !classNameValidator.exactMatch(baseClass)) { + if (error) + *error = tr("Invalid base class name"); + return false; + } + } + + if (!m_d->m_ui.headerFileLineEdit->isValid()) { + if (error) + *error = tr("Invalid header file name: %1").arg(m_d->m_ui.headerFileLineEdit->errorMessage()); + return false; + } + + if (!m_d->m_ui.sourceFileLineEdit->isValid()) { + if (error) + *error = tr("Invalid source file name: %1").arg(m_d->m_ui.sourceFileLineEdit->errorMessage()); + return false; + } + + if (isFormInputVisible()) { + if (!m_d->m_ui.formFileLineEdit->isValid()) { + if (error) + *error = tr("Invalid form file name: %1").arg(m_d->m_ui.formFileLineEdit->errorMessage()); + return false; + } + } + + if (isPathInputVisible()) { + if (!m_d->m_ui.pathChooser->isValid()) { + if (error) + *error = m_d->m_ui.pathChooser->errorMessage(); + return false; + } + } + return true; +} + +void NewClassWidget::updateFileNames(const QString &baseName) +{ + if (debugNewClassWidget) + qDebug() << Q_FUNC_INFO << baseName << m_d->m_headerExtension << m_d->m_sourceExtension; + const QChar dot = QLatin1Char('.'); + m_d->m_ui.sourceFileLineEdit->setText(baseName + dot + m_d->m_sourceExtension); + m_d->m_ui.headerFileLineEdit->setText(baseName + dot + m_d->m_headerExtension); + m_d->m_ui.formFileLineEdit->setText(baseName + dot + m_d->m_formExtension); +} + +void NewClassWidget::slotActivated() +{ + if (m_d->m_valid) + emit activated(); +} + +QString NewClassWidget::fixSuffix(const QString &suffix) +{ + QString s = suffix; + if (s.startsWith(QLatin1Char('.'))) + s.remove(0, 1); + return s; +} + +// Utility to add a suffix to a file unless the user specified one +static QString ensureSuffix(QString f, const QString &extension) +{ + const QChar dot = QLatin1Char('.'); + if (f.contains(dot)) + return f; + f += dot; + f += extension; + return f; +} + +// If a non-empty name was passed, expand to directory and suffix +static QString expandFileName(const QDir &dir, const QString name, const QString &extension) +{ + if (name.isEmpty()) + return QString(); + return dir.absoluteFilePath(ensureSuffix(name, extension)); +} + +QStringList NewClassWidget::files() const +{ + QStringList rc; + const QDir dir = QDir(path()); + rc.push_back(expandFileName(dir, headerFileName(), headerExtension())); + rc.push_back(expandFileName(dir, sourceFileName(), sourceExtension())); + if (isFormInputVisible()) + rc.push_back(expandFileName(dir, formFileName(), formExtension())); + return rc; +} + + +} // namespace Utils +} // namespace Core diff --git a/src/libs/utils/newclasswidget.h b/src/libs/utils/newclasswidget.h new file mode 100644 index 00000000000..e534189d775 --- /dev/null +++ b/src/libs/utils/newclasswidget.h @@ -0,0 +1,150 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef NEWCLASSWIDGET_H +#define NEWCLASSWIDGET_H + +#include "utils_global.h" + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE +class QStringList; +QT_END_NAMESPACE + +namespace Core { +namespace Utils { + +struct NewClassWidgetPrivate; + +/* NewClassWidget: Utility widget for 'New Class' wizards. Prompts the user + * to enter a class name (optionally derived from some base class) and file + * names for header, source and form files. Has some smart logic to derive + * the file names from the class name. */ + +class QWORKBENCH_UTILS_EXPORT NewClassWidget : public QWidget +{ + Q_DISABLE_COPY(NewClassWidget) + Q_OBJECT + Q_PROPERTY(bool namespacesEnabled READ namespacesEnabled WRITE setNamespacesEnabled DESIGNABLE true) + Q_PROPERTY(bool baseClassInputVisible READ isBaseClassInputVisible WRITE setBaseClassInputVisible DESIGNABLE true) + Q_PROPERTY(bool baseClassEditable READ isBaseClassEditable WRITE setBaseClassEditable DESIGNABLE false) + Q_PROPERTY(bool formInputVisible READ isFormInputVisible WRITE setFormInputVisible DESIGNABLE true) + Q_PROPERTY(bool pathInputVisible READ isPathInputVisible WRITE setPathInputVisible DESIGNABLE true) + Q_PROPERTY(QString className READ className WRITE setClassName DESIGNABLE true) + Q_PROPERTY(QString baseClassName READ baseClassName WRITE setBaseClassName DESIGNABLE true) + Q_PROPERTY(QString sourceFileName READ sourceFileName DESIGNABLE false) + Q_PROPERTY(QString headerFileName READ headerFileName DESIGNABLE false) + Q_PROPERTY(QString formFileName READ formFileName DESIGNABLE false) + Q_PROPERTY(QString path READ path WRITE setPath DESIGNABLE true) + Q_PROPERTY(QStringList baseClassChoices READ baseClassChoices WRITE setBaseClassChoices DESIGNABLE true) + Q_PROPERTY(QString sourceExtension READ sourceExtension WRITE setSourceExtension DESIGNABLE true) + Q_PROPERTY(QString headerExtension READ headerExtension WRITE setHeaderExtension DESIGNABLE true) + Q_PROPERTY(QString formExtension READ formExtension WRITE setFormExtension DESIGNABLE true) + Q_PROPERTY(bool formInputCheckable READ formInputCheckable WRITE setFormInputCheckable DESIGNABLE true) + Q_PROPERTY(bool formInputChecked READ formInputChecked WRITE setFormInputChecked DESIGNABLE true) + // Utility "USER" property for wizards containing file names. + Q_PROPERTY(QStringList files READ files DESIGNABLE false USER true) +public: + explicit NewClassWidget(QWidget *parent = 0); + ~NewClassWidget(); + + bool namespacesEnabled() const; + bool isBaseClassInputVisible() const; + bool isBaseClassEditable() const; + bool isFormInputVisible() const; + bool isPathInputVisible() const; + bool formInputCheckable() const; + bool formInputChecked() const; + + QString className() const; + QString baseClassName() const; + QString sourceFileName() const; + QString headerFileName() const; + QString formFileName() const; + QString path() const; + QStringList baseClassChoices() const; + QString sourceExtension() const; + QString headerExtension() const; + QString formExtension() const; + + + bool isValid(QString *error = 0) const; + + QStringList files() const; + +signals: + void validChanged(); + void activated(); + +public slots: + void setNamespacesEnabled(bool b); + void setBaseClassInputVisible(bool visible); + void setBaseClassEditable(bool editable); + void setFormInputVisible(bool visible); + void setPathInputVisible(bool visible); + void setFormInputCheckable(bool v); + void setFormInputChecked(bool v); + + /* The name passed into the new class widget will be reformatted to be a + * valid class name. */ + void setClassName(const QString &suggestedName); + void setBaseClassName(const QString &); + void setPath(const QString &path); + void setBaseClassChoices(const QStringList &choices); + void setSourceExtension(const QString &e); + void setHeaderExtension(const QString &e); + void setFormExtension(const QString &e); + + /* Suggest a class name from the base class by stripping the leading 'Q' + * character. This will happen automagically if the base class combo + * changes until the class line edited is manually edited. */ + void suggestClassNameFromBase(); + +private slots: + void updateFileNames(const QString &t); + void slotValidChanged(); + void slotActivated(); + void classNameEdited(); + void slotFormInputChecked(); + +private: + void setFormInputCheckable(bool checkable, bool force); + + QString fixSuffix(const QString &suffix); + NewClassWidgetPrivate *m_d; +}; + +} // namespace Utils +} // namespace Core + +#endif // NEWCLASSWIDGET_H diff --git a/src/libs/utils/newclasswidget.ui b/src/libs/utils/newclasswidget.ui new file mode 100644 index 00000000000..14e0a6574b8 --- /dev/null +++ b/src/libs/utils/newclasswidget.ui @@ -0,0 +1,150 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Core::Utils::NewClassWidget</class> + <widget class="QWidget" name="Core::Utils::NewClassWidget"> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + <property name="margin"> + <number>0</number> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="classNameLabel"> + <property name="text"> + <string>Class name:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="Core::Utils::ClassNameValidatingLineEdit" name="classLineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="baseClassLabel"> + <property name="text"> + <string>Base class:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="baseClassComboBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="2" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="1"> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="headerLabel"> + <property name="text"> + <string>Header file:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="Core::Utils::FileNameValidatingLineEdit" name="headerFileLineEdit"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="sourceLabel"> + <property name="text"> + <string>Source file:</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="Core::Utils::FileNameValidatingLineEdit" name="sourceFileLineEdit"/> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="generateFormLabel"> + <property name="text"> + <string>Generate form:</string> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="formLabel"> + <property name="text"> + <string>Form file:</string> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="Core::Utils::FileNameValidatingLineEdit" name="formFileLineEdit"/> + </item> + <item row="7" column="0"> + <widget class="QLabel" name="pathLabel"> + <property name="text"> + <string>Path:</string> + </property> + </widget> + </item> + <item row="7" column="1"> + <widget class="Core::Utils::PathChooser" name="pathChooser" native="true"/> + </item> + <item row="5" column="1"> + <widget class="QCheckBox" name="generateFormCheckBox"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Core::Utils::PathChooser</class> + <extends>QWidget</extends> + <header location="global">pathchooser.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>Core::Utils::ClassNameValidatingLineEdit</class> + <extends>QLineEdit</extends> + <header>classnamevalidatinglineedit.h</header> + </customwidget> + <customwidget> + <class>Core::Utils::FileNameValidatingLineEdit</class> + <extends>QLineEdit</extends> + <header location="global">utils/filenamevalidatinglineedit.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp new file mode 100644 index 00000000000..188aa3b126b --- /dev/null +++ b/src/libs/utils/pathchooser.cpp @@ -0,0 +1,185 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "pathchooser.h" +#include "basevalidatinglineedit.h" + +#include <QtGui/QLineEdit> +#include <QtGui/QHBoxLayout> +#include <QtGui/QToolButton> +#include <QtGui/QFileDialog> +#include <QtGui/QDesktopServices> + +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QSettings> +#include <QtCore/QDebug> + +namespace Core { +namespace Utils { + +// ------------------ PathValidatingLineEdit +class PathValidatingLineEdit : public BaseValidatingLineEdit { +public: + explicit PathValidatingLineEdit(QWidget *parent = 0); + +protected: + virtual bool validate(const QString &value, QString *errorMessage) const; +}; + +PathValidatingLineEdit::PathValidatingLineEdit(QWidget *parent) : + BaseValidatingLineEdit(parent) +{ +} + +bool PathValidatingLineEdit::validate(const QString &value, QString *errorMessage) const +{ + return PathChooser::validatePath(value, errorMessage); +} + +// ------------------ PathChooserPrivate +struct PathChooserPrivate { + PathChooserPrivate(); + + PathValidatingLineEdit *m_lineEdit; +}; + +PathChooserPrivate::PathChooserPrivate() : + m_lineEdit(new PathValidatingLineEdit) +{ +} + +PathChooser::PathChooser(QWidget *parent) : + QWidget(parent), + m_d(new PathChooserPrivate) +{ + QHBoxLayout *hLayout = new QHBoxLayout; + hLayout->setContentsMargins(0, 0, 0, 0); + + connect(m_d->m_lineEdit, SIGNAL(validReturnPressed()), this, SIGNAL(returnPressed())); + connect(m_d->m_lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(changed())); + connect(m_d->m_lineEdit, SIGNAL(validChanged()), this, SIGNAL(validChanged())); + + m_d->m_lineEdit->setMinimumWidth(300); + hLayout->addWidget(m_d->m_lineEdit); + + QToolButton *browseButton = new QToolButton; + browseButton->setText(tr("...")); + connect(browseButton, SIGNAL(clicked()), this, SLOT(slotBrowse())); + + hLayout->addWidget(browseButton); + setLayout(hLayout); + setFocusProxy(m_d->m_lineEdit); +} + +PathChooser::~PathChooser() +{ + delete m_d; +} + +QString PathChooser::path() const +{ + return m_d->m_lineEdit->text(); +} + +void PathChooser::setPath(const QString &path) +{ + const QString defaultPath = path.isEmpty() ? homePath() : path; + m_d->m_lineEdit->setText(QDir::toNativeSeparators(defaultPath)); +} + +void PathChooser::slotBrowse() +{ + QString predefined = path(); + if (!predefined.isEmpty() && !QFileInfo(predefined).isDir()) + predefined.clear(); + // Prompt for a directory, delete trailing slashes unless it is "/", only + QString newPath = QFileDialog::getExistingDirectory(this, tr("Choose a path"), predefined); + if (!newPath .isEmpty()) { + if (newPath .size() > 1 && newPath .endsWith(QDir::separator())) + newPath .truncate(newPath .size() - 1); + setPath(newPath); + } +} + +bool PathChooser::isValid() const +{ + return m_d->m_lineEdit->isValid(); +} + +QString PathChooser::errorMessage() const +{ + return m_d->m_lineEdit->errorMessage(); +} + +bool PathChooser::validatePath(const QString &path, QString *errorMessage) +{ + if (path.isEmpty()) { + if (errorMessage) + *errorMessage = tr("The path must not be empty."); + return false; + } + // Must be a directory? + const QFileInfo fi(path); + if (fi.isDir()) + return true; // Happy! + + if (!fi.exists()) { + if (errorMessage) + *errorMessage = tr("The path '%1' does not exist.").arg(path); + return false; + } + // Must be something weird + if (errorMessage) + *errorMessage = tr("The path '%1' is not a directory.").arg(path); + return false; +} + +QString PathChooser::label() +{ + return tr("Path:"); +} + +QString PathChooser::homePath() +{ +#ifdef Q_OS_WIN + // Return 'users/<name>/Documents' on Windows, since Windows explorer + // does not let people actually display the contents of their home + // directory. Alternatively, create a QtCreator-specific directory? + return QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + return QDir::homePath(); +#endif +} + +} +} diff --git a/src/libs/utils/pathchooser.h b/src/libs/utils/pathchooser.h new file mode 100644 index 00000000000..e09040c4c06 --- /dev/null +++ b/src/libs/utils/pathchooser.h @@ -0,0 +1,90 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PATHCHOOSER_H +#define PATHCHOOSER_H + +#include "utils_global.h" + +#include <QtGui/QWidget> + +namespace Core { +namespace Utils { + +struct PathChooserPrivate; + +/* A Control that let's the user choose a path, consisting of a QLineEdit and + * a "Browse" button. Has some validation logic for embedding into + * QWizardPage. */ + +class QWORKBENCH_UTILS_EXPORT PathChooser : public QWidget +{ + Q_DISABLE_COPY(PathChooser) + Q_OBJECT + Q_PROPERTY(QString path READ path WRITE setPath DESIGNABLE true) + +public: + explicit PathChooser(QWidget *parent = 0); + virtual ~PathChooser(); + + bool isValid() const; + QString errorMessage() const; + + QString path() const; + + // Returns the suggested label title when used in a form layout + static QString label(); + + static bool validatePath(const QString &path, QString *errorMessage = 0); + + // Return the home directory, which needs some fixing under Windows. + static QString homePath(); + +signals: + void validChanged(); + void changed(); + void returnPressed(); + +public slots: + void setPath(const QString &); + +private slots: + void slotBrowse(); + +private: + PathChooserPrivate *m_d; +}; + +} +} + +#endif // PATHCHOOSER_H diff --git a/src/libs/utils/projectintropage.cpp b/src/libs/utils/projectintropage.cpp new file mode 100644 index 00000000000..bc17333fb97 --- /dev/null +++ b/src/libs/utils/projectintropage.cpp @@ -0,0 +1,215 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "projectintropage.h" +#include "filewizardpage.h" +#include "ui_projectintropage.h" + +#include <QtGui/QMessageBox> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> + +namespace Core { +namespace Utils { + +struct ProjectIntroPagePrivate +{ + ProjectIntroPagePrivate(); + Ui::ProjectIntroPage m_ui; + bool m_complete; + // Status label style sheets + const QString m_errorStyleSheet; + const QString m_warningStyleSheet; + const QString m_hintStyleSheet; +}; + +ProjectIntroPagePrivate:: ProjectIntroPagePrivate() : + m_complete(false), + m_errorStyleSheet(QLatin1String("background : red;")), + m_warningStyleSheet(QLatin1String("background : yellow;")), + m_hintStyleSheet() +{ +} + +ProjectIntroPage::ProjectIntroPage(QWidget *parent) : + QWizardPage(parent), + m_d(new ProjectIntroPagePrivate) +{ + m_d->m_ui.setupUi(this); + hideStatusLabel(); + m_d->m_ui.nameLineEdit->setInitialText(tr("<Enter_Name>")); + m_d->m_ui.nameLineEdit->setFocus(Qt::TabFocusReason); + connect(m_d->m_ui.pathChooser, SIGNAL(changed()), this, SLOT(slotChanged())); + connect(m_d->m_ui.nameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotChanged())); + connect(m_d->m_ui.pathChooser, SIGNAL(returnPressed()), this, SLOT(slotActivated())); + connect(m_d->m_ui.nameLineEdit, SIGNAL(validReturnPressed()), this, SLOT(slotActivated())); +} + +void ProjectIntroPage::insertControl(int row, QWidget *label, QWidget *control) +{ + m_d->m_ui.formLayout->insertRow(row, label, control); +} + +ProjectIntroPage::~ProjectIntroPage() +{ + delete m_d; +} + +QString ProjectIntroPage::name() const +{ + return m_d->m_ui.nameLineEdit->text(); +} + +QString ProjectIntroPage::path() const +{ + return m_d->m_ui.pathChooser->path(); +} + +void ProjectIntroPage::setPath(const QString &path) +{ + m_d->m_ui.pathChooser->setPath(path); +} + +void ProjectIntroPage::setName(const QString &name) +{ + m_d->m_ui.nameLineEdit->setText(name); +} + +QString ProjectIntroPage::description() const +{ + return m_d->m_ui.descriptionLabel->text(); +} + +void ProjectIntroPage::setDescription(const QString &description) +{ + m_d->m_ui.descriptionLabel->setText(description); +} + +void ProjectIntroPage::changeEvent(QEvent *e) +{ + switch(e->type()) { + case QEvent::LanguageChange: + m_d->m_ui.retranslateUi(this); + break; + default: + break; + } +} + +bool ProjectIntroPage::isComplete() const +{ + return m_d->m_complete; +} + +bool ProjectIntroPage::validate() +{ + // Validate and display status + if (!m_d->m_ui.pathChooser->isValid()) { + displayStatusMessage(Error, m_d->m_ui.pathChooser->errorMessage()); + return false; + } + + // Name valid? Ignore 'DisplayingInitialText' state. + bool nameValid = false; + switch (m_d->m_ui.nameLineEdit->state()) { + case BaseValidatingLineEdit::Invalid: + displayStatusMessage(Error, m_d->m_ui.nameLineEdit->errorMessage()); + return false; + case BaseValidatingLineEdit::DisplayingInitialText: + break; + case BaseValidatingLineEdit::Valid: + nameValid = true; + break; + } + + // Check existence of the directory + QString projectDir = path(); + projectDir += QDir::separator(); + projectDir += m_d->m_ui.nameLineEdit->text(); + const QFileInfo projectDirFile(projectDir); + if (!projectDirFile.exists()) { // All happy + hideStatusLabel(); + return nameValid; + } + + if (projectDirFile.isDir()) { + displayStatusMessage(Warning, tr("The project already exists.")); + return nameValid;; + } + // Not a directory, but something else, likely causing directory creation to fail + displayStatusMessage(Error, tr("A file with that name already exists.")); + return false; +} + +void ProjectIntroPage::slotChanged() +{ + const bool newComplete = validate(); + if (newComplete != m_d->m_complete) { + m_d->m_complete = newComplete; + emit completeChanged(); + } +} + +void ProjectIntroPage::slotActivated() +{ + if (m_d->m_complete) + emit activated(); +} + +bool ProjectIntroPage::validateProjectDirectory(const QString &name, QString *errorMessage) +{ + return ProjectNameValidatingLineEdit::validateProjectName(name, errorMessage); +} + +void ProjectIntroPage::displayStatusMessage(StatusLabelMode m, const QString &s) +{ + switch (m) { + case Error: + m_d->m_ui.stateLabel->setStyleSheet(m_d->m_errorStyleSheet); + break; + case Warning: + m_d->m_ui.stateLabel->setStyleSheet(m_d->m_warningStyleSheet); + break; + case Hint: + m_d->m_ui.stateLabel->setStyleSheet(m_d->m_hintStyleSheet); + break; + } + m_d->m_ui.stateLabel->setText(s); +} + +void ProjectIntroPage::hideStatusLabel() +{ + displayStatusMessage(Hint, QString()); +} + +} +} diff --git a/src/libs/utils/projectintropage.h b/src/libs/utils/projectintropage.h new file mode 100644 index 00000000000..56dcc253272 --- /dev/null +++ b/src/libs/utils/projectintropage.h @@ -0,0 +1,107 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROJECTINTROPAGE_H +#define PROJECTINTROPAGE_H + +#include "utils_global.h" + +#include <QtGui/QWizardPage> + +namespace Core { +namespace Utils { + +struct ProjectIntroPagePrivate; + +/* Standard wizard page for a single file letting the user choose name + * and path. Looks similar to FileWizardPage, but provides additional + * functionality: + * - Description label at the top for displaying introductory text + * - It does on the fly validation (connected to changed()) and displays + * warnings/errors in a status label at the bottom (the page is complete + * when fully validated, validatePage() is thus not implemented). + * + * Note: Careful when changing projectintropage.ui. It must have main + * geometry cleared and QLayout::SetMinimumSize constraint on the main + * layout, otherwise, QWizard will squeeze it due to its strange expanding + * hacks. */ + +class QWORKBENCH_UTILS_EXPORT ProjectIntroPage : public QWizardPage { + Q_OBJECT + Q_DISABLE_COPY(ProjectIntroPage) + Q_PROPERTY(QString description READ description WRITE setPath DESIGNABLE true) + Q_PROPERTY(QString path READ path WRITE setPath DESIGNABLE true) + Q_PROPERTY(QString name READ name WRITE setName DESIGNABLE true) +public: + explicit ProjectIntroPage(QWidget *parent = 0); + virtual ~ProjectIntroPage(); + + QString name() const; + QString path() const; + QString description() const; + + // Insert an additional control into the form layout for the target. + void insertControl(int row, QWidget *label, QWidget *control); + + virtual bool isComplete() const; + + // Validate a project directory name entry field + static bool validateProjectDirectory(const QString &name, QString *errorMessage); + +signals: + void activated(); + +public slots: + void setPath(const QString &path); + void setName(const QString &name); + void setDescription(const QString &description); + +private slots: + void slotChanged(); + void slotActivated(); + +protected: + virtual void changeEvent(QEvent *e); + +private: + enum StatusLabelMode { Error, Warning, Hint }; + + bool validate(); + void displayStatusMessage(StatusLabelMode m, const QString &); + void hideStatusLabel(); + + ProjectIntroPagePrivate *m_d; +}; + +} +} +#endif // PROJECTINTROPAGE_H diff --git a/src/libs/utils/projectintropage.ui b/src/libs/utils/projectintropage.ui new file mode 100644 index 00000000000..f530b78044e --- /dev/null +++ b/src/libs/utils/projectintropage.ui @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Core::Utils::ProjectIntroPage</class> + <widget class="QWizardPage" name="Core::Utils::ProjectIntroPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>208</width> + <height>143</height> + </rect> + </property> + <property name="windowTitle"> + <string>WizardPage</string> + </property> + <property name="title"> + <string>Introduction and project location</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="sizeConstraint"> + <enum>QLayout::SetMinimumSize</enum> + </property> + <item> + <widget class="QLabel" name="descriptionLabel"> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::MinimumExpanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QFrame" name="frame"> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="nameLabel"> + <property name="text"> + <string>Name:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="Core::Utils::ProjectNameValidatingLineEdit" name="nameLineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="pathLabel"> + <property name="text"> + <string>Path:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="Core::Utils::PathChooser" name="pathChooser" native="true"/> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="stateLabel"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Core::Utils::PathChooser</class> + <extends>QWidget</extends> + <header>pathchooser.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>Core::Utils::ProjectNameValidatingLineEdit</class> + <extends>QLineEdit</extends> + <header>projectnamevalidatinglineedit.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/libs/utils/projectnamevalidatinglineedit.cpp b/src/libs/utils/projectnamevalidatinglineedit.cpp new file mode 100644 index 00000000000..fb979d39345 --- /dev/null +++ b/src/libs/utils/projectnamevalidatinglineedit.cpp @@ -0,0 +1,66 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "projectnamevalidatinglineedit.h" +#include "filenamevalidatinglineedit.h" + +namespace Core { +namespace Utils { + +ProjectNameValidatingLineEdit::ProjectNameValidatingLineEdit(QWidget *parent) : + BaseValidatingLineEdit(parent) +{ +} + +bool ProjectNameValidatingLineEdit::validateProjectName(const QString &name, QString *errorMessage /* = 0*/) +{ + // Validation is file name + checking for dots + if (!FileNameValidatingLineEdit::validateFileName(name, errorMessage)) + return false; + + // We don't want dots in the directory name for some legacy Windows + // reason. Since we are cross-platform, we generally disallow it. + if (name.contains(QLatin1Char('.'))) { + if (errorMessage) + *errorMessage = tr("The name must not contain the '.'-character."); + return false; + } + return true; +} + +bool ProjectNameValidatingLineEdit::validate(const QString &value, QString *errorMessage) const +{ + return validateProjectName(value, errorMessage); +} + +} +} diff --git a/src/libs/utils/projectnamevalidatinglineedit.h b/src/libs/utils/projectnamevalidatinglineedit.h new file mode 100644 index 00000000000..c677cea1414 --- /dev/null +++ b/src/libs/utils/projectnamevalidatinglineedit.h @@ -0,0 +1,56 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROJECTNAMEVALIDATINGLINEEDIT_H +#define PROJECTNAMEVALIDATINGLINEEDIT_H + +#include "basevalidatinglineedit.h" + +namespace Core { +namespace Utils { + +class QWORKBENCH_UTILS_EXPORT ProjectNameValidatingLineEdit : public BaseValidatingLineEdit { + Q_OBJECT + Q_DISABLE_COPY(ProjectNameValidatingLineEdit) + +public: + explicit ProjectNameValidatingLineEdit(QWidget *parent = 0); + + static bool validateProjectName(const QString &name, QString *errorMessage /* = 0*/); + +protected: + virtual bool validate(const QString &value, QString *errorMessage) const; +}; + +} +} +#endif // PROJECTNAMEVALIDATINGLINEEDIT_H diff --git a/src/libs/utils/qtcolorbutton.cpp b/src/libs/utils/qtcolorbutton.cpp new file mode 100644 index 00000000000..8b54bdda1d4 --- /dev/null +++ b/src/libs/utils/qtcolorbutton.cpp @@ -0,0 +1,290 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qtcolorbutton.h" +#include <QtGui/QColorDialog> +#include <QtGui/QPainter> +#include <QtCore/QMimeData> +#include <QtGui/QDragEnterEvent> +#include <QtGui/QApplication> + +namespace Core { +namespace Utils { + +class QtColorButtonPrivate +{ + QtColorButton *q_ptr; + Q_DECLARE_PUBLIC(QtColorButton) +public: + QColor m_color; +#ifndef QT_NO_DRAGANDDROP + QColor m_dragColor; + QPoint m_dragStart; + bool m_dragging; +#endif + bool m_backgroundCheckered; + bool m_alphaAllowed; + + void slotEditColor(); + QColor shownColor() const; + QPixmap generatePixmap() const; +}; + +void QtColorButtonPrivate::slotEditColor() +{ + QColor newColor; + if (m_alphaAllowed) { + bool ok; + const QRgb rgba = QColorDialog::getRgba(m_color.rgba(), &ok, q_ptr); + if (!ok) + return; + newColor = QColor::fromRgba(rgba); + } else { + newColor = QColorDialog::getColor(m_color, q_ptr); + if (!newColor.isValid()) + return; + } + if (newColor == q_ptr->color()) + return; + q_ptr->setColor(newColor); + emit q_ptr->colorChanged(m_color); +} + +QColor QtColorButtonPrivate::shownColor() const +{ +#ifndef QT_NO_DRAGANDDROP + if (m_dragging) + return m_dragColor; +#endif + return m_color; +} + +QPixmap QtColorButtonPrivate::generatePixmap() const +{ + QPixmap pix(24, 24); + + int pixSize = 20; + QBrush br(shownColor()); + + QPixmap pm(2 * pixSize, 2 * pixSize); + QPainter pmp(&pm); + pmp.fillRect(0, 0, pixSize, pixSize, Qt::lightGray); + pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::lightGray); + pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::darkGray); + pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::darkGray); + pmp.fillRect(0, 0, 2 * pixSize, 2 * pixSize, shownColor()); + br = QBrush(pm); + + QPainter p(&pix); + int corr = 1; + QRect r = pix.rect().adjusted(corr, corr, -corr, -corr); + p.setBrushOrigin((r.width() % pixSize + pixSize) / 2 + corr, (r.height() % pixSize + pixSize) / 2 + corr); + p.fillRect(r, br); + + p.fillRect(r.width() / 4 + corr, r.height() / 4 + corr, + r.width() / 2, r.height() / 2, + QColor(shownColor().rgb())); + p.drawRect(pix.rect().adjusted(0, 0, -1, -1)); + + return pix; +} + +/////////////// + +QtColorButton::QtColorButton(QWidget *parent) + : QToolButton(parent) +{ + d_ptr = new QtColorButtonPrivate; + d_ptr->q_ptr = this; + d_ptr->m_dragging = false; + d_ptr->m_backgroundCheckered = true; + d_ptr->m_alphaAllowed = true; + + setAcceptDrops(true); + + connect(this, SIGNAL(clicked()), this, SLOT(slotEditColor())); + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred)); +} + +QtColorButton::~QtColorButton() +{ + delete d_ptr; +} + +void QtColorButton::setColor(const QColor &color) +{ + if (d_ptr->m_color == color) + return; + d_ptr->m_color = color; + update(); +} + +QColor QtColorButton::color() const +{ + return d_ptr->m_color; +} + +void QtColorButton::setBackgroundCheckered(bool checkered) +{ + if (d_ptr->m_backgroundCheckered == checkered) + return; + d_ptr->m_backgroundCheckered = checkered; + update(); +} + +bool QtColorButton::isBackgroundCheckered() const +{ + return d_ptr->m_backgroundCheckered; +} + +void QtColorButton::setAlphaAllowed(bool allowed) +{ + d_ptr->m_alphaAllowed = allowed; +} + +bool QtColorButton::isAlphaAllowed() const +{ + return d_ptr->m_alphaAllowed; +} + +void QtColorButton::paintEvent(QPaintEvent *event) +{ + QToolButton::paintEvent(event); + if (!isEnabled()) + return; + + const int pixSize = 10; + QBrush br(d_ptr->shownColor()); + if (d_ptr->m_backgroundCheckered) { + QPixmap pm(2 * pixSize, 2 * pixSize); + QPainter pmp(&pm); + pmp.fillRect(0, 0, pixSize, pixSize, Qt::white); + pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::white); + pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::black); + pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::black); + pmp.fillRect(0, 0, 2 * pixSize, 2 * pixSize, d_ptr->shownColor()); + br = QBrush(pm); + } + + QPainter p(this); + const int corr = 5; + QRect r = rect().adjusted(corr, corr, -corr, -corr); + p.setBrushOrigin((r.width() % pixSize + pixSize) / 2 + corr, (r.height() % pixSize + pixSize) / 2 + corr); + p.fillRect(r, br); + + //const int adjX = qRound(r.width() / 4.0); + //const int adjY = qRound(r.height() / 4.0); + //p.fillRect(r.adjusted(adjX, adjY, -adjX, -adjY), + // QColor(d_ptr->shownColor().rgb())); + /* + p.fillRect(r.adjusted(0, r.height() * 3 / 4, 0, 0), + QColor(d_ptr->shownColor().rgb())); + p.fillRect(r.adjusted(0, 0, 0, -r.height() * 3 / 4), + QColor(d_ptr->shownColor().rgb())); + */ + /* + const QColor frameColor0(0, 0, 0, qRound(0.2 * (0xFF - d_ptr->shownColor().alpha()))); + p.setPen(frameColor0); + p.drawRect(r.adjusted(adjX, adjY, -adjX - 1, -adjY - 1)); + */ + + const QColor frameColor1(0, 0, 0, 26); + p.setPen(frameColor1); + p.drawRect(r.adjusted(1, 1, -2, -2)); + const QColor frameColor2(0, 0, 0, 51); + p.setPen(frameColor2); + p.drawRect(r.adjusted(0, 0, -1, -1)); +} + +void QtColorButton::mousePressEvent(QMouseEvent *event) +{ +#ifndef QT_NO_DRAGANDDROP + if (event->button() == Qt::LeftButton) + d_ptr->m_dragStart = event->pos(); +#endif + QToolButton::mousePressEvent(event); +} + +void QtColorButton::mouseMoveEvent(QMouseEvent *event) +{ +#ifndef QT_NO_DRAGANDDROP + if (event->buttons() & Qt::LeftButton && + (d_ptr->m_dragStart - event->pos()).manhattanLength() > QApplication::startDragDistance()) { + QMimeData *mime = new QMimeData; + mime->setColorData(color()); + QDrag *drg = new QDrag(this); + drg->setMimeData(mime); + drg->setPixmap(d_ptr->generatePixmap()); + setDown(false); + event->accept(); + drg->start(); + return; + } +#endif + QToolButton::mouseMoveEvent(event); +} + +#ifndef QT_NO_DRAGANDDROP +void QtColorButton::dragEnterEvent(QDragEnterEvent *event) +{ + const QMimeData *mime = event->mimeData(); + if (!mime->hasColor()) + return; + + event->accept(); + d_ptr->m_dragColor = qvariant_cast<QColor>(mime->colorData()); + d_ptr->m_dragging = true; + update(); +} + +void QtColorButton::dragLeaveEvent(QDragLeaveEvent *event) +{ + event->accept(); + d_ptr->m_dragging = false; + update(); +} + +void QtColorButton::dropEvent(QDropEvent *event) +{ + event->accept(); + d_ptr->m_dragging = false; + if (d_ptr->m_dragColor == color()) + return; + setColor(d_ptr->m_dragColor); + emit colorChanged(color()); +} +#endif + +} // namespace Utils +} // namespace Core + +#include "moc_qtcolorbutton.cpp" diff --git a/src/libs/utils/qtcolorbutton.h b/src/libs/utils/qtcolorbutton.h new file mode 100644 index 00000000000..07355c883ec --- /dev/null +++ b/src/libs/utils/qtcolorbutton.h @@ -0,0 +1,84 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QTCOLORBUTTON_H +#define QTCOLORBUTTON_H + +#include "utils_global.h" + +#include <QtGui/QToolButton> + +namespace Core { +namespace Utils { + +class QWORKBENCH_UTILS_EXPORT QtColorButton : public QToolButton +{ + Q_OBJECT + Q_PROPERTY(bool backgroundCheckered READ isBackgroundCheckered WRITE setBackgroundCheckered) + Q_PROPERTY(bool alphaAllowed READ isAlphaAllowed WRITE setAlphaAllowed) +public: + QtColorButton(QWidget *parent = 0); + ~QtColorButton(); + + bool isBackgroundCheckered() const; + void setBackgroundCheckered(bool checkered); + + bool isAlphaAllowed() const; + void setAlphaAllowed(bool allowed); + + QColor color() const; + +public slots: + void setColor(const QColor &color); + +signals: + void colorChanged(const QColor &color); +protected: + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); +#ifndef QT_NO_DRAGANDDROP + void dragEnterEvent(QDragEnterEvent *event); + void dragLeaveEvent(QDragLeaveEvent *event); + void dropEvent(QDropEvent *event); +#endif +private: + class QtColorButtonPrivate *d_ptr; + friend class QtColorButtonPrivate; + Q_DISABLE_COPY(QtColorButton) + Q_PRIVATE_SLOT(d_ptr, void slotEditColor()) +}; + +} // namespace Utils +} // namespace Core + +#endif // QTCOLORBUTTON_H diff --git a/src/libs/utils/reloadpromptutils.cpp b/src/libs/utils/reloadpromptutils.cpp new file mode 100644 index 00000000000..ca1d9e23ded --- /dev/null +++ b/src/libs/utils/reloadpromptutils.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "reloadpromptutils.h" +#include <QtGui/QMessageBox> + +using namespace Core; +using namespace Core::Utils; + +QWORKBENCH_UTILS_EXPORT Core::Utils::ReloadPromptAnswer + Core::Utils::reloadPrompt(const QString &fileName, QWidget *parent) +{ + return reloadPrompt(QObject::tr("File Changed"), + QObject::tr("The file %1 has changed outside Qt Creator. Do you want to reload it?").arg(fileName), + parent); +} + +QWORKBENCH_UTILS_EXPORT Core::Utils::ReloadPromptAnswer + Core::Utils::reloadPrompt(const QString &title, const QString &prompt, QWidget *parent) +{ + switch (QMessageBox::question(parent, title, prompt, QMessageBox::Yes|QMessageBox::YesToAll|QMessageBox::No|QMessageBox::NoToAll, + QMessageBox::YesToAll)) { + case QMessageBox::Yes: + return ReloadCurrent; + case QMessageBox::YesToAll: + return ReloadAll; + case QMessageBox::No: + return ReloadSkipCurrent; + default: + break; + } + return ReloadNone; +} diff --git a/src/libs/utils/reloadpromptutils.h b/src/libs/utils/reloadpromptutils.h new file mode 100644 index 00000000000..deaf4b920a6 --- /dev/null +++ b/src/libs/utils/reloadpromptutils.h @@ -0,0 +1,54 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef RELOADPROMPTUTILS_H +#define RELOADPROMPTUTILS_H + +#include "utils_global.h" + +QT_BEGIN_NAMESPACE +class QString; +class QWidget; +QT_END_NAMESPACE + +namespace Core { +namespace Utils { + +enum ReloadPromptAnswer { ReloadCurrent, ReloadAll, ReloadSkipCurrent, ReloadNone }; + +QWORKBENCH_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const QString &fileName, QWidget *parent); +QWORKBENCH_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const QString &title, const QString &prompt, QWidget *parent); + +} // namespace Utils +} // namespace Core + +#endif // RELOADPROMPTUTILS_H diff --git a/src/libs/utils/settingsutils.cpp b/src/libs/utils/settingsutils.cpp new file mode 100644 index 00000000000..ca8de01828e --- /dev/null +++ b/src/libs/utils/settingsutils.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "settingsutils.h" +#include <QtCore/QString> + +namespace Core { +namespace Utils { + +QWORKBENCH_UTILS_EXPORT QString settingsKey(const QString &category) +{ + QString rc(category); + const QChar underscore = QLatin1Char('_'); + const int size = rc.size(); + for (int i = 0; i < size;i++) { + const QChar c = rc.at(i); + if (!c.isLetterOrNumber() && c != underscore) + rc[i] = underscore; + } + return rc; +} + +} +} diff --git a/src/libs/utils/settingsutils.h b/src/libs/utils/settingsutils.h new file mode 100644 index 00000000000..734a2f02f9d --- /dev/null +++ b/src/libs/utils/settingsutils.h @@ -0,0 +1,48 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SETTINGSTUTILS_H +#define SETTINGSTUTILS_H + +#include "utils_global.h" + +namespace Core { +namespace Utils { + +// Create a usable settings key from a category, +// for example Editor|C++ -> Editor_C__ +QWORKBENCH_UTILS_EXPORT QString settingsKey(const QString &category); + +} // namespace Utils +} // namespace Core + +#endif // SETTINGSTUTILS_H diff --git a/src/libs/utils/submiteditorwidget.cpp b/src/libs/utils/submiteditorwidget.cpp new file mode 100644 index 00000000000..aeafcd828d5 --- /dev/null +++ b/src/libs/utils/submiteditorwidget.cpp @@ -0,0 +1,305 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "submiteditorwidget.h" +#include "ui_submiteditorwidget.h" + +#include <QtCore/QDebug> +#include <QtCore/QPointer> + +enum { debug= 0 }; + +namespace Core { +namespace Utils { + +struct SubmitEditorWidgetPrivate { + SubmitEditorWidgetPrivate(); + + Ui::SubmitEditorWidget m_ui; + bool m_filesSelected; + bool m_filesChecked; +}; + +SubmitEditorWidgetPrivate::SubmitEditorWidgetPrivate() : + m_filesSelected(false), + m_filesChecked(false) +{ +} + +SubmitEditorWidget::SubmitEditorWidget(QWidget *parent) : + QWidget(parent), + m_d(new SubmitEditorWidgetPrivate) +{ + m_d->m_ui.setupUi(this); + // File List + m_d->m_ui.fileList->setSelectionMode(QAbstractItemView::ExtendedSelection); + connect(m_d->m_ui.fileList, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(triggerDiffSelected())); + connect(m_d->m_ui.fileList, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(fileItemChanged(QListWidgetItem*))); + connect(m_d->m_ui.fileList, SIGNAL(itemSelectionChanged()), this, SLOT(fileSelectionChanged())); + + // Text + m_d->m_ui.description->setFont(QFont(QLatin1String("Courier"))); + + setFocusPolicy(Qt::StrongFocus); + setFocusProxy(m_d->m_ui.description); +} + +SubmitEditorWidget::~SubmitEditorWidget() +{ + delete m_d; +} + +void SubmitEditorWidget::registerActions(QAction *editorUndoAction, QAction *editorRedoAction, + QAction *submitAction, QAction *diffAction) +{ + if (editorUndoAction) { + editorUndoAction->setEnabled(m_d->m_ui.description->document()->isUndoAvailable()); + connect(m_d->m_ui.description, SIGNAL(undoAvailable(bool)), editorUndoAction, SLOT(setEnabled(bool))); + connect(editorUndoAction, SIGNAL(triggered()), m_d->m_ui.description, SLOT(undo())); + } + if (editorRedoAction) { + editorRedoAction->setEnabled(m_d->m_ui.description->document()->isRedoAvailable()); + connect(m_d->m_ui.description, SIGNAL(redoAvailable(bool)), editorRedoAction, SLOT(setEnabled(bool))); + connect(editorRedoAction, SIGNAL(triggered()), m_d->m_ui.description, SLOT(redo())); + } + + if (submitAction) { + if (debug) + qDebug() << submitAction << m_d->m_ui.fileList->count() << "items" << m_d->m_filesChecked; + submitAction->setEnabled(m_d->m_filesChecked); + connect(this, SIGNAL(fileCheckStateChanged(bool)), submitAction, SLOT(setEnabled(bool))); + } + if (diffAction) { + if (debug) + qDebug() << diffAction << m_d->m_filesSelected; + diffAction->setEnabled(m_d->m_filesSelected); + connect(this, SIGNAL(fileSelectionChanged(bool)), diffAction, SLOT(setEnabled(bool))); + connect(diffAction, SIGNAL(triggered()), this, SLOT(triggerDiffSelected())); + } +} + +void SubmitEditorWidget::unregisterActions(QAction *editorUndoAction, QAction *editorRedoAction, + QAction *submitAction, QAction *diffAction) +{ + if (editorUndoAction) { + disconnect(m_d->m_ui.description, SIGNAL(undoAvailableChanged(bool)), editorUndoAction, SLOT(setEnabled(bool))); + disconnect(editorUndoAction, SIGNAL(triggered()), m_d->m_ui.description, SLOT(undo())); + } + if (editorRedoAction) { + disconnect(m_d->m_ui.description, SIGNAL(redoAvailableChanged(bool)), editorRedoAction, SLOT(setEnabled(bool))); + disconnect(editorRedoAction, SIGNAL(triggered()), m_d->m_ui.description, SLOT(redo())); + } + + if (submitAction) + disconnect(this, SIGNAL(fileCheckStateChanged(bool)), submitAction, SLOT(setEnabled(bool))); + + if (diffAction) { + disconnect(this, SIGNAL(fileSelectionChanged(bool)), diffAction, SLOT(setEnabled(bool))); + disconnect(diffAction, SIGNAL(triggered()), this, SLOT(triggerDiffSelected())); + } +} + + +QString SubmitEditorWidget::trimmedDescriptionText() const +{ + // Make sure we have one terminating NL + QString text = descriptionText().trimmed(); + text += QLatin1Char('\n'); + return text; +} + +QString SubmitEditorWidget::descriptionText() const +{ + return m_d->m_ui.description->toPlainText(); +} + +void SubmitEditorWidget::setDescriptionText(const QString &text) +{ + m_d->m_ui.description->setPlainText(text); +} + +QStringList SubmitEditorWidget::fileList() const +{ + QStringList rc; + const int count = m_d->m_ui.fileList->count(); + for (int i = 0; i < count; i++) + rc.push_back(m_d->m_ui.fileList->item(i)->text()); + return rc; +} + +void SubmitEditorWidget::addFilesUnblocked(const QStringList &list, bool checked, bool userCheckable) +{ + if (debug) + qDebug() << Q_FUNC_INFO << list << checked << userCheckable; + foreach (const QString &f, list) { + QListWidgetItem *item = new QListWidgetItem(f); + item->setCheckState(checked ? Qt::Checked : Qt::Unchecked); + if (!userCheckable) + item->setFlags(item->flags() & ~Qt::ItemIsUserCheckable); + m_d->m_ui.fileList->addItem(item); + } +} + +void SubmitEditorWidget::addFiles(const QStringList &list, bool checked, bool userCheckable) +{ + if (list.empty()) + return; + + const bool blocked = m_d->m_ui.fileList->blockSignals(true); + addFilesUnblocked(list, checked, userCheckable); + m_d->m_ui.fileList->blockSignals(blocked); + // Did we gain any checked files..update action accordingly + if (!m_d->m_filesChecked && checked) { + m_d->m_filesChecked = true; + emit fileCheckStateChanged(m_d->m_filesChecked); + } +} + +void SubmitEditorWidget::setFileList(const QStringList &list) +{ + // Trigger enabling of menu action + m_d->m_ui.fileList->clearSelection(); + + const bool blocked = m_d->m_ui.fileList->blockSignals(true); + m_d->m_ui.fileList->clear(); + if (!list.empty()) { + addFilesUnblocked(list, true, true); + // Checked files added? + if (!m_d->m_filesChecked) { + m_d->m_filesChecked = true; + emit fileCheckStateChanged(m_d->m_filesChecked); + } + } + m_d->m_ui.fileList->blockSignals(blocked); +} + +static bool containsCheckState(const QListWidget *lw, Qt::CheckState cs) +{ + const int count = lw->count(); + for (int i = 0; i < count; i++) + if (lw->item(i)->checkState() == cs) + return true; + return false; +} + +QStringList SubmitEditorWidget::selectedFiles() const +{ + QStringList rc; + const int count = m_d->m_ui.fileList->count(); + for (int i = 0; i < count; i++) { + const QListWidgetItem *item = m_d->m_ui.fileList->item(i); + if (item->isSelected()) + rc.push_back(item->text()); + } + return rc; +} + +QStringList SubmitEditorWidget::checkedFiles() const +{ + QStringList rc; + const int count = m_d->m_ui.fileList->count(); + for (int i = 0; i < count; i++) { + const QListWidgetItem *item = m_d->m_ui.fileList->item(i); + if (item->checkState() == Qt::Checked) + rc.push_back(item->text()); + } + return rc; +} + +QPlainTextEdit *SubmitEditorWidget::descriptionEdit() const +{ + return m_d->m_ui.description; +} + +void SubmitEditorWidget::triggerDiffSelected() +{ + const QStringList sel = selectedFiles(); + if (!sel.empty()) + emit diffSelected(sel); +} + +void SubmitEditorWidget::fileItemChanged(QListWidgetItem *item) +{ + const Qt::CheckState st = item->checkState(); + if (debug) + qDebug() << Q_FUNC_INFO << st << item->text() << m_d->m_filesChecked; + // Enable the actions according to check state + switch (st) { + case Qt::Unchecked: // Item was unchecked: Any checked items left? + if (m_d->m_filesChecked && !containsCheckState(m_d->m_ui.fileList, Qt::Checked)) { + m_d->m_filesChecked = false; + emit fileCheckStateChanged(m_d->m_filesChecked); + } + break; + case Qt::Checked: + // Item was Checked. First one? + if (!m_d->m_filesChecked) { + m_d->m_filesChecked = true; + emit fileCheckStateChanged(m_d->m_filesChecked); + } + break; + case Qt::PartiallyChecked: // Errm? + break; + } +} + +void SubmitEditorWidget::fileSelectionChanged() +{ + const bool newFilesSelected = !m_d->m_ui.fileList->selectedItems().empty(); + if (debug) + qDebug() << Q_FUNC_INFO << newFilesSelected; + if (m_d->m_filesSelected != newFilesSelected) { + m_d->m_filesSelected = newFilesSelected; + emit fileSelectionChanged(m_d->m_filesSelected); + if (debug) + qDebug() << Q_FUNC_INFO << m_d->m_filesSelected; + } +} + +void SubmitEditorWidget::changeEvent(QEvent *e) +{ + switch(e->type()) { + case QEvent::LanguageChange: + m_d->m_ui.retranslateUi(this); + break; + default: + break; + } +} + +void SubmitEditorWidget::insertTopWidget(QWidget *w) +{ + m_d->m_ui.vboxLayout->insertWidget(0, w); +} + +} // namespace Utils +} // namespace Core diff --git a/src/libs/utils/submiteditorwidget.h b/src/libs/utils/submiteditorwidget.h new file mode 100644 index 00000000000..3c40ccecba7 --- /dev/null +++ b/src/libs/utils/submiteditorwidget.h @@ -0,0 +1,122 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SUBMITEDITORWIDGET_H +#define SUBMITEDITORWIDGET_H + +#include "utils_global.h" + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE +class QPlainTextEdit; +class QListWidgetItem; +class QAction; +QT_END_NAMESPACE + +namespace Core { +namespace Utils { + +struct SubmitEditorWidgetPrivate; + +/* The submit editor presents the commit message in a text editor and an + * checkable list of modified files in a list window. The user can delete + * files from the list by pressing unchecking them or diff the selection + * by doubleclicking. + * + * Additionally, standard creator actions can be registered: + * Undo/redo will be set up to work with the description editor. + * Submit will be set up to be enabled according to checkstate. + * Diff will be set up to trigger diffSelected(). + * + * Note that the actions are connected by signals; in the rare event that there + * are several instances of the SubmitEditorWidget belonging to the same + * context active, the actions must be registered/unregistered in the editor + * change event. + * Care should be taken to ensure the widget is deleted properly when the + * editor closes. */ + +class QWORKBENCH_UTILS_EXPORT SubmitEditorWidget : public QWidget { + Q_OBJECT + Q_DISABLE_COPY(SubmitEditorWidget) + Q_PROPERTY(QString descriptionText READ descriptionText WRITE setDescriptionText DESIGNABLE true) + Q_PROPERTY(QStringList fileList READ fileList WRITE setFileList DESIGNABLE true) +public: + explicit SubmitEditorWidget(QWidget *parent = 0); + virtual ~SubmitEditorWidget(); + + void registerActions(QAction *editorUndoAction, QAction *editorRedoAction, + QAction *submitAction = 0, QAction *diffAction = 0); + void unregisterActions(QAction *editorUndoAction, QAction *editorRedoAction, + QAction *submitAction = 0, QAction *diffAction = 0); + + QString descriptionText() const; + void setDescriptionText(const QString &text); + // Should be used to normalize newlines. + QString trimmedDescriptionText() const; + + // The raw file list + QStringList fileList() const; + void addFiles(const QStringList&, bool checked = true, bool userCheckable = true); + void setFileList(const QStringList&); + + // Files to be included in submit + QStringList checkedFiles() const; + + // Selected files for diff + QStringList selectedFiles() const; + + QPlainTextEdit *descriptionEdit() const; + +signals: + void diffSelected(const QStringList &); + void fileSelectionChanged(bool someFileSelected); + void fileCheckStateChanged(bool someFileChecked); + +protected: + virtual void changeEvent(QEvent *e); + void insertTopWidget(QWidget *w); + +private slots: + void triggerDiffSelected(); + void fileItemChanged(QListWidgetItem *); + void fileSelectionChanged(); + +private: + void addFilesUnblocked(const QStringList &list, bool checked, bool userCheckable); + + SubmitEditorWidgetPrivate *m_d; +}; + +} +} +#endif // SUBMITEDITORWIDGET_H diff --git a/src/libs/utils/submiteditorwidget.ui b/src/libs/utils/submiteditorwidget.ui new file mode 100644 index 00000000000..1a30e8b7919 --- /dev/null +++ b/src/libs/utils/submiteditorwidget.ui @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Core::Utils::SubmitEditorWidget</class> + <widget class="QWidget" name="Core::Utils::SubmitEditorWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>582</width> + <height>502</height> + </rect> + </property> + <property name="windowTitle"> + <string>Subversion Submit</string> + </property> + <layout class="QVBoxLayout"> + <item> + <widget class="QGroupBox" name="descriptionBox"> + <property name="title"> + <string>Des&cription</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <layout class="QGridLayout"> + <item row="0" column="0"> + <widget class="QPlainTextEdit" name="description"> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>F&iles</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <layout class="QGridLayout"> + <item row="0" column="0"> + <widget class="QListWidget" name="fileList"> + <property name="font"> + <font/> + </property> + <property name="textElideMode"> + <enum>Qt::ElideNone</enum> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/libs/utils/synchronousprocess.cpp b/src/libs/utils/synchronousprocess.cpp new file mode 100644 index 00000000000..71bcdffb6cc --- /dev/null +++ b/src/libs/utils/synchronousprocess.cpp @@ -0,0 +1,356 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "synchronousprocess.h" + +#include <QtCore/QDebug> +#include <QtCore/QTimer> +#include <QtCore/QEventLoop> +#include <QtCore/QTextCodec> + +#include <QtGui/QApplication> + +enum { debug = 0 }; + +enum { defaultMaxHangTimerCount = 10 }; + +namespace Core { +namespace Utils { + +// ----------- SynchronousProcessResponse +SynchronousProcessResponse::SynchronousProcessResponse() : + result(StartFailed), + exitCode(-1) +{ +} + +void SynchronousProcessResponse::clear() +{ + result = StartFailed; + exitCode = -1; + stdOut.clear(); + stdErr.clear(); +} + +QWORKBENCH_UTILS_EXPORT QDebug operator<<(QDebug str, const SynchronousProcessResponse& r) +{ + QDebug nsp = str.nospace(); + nsp << "SynchronousProcessResponse: result=" << r.result << " ex=" << r.exitCode << '\n' + << r.stdOut.size() << " bytes stdout, stderr=" << r.stdErr << '\n'; + return str; +} + +// Data for one channel buffer (stderr/stdout) +struct ChannelBuffer { + ChannelBuffer(); + void clearForRun(); + QByteArray linesRead(); + + QByteArray data; + bool firstData; + bool bufferedSignalsEnabled; + bool firstBuffer; + int bufferPos; +}; + +ChannelBuffer::ChannelBuffer() : + firstData(true), + bufferedSignalsEnabled(false), + firstBuffer(true), + bufferPos(0) +{ +} + +void ChannelBuffer::clearForRun() +{ + firstData = true; + firstBuffer = true; + bufferPos = 0; +} + +/* Check for complete lines read from the device and return them, moving the + * buffer position. This is based on the assumption that '\n' is the new line + * marker in any sane codec. */ +QByteArray ChannelBuffer::linesRead() +{ + // Any new lines? + const int lastLineIndex = data.lastIndexOf('\n'); + if (lastLineIndex == -1 || lastLineIndex <= bufferPos) + return QByteArray(); + const int nextBufferPos = lastLineIndex + 1; + const QByteArray lines = data.mid(bufferPos, nextBufferPos - bufferPos); + bufferPos = nextBufferPos; + return lines; +} + +// ----------- SynchronousProcessPrivate +struct SynchronousProcessPrivate { + SynchronousProcessPrivate(); + void clearForRun(); + + QTextCodec *m_stdOutCodec; + QProcess m_process; + QTimer m_timer; + QEventLoop m_eventLoop; + SynchronousProcessResponse m_result; + int m_hangTimerCount; + int m_maxHangTimerCount; + + ChannelBuffer m_stdOut; + ChannelBuffer m_stdErr; +}; + +SynchronousProcessPrivate::SynchronousProcessPrivate() : + m_stdOutCodec(0), + m_hangTimerCount(0), + m_maxHangTimerCount(defaultMaxHangTimerCount) +{ +} + +void SynchronousProcessPrivate::clearForRun() +{ + m_hangTimerCount = 0; + m_stdOut.clearForRun(); + m_stdErr.clearForRun(); + m_result.clear(); +} + +// ----------- SynchronousProcess +SynchronousProcess::SynchronousProcess() : + m_d(new SynchronousProcessPrivate) +{ + m_d->m_timer.setInterval(1000); + connect(&m_d->m_timer, SIGNAL(timeout()), this, SLOT(slotTimeout())); + connect(&m_d->m_process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(finished(int,QProcess::ExitStatus))); + connect(&m_d->m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(error(QProcess::ProcessError))); + connect(&m_d->m_process, SIGNAL(readyReadStandardOutput()), + this, SLOT(stdOutReady())); + connect(&m_d->m_process, SIGNAL(readyReadStandardError()), + this, SLOT(stdErrReady())); +} + +SynchronousProcess::~SynchronousProcess() +{ + delete m_d; +} + +void SynchronousProcess::setTimeout(int timeoutMS) +{ + m_d->m_maxHangTimerCount = qMax(2, timeoutMS / 1000); +} + +int SynchronousProcess::timeout() const +{ + return 1000 * m_d->m_maxHangTimerCount; +} + +void SynchronousProcess::setStdOutCodec(QTextCodec *c) +{ + m_d->m_stdOutCodec = c; +} + +QTextCodec *SynchronousProcess::stdOutCodec() const +{ + return m_d->m_stdOutCodec; +} + +bool SynchronousProcess::stdOutBufferedSignalsEnabled() const +{ + return m_d->m_stdOut.bufferedSignalsEnabled; +} + +void SynchronousProcess::setStdOutBufferedSignalsEnabled(bool v) +{ + m_d->m_stdOut.bufferedSignalsEnabled = v; +} + +bool SynchronousProcess::stdErrBufferedSignalsEnabled() const +{ + return m_d->m_stdErr.bufferedSignalsEnabled; +} + +void SynchronousProcess::setStdErrBufferedSignalsEnabled(bool v) +{ + m_d->m_stdErr.bufferedSignalsEnabled = v; +} + +QStringList SynchronousProcess::environment() const +{ + return m_d->m_process.environment(); +} + +void SynchronousProcess::setEnvironment(const QStringList &e) +{ + m_d->m_process.setEnvironment(e); +} + +SynchronousProcessResponse SynchronousProcess::run(const QString &binary, + const QStringList &args) +{ + if (debug) + qDebug() << '>' << Q_FUNC_INFO << binary << args; + + m_d->clearForRun(); + m_d->m_timer.start(); + + QApplication::setOverrideCursor(Qt::WaitCursor); + + m_d->m_process.start(binary, args, QIODevice::ReadOnly); + m_d->m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents); + if (m_d->m_result.result == SynchronousProcessResponse::Finished || m_d->m_result.result == SynchronousProcessResponse::FinishedError) { + processStdOut(false); + processStdErr(false); + } + + m_d->m_result.stdOut = convertStdOut(m_d->m_stdOut.data); + m_d->m_result.stdErr = convertStdErr(m_d->m_stdErr.data); + + m_d->m_timer.stop(); + QApplication::restoreOverrideCursor(); + + if (debug) + qDebug() << '<' << Q_FUNC_INFO << binary << m_d->m_result; + return m_d->m_result; +} + +void SynchronousProcess::slotTimeout() +{ + if (++m_d->m_hangTimerCount > m_d->m_maxHangTimerCount) { + m_d->m_process.kill(); + m_d->m_result.result = SynchronousProcessResponse::Hang; + } + + if (debug) + qDebug() << Q_FUNC_INFO << m_d->m_hangTimerCount; +} + +void SynchronousProcess::finished(int exitCode, QProcess::ExitStatus e) +{ + if (debug) + qDebug() << Q_FUNC_INFO << exitCode << e; + m_d->m_hangTimerCount = 0; + switch (e) { + case QProcess::NormalExit: + m_d->m_result.result = exitCode ? SynchronousProcessResponse::FinishedError : SynchronousProcessResponse::Finished; + m_d->m_result.exitCode = exitCode; + break; + case QProcess::CrashExit: + m_d->m_result.result = SynchronousProcessResponse::TerminatedAbnormally; + m_d->m_result.exitCode = -1; + break; + } + m_d->m_eventLoop.quit(); +} + +void SynchronousProcess::error(QProcess::ProcessError e) +{ + m_d->m_hangTimerCount = 0; + if (debug) + qDebug() << Q_FUNC_INFO << e; + m_d->m_result.result = SynchronousProcessResponse::StartFailed; + m_d->m_eventLoop.quit(); +} + +void SynchronousProcess::stdOutReady() +{ + m_d->m_hangTimerCount = 0; + processStdOut(true); +} + +void SynchronousProcess::stdErrReady() +{ + m_d->m_hangTimerCount = 0; + processStdErr(true); +} + +QString SynchronousProcess::convertStdErr(const QByteArray &ba) +{ + return QString::fromLocal8Bit(ba).remove(QLatin1Char('\r')); +} + +QString SynchronousProcess::convertStdOut(const QByteArray &ba) const +{ + QString stdOut = m_d->m_stdOutCodec ? m_d->m_stdOutCodec->toUnicode(ba) : QString::fromLocal8Bit(ba); + return stdOut.remove(QLatin1Char('\r')); +} + +void SynchronousProcess::processStdOut(bool emitSignals) +{ + // Handle binary data + const QByteArray ba = m_d->m_process.readAllStandardOutput(); + if (debug > 1) + qDebug() << Q_FUNC_INFO << emitSignals << ba; + if (!ba.isEmpty()) { + m_d->m_stdOut.data += ba; + if (emitSignals) { + // Emit binary signals + emit stdOut(ba, m_d->m_stdOut.firstData); + m_d->m_stdOut.firstData = false; + // Buffered. Emit complete lines? + if (m_d->m_stdOut.bufferedSignalsEnabled) { + const QByteArray lines = m_d->m_stdOut.linesRead(); + if (!lines.isEmpty()) { + emit stdOutBuffered(convertStdOut(lines), m_d->m_stdOut.firstBuffer); + m_d->m_stdOut.firstBuffer = false; + } + } + } + } +} + +void SynchronousProcess::processStdErr(bool emitSignals) +{ + // Handle binary data + const QByteArray ba = m_d->m_process.readAllStandardError(); + if (debug > 1) + qDebug() << Q_FUNC_INFO << emitSignals << ba; + if (!ba.isEmpty()) { + m_d->m_stdErr.data += ba; + if (emitSignals) { + // Emit binary signals + emit stdErr(ba, m_d->m_stdErr.firstData); + m_d->m_stdErr.firstData = false; + if (m_d->m_stdErr.bufferedSignalsEnabled) { + // Buffered. Emit complete lines? + const QByteArray lines = m_d->m_stdErr.linesRead(); + if (!lines.isEmpty()) { + emit stdErrBuffered(convertStdErr(lines), m_d->m_stdErr.firstBuffer); + m_d->m_stdErr.firstBuffer = false; + } + } + } + } +} + +} +} diff --git a/src/libs/utils/synchronousprocess.h b/src/libs/utils/synchronousprocess.h new file mode 100644 index 00000000000..9458655d6bf --- /dev/null +++ b/src/libs/utils/synchronousprocess.h @@ -0,0 +1,139 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SYNCHRONOUSPROCESS_H +#define SYNCHRONOUSPROCESS_H + +#include <QtCore/QObject> +#include <QtCore/QProcess> +#include <QtCore/QStringList> + +#include "utils_global.h" + +QT_BEGIN_NAMESPACE +class QTextCodec; +class QDebug; +class QByteArray; +QT_END_NAMESPACE + +namespace Core { +namespace Utils { + +struct SynchronousProcessPrivate; + +/* Result of SynchronousProcess execution */ +struct QWORKBENCH_UTILS_EXPORT SynchronousProcessResponse { + enum Result { + // Finished with return code 0 + Finished, + // Finished with return code != 0 + FinishedError, + // Process terminated abnormally (kill) + TerminatedAbnormally, + // Executable could not be started + StartFailed, + // Hang, no output after time out + Hang }; + + SynchronousProcessResponse(); + void clear(); + + Result result; + int exitCode; + QString stdOut; + QString stdErr; +}; + +QWORKBENCH_UTILS_EXPORT QDebug operator<<(QDebug str, const SynchronousProcessResponse &); + +/* SynchronousProcess: Runs a synchronous process in its own event loop + * that blocks only user input events. Thus, it allows for the gui to + * repaint and append output to log windows. + * + * The stdOut(), stdErr() signals are emitted unbuffered as the process + * writes them. + * + * The stdOutBuffered(), stdErrBuffered() signals are emitted with complete + * lines based on the '\n' marker if they are enabled using + * stdOutBufferedSignalsEnabled()/setStdErrBufferedSignalsEnabled(). + * They would typically be used for log windows. */ + +class QWORKBENCH_UTILS_EXPORT SynchronousProcess : public QObject { + Q_OBJECT +public: + SynchronousProcess(); + virtual ~SynchronousProcess(); + + /* Timeout for hanging processes (no reaction on stderr/stdout)*/ + void setTimeout(int timeoutMS); + int timeout() const; + + void setStdOutCodec(QTextCodec *c); + QTextCodec *stdOutCodec() const; + + bool stdOutBufferedSignalsEnabled() const; + void setStdOutBufferedSignalsEnabled(bool); + + bool stdErrBufferedSignalsEnabled() const; + void setStdErrBufferedSignalsEnabled(bool); + + QStringList environment() const; + void setEnvironment(const QStringList &); + + SynchronousProcessResponse run(const QString &binary, const QStringList &args); + +signals: + void stdOut(const QByteArray &data, bool firstTime); + void stdErr(const QByteArray &data, bool firstTime); + + void stdOutBuffered(const QString &data, bool firstTime); + void stdErrBuffered(const QString &data, bool firstTime); + +private slots: + void slotTimeout(); + void finished(int exitCode, QProcess::ExitStatus e); + void error(QProcess::ProcessError); + void stdOutReady(); + void stdErrReady(); + +private: + void processStdOut(bool emitSignals); + void processStdErr(bool emitSignals); + static QString convertStdErr(const QByteArray &); + QString convertStdOut(const QByteArray &) const; + + SynchronousProcessPrivate *m_d; +}; + +} +} +#endif diff --git a/src/libs/utils/utils.pri b/src/libs/utils/utils.pri new file mode 100644 index 00000000000..4e173f2cad1 --- /dev/null +++ b/src/libs/utils/utils.pri @@ -0,0 +1 @@ +LIBS *= -l$$qtLibraryTarget(Utils) diff --git a/src/libs/utils/utils.pro b/src/libs/utils/utils.pro new file mode 100644 index 00000000000..d98ca1d889b --- /dev/null +++ b/src/libs/utils/utils.pro @@ -0,0 +1,53 @@ +TEMPLATE = lib +TARGET = Utils + +DEFINES += QWORKBENCH_UTILS_LIBRARY + +include(../../qworkbenchlibrary.pri) + +SOURCES += \ + reloadpromptutils.cpp \ + settingsutils.cpp \ + filesearch.cpp \ + pathchooser.cpp \ + filewizardpage.cpp \ + filewizarddialog.cpp \ + projectintropage.cpp \ + basevalidatinglineedit.cpp \ + filenamevalidatinglineedit.cpp \ + projectnamevalidatinglineedit.cpp \ + codegeneration.cpp \ + newclasswidget.cpp \ + classnamevalidatinglineedit.cpp \ + linecolumnlabel.cpp \ + fancylineedit.cpp \ + qtcolorbutton.cpp \ + submiteditorwidget.cpp \ + synchronousprocess.cpp + +HEADERS += \ + utils_global.h \ + reloadpromptutils.h \ + settingsutils.h \ + filesearch.h \ + listutils.h \ + pathchooser.h \ + filewizardpage.h \ + filewizarddialog.h \ + projectintropage.h \ + basevalidatinglineedit.h \ + filenamevalidatinglineedit.h \ + projectnamevalidatinglineedit.h \ + codegeneration.h \ + newclasswidget.h \ + classnamevalidatinglineedit.h \ + linecolumnlabel.h \ + fancylineedit.h \ + qtcolorbutton.h \ + submiteditorwidget.h \ + synchronousprocess.h + +FORMS += filewizardpage.ui \ + projectintropage.ui \ + newclasswidget.ui \ + submiteditorwidget.ui diff --git a/src/libs/utils/utils_global.h b/src/libs/utils/utils_global.h new file mode 100644 index 00000000000..3a91f77a2b9 --- /dev/null +++ b/src/libs/utils/utils_global.h @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/**************************************************************************** +** +** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef UTILS_GLOBAL_H +#define UTILS_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(QWORKBENCH_UTILS_LIBRARY) +# define QWORKBENCH_UTILS_EXPORT Q_DECL_EXPORT +#else +# define QWORKBENCH_UTILS_EXPORT Q_DECL_IMPORT +#endif + +#endif // UTILS_GLOBAL_H diff --git a/src/plugins/bineditor/BinEditor.mimetypes.xml b/src/plugins/bineditor/BinEditor.mimetypes.xml new file mode 100644 index 00000000000..4fcd9603652 --- /dev/null +++ b/src/plugins/bineditor/BinEditor.mimetypes.xml @@ -0,0 +1,39 @@ +<?xml version="1.0"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="application/octet-stream"> + <comment>unknown</comment> + <comment xml:lang="bg">Неизвестен тип</comment> + <comment xml:lang="ca">desconegut</comment> + <comment xml:lang="cs">Neznámý</comment> + <comment xml:lang="cy">Anhysbys</comment> + <comment xml:lang="da">ukendt</comment> + <comment xml:lang="de">unbekannt</comment> + <comment xml:lang="el">αγνωστο</comment> + <comment xml:lang="eo">nekonata</comment> + <comment xml:lang="es">desconocido</comment> + <comment xml:lang="eu">ezezaguna</comment> + <comment xml:lang="fi">tuntematon</comment> + <comment xml:lang="fr">inconnu</comment> + <comment xml:lang="hu">ismeretlen</comment> + <comment xml:lang="it">Sconosciuto</comment> + <comment xml:lang="ja">不明</comment> + <comment xml:lang="ko">알 수 없음</comment> + <comment xml:lang="lt">nežinoma</comment> + <comment xml:lang="ms">Entah</comment> + <comment xml:lang="nb">ukjent</comment> + <comment xml:lang="nl">onbekend</comment> + <comment xml:lang="nn">ukjend</comment> + <comment xml:lang="pl">nieznany typ</comment> + <comment xml:lang="pt">desconhecido</comment> + <comment xml:lang="pt_BR">Desconhecido</comment> + <comment xml:lang="ru">неизвестный</comment> + <comment xml:lang="rw">itazwi</comment> + <comment xml:lang="sq">nuk njihet</comment> + <comment xml:lang="sr">непознато</comment> + <comment xml:lang="sv">okänd</comment> + <comment xml:lang="uk">невідомо</comment> + <comment xml:lang="vi">không rõ</comment> + <comment xml:lang="zh_CN">未知</comment> + <comment xml:lang="zh_TW">不明</comment> + </mime-type> +</mime-info> diff --git a/src/plugins/bineditor/BinEditor.pluginspec b/src/plugins/bineditor/BinEditor.pluginspec new file mode 100644 index 00000000000..9e09f93eecb --- /dev/null +++ b/src/plugins/bineditor/BinEditor.pluginspec @@ -0,0 +1,11 @@ +<plugin name="BinEditor" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Binary editor component.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> + <dependency name="TextEditor" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/bineditor/bineditor.cpp b/src/plugins/bineditor/bineditor.cpp new file mode 100644 index 00000000000..4ca6e18707c --- /dev/null +++ b/src/plugins/bineditor/bineditor.cpp @@ -0,0 +1,871 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "bineditor.h" + +#include <texteditor/fontsettings.h> +#include <texteditor/texteditorconstants.h> + +#include <QtGui/QScrollBar> +#include <QtGui/QFontMetrics> +#include <QtGui/QPainter> +#include <QtGui/QScrollBar> +#include <QtGui/QWheelEvent> +#include <QtGui/QApplication> +#include <QtGui/QClipboard> +#include <QtCore/QDebug> + +using namespace BINEditor; + +static QByteArray calculateHexPattern(const QByteArray &pattern) +{ + QByteArray result; + if (pattern.size() % 2 == 0) { + bool ok = true; + int i = 0; + while (i < pattern.size()) { + ushort s = pattern.mid(i, 2).toUShort(&ok, 16); + if (!ok) { + return QByteArray(); + } + result.append(s); + i += 2; + } + } + return result; +} + +BinEditor::BinEditor(QWidget *parent) + : QAbstractScrollArea(parent) +{ + m_ieditor = 0; + init(); + m_unmodifiedState = 0; + m_hexCursor = true; + m_cursorPosition = 0; + m_anchorPosition = 0; + m_lowNibble = false; + m_cursorVisible = false; + setFocusPolicy(Qt::WheelFocus); + m_addressString = QString(9, QLatin1Char(':')); +} + +BinEditor::~BinEditor() +{ +} + +void BinEditor::init() +{ + QFontMetrics fm(fontMetrics()); + m_margin = 4; + m_descent = fm.descent(); + m_ascent = fm.ascent(); + m_lineHeight = fm.lineSpacing(); + m_charWidth = fm.width(QChar(QLatin1Char('M'))); + m_columnWidth = 2 * m_charWidth + fm.width(QChar(QLatin1Char(' '))); + m_numLines = m_data.size() / 16 + 1; + m_numVisibleLines = viewport()->height() / m_lineHeight; + m_textWidth = 16 * m_charWidth + m_charWidth; + int m_numberWidth = fm.width(QChar(QLatin1Char('9'))); + m_labelWidth = 8 * m_numberWidth + 2 * m_charWidth; + + int expectedCharWidth = m_columnWidth / 3; + const char *hex = "0123456789abcdef"; + m_isMonospacedFont = true; + while (*hex) { + if (fm.width(QLatin1Char(*hex)) != expectedCharWidth) { + m_isMonospacedFont = false; + break; + } + ++hex; + } + + horizontalScrollBar()->setRange(0, 2 * m_margin + 16 * m_columnWidth + + m_labelWidth + m_textWidth - viewport()->width()); + horizontalScrollBar()->setPageStep(viewport()->width()); + verticalScrollBar()->setRange(0, m_numLines - m_numVisibleLines); + verticalScrollBar()->setPageStep(m_numVisibleLines); +} + + +void BinEditor::setFontSettings(const TextEditor::FontSettings &fs) +{ + setFont(fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_TEXT)).font()); +} + +void BinEditor::setBlinkingCursorEnabled(bool enable) +{ + if (enable && QApplication::cursorFlashTime() > 0) + m_cursorBlinkTimer.start(QApplication::cursorFlashTime() / 2, this); + else + m_cursorBlinkTimer.stop(); + m_cursorVisible = enable; + updateLines(); +} + +void BinEditor::focusInEvent(QFocusEvent *) +{ + setBlinkingCursorEnabled(true); +} + +void BinEditor::focusOutEvent(QFocusEvent *) +{ + setBlinkingCursorEnabled(false); +} + +void BinEditor::timerEvent(QTimerEvent *e) +{ + if (e->timerId() == m_autoScrollTimer.timerId()) { + QRect visible = viewport()->rect(); + QPoint pos; + const QPoint globalPos = QCursor::pos(); + pos = viewport()->mapFromGlobal(globalPos); + QMouseEvent ev(QEvent::MouseMove, pos, globalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + mouseMoveEvent(&ev); + int deltaY = qMax(pos.y() - visible.top(), visible.bottom() - pos.y()) - visible.height(); + int deltaX = qMax(pos.x() - visible.left(), visible.right() - pos.x()) - visible.width(); + int delta = qMax(deltaX, deltaY); + if (delta >= 0) { + if (delta < 7) + delta = 7; + int timeout = 4900 / (delta * delta); + m_autoScrollTimer.start(timeout, this); + + if (deltaY > 0) + verticalScrollBar()->triggerAction(pos.y() < visible.center().y() ? + QAbstractSlider::SliderSingleStepSub + : QAbstractSlider::SliderSingleStepAdd); + if (deltaX > 0) + horizontalScrollBar()->triggerAction(pos.x() < visible.center().x() ? + QAbstractSlider::SliderSingleStepSub + : QAbstractSlider::SliderSingleStepAdd); + } + } else if (e->timerId() == m_cursorBlinkTimer.timerId()) { + m_cursorVisible = !m_cursorVisible; + updateLines(); + } + QAbstractScrollArea::timerEvent(e); +} + + +void BinEditor::setModified(bool modified) +{ + int unmodifiedState = modified ? -1 : m_undoStack.size(); + if (unmodifiedState == m_unmodifiedState) + return; + m_unmodifiedState = unmodifiedState; + emit modificationChanged(m_undoStack.size() != m_unmodifiedState); +} + +bool BinEditor::isModified() const +{ + return (m_undoStack.size() != m_unmodifiedState); +} + +void BinEditor::setData(const QByteArray &data) +{ + m_data = data; + m_unmodifiedState = 0; + m_undoStack.clear(); + m_redoStack.clear(); + init(); + emit cursorPositionChanged(m_cursorPosition); + + viewport()->update(); +} + +QByteArray BinEditor::data() const +{ + return m_data; +} + +void BinEditor::resizeEvent(QResizeEvent *) +{ + init(); +} + +void BinEditor::scrollContentsBy(int dx, int dy) +{ + viewport()->scroll(isRightToLeft() ? -dx : dx, dy * m_lineHeight); +} + +void BinEditor::changeEvent(QEvent *e) +{ + QAbstractScrollArea::changeEvent(e); + if(e->type() == QEvent::ActivationChange) { + if (!isActiveWindow()) + m_autoScrollTimer.stop(); + } + init(); + viewport()->update(); +} + + +void BinEditor::wheelEvent(QWheelEvent *e) +{ + if (e->modifiers() & Qt::ControlModifier) { + const int delta = e->delta(); + if (delta < 0) + zoomOut(); + else if (delta > 0) + zoomIn(); + return; + } + QAbstractScrollArea::wheelEvent(e); +} + + + +QRect BinEditor::cursorRect() const +{ + int topLine = verticalScrollBar()->value(); + int line = m_cursorPosition / 16; + int y = (line - topLine) * m_lineHeight; + int xoffset = horizontalScrollBar()->value(); + int column = m_cursorPosition % 16; + int x = m_hexCursor ? + (-xoffset + m_margin + m_labelWidth + column * m_columnWidth) + : (-xoffset + m_margin + m_labelWidth + 16 * m_columnWidth + m_charWidth + column * m_charWidth); + int w = m_hexCursor ? m_columnWidth : m_charWidth; + return QRect(x, y, w, m_lineHeight); +} + +int BinEditor::posAt(const QPoint &pos) const +{ + int xoffset = horizontalScrollBar()->value(); + int x = xoffset + pos.x() - m_margin - m_labelWidth; + int column = qMin(15, qMax(0,x) / m_columnWidth); + int topLine = verticalScrollBar()->value(); + int line = pos.y() / m_lineHeight; + + + if (x > 16 * m_columnWidth + m_charWidth/2) { + x -= 16 * m_columnWidth + m_charWidth; + for (column = 0; column < 15; ++column) { + int pos = (topLine + line) * 16 + column; + if (pos < 0 || pos >= m_data.size()) + break; + QChar qc(QLatin1Char(m_data.at(pos))); + if (!qc.isPrint()) + qc = 0xB7; + x -= fontMetrics().width(qc); + if (x <= 0) + break; + } + } + + return (qMin(m_data.size(), qMin(m_numLines, topLine + line) * 16) + column); +} + +bool BinEditor::inTextArea(const QPoint &pos) const +{ + int xoffset = horizontalScrollBar()->value(); + int x = xoffset + pos.x() - m_margin - m_labelWidth; + return (x > 16 * m_columnWidth + m_charWidth/2); +} + + +void BinEditor::updateLines(int fromPosition, int toPosition) +{ + if (fromPosition < 0) + fromPosition = m_cursorPosition; + if (toPosition < 0) + toPosition = fromPosition; + int topLine = verticalScrollBar()->value(); + int firstLine = qMin(fromPosition, toPosition) / 16; + int lastLine = qMax(fromPosition, toPosition) / 16; + int y = (firstLine - topLine) * m_lineHeight; + int h = (lastLine - firstLine + 1 ) * m_lineHeight; + + viewport()->update(0, y, viewport()->width(), h); +} + +int BinEditor::find(const QByteArray &pattern, int from, QTextDocument::FindFlags findFlags) +{ + if (pattern.isEmpty()) + return false; + bool backwards = (findFlags & QTextDocument::FindBackward); + int found = backwards ? m_data.lastIndexOf(pattern, from) + : m_data.indexOf(pattern, from); + int foundHex = -1; + QByteArray hexPattern = calculateHexPattern(pattern); + if (!hexPattern.isEmpty()) { + foundHex = backwards ? m_data.lastIndexOf(hexPattern, from) + : m_data.indexOf(hexPattern, from); + } + + int pos = (found >= 0 && (foundHex < 0 || found < foundHex)) ? found : foundHex; + if (pos >= 0) { + setCursorPosition(pos); + setCursorPosition(pos + (found == pos ? pattern.size() : hexPattern.size()), KeepAnchor); + } + + return pos; +} + +int BinEditor::findPattern(const QByteArray &data, int from, int offset, int *match) +{ + if (m_searchPattern.isEmpty()) + return -1; + int normal = m_searchPattern.isEmpty()? -1 : data.indexOf(m_searchPattern, from - offset); + int hex = m_searchPatternHex.isEmpty()? -1 : data.indexOf(m_searchPatternHex, from - offset); + + if (normal >= 0 && (hex < 0 || normal < hex)) { + if (match) + *match = m_searchPattern.length(); + return normal + offset; + } + if (hex >= 0) { + if (match) + *match = m_searchPatternHex.length(); + return hex + offset; + } + + return -1; +} + + +void BinEditor::drawItems(QPainter *painter, int x, int y, const QString &itemString) +{ + if (m_isMonospacedFont) { + painter->drawText(x, y, itemString); + } else { + for (int i = 0; i < 16; ++i) + painter->drawText(x + i*m_columnWidth, y, itemString.mid(i*3, 2)); + } +} + +QString BinEditor::addressString(uint address) +{ + QChar *addressStringData = m_addressString.data(); + const char *hex = "0123456789abcdef"; + for (int h = 0; h < 4; ++h) { + int shift = 4*(7-h); + addressStringData[h] = hex[(address & (0xf<<shift))>>shift]; + } + for (int h = 4; h < 8; ++h) { + int shift = 4*(7-h); + addressStringData[h+1] = hex[(address & (0xf<<shift))>>shift]; + } + return m_addressString; + +} + +void BinEditor::paintEvent(QPaintEvent *e) +{ + QPainter painter(viewport()); + int topLine = verticalScrollBar()->value(); + int xoffset = horizontalScrollBar()->value(); + painter.drawLine(-xoffset + m_margin + m_labelWidth - m_charWidth/2, 0, + -xoffset + m_margin + m_labelWidth - m_charWidth/2, viewport()->height()); + painter.drawLine(-xoffset + m_margin + m_labelWidth + 16 * m_columnWidth + m_charWidth/2, 0, + -xoffset + m_margin + m_labelWidth + 16 * m_columnWidth + m_charWidth/2, viewport()->height()); + + + int viewport_height = viewport()->height(); + QBrush alternate_base = palette().alternateBase(); + for (int i = 0; i < 8; ++i) { + int bg_x = -xoffset + m_margin + (2 * i + 1) * m_columnWidth + m_labelWidth; + QRect r(bg_x - m_charWidth/2, 0, m_columnWidth, viewport_height); + painter.fillRect(e->rect() & r, palette().alternateBase()); + } + + int matchLength = 0; + + QByteArray patternData; + int patternOffset = qMax(0, topLine*16 - m_searchPattern.size()); + if (!m_searchPattern.isEmpty()) + patternData = m_data.mid(patternOffset, m_numVisibleLines * 16); + + int foundPatternAt = findPattern(patternData, patternOffset, patternOffset, &matchLength); + + int selStart = qMin(m_cursorPosition, m_anchorPosition); + int selEnd = qMax(m_cursorPosition, m_anchorPosition); + + QString itemString(QLatin1String("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00")); + QChar *itemStringData = itemString.data(); + const char *hex = "0123456789abcdef"; + + painter.setPen(palette().text().color()); + for (int i = 0; i <= m_numVisibleLines; ++i) { + int line = topLine + i; + if (line >= m_numLines) + break; + + int y = i * m_lineHeight + m_ascent; + if (y - m_ascent > e->rect().bottom()) + break; + if (y + m_descent < e->rect().top()) + continue; + + + painter.drawText(-xoffset, i * m_lineHeight + m_ascent, addressString(((uint) line) * 16)); + QString printable; + int cursor = -1; + for (int c = 0; c < 16; ++c) { + int pos = line * 16 + c; + if (pos >= m_data.size()) + break; + QChar qc(QLatin1Char(m_data.at(pos))); + if (qc.unicode() >= 127 || !qc.isPrint()) + qc = 0xB7; + printable += qc; + } + + QRect selectionRect; + QRect printableSelectionRect; + + bool isFullySelected = (selStart < selEnd && selStart <= line*16 && (line+1)*16 <= selEnd); + + for (int c = 0; c < 16; ++c) { + int pos = line * 16 + c; + if (pos >= m_data.size()) { + while(c < 16) { + itemStringData[c*3] = itemStringData[c*3+1] = ' '; + ++c; + } + break; + } + + if (foundPatternAt >= 0 && pos >= foundPatternAt + matchLength) + foundPatternAt = findPattern(patternData, foundPatternAt + matchLength, patternOffset, &matchLength); + + + uchar value = (uchar)m_data.at(pos); + itemStringData[c*3] = hex[value >> 4]; + itemStringData[c*3+1] = hex[value & 0xf]; + + int item_x = -xoffset + m_margin + c * m_columnWidth + m_labelWidth; + + if (foundPatternAt >= 0 && pos >= foundPatternAt && pos < foundPatternAt + matchLength) { + painter.fillRect(item_x, y-m_ascent, m_columnWidth, m_lineHeight, QColor(0xffef0b)); + int printable_item_x = -xoffset + m_margin + m_labelWidth + 16 * m_columnWidth + m_charWidth + + painter.fontMetrics().width( printable.left(c)); + painter.fillRect(printable_item_x, y-m_ascent, + painter.fontMetrics().width(printable.at(c)), + m_lineHeight, QColor(0xffef0b)); + } + + if (selStart < selEnd && !isFullySelected && pos >= selStart && pos < selEnd) { + selectionRect |= QRect(item_x, y-m_ascent, m_columnWidth, m_lineHeight); + int printable_item_x = -xoffset + m_margin + m_labelWidth + 16 * m_columnWidth + m_charWidth + + painter.fontMetrics().width( printable.left(c)); + printableSelectionRect |= QRect(printable_item_x, y-m_ascent, + painter.fontMetrics().width(printable.at(c)), + m_lineHeight); + } + + if (pos == m_cursorPosition) + cursor = c; + + } + + int x = -xoffset + m_margin + m_labelWidth; + + if (isFullySelected) { + painter.save(); + painter.fillRect(x, y-m_ascent, 16*m_columnWidth, m_lineHeight, palette().highlight()); + painter.setPen(palette().highlightedText().color()); + drawItems(&painter, x, y, itemString); + painter.restore(); + } else { + drawItems(&painter, x, y, itemString); + if (!selectionRect.isEmpty()) { + painter.save(); + painter.fillRect(selectionRect, palette().highlight()); + painter.setPen(palette().highlightedText().color()); + painter.setClipRect(selectionRect); + drawItems(&painter, x, y, itemString); + painter.restore(); + } + } + + + if (cursor >= 0) { + int w = painter.fontMetrics().boundingRect(itemString.mid(cursor*3, 2)).width(); + QRect cursorRect(x + cursor * m_columnWidth, y - m_ascent, w + 1, m_lineHeight); + painter.save(); + painter.setPen(Qt::red); + painter.drawRect(cursorRect.adjusted(0, 0, 0, -1)); + painter.restore(); + if (m_hexCursor && m_cursorVisible) { + if (m_lowNibble) + cursorRect.adjust(painter.fontMetrics().width(itemString.left(1)), 0, 0, 0); + painter.fillRect(cursorRect, Qt::red); + painter.save(); + painter.setClipRect(cursorRect); + painter.setPen(Qt::white); + drawItems(&painter, x, y, itemString); + painter.restore(); + } + } + + int text_x = -xoffset + m_margin + m_labelWidth + 16 * m_columnWidth + m_charWidth; + + if (isFullySelected) { + painter.save(); + painter.fillRect(text_x, y-m_ascent, painter.fontMetrics().width(printable), m_lineHeight, + palette().highlight()); + painter.setPen(palette().highlightedText().color()); + painter.drawText(text_x, y, printable); + painter.restore(); + }else { + painter.drawText(text_x, y, printable); + if (!printableSelectionRect.isEmpty()) { + painter.save(); + painter.fillRect(printableSelectionRect, palette().highlight()); + painter.setPen(palette().highlightedText().color()); + painter.setClipRect(printableSelectionRect); + painter.drawText(text_x, y, printable); + painter.restore(); + } + } + + if (cursor >= 0) { + QRect cursorRect(text_x + painter.fontMetrics().width(printable.left(cursor)), + y-m_ascent, + painter.fontMetrics().width(printable.at(cursor)), + m_lineHeight); + painter.save(); + if (m_hexCursor || !m_cursorVisible) { + painter.setPen(Qt::red); + painter.drawRect(cursorRect.adjusted(0, 0, 0, -1)); + } else { + painter.setClipRect(cursorRect); + painter.fillRect(cursorRect, Qt::red); + painter.setPen(Qt::white); + painter.drawText(text_x, y, printable); + } + painter.restore(); + } + } +} + + +int BinEditor::cursorPosition() const +{ + return m_cursorPosition; +} + +void BinEditor::setCursorPosition(int pos, MoveMode moveMode) +{ + pos = qMin(m_data.size()-1, qMax(0, pos)); + if (pos == m_cursorPosition + && (m_anchorPosition == m_cursorPosition || moveMode == KeepAnchor) + && !m_lowNibble) + return; + + int oldCursorPosition = m_cursorPosition; + + bool hasSelection = m_anchorPosition != m_cursorPosition; + m_lowNibble = false; + if (!hasSelection) + updateLines(); + m_cursorPosition = pos; + if (moveMode == MoveAnchor) { + if (hasSelection) + updateLines(m_anchorPosition, oldCursorPosition); + m_anchorPosition = m_cursorPosition; + } + + hasSelection = m_anchorPosition != m_cursorPosition; + updateLines(hasSelection ? oldCursorPosition : m_cursorPosition, m_cursorPosition); + ensureCursorVisible(); + if (hasSelection != (m_anchorPosition != m_anchorPosition)) + emit copyAvailable(m_anchorPosition != m_cursorPosition); + emit cursorPositionChanged(m_cursorPosition); +} + + +void BinEditor::ensureCursorVisible() +{ + QRect cr = cursorRect(); + QRect vr = viewport()->rect(); + if (!vr.contains(cr)) { + if (cr.top() < vr.top()) + verticalScrollBar()->setValue(m_cursorPosition / 16); + else if (cr.bottom() > vr.bottom()) + verticalScrollBar()->setValue(m_cursorPosition / 16 - m_numVisibleLines + 1); + } +} + +void BinEditor::mousePressEvent(QMouseEvent *e) +{ + if (e->button() != Qt::LeftButton) + return; + setCursorPosition(posAt(e->pos())); + setBlinkingCursorEnabled(true); + if (m_hexCursor == inTextArea(e->pos())) { + m_hexCursor = !m_hexCursor; + updateLines(); + } +} + +void BinEditor::mouseMoveEvent(QMouseEvent *e) +{ + if (!(e->buttons() & Qt::LeftButton)) + return; + setCursorPosition(posAt(e->pos()), KeepAnchor); + if (m_hexCursor == inTextArea(e->pos())) { + m_hexCursor = !m_hexCursor; + updateLines(); + } + QRect visible = viewport()->rect(); + if (visible.contains(e->pos())) + m_autoScrollTimer.stop(); + else if (!m_autoScrollTimer.isActive()) + m_autoScrollTimer.start(100, this); +} + +void BinEditor::mouseReleaseEvent(QMouseEvent *) +{ + if (m_autoScrollTimer.isActive()) { + m_autoScrollTimer.stop(); + ensureCursorVisible(); + } +} + +void BinEditor::selectAll() +{ + setCursorPosition(0); + setCursorPosition(m_data.size()-1, KeepAnchor); +} + +void BinEditor::clear() +{ + setData(QByteArray()); +} + +bool BinEditor::event(QEvent *e) { + if (e->type() == QEvent::KeyPress) { + switch (static_cast<QKeyEvent*>(e)->key()) { + case Qt::Key_Tab: + case Qt::Key_Backtab: + m_hexCursor = !m_hexCursor; + setBlinkingCursorEnabled(true); + ensureCursorVisible(); + e->accept(); + return true; + default:; + } + } + return QAbstractScrollArea::event(e); +} + +void BinEditor::keyPressEvent(QKeyEvent *e) +{ + + if (e == QKeySequence::SelectAll) { + e->accept(); + selectAll(); + return; + } else if (e == QKeySequence::Copy) { + e->accept(); + copy(); + return; + } else if (e == QKeySequence::Undo) { + e->accept(); + undo(); + return; + } else if (e == QKeySequence::Redo) { + e->accept(); + redo(); + return; + } + + + MoveMode moveMode = e->modifiers() & Qt::ShiftModifier ? KeepAnchor : MoveAnchor; + switch (e->key()) { + case Qt::Key_Up: + setCursorPosition(m_cursorPosition - 16, moveMode); + break; + case Qt::Key_Down: + setCursorPosition(m_cursorPosition + 16, moveMode); + break; + case Qt::Key_Right: + setCursorPosition(m_cursorPosition + 1, moveMode); + break; + case Qt::Key_Left: + setCursorPosition(m_cursorPosition - 1, moveMode); + break; + case Qt::Key_PageUp: + case Qt::Key_PageDown: { + int line = qMax(0, m_cursorPosition / 16 - verticalScrollBar()->value()); + verticalScrollBar()->triggerAction(e->key() == Qt::Key_PageUp ? + QScrollBar::SliderPageStepSub : QScrollBar::SliderPageStepAdd); + setCursorPosition((verticalScrollBar()->value() + line) * 16 + m_cursorPosition % 16, moveMode); + } break; + + case Qt::Key_Home: + setCursorPosition((e->modifiers() & Qt::ControlModifier) ? + 0 : (m_cursorPosition/16 * 16), moveMode); + break; + case Qt::Key_End: + setCursorPosition((e->modifiers() & Qt::ControlModifier) ? + (m_data.size()-1) : (m_cursorPosition/16 * 16 + 15), moveMode); + break; + + default: { + QString text = e->text(); + for (int i = 0; i < text.length(); ++i) { + QChar c = text.at(i); + if (m_hexCursor) { + c = c.toLower(); + int nibble = -1; + if (c.unicode() >= 'a' && c.unicode() <= 'f') + nibble = c.unicode() - 'a' + 10; + else if (c.unicode() >= '0' && c.unicode() <= '9') + nibble = c.unicode() - '0'; + if (nibble < 0) + continue; + if (m_lowNibble) { + changeData(m_cursorPosition, nibble + (m_data[m_cursorPosition] & 0xf0)); + m_lowNibble = false; + setCursorPosition(m_cursorPosition + 1); + } else { + changeData(m_cursorPosition, (nibble << 4) + (m_data[m_cursorPosition] & 0x0f), true); + m_lowNibble = true; + updateLines(); + } + } else { + if (c.unicode() >= 128 || !c.isPrint()) + continue; + changeData(m_cursorPosition, c.unicode(), m_cursorPosition + 1); + setCursorPosition(m_cursorPosition + 1); + } + setBlinkingCursorEnabled(true); + } + } + } + + e->accept(); +} + +void BinEditor::zoomIn(int range) +{ + QFont f = font(); + const int newSize = f.pointSize() + range; + if (newSize <= 0) + return; + f.setPointSize(newSize); + setFont(f); +} + +void BinEditor::zoomOut(int range) +{ + zoomIn(-range); +} + +void BinEditor::copy() +{ + int selStart = qMin(m_cursorPosition, m_anchorPosition); + int selEnd = qMax(m_cursorPosition, m_anchorPosition); + if (selStart < selEnd) + QApplication::clipboard()->setText(QString::fromLatin1(m_data.mid(selStart, selEnd - selStart))); +} + +void BinEditor::highlightSearchResults(const QByteArray &pattern, QTextDocument::FindFlags /*findFlags*/) +{ + if (m_searchPattern == pattern) + return; + m_searchPattern = pattern; + m_searchPatternHex = calculateHexPattern(pattern); + viewport()->update(); +} + + +void BinEditor::changeData(int position, uchar character, bool highNibble) +{ + m_redoStack.clear(); + if (m_unmodifiedState > m_undoStack.size()) + m_unmodifiedState = -1; + BinEditorEditCommand cmd; + cmd.position = position; + cmd.character = (uchar) m_data[position]; + cmd.highNibble = highNibble; + + if (!highNibble && !m_undoStack.isEmpty() && m_undoStack.top().position == position && m_undoStack.top().highNibble) { + // compress + cmd.character = m_undoStack.top().character; + m_undoStack.pop(); + } + + m_data[position] = (char) character; + bool emitModificationChanged = (m_undoStack.size() == m_unmodifiedState); + m_undoStack.push(cmd); + if (emitModificationChanged) { + emit modificationChanged(m_undoStack.size() != m_unmodifiedState); + } + + if (m_undoStack.size() == 1) + emit undoAvailable(true); +} + + +void BinEditor::undo() +{ + if (m_undoStack.isEmpty()) + return; + bool emitModificationChanged = (m_undoStack.size() == m_unmodifiedState); + BinEditorEditCommand cmd = m_undoStack.pop(); + emitModificationChanged |= (m_undoStack.size() == m_unmodifiedState); + uchar c = m_data[cmd.position]; + m_data[cmd.position] = (char)cmd.character; + cmd.character = c; + m_redoStack.push(cmd); + setCursorPosition(cmd.position); + if (emitModificationChanged) + emit modificationChanged(m_undoStack.size() != m_unmodifiedState); + if (!m_undoStack.size()) + emit undoAvailable(false); + if (m_redoStack.size() == 1) + emit redoAvailable(true); +} + +void BinEditor::redo() +{ + if (m_redoStack.isEmpty()) + return; + BinEditorEditCommand cmd = m_redoStack.pop(); + uchar c = m_data[cmd.position]; + m_data[cmd.position] = (char)cmd.character; + cmd.character = c; + bool emitModificationChanged = (m_undoStack.size() == m_unmodifiedState); + m_undoStack.push(cmd); + setCursorPosition(cmd.position + 1); + if (emitModificationChanged) + emit modificationChanged(m_undoStack.size() != m_unmodifiedState); + if (m_undoStack.size() == 1) + emit undoAvailable(true); + if (!m_redoStack.size()) + emit redoAvailable(false); +} diff --git a/src/plugins/bineditor/bineditor.h b/src/plugins/bineditor/bineditor.h new file mode 100644 index 00000000000..2eae59af1a7 --- /dev/null +++ b/src/plugins/bineditor/bineditor.h @@ -0,0 +1,181 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BINEDITOR_H +#define BINEDITOR_H + +#include <QtGui/qabstractscrollarea.h> +#include <QtCore/qbasictimer.h> +#include <QtCore/qstack.h> +#include <QtGui/qtextdocument.h> +#include <QtGui/qtextformat.h> + +namespace Core { +class IEditor; +} + +namespace TextEditor { +class FontSettings; +} + +namespace BINEditor { + +class BinEditor : public QAbstractScrollArea +{ + Q_OBJECT + Q_PROPERTY(bool modified READ isModified WRITE setModified DESIGNABLE false) +public: + + BinEditor(QWidget *parent = 0); + ~BinEditor(); + + void setData(const QByteArray &data); + QByteArray data() const; + + void zoomIn(int range = 1); + void zoomOut(int range = 1); + + enum MoveMode { + MoveAnchor, + KeepAnchor + }; + + int cursorPosition() const; + void setCursorPosition(int pos, MoveMode moveMode = MoveAnchor); + + void setModified(bool); + bool isModified() const; + + int find(const QByteArray &pattern, int from = 0, QTextDocument::FindFlags findFlags = 0); + + void selectAll(); + void clear(); + + void undo(); + void redo(); + + Core::IEditor *editorInterface() const { return m_ieditor; } + void setEditorInterface(Core::IEditor *ieditor) { m_ieditor = ieditor; } + + bool hasSelection() const { return m_cursorPosition != m_anchorPosition; } + int selectionStart() const { return qMin(m_anchorPosition, m_cursorPosition); } + int selectionEnd() const { return qMax(m_anchorPosition, m_cursorPosition); } + + bool event(QEvent*); + + bool isUndoAvailable() const { return m_undoStack.size(); } + bool isRedoAvailable() const { return m_redoStack.size(); } + + QString addressString(uint address); + + +public Q_SLOTS: + void setFontSettings(const TextEditor::FontSettings &fs); + void highlightSearchResults(const QByteArray &pattern, QTextDocument::FindFlags findFlags = 0); + void copy(); + +Q_SIGNALS: + void modificationChanged(bool modified); + void undoAvailable(bool); + void redoAvailable(bool); + void copyAvailable(bool); + void cursorPositionChanged(int position); + +protected: + void scrollContentsBy(int dx, int dy); + void paintEvent(QPaintEvent *e); + void resizeEvent(QResizeEvent *); + void changeEvent(QEvent *); + void wheelEvent(QWheelEvent *e); + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void keyPressEvent(QKeyEvent *e); + void focusInEvent(QFocusEvent *); + void focusOutEvent(QFocusEvent *); + void timerEvent(QTimerEvent *); + +private: + QByteArray m_data; + int m_unmodifiedState; + int m_margin; + int m_descent; + int m_ascent; + int m_lineHeight; + int m_charWidth; + int m_labelWidth; + int m_textWidth; + int m_columnWidth; + int m_numLines; + int m_numVisibleLines; + + + bool m_cursorVisible; + int m_cursorPosition; + int m_anchorPosition; + bool m_hexCursor; + bool m_lowNibble; + bool m_isMonospacedFont; + + QByteArray m_searchPattern; + QByteArray m_searchPatternHex; + + QBasicTimer m_cursorBlinkTimer; + + void init(); + int posAt(const QPoint &pos) const; + bool inTextArea(const QPoint &pos) const; + QRect cursorRect() const; + void updateLines(int fromPosition = -1, int toPosition = -1); + void ensureCursorVisible(); + void setBlinkingCursorEnabled(bool enable); + + void changeData(int position, uchar character, bool highNibble = false); + + int findPattern(const QByteArray &data, int from, int offset, int *match); + void drawItems(QPainter *painter, int x, int y, const QString &itemString); + + struct BinEditorEditCommand { + int position; + uchar character; + bool highNibble; + }; + QStack<BinEditorEditCommand> m_undoStack, m_redoStack; + + QBasicTimer m_autoScrollTimer; + Core::IEditor *m_ieditor; + QString m_addressString; +}; + +} // namespace BINEditor + +#endif // BINEDITOR_H diff --git a/src/plugins/bineditor/bineditor.pro b/src/plugins/bineditor/bineditor.pro new file mode 100644 index 00000000000..08a67fdf05f --- /dev/null +++ b/src/plugins/bineditor/bineditor.pro @@ -0,0 +1,13 @@ +TEMPLATE = lib +TARGET = BinEditor + +include(bineditor_dependencies.pri) + +HEADERS += bineditorplugin.h \ + bineditor.h \ + bineditorconstants.h + +SOURCES += bineditorplugin.cpp \ + bineditor.cpp + +RESOURCES += bineditor.qrc diff --git a/src/plugins/bineditor/bineditor.qrc b/src/plugins/bineditor/bineditor.qrc new file mode 100644 index 00000000000..c7215495dba --- /dev/null +++ b/src/plugins/bineditor/bineditor.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/bineditor" > + <file>BinEditor.mimetypes.xml</file> + </qresource> +</RCC> diff --git a/src/plugins/bineditor/bineditor_dependencies.pri b/src/plugins/bineditor/bineditor_dependencies.pri new file mode 100644 index 00000000000..30120bd3f51 --- /dev/null +++ b/src/plugins/bineditor/bineditor_dependencies.pri @@ -0,0 +1,4 @@ +include(../../qworkbenchplugin.pri) +include(../../libs/utils/utils.pri) +include(../../plugins/texteditor/texteditor.pri) +include(../../plugins/coreplugin/coreplugin.pri) diff --git a/src/plugins/bineditor/bineditorconstants.h b/src/plugins/bineditor/bineditorconstants.h new file mode 100644 index 00000000000..f67d6c522d7 --- /dev/null +++ b/src/plugins/bineditor/bineditorconstants.h @@ -0,0 +1,43 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BINEDITORCONSTANTS_H +#define BINEDITORCONSTANTS_H + +namespace BINEditor { + namespace Constants { + const char * const C_BINEDITOR = "Binary Editor"; + const char * const C_BINEDITOR_MIMETYPE = "application/octet-stream"; + } +} + +#endif // BINEDITORCONSTANTS_H diff --git a/src/plugins/bineditor/bineditorplugin.cpp b/src/plugins/bineditor/bineditorplugin.cpp new file mode 100644 index 00000000000..ee223f3deae --- /dev/null +++ b/src/plugins/bineditor/bineditorplugin.cpp @@ -0,0 +1,503 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "bineditorplugin.h" +#include "bineditor.h" +#include "bineditorconstants.h" + +#include <QtCore/QFileInfo> +#include <QtGui/QMenu> +#include <QtGui/QAction> +#include <QtGui/QMainWindow> +#include <QtGui/QHBoxLayout> +#include <QtCore/QFile> + +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/texteditorsettings.h> +#include <texteditor/fontsettings.h> +#include <find/ifindsupport.h> +#include <utils/linecolumnlabel.h> +#include <utils/reloadpromptutils.h> + +using namespace BINEditor; +using namespace BINEditor::Internal; + + +class BinEditorFind : public Find::IFindSupport +{ + Q_OBJECT +public: + BinEditorFind(BinEditor *editor) { m_editor = editor; m_incrementalStartPos = -1; } + ~BinEditorFind() {} + + bool supportsReplace() const { return false; } + void resetIncrementalSearch() { m_incrementalStartPos = -1; } + void clearResults() { m_editor->highlightSearchResults(QByteArray()); } + QString currentFindString() const { return QString(); } + QString completedFindString() const { return QString(); } + + + int find(const QByteArray &pattern, int pos, QTextDocument::FindFlags findFlags) { + if (pattern.isEmpty()) { + m_editor->setCursorPosition(pos); + return pos; + } + + int found = m_editor->find(pattern, pos, findFlags); + if (found < 0) + found = m_editor->find(pattern, + (findFlags & QTextDocument::FindBackward)?m_editor->data().size()-1:0, + findFlags); + return found; + } + + bool findIncremental(const QString &txt, QTextDocument::FindFlags findFlags) { + QByteArray pattern = txt.toLatin1(); + if (m_incrementalStartPos < 0) + m_incrementalStartPos = m_editor->selectionStart(); + int pos = m_incrementalStartPos; + findFlags &= ~QTextDocument::FindBackward; + int found = find(pattern, pos, findFlags); + if (found >= 0) + m_editor->highlightSearchResults(pattern, findFlags); + else + m_editor->highlightSearchResults(QByteArray(), 0); + return found >= 0; + } + + bool findStep(const QString &txt, QTextDocument::FindFlags findFlags) { + QByteArray pattern = txt.toLatin1(); + bool wasReset = (m_incrementalStartPos < 0); + int pos = m_editor->cursorPosition(); + if (findFlags & QTextDocument::FindBackward) + pos = m_editor->selectionStart()-1; + int found = find(pattern, pos, findFlags); + if (found) + m_incrementalStartPos = found; + if (wasReset && found >= 0) + m_editor->highlightSearchResults(pattern, findFlags); + return found >= 0; + } + bool replaceStep(const QString &, const QString &, + QTextDocument::FindFlags) { return false;} + int replaceAll(const QString &, const QString &, + QTextDocument::FindFlags) { return 0; } + +private: + BinEditor *m_editor; + int m_incrementalStartPos; +}; + + +class BinEditorFile : public Core::IFile +{ + Q_OBJECT +public: + BinEditorFile(BinEditor *parent) : + Core::IFile(parent), + m_mimeType(QLatin1String(BINEditor::Constants::C_BINEDITOR_MIMETYPE)) + { + m_editor = parent; + } + ~BinEditorFile() {} + + virtual QString mimeType() const { return m_mimeType; } + + bool save(const QString &fileName = QString()) { + QFile file(fileName); + if (file.open(QIODevice::WriteOnly)) { + file.write(m_editor->data()); + file.close(); + m_editor->setModified(false); + m_editor->editorInterface()->setDisplayName(QFileInfo(fileName).fileName()); + m_fileName = fileName; + emit changed(); + return true; + } + return false; + } + + bool open(const QString &fileName) { + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) { + m_fileName = fileName; + m_editor->setData(file.readAll()); + m_editor->editorInterface()->setDisplayName(QFileInfo(fileName).fileName()); + file.close(); + return true; + } + return false; + } + + + void setFilename(const QString &filename) { + m_fileName = filename; + } + + QString fileName() const { + return m_fileName; + } + + QString defaultPath() const { return QString(); } + QString suggestedFileName() const { return QString(); } + QString fileFilter() const { return QString(); } + QString fileExtension() const { return QString(); } + + bool isModified() const { + return m_editor->isModified(); + } + bool isReadOnly() const { + const QFileInfo fi(m_fileName); + return !fi.isWritable(); + } + + bool isSaveAsAllowed() const { return true; } + + void modified(ReloadBehavior *behavior) { + const QString fileName = m_fileName; + + switch (*behavior) { + case Core::IFile::ReloadNone: + return; + case Core::IFile::ReloadAll: + open(fileName); + return; + case Core::IFile::ReloadPermissions: + emit changed(); + return; + case Core::IFile::AskForReload: + break; + } + + switch (Core::Utils::reloadPrompt(fileName, BinEditorPlugin::core()->mainWindow())) { + case Core::Utils::ReloadCurrent: + open(fileName); + break; + case Core::Utils::ReloadAll: + open(fileName); + *behavior = Core::IFile::ReloadAll; + break; + case Core::Utils::ReloadSkipCurrent: + break; + case Core::Utils::ReloadNone: + *behavior = Core::IFile::ReloadNone; + break; + } + } + +private: + const QString m_mimeType; + BinEditor *m_editor; + QString m_fileName; +}; + +class BinEditorInterface : public Core::IEditor +{ + Q_OBJECT +public: + BinEditorInterface(BinEditor *parent ) : Core::IEditor(parent) { + m_editor = parent; + m_file = new BinEditorFile(parent); + m_context << BinEditorPlugin::core()->uniqueIDManager()-> + uniqueIdentifier(Core::Constants::K_DEFAULT_BINARY_EDITOR); + m_context << BinEditorPlugin::core()->uniqueIDManager()->uniqueIdentifier(Constants::C_BINEDITOR); + m_cursorPositionLabel = new Core::Utils::LineColumnLabel; + + QHBoxLayout *l = new QHBoxLayout; + QWidget *w = new QWidget; + l->setMargin(0); + l->setContentsMargins(0, 0, 5, 0); + l->addStretch(1); + l->addWidget(m_cursorPositionLabel); + w->setLayout(l); + + m_toolBar = new QToolBar; + m_toolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + m_toolBar->addWidget(w); + + connect(m_editor, SIGNAL(cursorPositionChanged(int)), this, SLOT(updateCursorPosition(int))); + } + ~BinEditorInterface() {} + + QWidget *widget() { return m_editor; } + + QList<int> context() const { return m_context; } + + bool createNew(const QString & /* contents */ = QString()) { + m_editor->setData(QByteArray()); + m_file->setFilename(QString()); + return true; + } + bool open(const QString &fileName = QString()) { + return m_file->open(fileName); + } + Core::IFile *file() { return m_file; } + const char *kind() const { return Core::Constants::K_DEFAULT_BINARY_EDITOR; } + QString displayName() const { return m_displayName; } + void setDisplayName(const QString &title) { m_displayName = title; emit changed(); } + + bool duplicateSupported() const { return false; } + IEditor *duplicate(QWidget */*parent*/) { return 0; } + + QByteArray saveState() const { return QByteArray();} // TODO + bool restoreState(const QByteArray &/*state*/) {return false;} // TODO + + QToolBar *toolBar() { return m_toolBar; } + +signals: + void changed(); + +public slots: + void updateCursorPosition(int position) { + m_cursorPositionLabel->setText(m_editor->addressString((uint)position), + m_editor->addressString((uint)m_editor->data().size())); + } + +private: + BinEditor *m_editor; + QString m_displayName; + BinEditorFile *m_file; + QList<int> m_context; + QToolBar *m_toolBar; + Core::Utils::LineColumnLabel *m_cursorPositionLabel; +}; + + + + +///////////////////////////////// BinEditorFactory ////////////////////////////////// + +BinEditorFactory::BinEditorFactory(BinEditorPlugin *owner) : + m_kind(QLatin1String(Core::Constants::K_DEFAULT_BINARY_EDITOR)), + m_mimeTypes(QLatin1String(BINEditor::Constants::C_BINEDITOR_MIMETYPE)), + m_owner(owner) +{ +} + +QString BinEditorFactory::kind() const +{ + return m_kind; +} + +Core::IFile *BinEditorFactory::open(const QString &fileName) +{ + Core::IEditor *iface = m_owner->m_core->editorManager()->openEditor(fileName, kind()); + return iface ? iface->file() : 0; +} + +Core::IEditor *BinEditorFactory::createEditor(QWidget *parent) +{ + BinEditor *editor = new BinEditor(parent); + m_owner->initializeEditor(editor); + return editor->editorInterface(); +} + +QStringList BinEditorFactory::mimeTypes() const +{ + return m_mimeTypes; +} + +///////////////////////////////// BinEditorPlugin ////////////////////////////////// + +BinEditorPlugin *BinEditorPlugin::m_instance = 0; + +BinEditorPlugin::BinEditorPlugin() : + m_core(0) +{ + m_undoAction = m_redoAction = m_copyAction = m_selectAllAction = 0; + m_instance = this; +} + +BinEditorPlugin::~BinEditorPlugin() +{ + m_instance = 0; +} + +BinEditorPlugin *BinEditorPlugin::instance() +{ + return m_instance; +} + +Core::ICore *BinEditorPlugin::core() +{ + return m_instance->m_core; +} + +QAction *BinEditorPlugin::registerNewAction(const QString &id, const QString &title) +{ + + QAction *result = new QAction(title, this); + m_core->actionManager()->registerAction(result, id, m_context); + return result; +} + +QAction *BinEditorPlugin::registerNewAction(const QString &id, + QObject *receiver, + const char *slot, + const QString &title) +{ + QAction *rc = registerNewAction(id, title); + if (!rc) + return 0; + + connect(rc, SIGNAL(triggered()), receiver, slot); + return rc; +} + +void BinEditorPlugin::initializeEditor(BinEditor *editor) +{ + BinEditorInterface *editorInterface = new BinEditorInterface(editor); + QObject::connect(editor, SIGNAL(modificationChanged(bool)), editorInterface, SIGNAL(changed())); + editor->setEditorInterface(editorInterface); + + m_context << BinEditorPlugin::core()->uniqueIDManager()->uniqueIdentifier(Constants::C_BINEDITOR); + if (!m_undoAction) { + m_undoAction = registerNewAction(QLatin1String(Core::Constants::UNDO), + this, SLOT(undoAction()), + tr("&Undo")); + m_redoAction = registerNewAction(QLatin1String(Core::Constants::REDO), + this, SLOT(redoAction()), + tr("&Redo")); + m_copyAction = registerNewAction(QLatin1String(Core::Constants::COPY), + this, SLOT(copyAction())); + m_selectAllAction = registerNewAction(QLatin1String(Core::Constants::SELECTALL), + this, SLOT(selectAllAction())); + } + + // Font settings + TextEditor::TextEditorSettings *settings = TextEditor::TextEditorSettings::instance(); + editor->setFontSettings(settings->fontSettings()); + connect(settings, SIGNAL(fontSettingsChanged(TextEditor::FontSettings)), + editor, SLOT(setFontSettings(TextEditor::FontSettings))); + + QObject::connect(editor, SIGNAL(undoAvailable(bool)), this, SLOT(updateActions())); + QObject::connect(editor, SIGNAL(redoAvailable(bool)), this, SLOT(updateActions())); + QObject::connect(editor, SIGNAL(copyAvailable(bool)), this, SLOT(updateActions())); + + Aggregation::Aggregate *aggregate = new Aggregation::Aggregate; + BinEditorFind *binEditorFind = new BinEditorFind(editor); + aggregate->add(binEditorFind); + aggregate->add(editor); +} + +bool BinEditorPlugin::initialize(const QStringList & /*arguments*/, QString *errorMessage) +{ + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + if (!m_core->mimeDatabase()->addMimeTypes(QLatin1String(":/bineditor/BinEditor.mimetypes.xml"), errorMessage)) + return false; + + connect(m_core, SIGNAL(contextAboutToChange(Core::IContext *)), + this, SLOT(updateCurrentEditor(Core::IContext *))); + + addAutoReleasedObject(new BinEditorFactory(this)); + + return true; +} + +void BinEditorPlugin::extensionsInitialized() +{ +} + +void BinEditorPlugin::updateCurrentEditor(Core::IContext *object) +{ + do { + if (!object) { + if (!m_currentEditor) + return; + + m_currentEditor = 0; + break; + } + BinEditor *editor = qobject_cast<BinEditor *>(object->widget()); + if (!editor) { + if (!m_currentEditor) + return; + + m_currentEditor = 0; + break; + } + + if (editor == m_currentEditor) + return; + + m_currentEditor = editor; + + } while (false); + updateActions(); +} + +void BinEditorPlugin::updateActions() +{ + bool hasEditor = (m_currentEditor != 0); + if (m_selectAllAction) + m_selectAllAction->setEnabled(hasEditor); + if (m_undoAction) + m_undoAction->setEnabled(m_currentEditor && m_currentEditor->isUndoAvailable()); + if (m_redoAction) + m_redoAction->setEnabled(m_currentEditor && m_currentEditor->isRedoAvailable()); + if (m_copyAction) + m_copyAction->setEnabled(m_currentEditor && m_currentEditor->hasSelection()); +} + +void BinEditorPlugin::undoAction() +{ + if (m_currentEditor) + m_currentEditor->undo(); +} + +void BinEditorPlugin::redoAction() +{ + if (m_currentEditor) + m_currentEditor->redo(); +} + +void BinEditorPlugin::copyAction() +{ + if (m_currentEditor) + m_currentEditor->copy(); +} + +void BinEditorPlugin::selectAllAction() +{ + if (m_currentEditor) + m_currentEditor->selectAll(); +} + + +Q_EXPORT_PLUGIN(BinEditorPlugin) + +#include "bineditorplugin.moc" diff --git a/src/plugins/bineditor/bineditorplugin.h b/src/plugins/bineditor/bineditorplugin.h new file mode 100644 index 00000000000..6bd0da64b30 --- /dev/null +++ b/src/plugins/bineditor/bineditorplugin.h @@ -0,0 +1,124 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BINEDITORPLUGIN_H +#define BINEDITORPLUGIN_H + +#include <QtCore/qplugin.h> +#include <QtCore/QPointer> +#include <QtCore/QStringList> +#include <QtGui/QAction> + +#include <extensionsystem/iplugin.h> +#include <coreplugin/editormanager/ieditorfactory.h> + +namespace Core { +class ICore; +class IWizard; +} + +namespace BINEditor { +class BinEditor; +namespace Internal { +class BinEditorFactory; + + +class BinEditorPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + BinEditorPlugin(); + ~BinEditorPlugin(); + + static BinEditorPlugin *instance(); + static Core::ICore *core(); + + bool initialize(const QStringList &arguments, QString *error_message = 0); + void extensionsInitialized(); + + // Connect editor to settings changed signals. + void initializeEditor(BinEditor *editor); + +private slots: + void undoAction(); + void redoAction(); + void copyAction(); + void selectAllAction(); + void updateActions(); + + void updateCurrentEditor(Core::IContext *object); +private: + QList<int> m_context; + QAction *registerNewAction(const QString &id, const QString &title = QString()); + QAction *registerNewAction(const QString &id, QObject *receiver, const char *slot, + const QString &title = QString()); + QAction *m_undoAction; + QAction *m_redoAction; + QAction *m_copyAction; + QAction *m_selectAllAction; + + friend class BinEditorFactory; + Core::IEditor *createEditor(QWidget *parent); + + static BinEditorPlugin *m_instance; + + Core::ICore *m_core; + typedef QList<Core::IWizard *> WizardList; + WizardList m_wizards; + BinEditorFactory *m_factory; + QPointer<BinEditor> m_currentEditor; +}; + +class BinEditorFactory : public Core::IEditorFactory +{ + Q_OBJECT + +public: + explicit BinEditorFactory(BinEditorPlugin *owner); + + virtual QStringList mimeTypes() const; + + Core::IEditor *createEditor(QWidget *parent); + QString kind() const; + Core::IFile *open(const QString &fileName); + +private: + const QString m_kind; + const QStringList m_mimeTypes; + BinEditorPlugin *m_owner; +}; + +} // namespace Internal +} // namespace BINEditor + +#endif // BINEDITORPLUGIN_H diff --git a/src/plugins/bookmarks/Bookmarks.pluginspec b/src/plugins/bookmarks/Bookmarks.pluginspec new file mode 100644 index 00000000000..359ab6d203c --- /dev/null +++ b/src/plugins/bookmarks/Bookmarks.pluginspec @@ -0,0 +1,12 @@ +<plugin name="Bookmarks" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Bookmarks in text editors.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="TextEditor" version="0.9.1"/> + <dependency name="ProjectExplorer" version="0.9.1"/> + <dependency name="Core" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/bookmarks/bookmark.cpp b/src/plugins/bookmarks/bookmark.cpp new file mode 100644 index 00000000000..41f4659cb70 --- /dev/null +++ b/src/plugins/bookmarks/bookmark.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "bookmark.h" +#include "bookmarkmanager.h" +#include <QtCore/QDebug> +#include <QtGui/QTextBlock> + +using namespace Bookmarks::Internal; + +const QIcon Bookmark::m_bookmarkIcon = QIcon(":/bookmarks/images/bookmark.png"); + +Bookmark::Bookmark(const QString& fileName, int lineNumber, BookmarkManager *manager) + : BaseTextMark(fileName, lineNumber), m_manager(manager) +{ + m_fileName = fileName; + m_fileInfo.setFile(fileName); + m_onlyFile = m_fileInfo.fileName(); + m_path = m_fileInfo.path(); + m_lineNumber= lineNumber; +} + +QIcon Bookmark::icon() const +{ + return m_bookmarkIcon; +} + +void Bookmark::removedFromEditor() +{ + m_manager->removeBookmark(this); +} + +void Bookmark::updateLineNumber(int lineNumber) +{ + if (lineNumber != m_lineNumber) { + m_lineNumber = lineNumber; + m_manager->updateBookmark(this); + } +} + +void Bookmark::updateBlock(const QTextBlock &block) +{ + if (m_lineText != block.text()) { + m_lineText = block.text(); + m_manager->updateBookmark(this); + } +} + +QString Bookmark::lineText() const +{ + return m_lineText; +} + +QString Bookmark::filePath() const +{ + return m_fileName; +} + +QString Bookmark::fileName() const +{ + return m_onlyFile; +} + +QString Bookmark::path() const +{ + return m_path; +} diff --git a/src/plugins/bookmarks/bookmark.h b/src/plugins/bookmarks/bookmark.h new file mode 100644 index 00000000000..d54d6af3e56 --- /dev/null +++ b/src/plugins/bookmarks/bookmark.h @@ -0,0 +1,85 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BOOKMARK_H +#define BOOKMARK_H + +#include <texteditor/itexteditor.h> +#include <texteditor/basetextmark.h> + +QT_BEGIN_NAMESPACE +class QTreeWidgetItem; +QT_END_NAMESPACE + +#include <QtCore/QFileInfo> + +namespace Bookmarks { +namespace Internal { + +class BookmarkManager; + +class Bookmark : public TextEditor::BaseTextMark +{ + Q_OBJECT +public: + Bookmark(const QString& fileName, int lineNumber, BookmarkManager *manager); + + QIcon icon() const; + + void updateLineNumber(int lineNumber); + void updateBlock(const QTextBlock &block); + void removedFromEditor(); + + QString filePath() const; + QString fileName() const; + QString path() const; + QString lineText() const; + + inline int lineNumber() const { return m_lineNumber; } + +private: + static const QIcon m_bookmarkIcon; + + BookmarkManager *m_manager; + int m_lineNumber; + QString m_name; + QString m_fileName; + QString m_onlyFile; + QString m_path; + QString m_lineText; + QFileInfo m_fileInfo; +}; + +} // namespace Internal +} // namespace Bookmarks + +#endif // BOOKMARK_H diff --git a/src/plugins/bookmarks/bookmarkmanager.cpp b/src/plugins/bookmarks/bookmarkmanager.cpp new file mode 100644 index 00000000000..bb26bfbd4ff --- /dev/null +++ b/src/plugins/bookmarks/bookmarkmanager.cpp @@ -0,0 +1,742 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "bookmarkmanager.h" +#include "bookmark.h" +#include "bookmarksplugin.h" +#include "bookmarks_global.h" + +#include <projectexplorer/ProjectExplorerInterfaces> +#include <coreplugin/icore.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <texteditor/basetexteditor.h> + +#include <QtGui/QAction> +#include <QtCore/QFileInfo> +#include <QtCore/QDebug> +#include <QtGui/QPainter> +#include <QtGui/QContextMenuEvent> + +Q_DECLARE_METATYPE(Bookmarks::Internal::Bookmark*) + +using namespace Bookmarks; +using namespace Bookmarks::Internal; +using namespace ProjectExplorer; + +BookmarkDelegate::BookmarkDelegate(QObject *parent) + : QStyledItemDelegate(parent), m_normalPixmap(0), m_selectedPixmap(0) +{ +} + +BookmarkDelegate::~BookmarkDelegate() +{ + delete m_normalPixmap; + delete m_selectedPixmap; +} + +QSize BookmarkDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + + QFontMetrics fm(option.font); + QSize s; + s.setWidth(option.rect.width()); + s.setHeight(fm.height() * 2 + 10); + return s; +} + +void BookmarkDelegate::generateGradientPixmap(int width, int height, QColor color, bool selected) const +{ + + QColor c = color; + c.setAlpha(0); + + QPixmap *pixmap = new QPixmap(width+1, height); + pixmap->fill(c); + + QPainter painter(pixmap); + painter.setPen(Qt::NoPen); + + QLinearGradient lg; + lg.setCoordinateMode(QGradient::ObjectBoundingMode); + lg.setFinalStop(1,0); + + lg.setColorAt(0, c); + lg.setColorAt(0.4, color); + + painter.setBrush(lg); + painter.drawRect(0, 0, width+1, height); + + if (selected) + m_selectedPixmap = pixmap; + else + m_normalPixmap = pixmap; +} + +void BookmarkDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + painter->save(); + + QFontMetrics fm(opt.font); + static int lwidth = fm.width("8888") + 18; + + QColor backgroundColor; + QColor textColor; + + bool selected = opt.state & QStyle::State_Selected; + + if (selected) { + painter->setBrush(opt.palette.highlight().color()); + backgroundColor = opt.palette.highlight().color(); + if (!m_selectedPixmap) + generateGradientPixmap(lwidth, fm.height()+1, backgroundColor, selected); + } else { + painter->setBrush(opt.palette.background().color()); + backgroundColor = opt.palette.background().color(); + if (!m_normalPixmap) + generateGradientPixmap(lwidth, fm.height(), backgroundColor, selected); + } + painter->setPen(Qt::NoPen); + painter->drawRect(opt.rect); + + // Set Text Color + if (opt.state & QStyle::State_Selected) + textColor = opt.palette.highlightedText().color(); + else + textColor = opt.palette.text().color(); + + painter->setPen(textColor); + + + // TopLeft + QString topLeft = index.data(BookmarkManager::Filename ).toString(); + painter->drawText(6, 2 + opt.rect.top() + fm.ascent(), topLeft); + + QString topRight = index.data(BookmarkManager::LineNumber).toString(); + // Check wheter we need to be fancy and paint some background + int fwidth = fm.width(topLeft); + if (fwidth + lwidth > opt.rect.width()) { + int left = opt.rect.right() - lwidth; + painter->drawPixmap(left, opt.rect.top(), selected? *m_selectedPixmap : *m_normalPixmap); + } + // topRight + painter->drawText(opt.rect.right() - fm.width(topRight) - 6 , 2 + opt.rect.top() + fm.ascent(), topRight); + + // Directory + QColor mix; + mix.setRgbF(0.7 * textColor.redF() + 0.3 * backgroundColor.redF(), + 0.7 * textColor.greenF() + 0.3 * backgroundColor.greenF(), + 0.7 * textColor.blueF() + 0.3 * backgroundColor.blueF()); + painter->setPen(mix); +// +// QString directory = index.data(BookmarkManager::Directory).toString(); +// int availableSpace = opt.rect.width() - 12; +// if (fm.width(directory) > availableSpace) { +// // We need a shorter directory +// availableSpace -= fm.width("..."); +// +// int pos = directory.size(); +// int idx; +// forever { +// idx = directory.lastIndexOf("/", pos-1); +// if(idx == -1) { +// // Can't happen, this means the string did fit after all? +// break; +// } +// int width = fm.width(directory.mid(idx, pos-idx)); +// if (width > availableSpace) { +// directory = "..." + directory.mid(pos); +// break; +// } else { +// pos = idx; +// availableSpace -= width; +// } +// } +// } +// +// painter->drawText(3, opt.rect.top() + fm.ascent() + fm.height() + 6, directory); + + QString lineText = index.data(BookmarkManager::LineText).toString().trimmed(); + painter->drawText(6, opt.rect.top() + fm.ascent() + fm.height() + 6, lineText); + + // Separator lines + painter->setPen(QColor::fromRgb(150,150,150)); + painter->drawLine(0, opt.rect.bottom(), opt.rect.right(), opt.rect.bottom()); + painter->restore(); +} + +BookmarkView::BookmarkView(QWidget *parent) + : QListView(parent) +{ + setWindowTitle(tr("Bookmarks")); + setWindowIcon(QIcon(":/bookmarks/images/bookmark.png")); + + connect(this, SIGNAL(clicked(const QModelIndex &)), + this, SLOT(gotoBookmark(const QModelIndex &))); + + m_bookmarkContext = new BookmarkContext(this); + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + core->addContextObject(m_bookmarkContext); + + setItemDelegate(new BookmarkDelegate(this)); + setFrameStyle(QFrame::NoFrame); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setFocusPolicy(Qt::NoFocus); +} + +BookmarkView::~BookmarkView() +{ + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + core->removeContextObject(m_bookmarkContext); +} + +void BookmarkView::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu menu; + QAction *remove = menu.addAction("&Remove Bookmark"); + QAction *removeAll = menu.addAction("Remove all Bookmarks"); + m_contextMenuIndex = indexAt(event->pos()); + if (!m_contextMenuIndex.isValid()) + remove->setEnabled(false); + + if (model()->rowCount() == 0) + removeAll->setEnabled(false); + + connect(remove, SIGNAL(triggered()), + this, SLOT(removeFromContextMenu())); + connect(removeAll, SIGNAL(triggered()), + this, SLOT(removeAll())); + + + menu.exec(mapToGlobal(event->pos())); +} + +void BookmarkView::removeFromContextMenu() +{ + + removeBookmark(m_contextMenuIndex); +} + +void BookmarkView::removeBookmark(const QModelIndex& index) +{ + BookmarkManager *manager = static_cast<BookmarkManager *>(model()); + Bookmark *bm = manager->bookmarkForIndex(index); + manager->removeBookmark(bm); +} + +// The perforcemance of this function could be greatly improved. +// +void BookmarkView::removeAll() +{ + BookmarkManager *manager = static_cast<BookmarkManager *>(model()); + while (manager->rowCount()) { + QModelIndex index = manager->index(0, 0); + removeBookmark(index); + } +} + +void BookmarkView::setModel(QAbstractItemModel *model) +{ + BookmarkManager *manager = qobject_cast<BookmarkManager *>(model); + Q_ASSERT(manager); + QListView::setModel(model); + setSelectionModel(manager->selectionModel()); + setSelectionMode(QAbstractItemView::SingleSelection); + setSelectionBehavior(QAbstractItemView::SelectRows); +} + +void BookmarkView::gotoBookmark(const QModelIndex &index) +{ + static_cast<BookmarkManager *>(model())->gotoBookmark(index); +} + +//// +// BookmarkContext +//// + +BookmarkContext::BookmarkContext(BookmarkView *widget) + : m_bookmarkView(widget) +{ + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + m_context << core->uniqueIDManager()->uniqueIdentifier(Constants::BOOKMARKS_CONTEXT); +} + +QList<int> BookmarkContext::context() const +{ + return m_context; +} + +QWidget *BookmarkContext::widget() +{ + return m_bookmarkView; +} + +//// +// BookmarkManager +//// + +BookmarkManager::BookmarkManager() : + m_core(BookmarksPlugin::core()), + m_bookmarkIcon(QIcon(QLatin1String(":/bookmarks/images/bookmark.png"))) +{ + m_selectionModel = new QItemSelectionModel(this, this); + + connect(m_core, SIGNAL(contextChanged(Core::IContext*)), + this, SLOT(updateActionStatus())); + + ExtensionSystem::PluginManager *pm = m_core->pluginManager(); + ProjectExplorerPlugin *projectExplorer = pm->getObject<ProjectExplorerPlugin>(); + + connect(projectExplorer->session(), SIGNAL(sessionLoaded()), + this, SLOT(loadBookmarks())); + + updateActionStatus(); +} + +BookmarkManager::~BookmarkManager() +{ + DirectoryFileBookmarksMap::iterator it, end; + end = m_bookmarksMap.end(); + for (it = m_bookmarksMap.begin(); it != end; ++it) { + FileNameBookmarksMap *bookmarks = it.value(); + qDeleteAll(bookmarks->values()); + delete bookmarks; + } +} + +QItemSelectionModel *BookmarkManager::selectionModel() const +{ + return m_selectionModel; +} + +QModelIndex BookmarkManager::index(int row, int column, const QModelIndex &parent) const +{ + if (parent.isValid()) + return QModelIndex(); + else + return createIndex(row, column, 0); +} + +QModelIndex BookmarkManager::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + +int BookmarkManager::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + else + return m_bookmarksList.count(); +} + +int BookmarkManager::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + return 3; +} + +QVariant BookmarkManager::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.column() !=0 || index.row() < 0 || index.row() >= m_bookmarksList.count()) + return QVariant(); + + if (role == BookmarkManager::Filename) + return m_bookmarksList.at(index.row())->fileName(); + else if (role == BookmarkManager::LineNumber) + return m_bookmarksList.at(index.row())->lineNumber(); + else if (role == BookmarkManager::Directory) + return m_bookmarksList.at(index.row())->path(); + else if (role == BookmarkManager::LineText) + return m_bookmarksList.at(index.row())->lineText(); + else if (role == Qt::ToolTipRole) + return m_bookmarksList.at(index.row())->filePath(); + + return QVariant(); +} + +void BookmarkManager::toggleBookmark() +{ + TextEditor::ITextEditor *editor = currentTextEditor(); + if (!editor) + return; + + const QFileInfo fi(editor->file()->fileName()); + const int editorLine = editor->currentLine(); + + // Remove any existing bookmark on this line + if (Bookmark *mark = findBookmark(fi.path(), fi.fileName(), editorLine)) { + // TODO check if the bookmark is really on the same markable Interface + removeBookmark(mark); + return; + } + + // Add a new bookmark if no bookmark existed on this line + Bookmark *bookmark = new Bookmark(fi.filePath(), editorLine, this); + addBookmark(bookmark); +} + +void BookmarkManager::updateBookmark(Bookmark *bookmark) +{ + int idx = m_bookmarksList.indexOf(bookmark); + emit dataChanged(index(idx, 0, QModelIndex()), index(idx, 2, QModelIndex())); + saveBookmarks(); +} + +void BookmarkManager::removeBookmark(Bookmark *bookmark) +{ + const QFileInfo fi(bookmark->filePath() ); + FileNameBookmarksMap *files = m_bookmarksMap.value(fi.path()); + + FileNameBookmarksMap::iterator i = files->begin(); + while (i != files->end()) { + if (i.value() == bookmark) { + files->erase(i); + delete bookmark; + break; + } + ++i; + } + if (files->count() <= 0) { + m_bookmarksMap.remove(fi.path()); + delete files; + } + + int idx = m_bookmarksList.indexOf(bookmark); + beginRemoveRows(QModelIndex(), idx, idx); + m_bookmarksList.removeAt(idx); + endRemoveRows(); + + if (selectionModel()->currentIndex().isValid()) + selectionModel()->setCurrentIndex(selectionModel()->currentIndex(), QItemSelectionModel::Select | QItemSelectionModel::Clear); + + updateActionStatus(); + saveBookmarks(); +} + +Bookmark *BookmarkManager::bookmarkForIndex(QModelIndex index) +{ + if (!index.isValid() || index.row() >= m_bookmarksList.size()) + return 0; + return m_bookmarksList.at(index.row()); +} + +void BookmarkManager::gotoBookmark(const QModelIndex &idx) +{ + gotoBookmark(m_bookmarksList.at(idx.row())); +} + +void BookmarkManager::gotoBookmark(Bookmark* bookmark) +{ + TextEditor::BaseTextEditor::openEditorAt(bookmark->filePath(), + bookmark->lineNumber()); +} + +void BookmarkManager::nextInDocument() +{ + documentPrevNext(true); +} + +void BookmarkManager::prevInDocument() +{ + documentPrevNext(false); +} + +void BookmarkManager::documentPrevNext(bool next) +{ + TextEditor::ITextEditor *editor = currentTextEditor(); + int editorLine = editor->currentLine(); + QFileInfo fi(editor->file()->fileName()); + if (!m_bookmarksMap.contains(fi.path())) + return; + + int firstLine = -1; + int lastLine = -1; + int prevLine = -1; + int nextLine = -1; + const QList<Bookmark*> marks = m_bookmarksMap.value(fi.path())->values(fi.fileName()); + for (int i = 0; i < marks.count(); ++i) { + int markLine = marks.at(i)->lineNumber(); + if (firstLine == -1 || firstLine > markLine) + firstLine = markLine; + if (lastLine < markLine) + lastLine = markLine; + if (markLine < editorLine && prevLine < markLine) + prevLine = markLine; + if (markLine > editorLine && + (nextLine == -1 || nextLine > markLine)) + nextLine = markLine; + } + + m_core->editorManager()->addCurrentPositionToNavigationHistory(true); + if (next) { + if (nextLine == -1) + editor->gotoLine(firstLine); + else + editor->gotoLine(nextLine); + } else { + if (prevLine == -1) + editor->gotoLine(lastLine); + else + editor->gotoLine(prevLine); + } + m_core->editorManager()->addCurrentPositionToNavigationHistory(); +} + +void BookmarkManager::next() +{ + QModelIndex current = selectionModel()->currentIndex(); + if (!current.isValid()) + return; + int row = current.row() + 1; + if (row == m_bookmarksList.size()) + row = 0; + QModelIndex newIndex = current.sibling(row, current.column()); + selectionModel()->setCurrentIndex(newIndex, QItemSelectionModel::Select | QItemSelectionModel::Clear); + gotoBookmark(newIndex); +} + +void BookmarkManager::prev() +{ + QModelIndex current = selectionModel()->currentIndex(); + if (!current.isValid()) + return; + int row = current.row(); + if (row == 0) + row = m_bookmarksList.size(); + --row; + QModelIndex newIndex = current.sibling(row, current.column()); + selectionModel()->setCurrentIndex(newIndex, QItemSelectionModel::Select | QItemSelectionModel::Clear); + gotoBookmark(newIndex); +} + +TextEditor::ITextEditor *BookmarkManager::currentTextEditor() const +{ + Core::IEditor *currEditor = m_core->editorManager()->currentEditor(); + if (!currEditor) + return 0; + return qobject_cast<TextEditor::ITextEditor *>(currEditor); +} + +/* Returns the current session. */ +SessionManager* BookmarkManager::sessionManager() const +{ + ExtensionSystem::PluginManager *pm = m_core->pluginManager(); + ProjectExplorerPlugin *pe = pm->getObject<ProjectExplorerPlugin>(); + return pe->session(); +} + +BookmarkManager::State BookmarkManager::state() const +{ + if (m_bookmarksMap.empty()) + return NoBookMarks; + + TextEditor::ITextEditor *editor = currentTextEditor(); + if (!editor) + return HasBookMarks; + + const QFileInfo fi(editor->file()->fileName()); + + const DirectoryFileBookmarksMap::const_iterator dit = m_bookmarksMap.constFind(fi.path()); + if (dit == m_bookmarksMap.constEnd()) + return HasBookMarks; + + return HasBookmarksInDocument; +} + +void BookmarkManager::updateActionStatus() +{ + emit updateActions(state()); +} + +void BookmarkManager::moveUp() +{ + QModelIndex current = selectionModel()->currentIndex(); + int row = current.row(); + if (row == 0) + row = m_bookmarksList.size(); + --row; + + // swap current.row() and row + + Bookmark *b = m_bookmarksList.at(row); + m_bookmarksList[row] = m_bookmarksList.at(current.row()); + m_bookmarksList[current.row()] = b; + + QModelIndex topLeft = current.sibling(row, 0); + QModelIndex bottomRight = current.sibling(current.row(), 2); + emit dataChanged(topLeft, bottomRight); + selectionModel()->setCurrentIndex(current.sibling(row, 0), QItemSelectionModel::Select | QItemSelectionModel::Clear); +} + +void BookmarkManager::moveDown() +{ + QModelIndex current = selectionModel()->currentIndex(); + int row = current.row(); + ++row; + if (row == m_bookmarksList.size()) + row = 0; + + // swap current.row() and row + Bookmark *b = m_bookmarksList.at(row); + m_bookmarksList[row] = m_bookmarksList.at(current.row()); + m_bookmarksList[current.row()] = b; + + QModelIndex topLeft = current.sibling(current.row(), 0); + QModelIndex bottomRight = current.sibling(row, 2); + emit dataChanged(topLeft, bottomRight); + selectionModel()->setCurrentIndex(current.sibling(row, 0), QItemSelectionModel::Select | QItemSelectionModel::Clear); +} + +/* Returns the bookmark at the given file and line number, or 0 if no such bookmark exists. */ +Bookmark* BookmarkManager::findBookmark(const QString &path, const QString &fileName, int lineNumber) +{ + if (m_bookmarksMap.contains(path)) { + foreach (Bookmark *bookmark, m_bookmarksMap.value(path)->values(fileName)) { + if (bookmark->lineNumber() == lineNumber) + return bookmark; + } + } + return 0; +} + +/* Adds a bookmark to the internal data structures. The 'userset' parameter + * determines whether action status should be updated and whether the bookmarks + * should be saved to the session settings. + */ +void BookmarkManager::addBookmark(Bookmark *bookmark, bool userset) +{ + beginInsertRows(QModelIndex(), m_bookmarksList.size(), m_bookmarksList.size()); + const QFileInfo fi(bookmark->filePath()); + const QString &path = fi.path(); + + if (!m_bookmarksMap.contains(path)) + m_bookmarksMap.insert(path, new FileNameBookmarksMap()); + m_bookmarksMap.value(path)->insert(fi.fileName(), bookmark); + + m_bookmarksList.append(bookmark); + + endInsertRows(); + if (userset) { + updateActionStatus(); + saveBookmarks(); + } + selectionModel()->setCurrentIndex(index(m_bookmarksList.size()-1 , 0, QModelIndex()), QItemSelectionModel::Select | QItemSelectionModel::Clear); +} + +/* Adds a new bookmark based on information parsed from the string. */ +void BookmarkManager::addBookmark(const QString &s) +{ + int index2 = s.lastIndexOf(':'); + int index1 = s.indexOf(':'); + if (index2 != -1 || index1 != -1) { + const QString &filePath = s.mid(index1+1, index2-index1-1); + const int lineNumber = s.mid(index2 + 1).toInt(); + const QFileInfo fi(filePath); + + if (!filePath.isEmpty() && !findBookmark(fi.path(), fi.fileName(), lineNumber)) { + Bookmark *b = new Bookmark(filePath, lineNumber, this); + addBookmark(b, false); + } + } else { + qDebug() << "BookmarkManager::addBookmark() Invalid bookmark string:" << s; + } +} + +/* Puts the bookmark in a string for storing it in the settings. */ +QString BookmarkManager::bookmarkToString(const Bookmark *b) +{ + const QLatin1Char colon(':'); + // Empty string was the name of the bookmark, which now is always "" + return QLatin1String("") + colon + b->filePath() + colon + QString::number(b->lineNumber()); +} + +/* Saves the bookmarks to the session settings. */ +void BookmarkManager::saveBookmarks() +{ + SessionManager *s = sessionManager(); + if (!s) + return; + + QStringList list; + foreach (const FileNameBookmarksMap *bookmarksMap, m_bookmarksMap) + foreach (const Bookmark *bookmark, *bookmarksMap) + list << bookmarkToString(bookmark); + + s->setValue("Bookmarks", list); +} + +/* Loads the bookmarks from the session settings. */ +void BookmarkManager::loadBookmarks() +{ + SessionManager *s = sessionManager(); + if (!s) + return; + + const QStringList &list = s->value("Bookmarks").toStringList(); + foreach (const QString &bookmarkString, list) + addBookmark(bookmarkString); + + updateActionStatus(); +} + +// BookmarkViewFactory + +BookmarkViewFactory::BookmarkViewFactory(BookmarkManager *bm) + : m_manager(bm) +{ + +} + +QString BookmarkViewFactory::displayName() +{ + return "Bookmarks"; +} + +QKeySequence BookmarkViewFactory::activationSequence() +{ + return QKeySequence(Qt::ALT + Qt::Key_M); +} + +Core::NavigationView BookmarkViewFactory::createWidget() +{ + BookmarkView *bookmarkView = new BookmarkView(); + bookmarkView->setModel(m_manager); + Core::NavigationView view; + view.widget = bookmarkView; + return view; +} diff --git a/src/plugins/bookmarks/bookmarkmanager.h b/src/plugins/bookmarks/bookmarkmanager.h new file mode 100644 index 00000000000..45e4bf3b7b1 --- /dev/null +++ b/src/plugins/bookmarks/bookmarkmanager.h @@ -0,0 +1,193 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BOOKMARKMANAGER_H +#define BOOKMARKMANAGER_H + +#include <QtCore/QAbstractItemModel> +#include <QtGui/QListView> +#include <QtCore/QList> +#include <QtGui/QPixmap> +#include <QtGui/QStyledItemDelegate> + +#include <coreplugin/icontext.h> +#include <coreplugin/inavigationwidgetfactory.h> + +namespace ProjectExplorer { +class SessionManager; +} + +namespace Core { +class ICore; +class IEditor; +} + +namespace TextEditor { +class ITextEditor; +} + +namespace Bookmarks { +namespace Internal { + +class Bookmark; +class BookmarksPlugin; +class BookmarkContext; + +class BookmarkManager : public QAbstractItemModel +{ + Q_OBJECT + +public: + BookmarkManager(); + ~BookmarkManager(); + void updateBookmark(Bookmark *bookmark); + void removeBookmark(Bookmark *bookmark); // Does not remove the mark + Bookmark *bookmarkForIndex(QModelIndex index); + + enum State { NoBookMarks, HasBookMarks, HasBookmarksInDocument }; + State state() const; + + // Model stuff + 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; + + void gotoBookmark(const QModelIndex &); + + // this QItemSelectionModel is shared by all views + QItemSelectionModel *selectionModel() const; + + enum Roles {Filename = Qt::UserRole, LineNumber = Qt::UserRole + 1, Directory = Qt::UserRole + 2, LineText = Qt::UserRole + 3}; + +public slots: + void toggleBookmark(); + void nextInDocument(); + void prevInDocument(); + void next(); + void prev(); + void moveUp(); + void moveDown(); + +signals: + void updateActions(int state); + void currentIndexChanged(const QModelIndex &); + +private slots: + void updateActionStatus(); + void gotoBookmark(Bookmark *bookmark); + void loadBookmarks(); +private: + TextEditor::ITextEditor *currentTextEditor() const; + ProjectExplorer::SessionManager* sessionManager() const; + + void documentPrevNext(bool next); + + Bookmark* findBookmark(const QString &path, const QString &fileName, int lineNumber); + void addBookmark(Bookmark *bookmark, bool userset = true); + void addBookmark(const QString &s); + static QString bookmarkToString(const Bookmark *b); + void saveBookmarks(); + + typedef QMultiMap<QString, Bookmark*> FileNameBookmarksMap; + typedef QMap<QString, FileNameBookmarksMap*> DirectoryFileBookmarksMap; + + DirectoryFileBookmarksMap m_bookmarksMap; + Core::ICore *m_core; + + QIcon m_bookmarkIcon; + + QList<Bookmark *> m_bookmarksList; + QItemSelectionModel *m_selectionModel; +}; + +class BookmarkView : public QListView { + Q_OBJECT +public: + BookmarkView(QWidget *parent = 0); + ~BookmarkView(); + void setModel(QAbstractItemModel * model); +public slots: + void gotoBookmark(const QModelIndex &index); +protected slots: + void removeFromContextMenu(); + void removeAll(); +protected: + void contextMenuEvent(QContextMenuEvent *event); + void removeBookmark(const QModelIndex& index); +private: + BookmarkContext *m_bookmarkContext; + QModelIndex m_contextMenuIndex; +}; + +class BookmarkContext : public Core::IContext +{ +public: + BookmarkContext(BookmarkView *widget); + virtual QList<int> context() const; + virtual QWidget *widget(); +private: + BookmarkView *m_bookmarkView; + QList<int> m_context; +}; + +class BookmarkViewFactory : public Core::INavigationWidgetFactory +{ +public: + BookmarkViewFactory(BookmarkManager *bm); + virtual QString displayName(); + virtual QKeySequence activationSequence(); + virtual Core::NavigationView createWidget(); +private: + BookmarkManager *m_manager; +}; + +class BookmarkDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + BookmarkDelegate(QObject * parent = 0); + ~BookmarkDelegate(); + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + +private: + void generateGradientPixmap(int width, int height, QColor color, bool selected) const; + mutable QPixmap *m_normalPixmap; + mutable QPixmap *m_selectedPixmap; +}; + +} // namespace Internal +} // namespace Bookmarks + +#endif // BOOKMARKMANAGER_H diff --git a/src/plugins/bookmarks/bookmarks.pro b/src/plugins/bookmarks/bookmarks.pro new file mode 100644 index 00000000000..1fcbffff7b4 --- /dev/null +++ b/src/plugins/bookmarks/bookmarks.pro @@ -0,0 +1,18 @@ +TEMPLATE = lib +TARGET = Bookmarks + +include(../../qworkbenchplugin.pri) +include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/texteditor/texteditor.pri) + +HEADERS += bookmarksplugin.h \ + bookmark.h \ + bookmarkmanager.h \ + bookmarks_global.h + +SOURCES += bookmarksplugin.cpp \ + bookmark.cpp \ + bookmarkmanager.cpp + +RESOURCES += bookmarks.qrc diff --git a/src/plugins/bookmarks/bookmarks.qrc b/src/plugins/bookmarks/bookmarks.qrc new file mode 100644 index 00000000000..f0a890bf374 --- /dev/null +++ b/src/plugins/bookmarks/bookmarks.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/bookmarks" > + <file>images/bookmark.png</file> + </qresource> +</RCC> diff --git a/src/plugins/bookmarks/bookmarks_global.h b/src/plugins/bookmarks/bookmarks_global.h new file mode 100644 index 00000000000..e29f4e58650 --- /dev/null +++ b/src/plugins/bookmarks/bookmarks_global.h @@ -0,0 +1,56 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BOOKMARKS_GLOBAL_H +#define BOOKMARKS_GLOBAL_H + +namespace Bookmarks { +namespace Constants { + +const char * const BOOKMARKS_TOGGLE_ACTION = "Bookmarks.Toggle"; +const char * const BOOKMARKS_MOVEUP_ACTION = "Bookmarks.MoveUp"; +const char * const BOOKMARKS_MOVEDOWN_ACTION = "Bookmarks.MoveDown"; +const char * const BOOKMARKS_PREV_ACTION = "Bookmarks.Previous"; +const char * const BOOKMARKS_NEXT_ACTION = "Bookmarks.Next"; +const char * const BOOKMARKS_PREVDIR_ACTION = "Bookmarks.Previous.Directory"; +const char * const BOOKMARKS_NEXTDIR_ACTION = "Bookmarks.Next.Directory"; +const char * const BOOKMARKS_PREVDOC_ACTION = "Bookmarks.Previous.Document"; +const char * const BOOKMARKS_NEXTDOC_ACTION = "Bookmarks.Next.Document"; + +const char * const BOOKMARKS_MENU = "Bookmarks.Menu"; +const char * const BOOKMARKS_CONTEXT = "Bookmarks"; + +} //namespace Constants +} //namespace Bookmarks + +#endif //BOOKMARKS_GLOBAL_H + diff --git a/src/plugins/bookmarks/bookmarksplugin.cpp b/src/plugins/bookmarks/bookmarksplugin.cpp new file mode 100644 index 00000000000..76df8e57774 --- /dev/null +++ b/src/plugins/bookmarks/bookmarksplugin.cpp @@ -0,0 +1,185 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "bookmarksplugin.h" +#include "bookmarkmanager.h" +#include "bookmarks_global.h" + +#include <QtCore/qplugin.h> +#include <QtGui/QMenu> +#include <QDebug> + +#include <texteditor/texteditorconstants.h> +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> + +using namespace Bookmarks::Constants; +using namespace Bookmarks::Internal; + +BookmarksPlugin *BookmarksPlugin::m_instance = 0; + +BookmarksPlugin::BookmarksPlugin(): + m_bookmarkManager(0), + m_core(0) +{ + m_instance = this; +} + +void BookmarksPlugin::extensionsInitialized() +{ +} + +bool BookmarksPlugin::initialize(const QStringList & /*arguments*/, QString *) +{ + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + Core::ActionManagerInterface *am = m_core->actionManager(); + + QList<int> context = QList<int>() << m_core->uniqueIDManager()-> + uniqueIdentifier(Constants::BOOKMARKS_CONTEXT); + QList<int> textcontext, globalcontext; + textcontext << m_core->uniqueIDManager()-> + uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR); + globalcontext << Core::Constants::C_GLOBAL_ID; + + Core::IActionContainer *mtools = + am->actionContainer(Core::Constants::M_TOOLS); + + Core::IActionContainer *mbm = + am->createMenu(QLatin1String(BOOKMARKS_MENU)); + mbm->menu()->setTitle(tr("&Bookmarks")); + mtools->addMenu(mbm); + + //Toggle + m_toggleAction = new QAction(tr("Toggle Bookmark"), this); + Core::ICommand *cmd = + am->registerAction(m_toggleAction, BOOKMARKS_TOGGLE_ACTION, textcontext); +#ifndef Q_OS_MAC + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+M"))); +#else + cmd->setDefaultKeySequence(QKeySequence(tr("Meta+M"))); +#endif + mbm->addAction(cmd); + + QAction *sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("Bookmarks.Sep.Toggle"), textcontext); + mbm->addAction(cmd); + + // Move Up + m_moveUpAction = new QAction(tr("Move Up"), this); + cmd = am->registerAction(m_moveUpAction, BOOKMARKS_MOVEUP_ACTION, context); + mbm->addAction(cmd); + + // Move Down + m_moveDownAction = new QAction(tr("Move Down"), this); + cmd = am->registerAction(m_moveDownAction, BOOKMARKS_MOVEDOWN_ACTION, context); + mbm->addAction(cmd); + + sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("Bookmarks.Sep.Navigation"), context); + mbm->addAction(cmd); + + //Previous + m_prevAction = new QAction(tr("Previous Bookmark"), this); + cmd = am->registerAction(m_prevAction, BOOKMARKS_PREV_ACTION, globalcontext); +#ifndef Q_OS_MAC + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+,"))); +#else + cmd->setDefaultKeySequence(QKeySequence(tr("Meta+,"))); +#endif + mbm->addAction(cmd); + + //Next + m_nextAction = new QAction(tr("Next Bookmark"), this); + cmd = am->registerAction(m_nextAction, BOOKMARKS_NEXT_ACTION, globalcontext); +#ifndef Q_OS_MAC + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+."))); +#else + cmd->setDefaultKeySequence(QKeySequence(tr("Meta+."))); +#endif + mbm->addAction(cmd); + + sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("Bookmarks.Sep.DirNavigation"), globalcontext); + mbm->addAction(cmd); + + //Previous Doc + m_docPrevAction = new QAction(tr("Previous Bookmark In Document"), this); + cmd = am->registerAction(m_docPrevAction, BOOKMARKS_PREVDOC_ACTION, globalcontext); + mbm->addAction(cmd); + + //Next Doc + m_docNextAction = new QAction(tr("Next Bookmark In Document"), this); + cmd = am->registerAction(m_docNextAction, BOOKMARKS_NEXTDOC_ACTION, globalcontext); + mbm->addAction(cmd); + + m_bookmarkManager = new BookmarkManager; + + connect(m_toggleAction, SIGNAL(triggered()), m_bookmarkManager, SLOT(toggleBookmark())); + connect(m_prevAction, SIGNAL(triggered()), m_bookmarkManager, SLOT(prev())); + connect(m_nextAction, SIGNAL(triggered()), m_bookmarkManager, SLOT(next())); + connect(m_docPrevAction, SIGNAL(triggered()), m_bookmarkManager, SLOT(prevInDocument())); + connect(m_docNextAction, SIGNAL(triggered()), m_bookmarkManager, SLOT(nextInDocument())); + connect(m_moveUpAction, SIGNAL(triggered()), m_bookmarkManager, SLOT(moveUp())); + connect(m_moveDownAction, SIGNAL(triggered()), m_bookmarkManager, SLOT(moveDown())); + connect(m_bookmarkManager, SIGNAL(updateActions(int)), this, SLOT(updateActions(int))); + updateActions(m_bookmarkManager->state()); + addAutoReleasedObject(new BookmarkViewFactory(m_bookmarkManager)); + + return true; +} + +BookmarksPlugin::~BookmarksPlugin() +{ + delete m_bookmarkManager; +} + +void BookmarksPlugin::updateActions(int state) +{ + + const bool hasbm = state >= BookmarkManager::HasBookMarks; + const bool hasdocbm = state == BookmarkManager::HasBookmarksInDocument; + + m_toggleAction->setEnabled(true); + m_prevAction->setEnabled(hasbm); + m_nextAction->setEnabled(hasbm); + m_docPrevAction->setEnabled(hasdocbm); + m_docNextAction->setEnabled(hasdocbm); + m_moveUpAction->setEnabled(hasbm); + m_moveDownAction->setEnabled(hasbm); +} + +Q_EXPORT_PLUGIN(BookmarksPlugin) diff --git a/src/plugins/bookmarks/bookmarksplugin.h b/src/plugins/bookmarks/bookmarksplugin.h new file mode 100644 index 00000000000..7da45ef5ee7 --- /dev/null +++ b/src/plugins/bookmarks/bookmarksplugin.h @@ -0,0 +1,86 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BOOKMARKS_H +#define BOOKMARKS_H + +#include <QtCore/QObject> +#include <QtCore/QMultiMap> + +#include <extensionsystem/iplugin.h> + +QT_FORWARD_DECLARE_CLASS(QAction) + +namespace Core { +class ICore; +} + +namespace Bookmarks { +namespace Internal { + +class BookmarkManager; + +class BookmarksPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + BookmarksPlugin(); + ~BookmarksPlugin(); + + static BookmarksPlugin *instance() { return m_instance; } + static Core::ICore *core() { return m_instance->m_core; } + + bool initialize(const QStringList &arguments, QString *error_message); + void extensionsInitialized(); + +public slots: + void updateActions(int stateMask); + +private: + static BookmarksPlugin *m_instance; + BookmarkManager *m_bookmarkManager; + Core::ICore *m_core; + + QAction *m_toggleAction; + QAction *m_prevAction; + QAction *m_nextAction; + QAction *m_docPrevAction; + QAction *m_docNextAction; + QAction *m_moveUpAction; + QAction *m_moveDownAction; +}; + +} // namespace Internal +} // namespace Bookmarks + +#endif // BOOKMARKS_H diff --git a/src/plugins/bookmarks/images/bookmark.png b/src/plugins/bookmarks/images/bookmark.png Binary files differnew file mode 100644 index 00000000000..7b2e5fd0cef --- /dev/null +++ b/src/plugins/bookmarks/images/bookmark.png diff --git a/src/plugins/cmakeprojectmanager/CMakeProject.mimetypes.xml b/src/plugins/cmakeprojectmanager/CMakeProject.mimetypes.xml new file mode 100644 index 00000000000..d3c8f41b8d5 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/CMakeProject.mimetypes.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="text/x-cmake"> + <sub-class-of type="text/plain"/> + <comment>CMake Project file</comment> + <glob pattern="CMakeLists.txt"/> + </mime-type> +</mime-info> diff --git a/src/plugins/cmakeprojectmanager/CMakeProjectManager.pluginspec b/src/plugins/cmakeprojectmanager/CMakeProjectManager.pluginspec new file mode 100644 index 00000000000..84eefae0ee8 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/CMakeProjectManager.pluginspec @@ -0,0 +1,14 @@ +<plugin name="CMakeProjectManager" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>### TODO</license> + <description>CMake support</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="TextEditor" version="0.9.1"/> + <dependency name="ProjectExplorer" version="0.9.1"/> + <dependency name="CppTools" version="0.9.1"/> + <dependency name="CppEditor" version="0.9.1"/> + <dependency name="Help" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp new file mode 100644 index 00000000000..2c98124bb90 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -0,0 +1,441 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cmakeproject.h" +#include "cmakeprojectconstants.h" +#include "cmakeprojectnodes.h" +#include <extensionsystem/pluginmanager.h> +#include <cpptools/cppmodelmanagerinterface.h> +#include <QtCore/QDebug> + +using namespace CMakeProjectManager; +using namespace CMakeProjectManager::Internal; + +CMakeProject::CMakeProject(CMakeManager *manager, const QString &fileName) + : m_manager(manager), m_fileName(fileName), m_rootNode(new CMakeProjectNode(m_fileName)) +{ + //TODO + m_file = new CMakeFile(this, fileName); + QDir dir = QFileInfo(m_fileName).absoluteDir(); + QString cbpFile = findCbpFile(dir); + if (cbpFile.isEmpty()) + cbpFile = createCbpFile(dir); + QList<ProjectExplorer::FileNode *> fileList; + QStringList includeFiles; + if (parseCbpFile(cbpFile, fileList, includeFiles)) { + buildTree(m_rootNode, fileList); + foreach(ProjectExplorer::FileNode *fn, fileList) + m_files.append(fn->path()); + m_files.sort(); + + includeFiles.sort(); + includeFiles.removeDuplicates(); + CppTools::CppModelManagerInterface *modelmanager = ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>(); + if (modelmanager) { + CppTools::CppModelManagerInterface::ProjectInfo *pinfo = modelmanager->projectInfo(this); + pinfo->includePaths = includeFiles; + pinfo->sourceFiles = m_files; // TODO we only want C++ files, not all other stuff that might be in the project + // TODO defines + } + } else { + // TODO report error + } +} + +CMakeProject::~CMakeProject() +{ + delete m_rootNode; +} + +QString CMakeProject::findCbpFile(const QDir &directory) +{ + // Find the cbp file + // TODO the cbp file is named like the project() command in the CMakeList.txt file + // so this method below could find the wrong cbp file, if the user changes the project() + // name + foreach(const QString &cbpFile , directory.entryList()) + { + if (cbpFile.endsWith(".cbp")) { + return directory.path() + "/" + cbpFile; + } + } + return QString::null; +} + + +QString CMakeProject::createCbpFile(const QDir &) +{ + // TODO create a cbp file. + // Issue: Where to create it? We want to do that in the build directory + // but at this stage we don't know the build directory yet + // So create it in a temp directory? + // Issue: We want to reuse whatever CMakeCache.txt that is alread there, which + // would indicate, creating it in the build directory + // Or we could use a temp directory and use -C builddirectory + return QString::null; +} + +bool CMakeProject::parseCbpFile(const QString &fileName, QList<ProjectExplorer::FileNode *> &fileList, QStringList &includeFiles) +{ + QFile fi(fileName); + if (fi.exists() && fi.open(QFile::ReadOnly)) { + QXmlStreamReader stream(&fi); + + while(!stream.atEnd()) { + stream.readNext(); + if (stream.name() == "CodeBlocks_project_file") { + parseCodeBlocks_project_file(stream, fileList, includeFiles); + } else if (stream.isStartElement()) { + parseUnknownElement(stream); + } + } + fi.close(); + return true; + } + return false; +} + +void CMakeProject::parseCodeBlocks_project_file(QXmlStreamReader &stream, QList<ProjectExplorer::FileNode *> &fileList, QStringList &includeFiles) +{ + while(!stream.atEnd()) { + stream.readNext(); + if (stream.isEndElement()) { + return; + } else if (stream.name() == "Project") { + parseProject(stream, fileList, includeFiles); + } else if (stream.isStartElement()) { + parseUnknownElement(stream); + } + } +} + +void CMakeProject::parseProject(QXmlStreamReader &stream, QList<ProjectExplorer::FileNode *> &fileList, QStringList &includeFiles) +{ + while(!stream.atEnd()) { + stream.readNext(); + if (stream.isEndElement()) { + return; + } else if (stream.name() == "Unit") { + parseUnit(stream, fileList); + } else if (stream.name() == "Build") { + parseBuild(stream, includeFiles); + } else if (stream.isStartElement()) { + parseUnknownElement(stream); + } + } +} + +void CMakeProject::parseBuild(QXmlStreamReader &stream, QStringList &includeFiles) +{ + while(!stream.atEnd()) { + stream.readNext(); + if (stream.isEndElement()) { + return; + } else if (stream.name() == "Target") { + parseTarget(stream, includeFiles); + } else if (stream.isStartElement()) { + parseUnknownElement(stream); + } + } +} + +void CMakeProject::parseTarget(QXmlStreamReader &stream, QStringList &includeFiles) +{ + while(!stream.atEnd()) { + stream.readNext(); + if (stream.isEndElement()) { + return; + } else if (stream.name() == "Compiler") { + parseCompiler(stream, includeFiles); + } else if (stream.isStartElement()) { + parseUnknownElement(stream); + } + } +} + +void CMakeProject::parseCompiler(QXmlStreamReader &stream, QStringList &includeFiles) +{ + while(!stream.atEnd()) { + stream.readNext(); + if (stream.isEndElement()) { + return; + } else if (stream.name() == "Add") { + parseAdd(stream, includeFiles); + } else if (stream.isStartElement()) { + parseUnknownElement(stream); + } + } +} + +void CMakeProject::parseAdd(QXmlStreamReader &stream, QStringList &includeFiles) +{ + includeFiles.append(stream.attributes().value("directory").toString()); + while(!stream.atEnd()) { + stream.readNext(); + if (stream.isEndElement()) { + return; + } else if (stream.isStartElement()) { + parseUnknownElement(stream); + } + } +} + +void CMakeProject::parseUnit(QXmlStreamReader &stream, QList<ProjectExplorer::FileNode *> &fileList) +{ + //qDebug()<<stream.attributes().value("filename"); + QString fileName = stream.attributes().value("filename").toString(); + if (!fileName.endsWith(".rule")) + fileList.append( new ProjectExplorer::FileNode(fileName, ProjectExplorer::SourceType, false)); + while(!stream.atEnd()) { + stream.readNext(); + if (stream.isEndElement()) { + return; + } else if (stream.isStartElement()) { + parseUnknownElement(stream); + } + } +} +void CMakeProject::parseUnknownElement(QXmlStreamReader &stream) +{ + Q_ASSERT(stream.isStartElement()); + + while (!stream.atEnd()) { + stream.readNext(); + + if (stream.isEndElement()) + break; + + if (stream.isStartElement()) + parseUnknownElement(stream); + } +} + +void CMakeProject::buildTree(CMakeProjectNode *rootNode, QList<ProjectExplorer::FileNode *> list) +{ + //m_rootNode->addFileNodes(fileList, m_rootNode); + qSort(list.begin(), list.end(), ProjectExplorer::ProjectNode::sortNodesByPath); + foreach( ProjectExplorer::FileNode *fn, list) { + // Get relative path to rootNode + QString parentDir = QFileInfo(fn->path()).absolutePath(); + ProjectExplorer::FolderNode *folder = findOrCreateFolder(rootNode, parentDir); + rootNode->addFileNodes(QList<ProjectExplorer::FileNode *>()<< fn, folder); + } + //m_rootNode->addFileNodes(list, rootNode); +} + +ProjectExplorer::FolderNode *CMakeProject::findOrCreateFolder(CMakeProjectNode *rootNode, QString directory) +{ + QString relativePath = QDir(QFileInfo(rootNode->path()).path()).relativeFilePath(directory); + QStringList parts = relativePath.split("/"); + ProjectExplorer::FolderNode *parent = rootNode; + foreach(const QString &part, parts) { + // Find folder in subFolders + bool found = false; + foreach(ProjectExplorer::FolderNode *folder, parent->subFolderNodes()) { + if (QFileInfo(folder->path()).fileName() == part) { + // yeah found something :) + parent = folder; + found = true; + break; + } + } + if (!found) { + // No FolderNode yet, so create it + ProjectExplorer::FolderNode *tmp = new ProjectExplorer::FolderNode(part); + rootNode->addFolderNodes(QList<ProjectExplorer::FolderNode *>() << tmp, parent); + parent = tmp; + } + } + return parent; +} + +QString CMakeProject::name() const +{ + // TODO + return ""; +} + +Core::IFile *CMakeProject::file() const +{ + return m_file; +} + +ProjectExplorer::IProjectManager *CMakeProject::projectManager() const +{ + return m_manager; +} + +QList<Core::IFile *> CMakeProject::dependencies() +{ + return QList<Core::IFile *>(); +} + +QList<ProjectExplorer::Project *> CMakeProject::dependsOn() +{ + return QList<Project *>(); +} + +bool CMakeProject::isApplication() const +{ + return true; +} + +ProjectExplorer::Environment CMakeProject::environment(const QString &buildConfiguration) const +{ + Q_UNUSED(buildConfiguration) + //TODO + return ProjectExplorer::Environment::systemEnvironment(); +} + +QString CMakeProject::buildDirectory(const QString &buildConfiguration) const +{ + Q_UNUSED(buildConfiguration) + //TODO + return ""; +} + +ProjectExplorer::BuildStepConfigWidget *CMakeProject::createConfigWidget() +{ + return new CMakeBuildSettingsWidget; +} + +QList<ProjectExplorer::BuildStepConfigWidget*> CMakeProject::subConfigWidgets() +{ + return QList<ProjectExplorer::BuildStepConfigWidget*>(); +} + +// This method is called for new build configurations +// You should probably set some default values in this method + void CMakeProject::newBuildConfiguration(const QString &buildConfiguration) + { + Q_UNUSED(buildConfiguration); + //TODO + } + +ProjectExplorer::ProjectNode *CMakeProject::rootProjectNode() const +{ + return m_rootNode; +} + + +QStringList CMakeProject::files(FilesMode fileMode) const +{ + Q_UNUSED(fileMode); + // TODO + return m_files; +} + +void CMakeProject::saveSettingsImpl(ProjectExplorer::PersistentSettingsWriter &writer) +{ + // TODO + Q_UNUSED(writer) +} + +void CMakeProject::restoreSettingsImpl(ProjectExplorer::PersistentSettingsReader &reader) +{ + // TODO + Q_UNUSED(reader) +} + + +CMakeFile::CMakeFile(CMakeProject *parent, QString fileName) + : Core::IFile(parent), m_project(parent), m_fileName(fileName) +{ + +} + +bool CMakeFile::save(const QString &fileName) +{ + // TODO + // Once we have an texteditor open for this file, we probably do + // need to implement this, don't we. + Q_UNUSED(fileName); + return false; +} + +QString CMakeFile::fileName() const +{ + return m_fileName; +} + +QString CMakeFile::defaultPath() const +{ + return QString(); +} + +QString CMakeFile::suggestedFileName() const +{ + return QString(); +} + +QString CMakeFile::mimeType() const +{ + return Constants::CMAKEMIMETYPE; +} + + +bool CMakeFile::isModified() const +{ + return false; +} + +bool CMakeFile::isReadOnly() const +{ + return true; +} + +bool CMakeFile::isSaveAsAllowed() const +{ + return false; +} + +void CMakeFile::modified(ReloadBehavior *behavior) +{ + Q_UNUSED(behavior); +} + + +CMakeBuildSettingsWidget::CMakeBuildSettingsWidget() +{ + +} + +QString CMakeBuildSettingsWidget::displayName() const +{ + return "CMake"; +} + +void CMakeBuildSettingsWidget::init(const QString &buildConfiguration) +{ + Q_UNUSED(buildConfiguration); + // TODO +} diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h new file mode 100644 index 00000000000..b5a8d0b6b4a --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeproject.h @@ -0,0 +1,158 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CMAKEPROJECT_H +#define CMAKEPROJECT_H + +#include <projectexplorer/project.h> +#include <projectexplorer/projectnodes.h> +#include <projectexplorer/buildstep.h> +#include <coreplugin/ifile.h> +#include <QtCore/QXmlStreamReader> + +#include "cmakeprojectmanager.h" +#include "cmakeprojectnodes.h" + +namespace CMakeProjectManager { +namespace Internal{ + +class CMakeFile; + +class CMakeProject : public ProjectExplorer::Project +{ + Q_OBJECT +public: + CMakeProject(CMakeManager *manager, const QString &filename); + ~CMakeProject(); + + virtual QString name() const; + virtual Core::IFile *file() const; + virtual ProjectExplorer::IProjectManager *projectManager() const; + + virtual QList<Core::IFile *> dependencies(); //NBS TODO remove + virtual QList<ProjectExplorer::Project *> dependsOn(); //NBS TODO implement dependsOn + + virtual bool isApplication() const; + + virtual ProjectExplorer::Environment environment(const QString &buildConfiguration) const; + virtual QString buildDirectory(const QString &buildConfiguration) const; + + virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); + virtual QList<ProjectExplorer::BuildStepConfigWidget*> subConfigWidgets(); + + // This method is called for new build configurations + // You should probably set some default values in this method + virtual void newBuildConfiguration(const QString &buildConfiguration); + +// // Returns the list of different views (such as "File View" or "Project View") the project supports. +// virtual QStringList supportedModels() const = 0; +// +// // Returns the tree representing the requested view. +// virtual QModelIndex model(const QString &modelId) const = 0; + + virtual ProjectExplorer::ProjectNode *rootProjectNode() const; + +// // Conversion functions +// virtual QModelIndex indexForNode(const Node *node, const QString &modelId) const = 0; +// virtual Node *nodeForIndex(const QModelIndex &index) const = 0; +// virtual Node *nodeForFile(const QString &filePath) const = 0; + + virtual QStringList files(FilesMode fileMode) const; + +private: + QString findCbpFile(const QDir &); + QString createCbpFile(const QDir &); + bool parseCbpFile(const QString &fileName, QList<ProjectExplorer::FileNode *> &fileList, QStringList &includeFiles); + void parseCodeBlocks_project_file(QXmlStreamReader &stream, QList<ProjectExplorer::FileNode *> &fileList, QStringList &includeFiles); + void parseProject(QXmlStreamReader &stream, QList<ProjectExplorer::FileNode *> &fileList, QStringList &includeFiles); + void parseBuild(QXmlStreamReader &stream, QStringList &includeFiles); + void parseTarget(QXmlStreamReader &stream, QStringList &includeFiles); + void parseCompiler(QXmlStreamReader &stream, QStringList &includeFiles); + void parseAdd(QXmlStreamReader &stream, QStringList &includeFiles); + void parseUnit(QXmlStreamReader &stream, QList<ProjectExplorer::FileNode *> &fileList); + void parseUnknownElement(QXmlStreamReader &stream); + void buildTree(CMakeProjectNode *rootNode, QList<ProjectExplorer::FileNode *> list); + ProjectExplorer::FolderNode *findOrCreateFolder(CMakeProjectNode *rootNode, QString directory); + + + CMakeManager *m_manager; + QString m_fileName; + CMakeFile *m_file; + + // TODO probably need a CMake specific node structure + CMakeProjectNode* m_rootNode; + QStringList m_files; + +protected: + virtual void saveSettingsImpl(ProjectExplorer::PersistentSettingsWriter &writer); + virtual void restoreSettingsImpl(ProjectExplorer::PersistentSettingsReader &reader); + +}; + +class CMakeFile : public Core::IFile +{ + Q_OBJECT +public: + CMakeFile(CMakeProject *parent, QString fileName); + + bool save(const QString &fileName = QString()); + QString fileName() const; + + QString defaultPath() const; + QString suggestedFileName() const; + QString mimeType() const; + + bool isModified() const; + bool isReadOnly() const; + bool isSaveAsAllowed() const; + + void modified(ReloadBehavior *behavior); +private: + CMakeProject *m_project; + QString m_fileName; +}; + +class CMakeBuildSettingsWidget : public ProjectExplorer::BuildStepConfigWidget +{ +public: + CMakeBuildSettingsWidget(); + virtual QString displayName() const; + + // This is called to set up the config widget before showing it + // buildConfiguration is QString::null for the non buildConfiguration specific page + virtual void init(const QString &buildConfiguration); +}; + +} +} + +#endif // CMAKEPROJECT_H diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.qrc b/src/plugins/cmakeprojectmanager/cmakeproject.qrc new file mode 100644 index 00000000000..e9ae51f4607 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeproject.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/cmakeproject" > + <file>CMakeProject.mimetypes.xml</file> + </qresource> +</RCC> diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h b/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h new file mode 100644 index 00000000000..02c76f6682d --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h @@ -0,0 +1,45 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CMAKEPROJECTCONSTANTS_H +#define CMAKEPROJECTCONSTANTS_H + +namespace CMakeProjectManager { +namespace Constants { + +const char * const PROJECTCONTEXT = "CMakeProject.ProjectContext"; +const char * const CMAKEMIMETYPE = "text/x-cmake"; // TOOD check that this is correct + +} +} + +#endif // CMAKEPROJECTCONSTANTS_H diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp new file mode 100644 index 00000000000..4a179967ccb --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cmakeprojectmanager.h" +#include "cmakeprojectconstants.h" +#include "cmakeproject.h" +#include "cmakeprojectconstants.h" + +#include <coreplugin/icore.h> +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <projectexplorer/projectexplorerconstants.h> + + +using namespace CMakeProjectManager::Internal; + +CMakeManager::CMakeManager() +{ + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + m_projectContext = core->uniqueIDManager()->uniqueIdentifier(CMakeProjectManager::Constants::PROJECTCONTEXT); + m_projectLanguage = core->uniqueIDManager()->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX); +} + +int CMakeManager::projectContext() const +{ + return m_projectContext; +} + +int CMakeManager::projectLanguage() const +{ + return m_projectLanguage; +} + +ProjectExplorer::Project *CMakeManager::openProject(const QString &fileName) +{ + // TODO check wheter this project is already opened + return new CMakeProject(this, fileName); +} + +QString CMakeManager::mimeType() const +{ + return Constants::CMAKEMIMETYPE; +} diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h new file mode 100644 index 00000000000..3808b72efba --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h @@ -0,0 +1,60 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CMAKEPROJECTMANAGER_H +#define CMAKEPROJECTMANAGER_H + +#include <projectexplorer/iprojectmanager.h> + +namespace CMakeProjectManager { +namespace Internal { + +class CMakeManager : public ProjectExplorer::IProjectManager +{ + Q_OBJECT +public: + CMakeManager(); + + virtual int projectContext() const; + virtual int projectLanguage() const; + + //virtual bool canOpenProject(const QString &fileName); + virtual ProjectExplorer::Project *openProject(const QString &fileName); + virtual QString mimeType() const; + //virtual QString fileFilter() const; +private: + int m_projectContext; + int m_projectLanguage; +}; +} +} +#endif // CMAKEPROJECTMANAGER_H diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro new file mode 100644 index 00000000000..d7825c8167a --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.pro @@ -0,0 +1,14 @@ +TEMPLATE = lib +TARGET = CMakeProjectManager +include(../../qworkbenchplugin.pri) +include(cmakeprojectmanager_dependencies.pri) +HEADERS = cmakeproject.h \ + cmakeprojectplugin.h \ + cmakeprojectmanager.h \ + cmakeprojectconstants.h \ + cmakeprojectnodes.h +SOURCES = cmakeproject.cpp \ + cmakeprojectplugin.cpp \ + cmakeprojectmanager.cpp \ + cmakeprojectnodes.cpp +RESOURCES += cmakeproject.qrc diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager_dependencies.pri b/src/plugins/cmakeprojectmanager/cmakeprojectmanager_dependencies.pri new file mode 100644 index 00000000000..80118ad122e --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager_dependencies.pri @@ -0,0 +1,4 @@ +include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/cpptools/cpptools.pri) +include(../../plugins/cppeditor/cppeditor.pri) +include(../../plugins/texteditor/texteditor.pri) diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp new file mode 100644 index 00000000000..338750158f7 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cmakeprojectnodes.h" + +using namespace CMakeProjectManager; +using namespace CMakeProjectManager::Internal; + +CMakeProjectNode::CMakeProjectNode(const QString &fileName) + : ProjectExplorer::ProjectNode(fileName) +{ +} + +bool CMakeProjectNode::hasTargets() const +{ + // TODO + return true; +} + +QList<ProjectExplorer::ProjectNode::ProjectAction> CMakeProjectNode::supportedActions() const +{ + return QList<ProjectAction>(); +} + +bool CMakeProjectNode::addSubProjects(const QStringList &proFilePaths) +{ + Q_UNUSED(proFilePaths); + return false; +} + +bool CMakeProjectNode::removeSubProjects(const QStringList &proFilePaths) +{ + Q_UNUSED(proFilePaths); + return false; +} + +bool CMakeProjectNode::addFiles(const ProjectExplorer::FileType fileType, const QStringList &filePaths, QStringList *notAdded) +{ + Q_UNUSED(fileType); + Q_UNUSED(filePaths); + Q_UNUSED(notAdded); + return false; +} + +// TODO: Maybe remove fileType, can be detected by project +bool CMakeProjectNode::removeFiles(const ProjectExplorer::FileType fileType, const QStringList &filePaths, QStringList *notRemoved) +{ + Q_UNUSED(fileType); + Q_UNUSED(filePaths); + Q_UNUSED(notRemoved); + return false; +} + +bool CMakeProjectNode::renameFile(const ProjectExplorer::FileType fileType, const QString &filePath, const QString &newFilePath) +{ + Q_UNUSED(fileType); + Q_UNUSED(filePath); + Q_UNUSED(newFilePath); + return false; +} diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h new file mode 100644 index 00000000000..a0a9453a47d --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h @@ -0,0 +1,66 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CMAKEPROJECTNODE_H +#define CMAKEPROJECTNODE_H + +#include <projectexplorer/projectnodes.h> + +namespace CMakeProjectManager { +namespace Internal { + +class CMakeProjectNode : public ProjectExplorer::ProjectNode +{ +public: + CMakeProjectNode(const QString &fileName); + virtual bool hasTargets() const; + virtual QList<ProjectExplorer::ProjectNode::ProjectAction> supportedActions() const; + virtual bool addSubProjects(const QStringList &proFilePaths); + virtual bool removeSubProjects(const QStringList &proFilePaths); + virtual bool addFiles(const ProjectExplorer::FileType fileType, + const QStringList &filePaths, + QStringList *notAdded = 0); + virtual bool removeFiles(const ProjectExplorer::FileType fileType, + const QStringList &filePaths, + QStringList *notRemoved = 0); + virtual bool renameFile(const ProjectExplorer::FileType fileType, + const QString &filePath, + const QString &newFilePath); + + // TODO is protected in base class, and that's correct + using ProjectNode::addFileNodes; + using ProjectNode::addFolderNodes; +}; +} +} + +#endif // CMAKEPROJECTNODE_H diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp new file mode 100644 index 00000000000..45c8c5d2132 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp @@ -0,0 +1,65 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cmakeprojectplugin.h" +#include <coreplugin/icore.h> +#include <coreplugin/mimedatabase.h> +#include <QtCore/qplugin.h> +#include <QtCore/QDebug> + +#include "cmakeprojectmanager.h" + +using namespace CMakeProjectManager::Internal; + +CMakeProjectPlugin::CMakeProjectPlugin() +{ +} + +CMakeProjectPlugin::~CMakeProjectPlugin() +{ +} + +bool CMakeProjectPlugin::initialize(const QStringList & /*arguments*/, QString *error_message) +{ + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + QString errorMessage; + core->mimeDatabase()->addMimeTypes(QLatin1String(":cmakeproject/CMakeProject.mimetypes.xml"), &errorMessage); + addAutoReleasedObject(new CMakeManager()); + return true; +} + +void CMakeProjectPlugin::extensionsInitialized() +{ + +} + +Q_EXPORT_PLUGIN(CMakeProjectPlugin) diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h new file mode 100644 index 00000000000..0620220667a --- /dev/null +++ b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.h @@ -0,0 +1,63 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CMAKEPROJECTPLUGIN_H +#define CMAKEPROJECTPLUGIN_H + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> + +namespace CMakeProjectManager { +namespace Internal { + +class CMakeProjectPlugin + : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + CMakeProjectPlugin(); + ~CMakeProjectPlugin(); + + bool initialize(const QStringList &arguments, QString *error_message); + + void extensionsInitialized(); + + +private: +}; + +} // namespace Internal +} // namespace CMakeProject + +#endif // CMAKEPROJECTPLUGIN_H diff --git a/src/plugins/coreplugin/Core.pluginspec b/src/plugins/coreplugin/Core.pluginspec new file mode 100644 index 00000000000..28b9067ed28 --- /dev/null +++ b/src/plugins/coreplugin/Core.pluginspec @@ -0,0 +1,7 @@ +<plugin name="Core" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>The core plugin for the Qt IDE.</description> + <url>http://www.trolltech.com/</url> +</plugin> diff --git a/src/plugins/coreplugin/actionmanager/actioncontainer.cpp b/src/plugins/coreplugin/actionmanager/actioncontainer.cpp new file mode 100644 index 00000000000..ab30677a628 --- /dev/null +++ b/src/plugins/coreplugin/actionmanager/actioncontainer.cpp @@ -0,0 +1,696 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "actioncontainer.h" +#include "command.h" +#include "coreimpl.h" + +#include "coreconstants.h" +#include "uniqueidmanager.h" + +#include <QtCore/QDebug> +#include <QtGui/QAction> +#include <QtGui/QToolBar> +#include <QtGui/QMenuBar> + +Q_DECLARE_METATYPE(Core::Internal::MenuActionContainer*) + +using namespace Core; +using namespace Core::Internal; + +/*! + \class IActionContainer + \mainclass + \ingroup qwb + \inheaderfile iactioncontainer.h + + \brief The class... + + The Action Container interface... +*/ + +/*! + \enum IActionContainer::ContainerType +*/ + +/*! + \enum IActionContainer::EmptyAction +*/ + +/*! + \fn virtual IActionContainer::setEmptyAction(EmptyAction ea) +*/ + +/*! + \fn virtual int IActionContainer::id() const +*/ + +/*! + \fn virtual ContainerType IActionContainer::type() const +*/ + +/*! + \fn virtual QMenu *IActionContainer::menu() const +*/ + +/*! + \fn virtual QToolBar *IActionContainer::toolBar() const +*/ + +/*! + \fn virtual QMenuBar *IActionContainer::menuBar() const +*/ + +/*! + \fn virtual QAction *IActionContainer::insertLocation(const QString &group) const +*/ + +/*! + \fn virtual void IActionContainer::appendGroup(const QString &group, bool global) +*/ + +/*! + \fn virtual void IActionContainer::addAction(Core::ICommand *action, const QString &group) +*/ + +/*! + \fn virtual void IActionContainer::addMenu(Core::IActionContainer *menu, const QString &group) +*/ + +/*! + \fn virtual bool IActionContainer::update() +*/ + +/*! + \fn virtual IActionContainer::~IActionContainer() +*/ + +// ---------- ActionContainer ------------ + +/*! + \class ActionContainer + \ingroup qwb + \inheaderfile actioncontainer.h +*/ + +/*! + \enum ActionContainer::ContainerState +*/ + +/*! +\fn ActionContainer::ActionContainer(ContainerType type, int id) +*/ +ActionContainer::ActionContainer(ContainerType type, int id) + : m_data(CS_None), m_type(type), m_id(id) +{ + +} + +/*! + \fn virtual ActionContainer::~ActionContainer() +*/ + +/*! + ... +*/ +void ActionContainer::setEmptyAction(EmptyAction ea) +{ + m_data = ((m_data & ~EA_Mask) | ea); +} + +/*! + ... +*/ +bool ActionContainer::hasEmptyAction(EmptyAction ea) const +{ + return (m_data & EA_Mask) == ea; +} + +/*! + ... +*/ +void ActionContainer::setState(ContainerState state) +{ + m_data |= state; +} + +/*! + ... +*/ +bool ActionContainer::hasState(ContainerState state) const +{ + return (m_data & state); +} + +/*! + ... +*/ +void ActionContainer::appendGroup(const QString &group, bool global) +{ + UniqueIDManager *idmanager = CoreImpl::instance()->uniqueIDManager(); + int gid = idmanager->uniqueIdentifier(group); + m_groups << gid; + if (global) + ActionManager::instance()->registerGlobalGroup(gid, m_id); +} + +/*! + ... +*/ +QAction *ActionContainer::insertLocation(const QString &group) const +{ + UniqueIDManager *idmanager = CoreImpl::instance()->uniqueIDManager(); + int grpid = idmanager->uniqueIdentifier(group); + int prevKey = 0; + int pos = ((grpid << 16) | 0xFFFF); + return beforeAction(pos, &prevKey); +} + +/*! +\fn virtual void ActionContainer::insertAction(QAction *before, QAction *action) = 0 +*/ + +/*! +\fn virtual void ActionContainer::insertMenu(QAction *before, QMenu *menu) = 0 +*/ + +/*! +\fn QList<ICommand *> ActionContainer::commands() const +*/ + +/*! +\fn QList<IActionContainer *> ActionContainer::subContainers() const +*/ + +/*! + ... +*/ +void ActionContainer::addAction(ICommand *action, const QString &group) +{ + if (!canAddAction(action)) + return; + + ActionManager *am = ActionManager::instance(); + Action *a = static_cast<Action *>(action); + if (a->stateFlags() & Command::CS_PreLocation) { + QList<CommandLocation> locs = a->locations(); + for (int i=0; i<locs.size(); ++i) { + if (IActionContainer *aci = am->actionContainer(locs.at(i).m_container)) { + ActionContainer *ac = static_cast<ActionContainer *>(aci); + ac->addAction(action, locs.at(i).m_position, false); + } + } + a->setStateFlags(a->stateFlags() | Command::CS_Initialized); + } else { + UniqueIDManager *idmanager = CoreImpl::instance()->uniqueIDManager(); + int grpid = idmanager->uniqueIdentifier(Constants::G_DEFAULT_TWO); + if (!group.isEmpty()) + grpid = idmanager->uniqueIdentifier(group); + if (!m_groups.contains(grpid) && !am->defaultGroups().contains(grpid)) + qWarning() << "*** addAction(): Unknown group: " << group; + int pos = ((grpid << 16) | 0xFFFF); + addAction(action, pos, true); + } +} + +/*! + ... +*/ +void ActionContainer::addMenu(IActionContainer *menu, const QString &group) +{ + if (!canAddMenu(menu)) + return; + + ActionManager *am = ActionManager::instance(); + MenuActionContainer *mc = static_cast<MenuActionContainer *>(menu); + if (mc->hasState(ActionContainer::CS_PreLocation)) { + CommandLocation loc = mc->location(); + if (IActionContainer *aci = am->actionContainer(loc.m_container)) { + ActionContainer *ac = static_cast<ActionContainer *>(aci); + ac->addMenu(menu, loc.m_position, false); + } + mc->setState(ActionContainer::CS_Initialized); + } else { + UniqueIDManager *idmanager = CoreImpl::instance()->uniqueIDManager(); + int grpid = idmanager->uniqueIdentifier(Constants::G_DEFAULT_TWO); + if (!group.isEmpty()) + grpid = idmanager->uniqueIdentifier(group); + if (!m_groups.contains(grpid) && !am->defaultGroups().contains(grpid)) + qWarning() << "*** addMenu(): Unknown group: " << group; + int pos = ((grpid << 16) | 0xFFFF); + addMenu(menu, pos, true); + } +} + +/*! + ... +*/ +int ActionContainer::id() const +{ + return m_id; +} + +/*! + ... +*/ +IActionContainer::ContainerType ActionContainer::type() const +{ + return m_type; +} + +/*! + ... +*/ +QMenu *ActionContainer::menu() const +{ + return 0; +} + +/*! + ... +*/ +QToolBar *ActionContainer::toolBar() const +{ + return 0; +} + +/*! + ... +*/ +QMenuBar *ActionContainer::menuBar() const +{ + return 0; +} + +/*! + ... +*/ +bool ActionContainer::canAddAction(ICommand *action) const +{ + if (action->type() != ICommand::CT_OverridableAction) + return false; + + Command *cmd = static_cast<Command *>(action); + if (cmd->stateFlags() & Command::CS_Initialized) + return false; + + return true; +} + +/*! + ... +*/ +bool ActionContainer::canAddMenu(IActionContainer *menu) const +{ + if (menu->type() != IActionContainer::CT_Menu) + return false; + + ActionContainer *container = static_cast<ActionContainer *>(menu); + if (container->hasState(ActionContainer::CS_Initialized)) + return false; + + return true; +} + +/*! + ... +*/ +void ActionContainer::addAction(ICommand *action, int pos, bool setpos) +{ + Action *a = static_cast<Action *>(action); + + int prevKey = 0; + QAction *ba = beforeAction(pos, &prevKey); + + if (setpos) { + pos = calcPosition(pos, prevKey); + CommandLocation loc; + loc.m_container = m_id; + loc.m_position = pos; + QList<CommandLocation> locs = a->locations(); + locs.append(loc); + a->setLocations(locs); + } + + m_commands.append(action); + m_posmap.insert(pos, action->id()); + insertAction(ba, a->action()); +} + +/*! + ... +*/ +void ActionContainer::addMenu(IActionContainer *menu, int pos, bool setpos) +{ + MenuActionContainer *mc = static_cast<MenuActionContainer *>(menu); + + int prevKey = 0; + QAction *ba = beforeAction(pos, &prevKey); + + if (setpos) { + pos = calcPosition(pos, prevKey); + CommandLocation loc; + loc.m_container = m_id; + loc.m_position = pos; + mc->setLocation(loc); + } + + m_subContainers.append(menu); + m_posmap.insert(pos, menu->id()); + insertMenu(ba, mc->menu()); +} + +/*! + ... + \internal +*/ +QAction *ActionContainer::beforeAction(int pos, int *prevKey) const +{ + ActionManager *am = ActionManager::instance(); + + int baId = -1; + + (*prevKey) = -1; + + QMap<int, int>::const_iterator i = m_posmap.constBegin(); + while (i != m_posmap.constEnd()) { + if (i.key() > pos) { + baId = i.value(); + break; + } + (*prevKey) = i.key(); + ++i; + } + + if (baId == -1) + return 0; + + if (ICommand *cmd = am->command(baId)) + return cmd->action(); + if (IActionContainer *container = am->actionContainer(baId)) + if (QMenu *menu = container->menu()) + return menu->menuAction(); + + return 0; +} + +/*! + ... + \internal +*/ +int ActionContainer::calcPosition(int pos, int prevKey) const +{ + int grp = (pos & 0xFFFF0000); + if (prevKey == -1) + return grp; + + int prevgrp = (prevKey & 0xFFFF0000); + + if (grp != prevgrp) + return grp; + + return grp + (prevKey & 0xFFFF) + 10; +} + +// ---------- MenuActionContainer ------------ + +/*! + \class MenuActionContainer + \ingroup qwb + \inheaderfile actioncontainer.h +*/ + +/*! + ... +*/ +MenuActionContainer::MenuActionContainer(int id) + : ActionContainer(CT_Menu, id), m_menu(0) +{ + setEmptyAction(EA_Disable); +} + +/*! + ... +*/ +void MenuActionContainer::setMenu(QMenu *menu) +{ + m_menu = menu; + + QVariant v; + qVariantSetValue<MenuActionContainer*>(v, this); + + m_menu->menuAction()->setData(v); +} + +/*! + ... +*/ +QMenu *MenuActionContainer::menu() const +{ + return m_menu; +} + +/*! + ... +*/ +void MenuActionContainer::insertAction(QAction *before, QAction *action) +{ + m_menu->insertAction(before, action); +} + +/*! + ... +*/ +void MenuActionContainer::insertMenu(QAction *before, QMenu *menu) +{ + m_menu->insertMenu(before, menu); +} + +/*! + ... +*/ +void MenuActionContainer::setLocation(const CommandLocation &location) +{ + m_location = location; +} + +/*! + ... +*/ +CommandLocation MenuActionContainer::location() const +{ + return m_location; +} + +/*! + ... +*/ +bool MenuActionContainer::update() +{ + if (hasEmptyAction(EA_None)) + return true; + + bool hasitems = false; + + foreach (IActionContainer *container, subContainers()) { + if (container == this) { + qWarning() << Q_FUNC_INFO << "container" << (this->menu() ? this->menu()->title() : "") << "contains itself as subcontainer"; + continue; + } + if (container->update()) { + hasitems = true; + break; + } + } + if (!hasitems) { + foreach (ICommand *command, commands()) { + if (command->isActive()) { + hasitems = true; + break; + } + } + } + + if (hasEmptyAction(EA_Hide)) + m_menu->setVisible(hasitems); + else if (hasEmptyAction(EA_Disable)) + m_menu->setEnabled(hasitems); + + return hasitems; +} + +// ---------- ToolBarActionContainer ------------ + +/*! + \class ToolBarActionContainer + \ingroup qwb + \inheaderfile actioncontainer.h +*/ + +/*! + ... +*/ +ToolBarActionContainer::ToolBarActionContainer(int id) + : ActionContainer(CT_ToolBar, id), m_toolBar(0) +{ + setEmptyAction(EA_None); +} + +/*! + ... +*/ +void ToolBarActionContainer::setToolBar(QToolBar *toolBar) +{ + m_toolBar = toolBar; +} + +/*! + ... +*/ +QToolBar *ToolBarActionContainer::toolBar() const +{ + return m_toolBar; +} + +/*! + ... +*/ +void ToolBarActionContainer::insertAction(QAction *before, QAction *action) +{ + m_toolBar->insertAction(before, action); +} + +/*! + ... +*/ +void ToolBarActionContainer::insertMenu(QAction *, QMenu *) +{ + // not implemented +} + +/*! + ... +*/ +bool ToolBarActionContainer::update() +{ + if (hasEmptyAction(EA_None)) + return true; + + bool hasitems = false; + foreach (ICommand *command, commands()) { + if (command->isActive()) { + hasitems = true; + break; + } + } + + if (hasEmptyAction(EA_Hide)) + m_toolBar->setVisible(hasitems); + else if (hasEmptyAction(EA_Disable)) + m_toolBar->setEnabled(hasitems); + + return hasitems; +} + +// ---------- MenuBarActionContainer ------------ + +/*! + \class MenuBarActionContainer + \ingroup qwb + \inheaderfile actioncontainer.h +*/ + +/*! + ... +*/ +MenuBarActionContainer::MenuBarActionContainer(int id) + : ActionContainer(CT_ToolBar, id), m_menuBar(0) +{ + setEmptyAction(EA_None); +} + +/*! + ... +*/ +void MenuBarActionContainer::setMenuBar(QMenuBar *menuBar) +{ + m_menuBar = menuBar; +} + +/*! + ... +*/ +QMenuBar *MenuBarActionContainer::menuBar() const +{ + return m_menuBar; +} + +/*! + ... +*/ +void MenuBarActionContainer::insertAction(QAction *before, QAction *action) +{ + m_menuBar->insertAction(before, action); +} + +/*! + ... +*/ +void MenuBarActionContainer::insertMenu(QAction *before, QMenu *menu) +{ + m_menuBar->insertMenu(before, menu); +} + +/*! + ... +*/ +bool MenuBarActionContainer::update() +{ + if (hasEmptyAction(EA_None)) + return true; + + bool hasitems = false; + QList<QAction *> actions = m_menuBar->actions(); + for (int i=0; i<actions.size(); ++i) { + if (actions.at(i)->isVisible()) { + hasitems = true; + break; + } + } + + if (hasEmptyAction(EA_Hide)) + m_menuBar->setVisible(hasitems); + else if (hasEmptyAction(EA_Disable)) + m_menuBar->setEnabled(hasitems); + + return hasitems; +} diff --git a/src/plugins/coreplugin/actionmanager/actioncontainer.h b/src/plugins/coreplugin/actionmanager/actioncontainer.h new file mode 100644 index 00000000000..8f629410a42 --- /dev/null +++ b/src/plugins/coreplugin/actionmanager/actioncontainer.h @@ -0,0 +1,157 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ACTIONCONTAINER_H +#define ACTIONCONTAINER_H + +#include "actionmanager.h" + +#include <coreplugin/actionmanager/iactioncontainer.h> +#include <coreplugin/actionmanager/icommand.h> + +namespace Core { +namespace Internal { + +class ActionContainer : public Core::IActionContainer +{ +public: + enum ContainerState { + CS_None = 0x000000, + CS_Initialized = 0x010000, + CS_PreLocation = 0x020000, + CS_UserDefined = 0x040000 + }; + + ActionContainer(ContainerType type, int id); + virtual ~ActionContainer() {} + + void setEmptyAction(EmptyAction ea); + bool hasEmptyAction(EmptyAction ea) const; + + void setState(ContainerState state); + bool hasState(ContainerState state) const; + + QAction *insertLocation(const QString &group) const; + void appendGroup(const QString &group, bool global = false); + void addAction(ICommand *action, const QString &group = QString()); + void addMenu(IActionContainer *menu, const QString &group = QString()); + + int id() const; + ContainerType type() const; + + QMenu *menu() const; + QToolBar *toolBar() const; + QMenuBar *menuBar() const; + + virtual void insertAction(QAction *before, QAction *action) = 0; + virtual void insertMenu(QAction *before, QMenu *menu) = 0; + + QList<ICommand *> commands() const { return m_commands; } + QList<IActionContainer *> subContainers() const { return m_subContainers; } +protected: + bool canAddAction(ICommand *action) const; + bool canAddMenu(IActionContainer *menu) const; + + void addAction(ICommand *action, int pos, bool setpos); + void addMenu(IActionContainer *menu, int pos, bool setpos); + +private: + QAction *beforeAction(int pos, int *prevKey) const; + int calcPosition(int pos, int prevKey) const; + + QList<int> m_groups; + int m_data; + ContainerType m_type; + int m_id; + QMap<int, int> m_posmap; + QList<IActionContainer *> m_subContainers; + QList<ICommand *> m_commands; +}; + +class MenuActionContainer : public ActionContainer +{ +public: + MenuActionContainer(int id); + + void setMenu(QMenu *menu); + QMenu *menu() const; + + void setLocation(const CommandLocation &location); + CommandLocation location() const; + + void insertAction(QAction *before, QAction *action); + void insertMenu(QAction *before, QMenu *menu); + bool update(); + +private: + QMenu *m_menu; + CommandLocation m_location; +}; + +class ToolBarActionContainer : public ActionContainer +{ +public: + ToolBarActionContainer(int id); + + void setToolBar(QToolBar *toolBar); + QToolBar *toolBar() const; + + void insertAction(QAction *before, QAction *action); + void insertMenu(QAction *before, QMenu *menu); + bool update(); + +private: + QToolBar *m_toolBar; +}; + +class MenuBarActionContainer : public ActionContainer +{ +public: + MenuBarActionContainer(int id); + + void setMenuBar(QMenuBar *menuBar); + QMenuBar *menuBar() const; + + void insertAction(QAction *before, QAction *action); + void insertMenu(QAction *before, QMenu *menu); + bool update(); + +private: + QMenuBar *m_menuBar; +}; + +} // namespace Internal +} // namespace Core + +#endif //ACTIONCONTAINER_H + + diff --git a/src/plugins/coreplugin/actionmanager/actionmanager.cpp b/src/plugins/coreplugin/actionmanager/actionmanager.cpp new file mode 100644 index 00000000000..f218a84c732 --- /dev/null +++ b/src/plugins/coreplugin/actionmanager/actionmanager.cpp @@ -0,0 +1,560 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "actionmanager.h" +#include "mainwindow.h" +#include "actioncontainer.h" +#include "command.h" +#include "uniqueidmanager.h" + +#include <coreplugin/coreconstants.h> + +#include <QtCore/QDebug> +#include <QtCore/QSettings> +#include <QtGui/QMenu> +#include <QtGui/QAction> +#include <QtGui/QShortcut> +#include <QtGui/QToolBar> +#include <QtGui/QMenuBar> + +namespace { + enum { warnAboutFindFailures = 0 }; +} + +/*! + \class Core::ActionManagerInterface + \mainclass + \ingroup qwb + \inheaderfile actionmanagerinterface.h + + \brief All actions should be registered in the ActionManager, since this enables the user to + e.g. change their shortcuts at a central place. + + The ActionManagerInterface is the central bookkeeper of actions and their shortcuts and layout. + You get the only implementation of this class from the core interface (ICore::actionManager()). + + The main reasons for the need of this class is to provide a central place where the user + can specify all his keyboard shortcuts, and to provide a solution for actions that should + behave differently in different contexts (like the copy/replace/undo/redo actions). + + All actions that are registered with the same string id (but different context lists) + are considered to be overloads of the same command. The action that is visible to the user + is the one returned by ICommand::action(). (If you provide yourself a user visible + representation of your action be sure to always use ICommand::action() for this.) + If this action is invoked by the user, the signal is forwarded to the registered action that + is valid for the current context. + + You use this class also to add items to registered + action containers like the applications menu bar. For this you register your action via the + registerAction methods, get the action container for a specific id (like specified in + Core::Constants) with a call of + actionContainer(const QString&) and add your command to this container. + + Guidelines: + \list + \o Always register your actions and shortcuts! + \o When registering an action with cmd=registerAction(action, id, contexts) be sure to connect + your own action connect(action, SIGNAL...) but make cmd->action() visible to the user, i.e. + widget->addAction(cmd->action()). + \o Use this class to add actions to the applications menus + \endlist + + \sa Core::ICore, Core::ICommand + \sa Core::IActionContainer +*/ + +/*! + \fn virtual IActionContainer *ActionManagerInterface::createMenu(const QString &id) = 0 + ... +*/ + +/*! + \fn virtual IActionContainer *ActionManagerInterface::createMenuBar(const QString &id) = 0 + ... +*/ + +/*! + \fn virtual ICommand *ActionManagerInterface::registerAction(QAction *action, const QString &id, const QList<int> &context) = 0 + ... +*/ + +/*! + \fn virtual ICommand *ActionManagerInterface::registerShortcut(QShortcut *shortcut, const QString &id, const QList<int> &context) = 0 + ... +*/ + +/*! + \fn virtual ICommand *ActionManagerInterface::registerAction(QAction *action, const QString &id) = 0 + ... +*/ + +/*! + \fn virtual void ActionManagerInterface::addAction(ICommand *action, const QString &globalGroup) = 0 + ... +*/ + +/*! + \fn virtual void ActionManagerInterface::addMenu(IActionContainer *menu, const QString &globalGroup) = 0 + ... +*/ + +/*! + \fn virtual ICommand *ActionManagerInterface::command(const QString &id) const = 0 + ... +*/ + +/*! + \fn virtual IActionContainer *ActionManagerInterface::actionContainer(const QString &id) const = 0 + ... +*/ + +/*! + \fn virtual ActionManagerInterface::~ActionManagerInterface() + ... +*/ + +using namespace Core; +using namespace Core::Internal; + +ActionManager* ActionManager::m_instance = 0; + +/*! + \class ActionManager + \ingroup qwb + \inheaderfile actionmanager.h + + \sa ActionContainer +*/ + +/*! + ... +*/ +ActionManager::ActionManager(MainWindow *mainWnd, UniqueIDManager *uidmgr) : + ActionManagerInterface(mainWnd), + m_mainWnd(mainWnd) +{ + m_defaultGroups << uidmgr->uniqueIdentifier(Constants::G_DEFAULT_ONE); + m_defaultGroups << uidmgr->uniqueIdentifier(Constants::G_DEFAULT_TWO); + m_defaultGroups << uidmgr->uniqueIdentifier(Constants::G_DEFAULT_THREE); + m_instance = this; + +} + +/*! + ... +*/ +ActionManager::~ActionManager() +{ + qDeleteAll(m_idCmdMap.values()); + qDeleteAll(m_idContainerMap.values()); +} + +/*! + ... +*/ +ActionManager* ActionManager::instance() +{ + return m_instance; +} + +/*! + ... +*/ +QList<int> ActionManager::defaultGroups() const +{ + return m_defaultGroups; +} + +/*! + ... +*/ +QList<Command *> ActionManager::commands() const +{ + return m_idCmdMap.values(); +} + +/*! + ... +*/ +QList<ActionContainer *> ActionManager::containers() const +{ + return m_idContainerMap.values(); +} + +/*! + ... +*/ +void ActionManager::registerGlobalGroup(int groupId, int containerId) +{ + if (m_globalgroups.contains(groupId)) { + qWarning() << "registerGlobalGroup: Global group " + << m_mainWnd->uniqueIDManager()->stringForUniqueIdentifier(groupId) + << " already registered"; + } else { + m_globalgroups.insert(groupId, containerId); + } +} + +/*! + ... +*/ +bool ActionManager::hasContext(int context) const +{ + return m_context.contains(context); +} + +/*! + ... +*/ +void ActionManager::setContext(const QList<int> &context) +{ + // here are possibilities for speed optimization if necessary: + // let commands (de-)register themselves for contexts + // and only update commands that are either in old or new contexts + m_context = context; + const IdCmdMap::const_iterator cmdcend = m_idCmdMap.constEnd(); + for (IdCmdMap::const_iterator it = m_idCmdMap.constBegin(); it != cmdcend; ++it) + it.value()->setCurrentContext(m_context); + + const IdContainerMap::const_iterator acend = m_idContainerMap.constEnd(); + for ( IdContainerMap::const_iterator it = m_idContainerMap.constBegin(); it != acend; ++it) + it.value()->update(); +} + +/*! + \internal +*/ +bool ActionManager::hasContext(QList<int> context) const +{ + for (int i=0; i<m_context.count(); ++i) { + if (context.contains(m_context.at(i))) + return true; + } + return false; +} + +/*! + ... +*/ +IActionContainer *ActionManager::createMenu(const QString &id) +{ + const int uid = m_mainWnd->uniqueIDManager()->uniqueIdentifier(id); + const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid); + if (it != m_idContainerMap.constEnd()) + return it.value(); + + QMenu *m = new QMenu(m_mainWnd); + m->setObjectName(id); + + MenuActionContainer *mc = new MenuActionContainer(uid); + mc->setMenu(m); + + m_idContainerMap.insert(uid, mc); + + return mc; +} + +/*! + ... +*/ +IActionContainer *ActionManager::createMenuBar(const QString &id) +{ + const int uid = m_mainWnd->uniqueIDManager()->uniqueIdentifier(id); + const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid); + if (it != m_idContainerMap.constEnd()) + return it.value(); + + QMenuBar *mb = new QMenuBar; // No parent (System menu bar on Mac OS X) + mb->setObjectName(id); + + MenuBarActionContainer *mbc = new MenuBarActionContainer(uid); + mbc->setMenuBar(mb); + + m_idContainerMap.insert(uid, mbc); + + return mbc; +} + +/*! + ... +*/ +ICommand *ActionManager::registerAction(QAction *action, const QString &id, const QList<int> &context) +{ + OverrideableAction *a = 0; + ICommand *c = registerOverridableAction(action, id, false); + a = static_cast<OverrideableAction *>(c); + if (a) + a->addOverrideAction(action, context); + return a; +} + +/*! + ... +*/ +ICommand *ActionManager::registerAction(QAction *action, const QString &id) +{ + return registerOverridableAction(action, id, true); +} + +/*! + \internal +*/ +ICommand *ActionManager::registerOverridableAction(QAction *action, const QString &id, bool checkUnique) +{ + OverrideableAction *a = 0; + const int uid = m_mainWnd->uniqueIDManager()->uniqueIdentifier(id); + if (Command *c = m_idCmdMap.value(uid, 0)) { + if (c->type() != ICommand::CT_OverridableAction) { + qWarning() << "registerAction: id" << id << "is registered with a different command type."; + return c; + } + a = static_cast<OverrideableAction *>(c); + } + if (!a) { + a = new OverrideableAction(uid); + m_idCmdMap.insert(uid, a); + } + + if (!a->action()) { + QAction *baseAction = new QAction(m_mainWnd); + baseAction->setObjectName(id); + baseAction->setCheckable(action->isCheckable()); + baseAction->setIcon(action->icon()); + baseAction->setIconText(action->iconText()); + baseAction->setText(action->text()); + baseAction->setToolTip(action->toolTip()); + baseAction->setStatusTip(action->statusTip()); + baseAction->setWhatsThis(action->whatsThis()); + baseAction->setChecked(action->isChecked()); + baseAction->setSeparator(action->isSeparator()); + baseAction->setShortcutContext(Qt::ApplicationShortcut); + baseAction->setEnabled(false); + baseAction->setObjectName(id); + baseAction->setParent(m_mainWnd); +#ifdef Q_OS_MAC + baseAction->setIconVisibleInMenu(false); +#endif + a->setAction(baseAction); + m_mainWnd->addAction(baseAction); + a->setKeySequence(a->keySequence()); + a->setDefaultKeySequence(QKeySequence()); + } else if (checkUnique) { + qWarning() << "registerOverridableAction: id" << id << "is already registered."; + } + + return a; +} + +/*! + ... +*/ +ICommand *ActionManager::registerShortcut(QShortcut *shortcut, const QString &id, const QList<int> &context) +{ + Shortcut *sc = 0; + int uid = m_mainWnd->uniqueIDManager()->uniqueIdentifier(id); + if (Command *c = m_idCmdMap.value(uid, 0)) { + if (c->type() != ICommand::CT_Shortcut) { + qWarning() << "registerShortcut: id" << id << "is registered with a different command type."; + return c; + } + sc = static_cast<Shortcut *>(c); + } else { + sc = new Shortcut(uid); + m_idCmdMap.insert(uid, sc); + } + + if (sc->shortcut()) { + qWarning() << "registerShortcut: action already registered (id" << id << "."; + return sc; + } + + if (!hasContext(context)) + shortcut->setEnabled(false); + shortcut->setObjectName(id); + shortcut->setParent(m_mainWnd); + sc->setShortcut(shortcut); + + if (context.isEmpty()) + sc->setContext(QList<int>() << 0); + else + sc->setContext(context); + + sc->setKeySequence(shortcut->key()); + sc->setDefaultKeySequence(QKeySequence()); + + return sc; +} + +/*! + \fn void ActionManager::addAction(Core::ICommand *action, const QString &globalGroup) +*/ +void ActionManager::addAction(ICommand *action, const QString &globalGroup) +{ + const int gid = m_mainWnd->uniqueIDManager()->uniqueIdentifier(globalGroup); + if (!m_globalgroups.contains(gid)) { + qWarning() << "addAction: Unknown global group " << globalGroup; + return; + } + + const int cid = m_globalgroups.value(gid); + if (IActionContainer *aci = actionContainer(cid)) { + aci->addAction(action, globalGroup); + } else { + qWarning() << "addAction: Cannot find container." << cid << '/' << gid; + } +} + +/*! + \fn void ActionManager::addMenu(Core::IActionContainer *menu, const QString &globalGroup) + +*/ +void ActionManager::addMenu(IActionContainer *menu, const QString &globalGroup) +{ + const int gid = m_mainWnd->uniqueIDManager()->uniqueIdentifier(globalGroup); + if (!m_globalgroups.contains(gid)) { + qWarning() << "addAction: Unknown global group " << globalGroup; + return; + } + + const int cid = m_globalgroups.value(gid); + if (IActionContainer *aci = actionContainer(cid)) { + aci->addMenu(menu, globalGroup); + } else { + qWarning() << "addAction: Cannot find container." << cid << '/' << gid; + } +} + +/*! + ... +*/ +ICommand *ActionManager::command(const QString &id) const +{ + const int uid = m_mainWnd->uniqueIDManager()->uniqueIdentifier(id); + const IdCmdMap::const_iterator it = m_idCmdMap.constFind(uid); + if (it == m_idCmdMap.constEnd()) { + if (warnAboutFindFailures) + qWarning() << "ActionManager::command(): failed to find :" << id << '/' << uid; + return 0; + } + return it.value(); +} + +/*! + ... +*/ +IActionContainer *ActionManager::actionContainer(const QString &id) const +{ + const int uid = m_mainWnd->uniqueIDManager()->uniqueIdentifier(id); + const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid); + if ( it == m_idContainerMap.constEnd()) { + if (warnAboutFindFailures) + qWarning() << "ActionManager::actionContainer(): failed to find :" << id << '/' << uid; + return 0; + } + return it.value(); +} + +/*! + ... +*/ +ICommand *ActionManager::command(int uid) const +{ + const IdCmdMap::const_iterator it = m_idCmdMap.constFind(uid); + if (it == m_idCmdMap.constEnd()) { + if (warnAboutFindFailures) + qWarning() << "ActionManager::command(): failed to find :" << m_mainWnd->uniqueIDManager()->stringForUniqueIdentifier(uid) << '/' << uid; + return 0; + } + return it.value(); +} + +/*! + ... +*/ +IActionContainer *ActionManager::actionContainer(int uid) const +{ + const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid); + if (it == m_idContainerMap.constEnd()) { + if (warnAboutFindFailures) + qWarning() << "ActionManager::actionContainer(): failed to find :" << m_mainWnd->uniqueIDManager()->stringForUniqueIdentifier(uid) << uid; + return 0; + } + return it.value(); +} +static const char *settingsGroup = "KeyBindings"; +static const char *idKey = "ID"; +static const char *sequenceKey = "Keysequence"; + +/*! + \internal +*/ +void ActionManager::initialize() +{ + QSettings *settings = m_mainWnd->settings(); + const int shortcuts = settings->beginReadArray(QLatin1String(settingsGroup)); + for (int i=0; i<shortcuts; ++i) { + settings->setArrayIndex(i); + const QString sid = settings->value(QLatin1String(idKey)).toString(); + const QKeySequence key(settings->value(QLatin1String(sequenceKey)).toString()); + const int id = m_mainWnd->uniqueIDManager()->uniqueIdentifier(sid); + + ICommand *cmd = command(id); + if (cmd) + cmd->setKeySequence(key); + } + settings->endArray(); +} + +/*! + ... +*/ +void ActionManager::saveSettings(QSettings *settings) +{ + settings->beginWriteArray(QLatin1String(settingsGroup)); + int count = 0; + + const IdCmdMap::const_iterator cmdcend = m_idCmdMap.constEnd(); + for (IdCmdMap::const_iterator j = m_idCmdMap.constBegin(); j != cmdcend; ++j) { + const int id = j.key(); + Command *cmd = j.value(); + QKeySequence key = cmd->keySequence(); + if (key != cmd->defaultKeySequence()) { + const QString sid = m_mainWnd->uniqueIDManager()->stringForUniqueIdentifier(id); + settings->setArrayIndex(count); + settings->setValue(QLatin1String(idKey), sid); + settings->setValue(QLatin1String(sequenceKey), key.toString()); + count++; + } + } + + settings->endArray(); +} diff --git a/src/plugins/coreplugin/actionmanager/actionmanager.h b/src/plugins/coreplugin/actionmanager/actionmanager.h new file mode 100644 index 00000000000..c0279e3b69b --- /dev/null +++ b/src/plugins/coreplugin/actionmanager/actionmanager.h @@ -0,0 +1,129 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ACTIONMANAGER_H +#define ACTIONMANAGER_H + +#include <coreplugin/actionmanager/actionmanagerinterface.h> + +#include <QtCore/QMap> +#include <QtCore/QHash> +#include <QtCore/QMultiHash> + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +struct CommandLocation { + int m_container; + int m_position; +}; + +namespace Core { + +class UniqueIDManager; + +namespace Internal { + +class ActionContainer; +class MainWindow; +class Command; + +class ActionManager : public Core::ActionManagerInterface +{ + Q_OBJECT + +public: + ActionManager(MainWindow *mainWnd, UniqueIDManager *uidmgr); + ~ActionManager(); + + void setContext(const QList<int> &context); + static ActionManager* instance(); + + void saveSettings(QSettings *settings); + QList<int> defaultGroups() const; + + QList<Command *> commands() const; + QList<ActionContainer *> containers() const; + + bool hasContext(int context) const; + + ICommand *command(int uid) const; + IActionContainer *actionContainer(int uid) const; + + void registerGlobalGroup(int groupId, int containerId); + + void initialize(); + + //ActionManager Interface + IActionContainer *createMenu(const QString &id); + IActionContainer *createMenuBar(const QString &id); + + ICommand *registerAction(QAction *action, const QString &id, + const QList<int> &context); + ICommand *registerAction(QAction *action, const QString &id); + ICommand *registerShortcut(QShortcut *shortcut, const QString &id, + const QList<int> &context); + + void addAction(Core::ICommand *action, const QString &globalGroup); + void addMenu(Core::IActionContainer *menu, const QString &globalGroup); + + Core::ICommand *command(const QString &id) const; + Core::IActionContainer *actionContainer(const QString &id) const; + +private: + bool hasContext(QList<int> context) const; + ICommand *registerOverridableAction(QAction *action, const QString &id, + bool checkUnique); + + static ActionManager* m_instance; + QList<int> m_defaultGroups; + + typedef QHash<int, Command *> IdCmdMap; + IdCmdMap m_idCmdMap; + + typedef QHash<int, ActionContainer *> IdContainerMap; + IdContainerMap m_idContainerMap; + + typedef QMap<int, int> GlobalGroupMap; + GlobalGroupMap m_globalgroups; + + QList<int> m_context; + + MainWindow *m_mainWnd; +}; + +} // namespace Internal +} // namespace Core + + +#endif //ACTIONMANAGER_H diff --git a/src/plugins/coreplugin/actionmanager/actionmanagerinterface.h b/src/plugins/coreplugin/actionmanager/actionmanagerinterface.h new file mode 100644 index 00000000000..9a3760ed764 --- /dev/null +++ b/src/plugins/coreplugin/actionmanager/actionmanagerinterface.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ACTIONMANAGERINTERFACE_H +#define ACTIONMANAGERINTERFACE_H + +#include "coreplugin/core_global.h" + +#include <coreplugin/actionmanager/iactioncontainer.h> +#include <coreplugin/actionmanager/icommand.h> + +#include <QtCore/QObject> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE +class QAction; +class QShortcut; +class QString; +QT_END_NAMESPACE + +namespace Core { + +class CORE_EXPORT ActionManagerInterface : public QObject +{ + Q_OBJECT +public: + ActionManagerInterface(QObject *parent = 0) : QObject(parent) {} + virtual ~ActionManagerInterface() {} + + virtual IActionContainer *createMenu(const QString &id) = 0; + virtual IActionContainer *createMenuBar(const QString &id) = 0; + + virtual ICommand *registerAction(QAction *action, const QString &id, const QList<int> &context) = 0; + virtual ICommand *registerShortcut(QShortcut *shortcut, const QString &id, const QList<int> &context) = 0; + + virtual ICommand *registerAction(QAction *action, const QString &id) = 0; + + virtual void addAction(ICommand *action, const QString &globalGroup) = 0; + virtual void addMenu(IActionContainer *menu, const QString &globalGroup) = 0; + + virtual ICommand *command(const QString &id) const = 0; + virtual IActionContainer *actionContainer(const QString &id) const = 0; +}; + +} // namespace Core + +#endif // ACTIONMANAGERINTERFACE_H diff --git a/src/plugins/coreplugin/actionmanager/command.cpp b/src/plugins/coreplugin/actionmanager/command.cpp new file mode 100644 index 00000000000..d5e0b17b367 --- /dev/null +++ b/src/plugins/coreplugin/actionmanager/command.cpp @@ -0,0 +1,579 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtCore/QDebug> +#include <QtGui/QAction> +#include <QtGui/QShortcut> + +#include "command.h" + +/*! + \class Core::ICommand + \mainclass + \ingroup qwb + \inheaderfile icommand.h + + \brief The class... + + The Command interface... +*/ + +/*! + \enum ICommand::CommandType +*/ + +/*! + \enum ICommand::CommandAttribute +*/ + +/*! + \fn void ICommand::setCategory(const QString &name) + + Sets the category to \a name. +*/ + +/*! + \fn virtual void ICommand::setDefaultKeySequence(const QKeySequence &key) +*/ + +/*! + \fn virtual int ICommand::id() const +*/ + +/*! + \fn virtual CommandType ICommand::type() const +*/ + +/*! + \fn virtual QAction *ICommand::action() const +*/ + +/*! + \fn virtual QShortcut *ICommand::shortcut() const +*/ + +/*! + \fn virtual void ICommand::setAttribute(CommandAttribute attr) +*/ + +/*! + \fn virtual void ICommand::removeAttribute(CommandAttribute attr) +*/ + +/*! + \fn virtual bool ICommand::hasAttribute(CommandAttribute attr) const +*/ + +/*! + \fn virtual bool ICommand::isActive() const +*/ + +/*! + \fn virtual ICommand::~ICommand() +*/ + +using namespace Core::Internal; + +/*! + \class Command + \ingroup qwb + \inheaderfile command.h +*/ + +/*! + \enum Command::CommandState +*/ + +/*! + \fn Command::Command(CommandType type, int id) +*/ +Command::Command(CommandType type, int id) + : m_type(type), m_id(id) +{ +} + +/*! + \fn virtual Command::~Command() +*/ + +/*! + ... +*/ +void Command::setStateFlags(int state) +{ + m_type |= (state & CS_Mask); +} + +/*! + ... +*/ +int Command::stateFlags() const +{ + return (m_type & CS_Mask); +} + +/*! + \fn virtual QString Command::name() const +*/ + +/*! + ... +*/ +void Command::setCategory(const QString &name) +{ + m_category = name; +} + +/*! + ... +*/ +QString Command::category() const +{ + if (m_category.isEmpty()) + return QObject::tr("Other"); + return m_category; +} + +/*! + ... +*/ +void Command::setDefaultKeySequence(const QKeySequence &key) +{ + m_defaultKey = key; +} + +/*! + ... +*/ +QKeySequence Command::defaultKeySequence() const +{ + return m_defaultKey; +} + +void Command::setDefaultText(const QString &text) +{ + m_defaultText = text; +} + +QString Command::defaultText() const +{ + return m_defaultText; +} + +/*! + ... +*/ +int Command::id() const +{ + return m_id; +} + +/*! + ... +*/ +Command::CommandType Command::type() const +{ + return (CommandType)(m_type & CT_Mask); +} + +/*! + ... +*/ +QAction *Command::action() const +{ + return 0; +} + +/*! + ... +*/ +QShortcut *Command::shortcut() const +{ + return 0; +} + +/*! + ... +*/ +void Command::setAttribute(CommandAttribute attr) +{ + m_type |= attr; +} + +/*! + ... +*/ +void Command::removeAttribute(CommandAttribute attr) +{ + m_type &= ~attr; +} + +/*! + ... +*/ +bool Command::hasAttribute(CommandAttribute attr) const +{ + return (m_type & attr); +} + +QString Command::stringWithAppendedShortcut(const QString &str) const +{ + return QString("%1 <span style=\"color: gray; font-size: small\">%2</span>").arg(str).arg( + keySequence().toString(QKeySequence::NativeText)); +} + +/*! + \fn virtual bool Command::setCurrentContext(const QList<int> &context) = 0 +*/ + +// ---------- Shortcut ------------ + +/*! + \class Shortcut + \ingroup qwb + \inheaderfile command.h +*/ + +/*! + ... +*/ +Shortcut::Shortcut(int id) + : Command(CT_Shortcut, id), m_shortcut(0) +{ + +} + +/*! + ... +*/ +QString Shortcut::name() const +{ + if (!m_shortcut) + return QString(); + + return m_shortcut->whatsThis(); +} + +/*! + ... +*/ +void Shortcut::setShortcut(QShortcut *shortcut) +{ + m_shortcut = shortcut; +} + +/*! + ... +*/ +QShortcut *Shortcut::shortcut() const +{ + return m_shortcut; +} + +/*! + ... +*/ +void Shortcut::setContext(const QList<int> &context) +{ + m_context = context; +} + +/*! + ... +*/ +QList<int> Shortcut::context() const +{ + return m_context; +} + +/*! + ... +*/ +void Shortcut::setDefaultKeySequence(const QKeySequence &key) +{ + setKeySequence(key); + Command::setDefaultKeySequence(key); +} + +void Shortcut::setKeySequence(const QKeySequence &key) +{ + m_shortcut->setKey(key); + emit keySequenceChanged(); +} + +QKeySequence Shortcut::keySequence() const +{ + return m_shortcut->key(); +} + +void Shortcut::setDefaultText(const QString &text) +{ + m_defaultText = text; +} + +QString Shortcut::defaultText() const +{ + return m_defaultText; +} + +/*! + ... +*/ +bool Shortcut::setCurrentContext(const QList<int> &context) +{ + foreach (int ctxt, m_context) { + if (context.contains(ctxt)) { + m_shortcut->setEnabled(true); + return true; + } + } + m_shortcut->setEnabled(false); + return false; +} + +/*! + ... +*/ +bool Shortcut::isActive() const +{ + return m_shortcut->isEnabled(); +} + +// ---------- Action ------------ + +/*! + \class Action + \ingroup qwb + \inheaderfile command.h +*/ + +/*! + ... +*/ +Action::Action(CommandType type, int id) + : Command(type, id), m_action(0) +{ + +} + +/*! + ... +*/ +QString Action::name() const +{ + if (!m_action) + return QString(); + + return m_action->text(); +} + +/*! + ... +*/ +void Action::setAction(QAction *action) +{ + m_action = action; + if (m_action) { + m_action->setParent(this); + m_toolTip = m_action->toolTip(); + } +} + +/*! + ... +*/ +QAction *Action::action() const +{ + return m_action; +} + +/*! + ... +*/ +void Action::setLocations(const QList<CommandLocation> &locations) +{ + m_locations = locations; +} + +/*! + ... +*/ +QList<CommandLocation> Action::locations() const +{ + return m_locations; +} + +/*! + ... +*/ +void Action::setDefaultKeySequence(const QKeySequence &key) +{ + setKeySequence(key); + Command::setDefaultKeySequence(key); +} + +void Action::setKeySequence(const QKeySequence &key) +{ + m_action->setShortcut(key); + updateToolTipWithKeySequence(); + emit keySequenceChanged(); +} + +void Action::updateToolTipWithKeySequence() +{ + if (m_action->shortcut().isEmpty()) + m_action->setToolTip(m_toolTip); + else + m_action->setToolTip(stringWithAppendedShortcut(m_toolTip)); +} + +QKeySequence Action::keySequence() const +{ + return m_action->shortcut(); +} + +// ---------- OverrideableAction ------------ + +/*! + \class OverrideableAction + \ingroup qwb + \inheaderfile command.h +*/ + +/*! + ... +*/ +OverrideableAction::OverrideableAction(int id) + : Action(CT_OverridableAction, id), m_currentAction(0), m_active(false), + m_contextInitialized(false) +{ +} + +/*! + ... +*/ +void OverrideableAction::setAction(QAction *action) +{ + Action::setAction(action); +} + +/*! + ... +*/ +bool OverrideableAction::setCurrentContext(const QList<int> &context) +{ + m_context = context; + + QAction *oldAction = m_currentAction; + m_currentAction = 0; + for (int i = 0; i < m_context.size(); ++i) { + if (QAction *a = m_contextActionMap.value(m_context.at(i), 0)) { + m_currentAction = a; + break; + } + } + + if (m_currentAction == oldAction && m_contextInitialized) + return true; + m_contextInitialized = true; + + if (oldAction) { + disconnect(oldAction, SIGNAL(changed()), this, SLOT(actionChanged())); + disconnect(m_action, SIGNAL(triggered(bool)), oldAction, SIGNAL(triggered(bool))); + disconnect(m_action, SIGNAL(toggled(bool)), oldAction, SLOT(setChecked(bool))); + } + if (m_currentAction) { + connect(m_currentAction, SIGNAL(changed()), this, SLOT(actionChanged())); + // we want to avoid the toggling semantic on slot trigger(), so we just connect the signals + connect(m_action, SIGNAL(triggered(bool)), m_currentAction, SIGNAL(triggered(bool))); + // we need to update the checked state, so we connect to setChecked slot, which also fires a toggled signal + connect(m_action, SIGNAL(toggled(bool)), m_currentAction, SLOT(setChecked(bool))); + actionChanged(); + m_active = true; + return true; + } + if (hasAttribute(CA_Hide)) + m_action->setVisible(false); + m_action->setEnabled(false); + m_active = false; + return false; +} + +/*! + ... +*/ +void OverrideableAction::addOverrideAction(QAction *action, const QList<int> &context) +{ + if (context.isEmpty()) { + m_contextActionMap.insert(0, action); + } else { + for (int i=0; i<context.size(); ++i) { + int k = context.at(i); + if (m_contextActionMap.contains(k)) + qWarning() << QString("addOverrideAction: action already registered for context when registering '%1'").arg(action->text()); + m_contextActionMap.insert(k, action); + } + } +} + +/*! + ... +*/ +void OverrideableAction::actionChanged() +{ + if (hasAttribute(CA_UpdateIcon)) { + m_action->setIcon(m_currentAction->icon()); + m_action->setIconText(m_currentAction->iconText()); + } + if (hasAttribute(CA_UpdateText)) { + m_action->setText(m_currentAction->text()); + m_toolTip = m_currentAction->toolTip(); + updateToolTipWithKeySequence(); + m_action->setStatusTip(m_currentAction->statusTip()); + m_action->setWhatsThis(m_currentAction->whatsThis()); + } + + bool block = m_action->blockSignals(true); + m_action->setChecked(m_currentAction->isChecked()); + m_action->blockSignals(block); + + m_action->setEnabled(m_currentAction->isEnabled()); + m_action->setVisible(m_currentAction->isVisible()); +} + +/*! + ... +*/ +bool OverrideableAction::isActive() const +{ + return m_active; +} diff --git a/src/plugins/coreplugin/actionmanager/command.h b/src/plugins/coreplugin/actionmanager/command.h new file mode 100644 index 00000000000..2d227d65424 --- /dev/null +++ b/src/plugins/coreplugin/actionmanager/command.h @@ -0,0 +1,178 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef COMMAND_H +#define COMMAND_H + +#include "icommand.h" +#include "actionmanager.h" + +#include <QtCore/QList> +#include <QtCore/QMultiMap> +#include <QtCore/QPointer> +#include <QtGui/QKeySequence> + +namespace Core { +namespace Internal { + +class Command : public Core::ICommand +{ + Q_OBJECT +public: + enum CommandState { + CS_PreLocation = 0x020000, + CS_LocationChanged = 0x040000, + CS_Initialized = 0x080000, + CS_Mask = 0xFF0000 + }; + + Command(CommandType type, int id); + virtual ~Command() {} + + void setStateFlags(int state); + int stateFlags() const; + + virtual QString name() const = 0; + + void setCategory(const QString &name); + QString category() const; + + void setDefaultKeySequence(const QKeySequence &key); + QKeySequence defaultKeySequence() const; + + void setDefaultText(const QString &text); + QString defaultText() const; + + int id() const; + CommandType type() const; + + QAction *action() const; + QShortcut *shortcut() const; + + void setAttribute(CommandAttribute attr); + void removeAttribute(CommandAttribute attr); + bool hasAttribute(CommandAttribute attr) const; + + virtual bool setCurrentContext(const QList<int> &context) = 0; + + QString stringWithAppendedShortcut(const QString &str) const; + +protected: + QString m_category; + int m_type; + int m_id; + QKeySequence m_defaultKey; + QString m_defaultText; +}; + +class Shortcut : public Command +{ + Q_OBJECT +public: + Shortcut(int id); + + QString name() const; + + void setDefaultKeySequence(const QKeySequence &key); + void setKeySequence(const QKeySequence &key); + QKeySequence keySequence() const; + + virtual void setDefaultText(const QString &key); + virtual QString defaultText() const; + + void setShortcut(QShortcut *shortcut); + QShortcut *shortcut() const; + + void setContext(const QList<int> &context); + QList<int> context() const; + bool setCurrentContext(const QList<int> &context); + + bool isActive() const; +private: + QList<int> m_context; + QShortcut *m_shortcut; + QString m_defaultText; +}; + +class Action : public Command +{ + Q_OBJECT +public: + Action(CommandType type, int id); + + QString name() const; + + void setDefaultKeySequence(const QKeySequence &key); + void setKeySequence(const QKeySequence &key); + QKeySequence keySequence() const; + + virtual void setAction(QAction *action); + QAction *action() const; + + void setLocations(const QList<CommandLocation> &locations); + QList<CommandLocation> locations() const; + +protected: + void updateToolTipWithKeySequence(); + + QAction *m_action; + QList<CommandLocation> m_locations; + QString m_toolTip; +}; + +class OverrideableAction : public Action +{ + Q_OBJECT + +public: + OverrideableAction(int id); + + void setAction(QAction *action); + bool setCurrentContext(const QList<int> &context); + void addOverrideAction(QAction *action, const QList<int> &context); + bool isActive() const; + +private slots: + void actionChanged(); + +private: + QPointer<QAction> m_currentAction; + QList<int> m_context; + QMap<int, QPointer<QAction> > m_contextActionMap; + bool m_active; + bool m_contextInitialized; +}; + +} // namespace Internal +} // namespace Core + +#endif //COMMAND_H diff --git a/src/plugins/coreplugin/actionmanager/commandsfile.cpp b/src/plugins/coreplugin/actionmanager/commandsfile.cpp new file mode 100644 index 00000000000..6849dd97711 --- /dev/null +++ b/src/plugins/coreplugin/actionmanager/commandsfile.cpp @@ -0,0 +1,126 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "coreimpl.h" +#include "commandsfile.h" +#include "shortcutsettings.h" +#include "command.h" + +#include <coreplugin/uniqueidmanager.h> + +#include <QtCore/QFile> +#include <QtXml/QDomDocument> + +using namespace Core; +using namespace Core::Internal; + +/*! + \class CommandsFile + \brief The CommandsFile class provides a collection of import and export commands. + \ingroup qwb + \inheaderfile commandsfile.h +*/ + +/*! + ... +*/ +CommandsFile::CommandsFile(const QString &filename) + : m_filename(filename) +{ + +} + +/*! + ... +*/ +QMap<QString, QKeySequence> CommandsFile::importCommands() const +{ + QMap<QString, QKeySequence> result; + + QFile file(m_filename); + if (!file.open(QIODevice::ReadOnly)) + return result; + + QDomDocument doc("KeyboardMappingScheme"); + if (!doc.setContent(&file)) + return result; + + QDomElement root = doc.documentElement(); + if (root.nodeName() != QLatin1String("mapping")) + return result; + + QDomElement ks = root.firstChildElement(); + for (; !ks.isNull(); ks = ks.nextSiblingElement()) { + if (ks.nodeName() == QLatin1String("shortcut")) { + QString id = ks.attribute(QLatin1String("id")); + QKeySequence shortcutkey; + QDomElement keyelem = ks.firstChildElement("key"); + if (!keyelem.isNull()) + shortcutkey = QKeySequence(keyelem.attribute("value")); + result.insert(id, shortcutkey); + } + } + + file.close(); + return result; +} + +/*! + ... +*/ +bool CommandsFile::exportCommands(const QList<ShortcutItem *> &items) +{ + UniqueIDManager *idmanager = CoreImpl::instance()->uniqueIDManager(); + + QFile file(m_filename); + if (!file.open(QIODevice::WriteOnly)) + return false; + + QDomDocument doc("KeyboardMappingScheme"); + QDomElement root = doc.createElement("mapping"); + doc.appendChild(root); + + for (int i=0; i<items.count(); ++i) { + ShortcutItem *item = items.at(i); + QDomElement ctag = doc.createElement("shortcut"); + ctag.setAttribute(QLatin1String("id"), idmanager->stringForUniqueIdentifier(item->m_cmd->id())); + root.appendChild(ctag); + + QDomElement ktag = doc.createElement("key"); + ktag.setAttribute(QLatin1String("value"), item->m_key.toString()); + ctag.appendChild(ktag); + } + + file.write(doc.toByteArray()); + file.close(); + return true; +} diff --git a/src/plugins/coreplugin/actionmanager/commandsfile.h b/src/plugins/coreplugin/actionmanager/commandsfile.h new file mode 100644 index 00000000000..890c2384f51 --- /dev/null +++ b/src/plugins/coreplugin/actionmanager/commandsfile.h @@ -0,0 +1,63 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef COMMANDSFILE_H +#define COMMANDSFILE_H + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtCore/QMap> +#include <QtCore/QList> +#include <QtGui/QKeySequence> + +namespace Core { +namespace Internal { + +struct ShortcutItem; + +class CommandsFile : public QObject { + Q_OBJECT + +public: + CommandsFile(const QString &filename); + + QMap<QString, QKeySequence> importCommands() const; + bool exportCommands(const QList<ShortcutItem *> &items); + +private: + QString m_filename; +}; + +} // namespace Internal +} // namespace Core + +#endif //COMMANDSFILE_H diff --git a/src/plugins/coreplugin/actionmanager/iactioncontainer.h b/src/plugins/coreplugin/actionmanager/iactioncontainer.h new file mode 100644 index 00000000000..a9adb471b12 --- /dev/null +++ b/src/plugins/coreplugin/actionmanager/iactioncontainer.h @@ -0,0 +1,81 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IACTIONCONTAINER_H +#define IACTIONCONTAINER_H + +#include <QtCore/QObject> +#include <QtGui/QMenu> +#include <QtGui/QToolBar> +#include <QtGui/QMenuBar> +#include <QtGui/QAction> + +namespace Core { + +class ICommand; +class IActionContainer : public QObject +{ +public: + enum ContainerType { + CT_Mask = 0xFF, + CT_Menu = 0x01, + CT_ToolBar = 0x02 + }; + + enum EmptyAction { + EA_Mask = 0xFF00, + EA_None = 0x0100, + EA_Hide = 0x0200, + EA_Disable = 0x0300 + }; + + virtual void setEmptyAction(EmptyAction ea) = 0; + + virtual int id() const = 0; + virtual ContainerType type() const = 0; + + virtual QMenu *menu() const = 0; + virtual QToolBar *toolBar() const = 0; + virtual QMenuBar *menuBar() const = 0; + + virtual QAction *insertLocation(const QString &group) const = 0; + virtual void appendGroup(const QString &group, bool global = false) = 0; + virtual void addAction(Core::ICommand *action, const QString &group = QString()) = 0; + virtual void addMenu(Core::IActionContainer *menu, const QString &group = QString()) = 0; + + virtual bool update() = 0; + virtual ~IActionContainer() {} +}; + +} // namespace Core + +#endif // IACTIONCONTAINER_H diff --git a/src/plugins/coreplugin/actionmanager/icommand.h b/src/plugins/coreplugin/actionmanager/icommand.h new file mode 100644 index 00000000000..c1a03a2f148 --- /dev/null +++ b/src/plugins/coreplugin/actionmanager/icommand.h @@ -0,0 +1,93 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ICOMMAND_H +#define ICOMMAND_H + +#include <coreplugin/core_global.h> + +#include <QtGui/QAction> +#include <QtGui/QShortcut> +#include <QtGui/QKeySequence> + +namespace Core { + +class CORE_EXPORT ICommand : public QObject +{ + Q_OBJECT +public: + enum CommandType { + CT_Shortcut = 0x0001, + CT_OverridableAction = 0x0002, + CT_Mask = 0x00FF + }; + + enum CommandAttribute { + CA_Hide = 0x0100, + CA_UpdateText = 0x0200, + CA_UpdateIcon = 0x0400, + CA_NonConfigureable = 0x8000, + CA_Mask = 0xFF00 + }; + + virtual void setDefaultKeySequence(const QKeySequence &key) = 0; + virtual void setKeySequence(const QKeySequence &key) = 0; + virtual QKeySequence defaultKeySequence() const = 0; + virtual QKeySequence keySequence() const = 0; + virtual void setDefaultText(const QString &text) = 0; + virtual QString defaultText() const = 0; + + virtual void setCategory(const QString &name) = 0; + + virtual int id() const = 0; + virtual CommandType type() const = 0; + + virtual QAction *action() const = 0; + virtual QShortcut *shortcut() const = 0; + + virtual void setAttribute(CommandAttribute attr) = 0; + virtual void removeAttribute(CommandAttribute attr) = 0; + virtual bool hasAttribute(CommandAttribute attr) const = 0; + + virtual bool isActive() const = 0; + + virtual ~ICommand() {} + + virtual QString stringWithAppendedShortcut(const QString &str) const = 0; + +signals: + void keySequenceChanged(); +}; + +} // namespace Core + +#endif // ICOMMAND_H diff --git a/src/plugins/coreplugin/basefilewizard.cpp b/src/plugins/coreplugin/basefilewizard.cpp new file mode 100644 index 00000000000..b54b43af8d3 --- /dev/null +++ b/src/plugins/coreplugin/basefilewizard.cpp @@ -0,0 +1,671 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "basefilewizard.h" +#include "mimedatabase.h" + +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/ifilewizardextension.h> +#include <utils/filewizarddialog.h> + +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QVector> +#include <QtCore/QDebug> +#include <QtCore/QSharedData> +#include <QtCore/QEventLoop> +#include <QtCore/QSharedPointer> + +#include <QtGui/QMessageBox> +#include <QtGui/QWizard> +#include <QtGui/QMainWindow> + +enum { debugWizard = 0 }; + +namespace Core { + +class GeneratedFilePrivate : public QSharedData { +public: + GeneratedFilePrivate() {} + explicit GeneratedFilePrivate(const QString &p); + QString path; + QString contents; + QString editorKind; +}; + +GeneratedFilePrivate::GeneratedFilePrivate(const QString &p) : + path(p) +{ +} + +GeneratedFile::GeneratedFile() : + m_d(new GeneratedFilePrivate) +{ +} + +GeneratedFile::GeneratedFile(const QString &p) : + m_d(new GeneratedFilePrivate(p)) +{ +} + +GeneratedFile::GeneratedFile(const GeneratedFile &rhs) : + m_d(rhs.m_d) +{ +} + +GeneratedFile &GeneratedFile::operator=(const GeneratedFile &rhs) +{ + if (this != &rhs) + m_d.operator=(rhs.m_d); + return *this; +} + +GeneratedFile::~GeneratedFile() +{ +} + +QString GeneratedFile::path() const +{ + return m_d->path; +} + +void GeneratedFile::setPath(const QString &p) +{ + m_d->path = p; +} + +QString GeneratedFile::contents() const +{ + return m_d->contents; +} + +void GeneratedFile::setContents(const QString &c) +{ + m_d->contents = c; +} + +QString GeneratedFile::editorKind() const +{ + return m_d->editorKind; +} + +void GeneratedFile::setEditorKind(const QString &k) +{ + m_d->editorKind = k; +} + +bool GeneratedFile::write(QString *errorMessage) const +{ + // Ensure the directory + const QFileInfo info(m_d->path); + const QDir dir = info.absoluteDir(); + if (!dir.exists()) { + if (!dir.mkpath(dir.absolutePath())) { + *errorMessage = BaseFileWizard::tr("Unable to create the directory %1.").arg(dir.absolutePath()); + return false; + } + } + // Write out + QFile file(m_d->path); + if (!file.open(QIODevice::WriteOnly|QIODevice::Text)) { + *errorMessage = BaseFileWizard::tr("Unable to open %1 for writing: %2").arg(m_d->path, file.errorString()); + return false; + } + if (file.write(m_d->contents.toUtf8()) == -1) { + *errorMessage = BaseFileWizard::tr("Error while writing to %1: %2").arg(m_d->path, file.errorString()); + return false; + } + file.close(); + return true; +} +// ------------ BaseFileWizardParameterData +class BaseFileWizardParameterData : public QSharedData { +public: + explicit BaseFileWizardParameterData(IWizard::Kind kind = IWizard::FileWizard); + + IWizard::Kind kind; + QIcon icon; + QString description; + QString name; + QString category; + QString trCategory; +}; + +BaseFileWizardParameterData::BaseFileWizardParameterData(IWizard::Kind k) : + kind(k) +{ +} + +BaseFileWizardParameters::BaseFileWizardParameters(IWizard::Kind kind) : + m_d(new BaseFileWizardParameterData(kind)) +{ +} + +BaseFileWizardParameters::BaseFileWizardParameters(const BaseFileWizardParameters &rhs) : + m_d(rhs.m_d) +{ +} + +BaseFileWizardParameters &BaseFileWizardParameters::operator=(const BaseFileWizardParameters &rhs) +{ + if (this != &rhs) + m_d.operator=(rhs.m_d); + return *this; +} + +BaseFileWizardParameters::~BaseFileWizardParameters() +{ +} + +IWizard::Kind BaseFileWizardParameters::kind() const +{ + return m_d->kind; +} + +void BaseFileWizardParameters::setKind(IWizard::Kind k) +{ + m_d->kind = k; +} + +QIcon BaseFileWizardParameters::icon() const +{ + return m_d->icon; +} + +void BaseFileWizardParameters::setIcon(const QIcon &icon) +{ + m_d->icon = icon; +} + +QString BaseFileWizardParameters::description() const +{ + return m_d->description; +} + +void BaseFileWizardParameters::setDescription(const QString &v) +{ + m_d->description = v; +} + + +QString BaseFileWizardParameters::name() const +{ + return m_d->name; +} + +void BaseFileWizardParameters::setName(const QString &v) +{ + m_d->name = v; +} + + +QString BaseFileWizardParameters::category() const +{ + return m_d->category; +} + +void BaseFileWizardParameters::setCategory(const QString &v) +{ + m_d->category = v; +} + +QString BaseFileWizardParameters::trCategory() const +{ + return m_d->trCategory; +} + +void BaseFileWizardParameters::setTrCategory(const QString &v) +{ + m_d->trCategory = v; +} + +/* WizardEventLoop: Special event loop that runs a QWizard and terminates if the page changes. + * Synopsis: + * \code + Wizard wizard(parent); + WizardEventLoop::WizardResult wr; + do { + wr = WizardEventLoop::execWizardPage(wizard); + } while (wr == WizardEventLoop::PageChanged); + * \endcode */ + +class WizardEventLoop : public QEventLoop +{ + Q_OBJECT + Q_DISABLE_COPY(WizardEventLoop) + WizardEventLoop(QObject *parent); + +public: + enum WizardResult { Accepted, Rejected , PageChanged }; + + static WizardResult execWizardPage(QWizard &w); + +private slots: + void pageChanged(int); + void accepted(); + void rejected(); + +private: + WizardResult execWizardPageI(); + + WizardResult m_result; +}; + +WizardEventLoop::WizardEventLoop(QObject *parent) : + QEventLoop(parent), + m_result(Rejected) +{ +} + +WizardEventLoop::WizardResult WizardEventLoop::execWizardPage(QWizard &wizard) +{ + /* Install ourselves on the wizard. Main trick is here to connect + * to the page changed signal and quit() on it. */ + WizardEventLoop *eventLoop = wizard.findChild<WizardEventLoop *>(); + if (!eventLoop) { + eventLoop = new WizardEventLoop(&wizard); + connect(&wizard, SIGNAL(currentIdChanged(int)), eventLoop, SLOT(pageChanged(int))); + connect(&wizard, SIGNAL(accepted()), eventLoop, SLOT(accepted())); + connect(&wizard, SIGNAL(rejected()), eventLoop, SLOT(rejected())); + wizard.setAttribute(Qt::WA_ShowModal, true); + wizard.show(); + } + const WizardResult result = eventLoop->execWizardPageI(); + // Quitting? + if (result != PageChanged) + delete eventLoop; + if (debugWizard) + qDebug() << "WizardEventLoop::runWizard" << wizard.pageIds() << " returns " << result; + + return result; +} + +WizardEventLoop::WizardResult WizardEventLoop::execWizardPageI() +{ + m_result = Rejected; + exec(QEventLoop::DialogExec); + return m_result; +} + +void WizardEventLoop::pageChanged(int /*page*/) +{ + m_result = PageChanged; + quit(); // ! +} + +void WizardEventLoop::accepted() +{ + m_result = Accepted; + quit(); +} + +void WizardEventLoop::rejected() +{ + m_result = Rejected; + quit(); +} + +// ---------------- BaseFileWizardPrivate +struct BaseFileWizardPrivate { + explicit BaseFileWizardPrivate(const Core::BaseFileWizardParameters ¶meters, + Core::ICore *core); + + const Core::BaseFileWizardParameters m_parameters; + QWizard *m_wizardDialog; + Core::ICore *m_core; +}; + +Core::BaseFileWizardPrivate::BaseFileWizardPrivate(const BaseFileWizardParameters ¶meters, + Core::ICore *core) : + m_parameters(parameters), + m_wizardDialog(0), + m_core(core) +{ +} + +// ---------------- Wizard +BaseFileWizard::BaseFileWizard(const BaseFileWizardParameters ¶meters, + Core::ICore *core, + QObject *parent) : + IWizard(parent), + m_d(new BaseFileWizardPrivate(parameters, core)) +{ +} + +BaseFileWizard::~BaseFileWizard() +{ + delete m_d; +} + +IWizard::Kind BaseFileWizard::kind() const +{ + return m_d->m_parameters.kind(); +} + +QIcon BaseFileWizard::icon() const +{ + return m_d->m_parameters.icon(); +} + +QString BaseFileWizard::description() const +{ + return m_d->m_parameters.description(); +} + +QString BaseFileWizard::name() const +{ + return m_d->m_parameters.name(); +} + +QString BaseFileWizard::category() const +{ + return m_d->m_parameters.category(); +} + +QString BaseFileWizard::trCategory() const +{ + return m_d->m_parameters.trCategory(); +} + +QStringList BaseFileWizard::runWizard(const QString &path, QWidget *parent) +{ + typedef QList<IFileWizardExtension*> ExtensionList; + + QString errorMessage; + // Compile extension pages, purge out unused ones + ExtensionList extensions = ExtensionSystem::PluginManager::instance()->getObjects<IFileWizardExtension>(); + WizardPageList allExtensionPages; + for (ExtensionList::iterator it = extensions.begin(); it != extensions.end(); ) { + const WizardPageList extensionPages = (*it)->extensionPages(this); + if (extensionPages.empty()) { + it = extensions.erase(it); + } else { + allExtensionPages += extensionPages; + ++it; + } + } + + if (debugWizard) + qDebug() << Q_FUNC_INFO << path << parent << "exs" << extensions.size() << allExtensionPages.size(); + + QWizardPage *firstExtensionPage = 0; + if (!allExtensionPages.empty()) + firstExtensionPage = allExtensionPages.front(); + + // Create dialog and run it. Ensure that the dialog is deleted when + // leaving the func, but not before the IFileWizardExtension::process + // has been called + const QSharedPointer<QWizard> wizard(createWizardDialog(parent, path, allExtensionPages)); + GeneratedFiles files; + // Run the wizard: Call generate files on switching to the first extension + // page is OR after 'Accepted' if there are no extension pages + while (true) { + const WizardEventLoop::WizardResult wr = WizardEventLoop::execWizardPage(*wizard); + if (wr == WizardEventLoop::Rejected) { + files.clear(); + break; + } + const bool accepted = wr == WizardEventLoop::Accepted; + const bool firstExtensionPageHit = wr == WizardEventLoop::PageChanged + && wizard->page(wizard->currentId()) == firstExtensionPage; + const bool needGenerateFiles = firstExtensionPageHit || (accepted && allExtensionPages.empty()); + if (needGenerateFiles) { + QString errorMessage; + files = generateFiles(wizard.data(), &errorMessage); + if (files.empty()) { + QMessageBox::critical(0, tr("File Generation Failure"), errorMessage); + break; + } + } + if (firstExtensionPageHit) + foreach(IFileWizardExtension *ex, extensions) + ex->firstExtensionPageShown(files); + if (accepted) + break; + } + if (files.empty()) + return QStringList(); + // Compile result list and prompt for overwrite + QStringList result; + foreach (const GeneratedFile &generatedFile, files) + result.push_back(generatedFile.path()); + + switch (promptOverwrite(path, result, &errorMessage)) { + case OverwriteCanceled: + return QStringList(); + case OverwriteError: + QMessageBox::critical(0, tr("Existing files"), errorMessage); + return QStringList(); + case OverwriteOk: + break; + } + + // Write + foreach (const GeneratedFile &generatedFile, files) { + if (!generatedFile.write(&errorMessage)) { + QMessageBox::critical(parent, tr("File Generation Failure"), errorMessage); + return QStringList(); + } + } + // Run the extensions + foreach(IFileWizardExtension *ex, extensions) + if (!ex->process(files, &errorMessage)) { + QMessageBox::critical(parent, tr("File Generation Failure"), errorMessage); + return QStringList(); + } + + // Post generation handler + if (!postGenerateFiles(files, &errorMessage)) { + QMessageBox::critical(0, tr("File Generation Failure"), errorMessage); + return QStringList(); + } + + return result; +} + +QPixmap BaseFileWizard::watermark() +{ + return QPixmap(QLatin1String(":/qworkbench/images/qtwatermark.png")); +} + +void BaseFileWizard::setupWizard(QWizard *w) +{ + w->setPixmap(QWizard::WatermarkPixmap, watermark()); +} + +bool BaseFileWizard::postGenerateFiles(const GeneratedFiles &l, QString *errorMessage) +{ + // File mode: open the editors in file mode and ensure editor pane + const Core::GeneratedFiles::const_iterator cend = l.constEnd(); + for (Core::GeneratedFiles::const_iterator it = l.constBegin(); it != cend; ++it) { + if (!m_d->m_core->editorManager()->openEditor(it->path(), it->editorKind())) { + *errorMessage = tr("Failed to open an editor for %1").arg(it->path()); + return false; + } + } + m_d->m_core->editorManager()->ensureEditorManagerVisible(); + return true; +} + +BaseFileWizard::OverwriteResult BaseFileWizard::promptOverwrite(const QString &location, + const QStringList &files, + QString *errorMessage) const +{ + if (debugWizard) + qDebug() << Q_FUNC_INFO << location << files; + + + bool existingFilesFound = false; + bool oddStuffFound = false; + + static const QString readOnlyMsg = tr(" [read only]"); + static const QString directoryMsg = tr(" [directory]"); + static const QString symLinkMsg = tr(" [symbolic link]"); + + // Format a file list message as ( "<file1> [readonly], <file2> [directory]"). + QString fileNamesMsgPart; + foreach (const QString &fileName, files) { + const QFileInfo fi(fileName); + if (fi.exists()) { + existingFilesFound = true; + if (!fileNamesMsgPart.isEmpty()) + fileNamesMsgPart += QLatin1String(", "); + fileNamesMsgPart += fi.fileName(); + do { + if (fi.isDir()) { + oddStuffFound = true; + fileNamesMsgPart += directoryMsg; + break; + } + if (fi.isSymLink()) { + oddStuffFound = true; + fileNamesMsgPart += symLinkMsg; + break; + } + if (!fi.isWritable()) { + oddStuffFound = true; + fileNamesMsgPart += readOnlyMsg; + } + } while (false); + } + } + + if (!existingFilesFound) + return OverwriteOk; + + if (oddStuffFound) { + *errorMessage = tr("The project directory %1 contains files which cannot be overwritten:\n%2.").arg(location).arg(fileNamesMsgPart); + return OverwriteError; + } + + const QString messageFormat = tr("The following files already exist in the directory %1:\n" + "%2.\nWould you like to overwrite them?"); + const QString message = messageFormat.arg(location).arg(fileNamesMsgPart); + const bool yes = (QMessageBox::question(core()->mainWindow(), + tr("Existing files"), message, + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No) + == QMessageBox::Yes); + return yes ? OverwriteOk : OverwriteCanceled; +} + +Core::ICore *BaseFileWizard::core() const +{ + return m_d->m_core; +} + +QList<IWizard*> BaseFileWizard::allWizards() +{ + return ExtensionSystem::PluginManager::instance()->getObjects<IWizard>(); +} + +// Utility to find all registered wizards of a certain kind + +class WizardKindPredicate { +public: + WizardKindPredicate(IWizard::Kind kind) : m_kind(kind) {} + bool operator()(const IWizard &w) const { return w.kind() == m_kind; } +private: + const IWizard::Kind m_kind; +}; + +QList<IWizard*> BaseFileWizard::findWizardsOfKind(Kind kind) +{ + return findWizards(WizardKindPredicate(kind)); +} + +QString BaseFileWizard::buildFileName(const QString &path, + const QString &baseName, + const QString &extension) +{ + QString rc = path; + if (!rc.isEmpty() && !rc.endsWith(QDir::separator())) + rc += QDir::separator(); + rc += baseName; + // Add extension unless user specified something else + const QChar dot = QLatin1Char('.'); + if (!extension.isEmpty() && !baseName.contains(dot)) { + if (!extension.startsWith(dot)) + rc += dot; + rc += extension; + } + if (debugWizard) + qDebug() << Q_FUNC_INFO << rc; + return rc; +} + +QString BaseFileWizard::preferredSuffix(const QString &mimeType) const +{ + const QString rc = m_d->m_core->mimeDatabase()->preferredSuffixByType(mimeType); + if (rc.isEmpty()) + qWarning("%s: WARNING: Unable to find a preferred suffix for %s.", + Q_FUNC_INFO, mimeType.toUtf8().constData()); + return rc; +} + +// ------------- StandardFileWizard( + +StandardFileWizard::StandardFileWizard(const BaseFileWizardParameters ¶meters, + Core::ICore *core, + QObject *parent) : + BaseFileWizard(parameters, core, parent) +{ +} + +QWizard *StandardFileWizard::createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const +{ + Core::Utils::FileWizardDialog *standardWizardDialog = new Core::Utils::FileWizardDialog(parent); + standardWizardDialog->setWindowTitle(tr("New %1").arg(name())); + setupWizard(standardWizardDialog); + standardWizardDialog->setPath(defaultPath); + foreach (QWizardPage *p, extensionPages) + standardWizardDialog->addPage(p); + return standardWizardDialog; +} + +GeneratedFiles StandardFileWizard::generateFiles(const QWizard *w, + QString *errorMessage) const +{ + const Core::Utils::FileWizardDialog *standardWizardDialog = qobject_cast<const Core::Utils::FileWizardDialog *>(w); + return generateFilesFromPath(standardWizardDialog->path(), + standardWizardDialog->name(), + errorMessage); +} + + +} // namespace Core +#include "basefilewizard.moc" diff --git a/src/plugins/coreplugin/basefilewizard.h b/src/plugins/coreplugin/basefilewizard.h new file mode 100644 index 00000000000..dcf965ecce4 --- /dev/null +++ b/src/plugins/coreplugin/basefilewizard.h @@ -0,0 +1,244 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BASEFILEWIZARD_H +#define BASEFILEWIZARD_H + +#include "core_global.h" + +#include <coreplugin/dialogs/iwizard.h> + +#include <QtGui/QIcon> +#include <QtCore/QSharedDataPointer> +#include <QtCore/QMap> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE +class QWizard; +class QWizardPage; +QT_END_NAMESPACE + +namespace Core { + +class ICore; +class IEditor; +class IFileWizardExtension; + +class BaseFileWizardParameterData; +struct BaseFileWizardPrivate; +class GeneratedFilePrivate; + +/*! + * Represents a file generated by a wizard. The Wizard class will check for + * each file whether it already exists and will report any errors that may + * occur during creation of the files. + */ +class CORE_EXPORT GeneratedFile { +public: + GeneratedFile(); + explicit GeneratedFile(const QString &path); + GeneratedFile(const GeneratedFile &); + GeneratedFile &operator=(const GeneratedFile&); + ~GeneratedFile(); + + // Full path of the file should be created, or the suggested file name + QString path() const; + void setPath(const QString &p); + + // Contents of the file + QString contents() const; + void setContents(const QString &c); + + // Kind of editor to open the file with + QString editorKind() const; + void setEditorKind(const QString &k); + + bool write(QString *errorMessage) const; + +private: + QSharedDataPointer<GeneratedFilePrivate> m_d; +}; + +typedef QList<GeneratedFile> GeneratedFiles; + +/* Parameter class for passing parameters to instances of class Wizard + * containing name, icon and such. */ + +class CORE_EXPORT BaseFileWizardParameters { +public: + explicit BaseFileWizardParameters(IWizard::Kind kind = IWizard::FileWizard); + BaseFileWizardParameters(const BaseFileWizardParameters &); + BaseFileWizardParameters &operator=(const BaseFileWizardParameters&); + ~BaseFileWizardParameters(); + + IWizard::Kind kind() const; + void setKind(IWizard::Kind k); + + QIcon icon() const; + void setIcon(const QIcon&); + + QString description() const; + void setDescription(const QString &); + + QString name() const; + void setName(const QString &); + + QString category() const; + void setCategory(const QString &); + + QString trCategory() const; + void setTrCategory(const QString &); + +private: + QSharedDataPointer<BaseFileWizardParameterData> m_d; +}; + +/* A generic wizard for creating files. + * + * The abstract methods: + * + * createWizardDialog() : Called to create the QWizard dialog to be shown + * generateFiles() : Generate file content + * + * must be implemented. The behaviour can be further customized by overwriting + * the virtual method: + * postGenerateFiles() : Called after generating the files. + */ + +class CORE_EXPORT BaseFileWizard : public IWizard +{ + Q_DISABLE_COPY(BaseFileWizard) + Q_OBJECT + +public: + virtual ~BaseFileWizard(); + + // IWizard + virtual Kind kind() const; + virtual QIcon icon() const; + virtual QString description() const; + virtual QString name() const; + + virtual QString category() const; + virtual QString trCategory() const; + + virtual QStringList runWizard(const QString &path, QWidget *parent); + + // Utility to find all registered wizards + static QList<IWizard*> allWizards(); + // Utility to find all registered wizards of a certain kind + static QList<IWizard*> findWizardsOfKind(Kind kind); + + // Build a file name, adding the extension unless baseName already has one + static QString buildFileName(const QString &path, const QString &baseName, const QString &extension); + + // Return standard pixmap to be used as watermark + static QPixmap watermark(); + // Set the standard watermark on a QWizard + static void setupWizard(QWizard *); + +protected: + typedef QList<QWizardPage *> WizardPageList; + + explicit BaseFileWizard(const BaseFileWizardParameters ¶meters, Core::ICore *core, QObject *parent = 0); + + // Overwrite to create the wizard dialog on the parent, adding + // the extension pages. + virtual QWizard *createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const = 0; + + // Overwrite to query the parameters from the dialog and generate the files. + virtual GeneratedFiles generateFiles(const QWizard *w, + QString *errorMessage) const = 0; + + /* Overwrite to perform steps to be done after files are actually created. + * The default implementation opens editors with the newly generated files. */ + virtual bool postGenerateFiles(const GeneratedFiles &l, QString *errorMessage); + + // Utility that returns the preferred suffix for a mime type + QString preferredSuffix(const QString &mimeType) const; + + // Utility that performs an overwrite check on a set of files. It checks if + // the file exists, can be overwritten at all and prompts the user. + enum OverwriteResult { OverwriteOk, OverwriteError, OverwriteCanceled }; + OverwriteResult promptOverwrite(const QString &location, + const QStringList &files, + QString *errorMessage) const; + Core::ICore *core() const; + +private: + BaseFileWizardPrivate *m_d; +}; + +// StandardFileWizard convenience class for creating one file. It uses +// Core::Utils::FileWizardDialog and introduces a new virtual to generate the +// files from path and name. + +class CORE_EXPORT StandardFileWizard : public BaseFileWizard { + Q_DISABLE_COPY(StandardFileWizard) + Q_OBJECT + +protected: + explicit StandardFileWizard(const BaseFileWizardParameters ¶meters, Core::ICore *core, QObject *parent = 0); + + // Implemented to create a Core::Utils::FileWizardDialog + virtual QWizard *createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const; + // Implemented to retrieve path and name and call generateFilesFromPath() + virtual GeneratedFiles generateFiles(const QWizard *w, + QString *errorMessage) const; + + // Newly introduced virtual that creates a file from a path + virtual GeneratedFiles generateFilesFromPath(const QString &path, + const QString &name, + QString *errorMessage) const = 0; +}; + +/* A utility to find all wizards supporting a view mode and matching a predicate */ +template <class Predicate> + QList<IWizard*> findWizards(Predicate predicate) +{ + // Filter all wizards + const QList<IWizard*> allWizards = BaseFileWizard::allWizards(); + QList<IWizard*> rc; + const QList<IWizard*>::const_iterator cend = allWizards.constEnd(); + for (QList<IWizard*>::const_iterator it = allWizards.constBegin(); it != cend; ++it) + if (predicate(*(*it))) + rc.push_back(*it); + return rc; +} + +} // namespace Core + +#endif // BASEFILEWIZARD_H diff --git a/src/plugins/coreplugin/basemode.cpp b/src/plugins/coreplugin/basemode.cpp new file mode 100644 index 00000000000..5573f733f8c --- /dev/null +++ b/src/plugins/coreplugin/basemode.cpp @@ -0,0 +1,122 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "basemode.h" + +#include <QtGui/QWidget> + +#include <extensionsystem/pluginmanager.h> + +using namespace Core; + +/*! + \class BaseMode + \mainclass + \ingroup qwb + \inheaderfile basemode.h + \brief A base implementation of the mode interface IMode. + + The BaseMode class can be used directly for most IMode implementations. It has setter functions + for the mode properties and a convenience constructor. + + The ownership of the widget is given to the BaseMode, so when the BaseMode is destroyed it + deletes its widget. + + A typical use case is to do the following in the init method of a plugin: + \code + bool MyPlugin::init(QString *error_message) + { + [...] + addObject(new Core::BaseMode("mymode", + "MyPlugin.UniqueModeName", + icon, + 50, // priority + new MyWidget)); + [...] + } + \endcode +*/ + +/*! + \fn BaseMode::BaseMode(QObject *parent) + + Creates a mode with empty name, no icon, lowest priority and no widget. You should use the + setter functions to give the mode a meaning. + + \a parent +*/ +BaseMode::BaseMode(QObject *parent): + IMode(parent), + m_priority(0), + m_widget(0) +{ +} + +/*! + \fn BaseMode::BaseMode(const QString &name, + const char * uniqueModeName, + const QIcon &icon, + int priority, + QWidget *widget, + QObject *parent) + + Creates a mode with the given properties. + + \a name + \a uniqueModeName + \a icon + \a priority + \a widget + \a parent +*/ +BaseMode::BaseMode(const QString &name, + const char * uniqueModeName, + const QIcon &icon, + int priority, + QWidget *widget, + QObject *parent): + IMode(parent), + m_name(name), + m_icon(icon), + m_priority(priority), + m_widget(widget), + m_uniqueModeName(uniqueModeName) +{ +} + +/*! + \fn BaseMode::~BaseMode() +*/ +BaseMode::~BaseMode() +{ + delete m_widget; +} diff --git a/src/plugins/coreplugin/basemode.h b/src/plugins/coreplugin/basemode.h new file mode 100644 index 00000000000..65bd5bb3784 --- /dev/null +++ b/src/plugins/coreplugin/basemode.h @@ -0,0 +1,86 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BASEMODE_H +#define BASEMODE_H + +#include "core_global.h" + +#include "imode.h" + +#include <QtCore/QObject> +#include <QtGui/QWidget> + +namespace Core { + +class CORE_EXPORT BaseMode + : public IMode +{ + Q_OBJECT + +public: + BaseMode(QObject *parent = 0); + BaseMode(const QString &name, + const char * uniqueModeName, + const QIcon &icon, + int priority, + QWidget *widget, + QObject *parent = 0); + ~BaseMode(); + + // IMode + QString name() const { return m_name; } + QIcon icon() const { return m_icon; } + int priority() const { return m_priority; } + QWidget *widget() { return m_widget; } + const char *uniqueModeName() const { return m_uniqueModeName; } + QList<int> context() const { return m_context; } + + void setName(const QString &name) { m_name = name; } + void setIcon(const QIcon &icon) { m_icon = icon; } + void setPriority(int priority) { m_priority = priority; } + void setWidget(QWidget *widget) { m_widget = widget; } + void setUniqueModeName(const char *uniqueModeName) { m_uniqueModeName = uniqueModeName; } + void setContext(const QList<int> &context) { m_context = context; } + +private: + QString m_name; + QIcon m_icon; + int m_priority; + QWidget *m_widget; + const char * m_uniqueModeName; + QList<int> m_context; +}; + +} // namespace Core + +#endif // BASEMODE_H diff --git a/src/plugins/coreplugin/baseview.cpp b/src/plugins/coreplugin/baseview.cpp new file mode 100644 index 00000000000..ef0ab5754cd --- /dev/null +++ b/src/plugins/coreplugin/baseview.cpp @@ -0,0 +1,189 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "baseview.h" + +#include <extensionsystem/ExtensionSystemInterfaces> + +using namespace Core; + +/*! + \class BaseView + \mainclass + \ingroup qwb + \inheaderfile baseview.h + \brief A base implementation of IView. + + The BaseView class can be used directly for most IView implementations. + It has setter functions for the views properties, and a convenience constructor + for the most important ones. + + The ownership of the widget is given to the BaseView, so when the BaseView is destroyed it + deletes its widget. + + A typical use case is to do the following in the init method of a plugin: + \code + bool MyPlugin::init(QString *error_message) + { + [...] + addObject(new Core::BaseView("myplugin.myview", + new MyWidget, + QList<int>() << myContextId, + Qt::LeftDockWidgetArea, + this)); + [...] + } + \endcode +*/ + +/*! + \fn BaseView::BaseView() + + Creates a View with empty view name, no widget, empty context, NoDockWidgetArea, + no menu group and empty default shortcut. You should use the setter functions + to give the view a meaning. +*/ +BaseView::BaseView(QObject *parent) + : IView(parent), + m_viewName(""), + m_widget(0), + m_context(QList<int>()), + m_defaultPosition(IView::First) +{ +} + +/*! + \fn BaseView::BaseView(const char *name, QWidget *widget, const QList<int> &context, Qt::DockWidgetArea position, QObject *parent) + + Creates a view with the given properties. + + \a name + \a widget + \a context + \a position + \a parent +*/ + +BaseView::BaseView(const char *name, QWidget *widget, const QList<int> &context, IView::ViewPosition position, QObject *parent) + : IView(parent), + m_viewName(name), + m_widget(widget), + m_context(context), + m_defaultPosition(position) +{ +} + +/*! + \fn BaseView::~BaseView() +*/ +BaseView::~BaseView() +{ + delete m_widget; +} + +/*! + \fn const QList<int> &BaseView::context() const +*/ +QList<int> BaseView::context() const +{ + return m_context; +} + +/*! + \fn QWidget *BaseView::widget() +*/ +QWidget *BaseView::widget() +{ + return m_widget; +} + +/*! + \fn const char *BaseView::uniqueViewName() const +*/ +const char *BaseView::uniqueViewName() const +{ + return m_viewName; +} + + +/*! + \fn IView::ViewPosition BaseView::defaultPosition() const +*/ +IView::ViewPosition BaseView::defaultPosition() const +{ + return m_defaultPosition; +} + +/*! + \fn void BaseView::setUniqueViewName(const char *name) + + \a name +*/ +void BaseView::setUniqueViewName(const char *name) +{ + m_viewName = name; +} + +/*! + \fn QWidget *BaseView::setWidget(QWidget *widget) + + The BaseView takes the ownership of the \a widget. The previously + set widget (if existent) is not deleted but returned, and responsibility + for deleting it moves to the caller of this method. +*/ +QWidget *BaseView::setWidget(QWidget *widget) +{ + QWidget *oldWidget = m_widget; + m_widget = widget; + return oldWidget; +} + +/*! + \fn void BaseView::setContext(const QList<int> &context) + + \a context +*/ +void BaseView::setContext(const QList<int> &context) +{ + m_context = context; +} + +/*! + \fn void BaseView::setDefaultPosition(IView::ViewPosition position) + + \a position +*/ +void BaseView::setDefaultPosition(IView::ViewPosition position) +{ + m_defaultPosition = position; +} + diff --git a/src/plugins/coreplugin/baseview.h b/src/plugins/coreplugin/baseview.h new file mode 100644 index 00000000000..dd3bb0503a5 --- /dev/null +++ b/src/plugins/coreplugin/baseview.h @@ -0,0 +1,71 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BASEVIEW_H +#define BASEVIEW_H + +#include "core_global.h" +#include "iview.h" +#include <QtCore/QPointer> + +namespace Core { + +class CORE_EXPORT BaseView + : public IView +{ + Q_OBJECT + +public: + BaseView(QObject *parent = 0); + BaseView(const char *name, QWidget *widget, const QList<int> &context, IView::ViewPosition position, QObject *parent = 0); + ~BaseView(); + + QList<int> context() const; + QWidget *widget(); + const char *uniqueViewName() const; + IView::ViewPosition defaultPosition() const; + + void setUniqueViewName(const char *name); + QWidget *setWidget(QWidget *widget); + void setContext(const QList<int> &context); + void setDefaultPosition(IView::ViewPosition position); + +private: + const char *m_viewName; + QPointer<QWidget> m_widget; + QList<int> m_context; + IView::ViewPosition m_defaultPosition; +}; + +} // namespace Core + +#endif // BASEVIEW_H diff --git a/src/plugins/coreplugin/core.qrc b/src/plugins/coreplugin/core.qrc new file mode 100644 index 00000000000..00cd47c92b0 --- /dev/null +++ b/src/plugins/coreplugin/core.qrc @@ -0,0 +1,71 @@ +<RCC> + <qresource prefix="/qworkbench" > + <file>html/images/bg_site_header_dark_grey.png</file> + <file>html/images/body_bg_circles_bottom_right.png</file> + <file>html/images/body_bg_gradient.png</file> + <file>html/images/btn_getting_started.png</file> + <file>html/images/btn_getting_started_hover.png</file> + <file>html/images/btn_restore_session.png</file> + <file>html/images/btn_restore_session_hover.png</file> + <file>html/images/list_bullet_arrow.png</file> + <file>html/images/mode_Project.png</file> + <file>html/images/nokia_logo.png</file> + <file>html/images/product_logo.png</file> + <file>html/images/qt_logo.png</file> + <file>html/images/rc_bottom_left.png</file> + <file>html/images/rc_bottom_mid.png</file> + <file>html/images/rc_bottom_right.png</file> + <file>html/images/rc_mid_left.png</file> + <file>html/images/rc_mid_mid.png</file> + <file>html/images/rc_mid_right.png</file> + <file>html/images/rc_top_left.png</file> + <file>html/images/rc_top_mid.png</file> + <file>html/images/rc_top_right.png</file> + <file>html/qt.css</file> + <file>html/recent_projects.html</file> + <file>html/recent_sessions.html</file> + <file>html/welcome.html</file> + <file>images/clean_pane_small.png</file> + <file>images/clear.png</file> + <file>images/closebutton.png</file> + <file>images/dir.png</file> + <file>images/editcopy.png</file> + <file>images/editcut.png</file> + <file>images/editpaste.png</file> + <file>images/empty14.png</file> + <file>images/filenew.png</file> + <file>images/fileopen.png</file> + <file>images/filesave.png</file> + <file>images/find.png</file> + <file>images/findnext.png</file> + <file>images/qtcreator_logo_128.png</file> + <file>images/qtcreator_logo_32.png</file> + <file>images/inputfield.png</file> + <file>images/inputfield_disabled.png</file> + <file>images/linkicon.png</file> + <file>images/locked.png</file> + <file>images/magnifier.png</file> + <file>images/minus.png</file> + <file>images/next.png</file> + <file>images/panel_button.png</file> + <file>images/panel_button_checked.png</file> + <file>images/panel_button_checked_hover.png</file> + <file>images/panel_button_hover.png</file> + <file>images/panel_button_pressed.png</file> + <file>images/plus.png</file> + <file>images/prev.png</file> + <file>images/pushbutton.png</file> + <file>images/pushbutton_hover.png</file> + <file>images/pushbutton_pressed.png</file> + <file>images/qtwatermark.png</file> + <file>images/redo.png</file> + <file>images/replace.png</file> + <file>images/reset.png</file> + <file>images/sidebaricon.png</file> + <file>images/splitbutton_horizontal.png</file> + <file>images/statusbar.png</file> + <file>images/undo.png</file> + <file>images/unknownfile.png</file> + <file>images/unlocked.png</file> + </qresource> +</RCC> diff --git a/src/plugins/coreplugin/core_global.h b/src/plugins/coreplugin/core_global.h new file mode 100644 index 00000000000..71737b31b73 --- /dev/null +++ b/src/plugins/coreplugin/core_global.h @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/**************************************************************************** +** +** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef CORE_GLOBAL_H +#define CORE_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(CORE_LIBRARY) +# define CORE_EXPORT Q_DECL_EXPORT +#else +# define CORE_EXPORT Q_DECL_IMPORT +#endif + +#endif // CORE_GLOBAL_H diff --git a/src/plugins/coreplugin/coreconstants.h b/src/plugins/coreplugin/coreconstants.h new file mode 100644 index 00000000000..6f0292041ef --- /dev/null +++ b/src/plugins/coreplugin/coreconstants.h @@ -0,0 +1,224 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CORECONSTANTS_H +#define CORECONSTANTS_H + +#include <extensionsystem/ExtensionSystemInterfaces> + +namespace Core { +namespace Constants { + +#define IDE_VERSION_MAJOR 0 +#define IDE_VERSION_MINOR 9 +#define IDE_VERSION_RELEASE 1 + +#define STRINGIFY_INTERNAL(x) #x +#define STRINGIFY(x) STRINGIFY_INTERNAL(x) + +#define IDE_VERSION STRINGIFY(IDE_VERSION_MAJOR) \ + "." STRINGIFY(IDE_VERSION_MINOR) \ + "." STRINGIFY(IDE_VERSION_RELEASE) + +const char * const IDE_VERSION_LONG = IDE_VERSION; +const char * const IDE_AUTHOR = "Nokia Corporation"; +const char * const IDE_YEAR = "2008"; + +#ifdef IDE_REVISION +const char * const IDE_REVISION_STR = STRINGIFY(IDE_REVISION); +#else +const char * const IDE_REVISION_STR = ""; +#endif + +#undef IDE_VERSION +#undef STRINGIFY +#undef STRINGIFY_INTERNAL + +//modes +const char * const MODE_WELCOME = "QtCreator.Mode.Welcome"; +const char * const MODE_EDIT = "QtCreator.Mode.Edit"; +const char * const MODE_OUTPUT = "QtCreator.Mode.Output"; +const int P_MODE_WELCOME = 100; +const int P_MODE_EDIT = 90; +const int P_MODE_OUTPUT = 10; + +//menubar +const char * const MENU_BAR = "QtCreator.MenuBar"; + +//menus +const char * const M_FILE = "QtCreator.Menu.File"; +const char * const M_FILE_OPEN = "QtCreator.Menu.File.Open"; +const char * const M_FILE_NEW = "QtCreator.Menu.File.New"; +const char * const M_FILE_RECENTFILES = "QtCreator.Menu.File.RecentFiles"; +const char * const M_EDIT = "QtCreator.Menu.Edit"; +const char * const M_EDIT_ADVANCED = "QtCreator.Menu.Edit.Advanced"; +const char * const M_TOOLS = "QtCreator.Menu.Tools"; +const char * const M_WINDOW = "QtCreator.Menu.Window"; +const char * const M_WINDOW_PANES = "QtCreator.Menu.Window.Panes"; +const char * const M_HELP = "QtCreator.Menu.Help"; + +//contexts +const char * const C_GLOBAL = "Global Context"; +const int C_GLOBAL_ID = 0; +const char * const C_WELCOME_MODE = "Core.WelcomeMode"; +const char * const C_EDIT_MODE = "Core.EditMode"; +const char * const C_EDITORMANAGER = "Core.EditorManager"; +const char * const C_NAVIGATION_PANE = "Core.NavigationPane"; + +//default editor kind +const char * const K_DEFAULT_TEXT_EDITOR = "Plain Text Editor"; +const char * const K_DEFAULT_BINARY_EDITOR = "Binary Editor"; + +//actions +const char * const UNDO = "QtCreator.Undo"; +const char * const REDO = "QtCreator.Redo"; +const char * const COPY = "QtCreator.Copy"; +const char * const PASTE = "QtCreator.Paste"; +const char * const CUT = "QtCreator.Cut"; +const char * const SELECTALL = "QtCreator.SelectAll"; + +const char * const GOTO = "QtCreator.Goto"; + +const char * const NEW = "QtCreator.New"; +const char * const OPEN = "QtCreator.Open"; +const char * const OPEN_WITH = "QtCreator.OpenWith"; +const char * const REVERTTOSAVED = "QtCreator.RevertToSaved"; +const char * const SAVE = "QtCreator.Save"; +const char * const SAVEAS = "QtCreator.SaveAs"; +const char * const SAVEALL = "QtCreator.SaveAll"; +const char * const PRINT = "QtCreator.Print"; +const char * const EXIT = "QtCreator.Exit"; + +const char * const OPTIONS = "QtCreator.Options"; +const char * const TOGGLE_SIDEBAR = "QtCreator.ToggleSidebar"; + +const char * const MINIMIZE_WINDOW = "QtCreator.MinimizeWindow"; +const char * const ZOOM_WINDOW = "QtCreator.ZoomWindow"; + +const char * const HORIZONTAL = "QtCreator.Horizontal"; +const char * const VERTICAL = "QtCreator.Vertical"; +const char * const REMOVE = "QtCreator.Remove"; +const char * const SAVEASDEFAULT = "QtCreator.SaveAsDefaultLayout"; +const char * const RESTOREDEFAULT = "QtCreator.RestoreDefaultLayout"; +const char * const CLOSE = "QtCreator.Close"; +const char * const DUPLICATEDOCUMENT = "QtCreator.DuplicateDocument"; +const char * const CLOSEALL = "QtCreator.CloseAll"; +const char * const GOTONEXT = "QtCreator.GotoNext"; +const char * const GOTOPREV = "QtCreator.GotoPrevious"; +const char * const GOTONEXTINHISTORY = "QtCreator.GotoNextInHistory"; +const char * const GOTOPREVINHISTORY = "QtCreator.GotoPreviousInHistory"; +const char * const GO_BACK = "QtCreator.GoBack"; +const char * const GO_FORWARD = "QtCreator.GoForward"; +const char * const GOTOPREVIOUSGROUP = "QtCreator.GotoPreviousTabGroup"; +const char * const GOTONEXTGROUP = "QtCreator.GotoNextTabGroup"; +const char * const WINDOWSLIST = "QtCreator.WindowsList"; +const char * const ABOUT_WORKBENCH = "QtCreator.AboutWorkbench"; +const char * const ABOUT_PLUGINS = "QtCreator.AboutPlugins"; +const char * const ABOUT_QT = "QtCreator.AboutQt"; +const char * const S_RETURNTOEDITOR = "QtCreator.ReturnToEditor"; +const char * const OPEN_IN_EXTERNAL_EDITOR = "QtCreattor.OpenInExternalEditor"; + +// default groups +const char * const G_DEFAULT_ONE = "QtCreator.Group.Default.One"; +const char * const G_DEFAULT_TWO = "QtCreator.Group.Default.Two"; +const char * const G_DEFAULT_THREE = "QtCreator.Group.Default.Three"; + +// main menu bar groups +const char * const G_FILE = "QtCreator.Group.File"; +const char * const G_EDIT = "QtCreator.Group.Edit"; +const char * const G_VIEW = "QtCreator.Group.View"; +const char * const G_TOOLS = "QtCreator.Group.Tools"; +const char * const G_WINDOW = "QtCreator.Group.Window"; +const char * const G_HELP = "QtCreator.Group.Help"; + +// file menu groups +const char * const G_FILE_NEW = "QtCreator.Group.File.New"; +const char * const G_FILE_OPEN = "QtCreator.Group.File.Open"; +const char * const G_FILE_PROJECT = "QtCreator.Group.File.Project"; +const char * const G_FILE_SAVE = "QtCreator.Group.File.Save"; +const char * const G_FILE_CLOSE = "QtCreator.Group.File.Close"; +const char * const G_FILE_PRINT = "QtCreator.Group.File.Print"; +const char * const G_FILE_OTHER = "QtCreator.Group.File.Other"; + +// edit menu groups +const char * const G_EDIT_UNDOREDO = "QtCreator.Group.Edit.UndoRedo"; +const char * const G_EDIT_COPYPASTE = "QtCreator.Group.Edit.CopyPaste"; +const char * const G_EDIT_SELECTALL = "QtCreator.Group.Edit.SelectAll"; +const char * const G_EDIT_FORMAT = "QtCreator.Group.Edit.Format"; + +const char * const G_EDIT_FIND = "QtCreator.Group.Edit.Find"; +const char * const G_EDIT_OTHER = "QtCreator.Group.Edit.Other"; + +// window menu groups +const char * const G_WINDOW_SIZE = "QtCreator.Group.Window.Size"; +const char * const G_WINDOW_PANES = "QtCreator.Group.Window.Panes"; +const char * const G_WINDOW_SPLIT = "QtCreator.Group.Window.Split"; +const char * const G_WINDOW_CLOSE = "QtCreator.Group.Window.Close"; +const char * const G_WINDOW_NAVIGATE = "QtCreator.Group.Window.Navigate"; +const char * const G_WINDOW_NAVIGATE_GROUPS = "QtCreator.Group.Window.Navigate.Groups"; +const char * const G_WINDOW_OTHER = "QtCreator.Group.Window.Other"; +const char * const G_WINDOW_LIST = "QtCreator.Group.Window.List"; + +// help groups (global) +const char * const G_HELP_HELP = "QtCreator.Group.Help.Help"; +const char * const G_HELP_ABOUT = "QtCreator.Group.Help.About"; + +const char * const ICON_MINUS = ":/qworkbench/images/minus.png"; +const char * const ICON_PLUS = ":/qworkbench/images/plus.png"; +const char * const ICON_NEWFILE = ":/qworkbench/images/filenew.png"; +const char * const ICON_OPENFILE = ":/qworkbench/images/fileopen.png"; +const char * const ICON_SAVEFILE = ":/qworkbench/images/filesave.png"; +const char * const ICON_UNDO = ":/qworkbench/images/undo.png"; +const char * const ICON_REDO = ":/qworkbench/images/redo.png"; +const char * const ICON_COPY = ":/qworkbench/images/editcopy.png"; +const char * const ICON_PASTE = ":/qworkbench/images/editpaste.png"; +const char * const ICON_CUT = ":/qworkbench/images/editcut.png"; +const char * const ICON_NEXT = ":/qworkbench/images/next.png"; +const char * const ICON_PREV = ":/qworkbench/images/prev.png"; +const char * const ICON_DIR = ":/qworkbench/images/dir.png"; +const char * const ICON_CLEAN_PANE = ":/qworkbench/images/clean_pane_small.png"; +const char * const ICON_CLEAR = ":/qworkbench/images/clear.png"; +const char * const ICON_FIND = ":/qworkbench/images/find.png"; +const char * const ICON_FINDNEXT = ":/qworkbench/images/findnext.png"; +const char * const ICON_REPLACE = ":/qworkbench/images/replace.png"; +const char * const ICON_RESET = ":/qworkbench/images/reset.png"; +const char * const ICON_MAGNIFIER = ":/qworkbench/images/magnifier.png"; +const char * const ICON_TOGGLE_SIDEBAR = ":/qworkbench/images/sidebaricon.png"; + +// wizard kind +const char * const WIZARD_TYPE_FILE = "QtCreator::WizardType::File"; +const char * const WIZARD_TYPE_CLASS = "QtCreator::WizardType::Class"; + +} // namespace Constants +} // namespace Core + +#endif // CORECONSTANTS_H diff --git a/src/plugins/coreplugin/coreimpl.cpp b/src/plugins/coreplugin/coreimpl.cpp new file mode 100644 index 00000000000..258aa0331bb --- /dev/null +++ b/src/plugins/coreplugin/coreimpl.cpp @@ -0,0 +1,211 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "coreimpl.h" + +#include <QtCore/QDir> +#include <QtCore/QCoreApplication> + +using namespace Core; +using namespace Core::Internal; + +CoreImpl *CoreImpl::m_instance = 0; + +CoreImpl *CoreImpl::instance() +{ + return m_instance; +} + +CoreImpl::CoreImpl(MainWindow *mainwindow) +{ + m_instance = this; + m_mainwindow = mainwindow; +} + +QStringList CoreImpl::showNewItemDialog(const QString &title, + const QList<IWizard *> &wizards, + const QString &defaultLocation) +{ + return m_mainwindow->showNewItemDialog(title, wizards, defaultLocation); +} + +void CoreImpl::showOptionsDialog(const QString &group, const QString &page) +{ + m_mainwindow->showOptionsDialog(group, page); +} + +ActionManagerInterface *CoreImpl::actionManager() const +{ + return m_mainwindow->actionManager(); +} + +FileManager *CoreImpl::fileManager() const +{ + return m_mainwindow->fileManager(); +} + +UniqueIDManager *CoreImpl::uniqueIDManager() const +{ + return m_mainwindow->uniqueIDManager(); +} + +MessageManager *CoreImpl::messageManager() const +{ + return m_mainwindow->messageManager(); +} + +ViewManagerInterface *CoreImpl::viewManager() const +{ + return m_mainwindow->viewManager(); +} + +ExtensionSystem::PluginManager *CoreImpl::pluginManager() const +{ + return m_mainwindow->pluginManager(); +} + +EditorManager *CoreImpl::editorManager() const +{ + return m_mainwindow->editorManager(); +} + +ProgressManagerInterface *CoreImpl::progressManager() const +{ + return m_mainwindow->progressManager(); +} + +ScriptManagerInterface *CoreImpl::scriptManager() const +{ + return m_mainwindow->scriptManager(); +} + +VariableManager *CoreImpl::variableManager() const +{ + return m_mainwindow->variableManager(); +} + +VCSManager *CoreImpl::vcsManager() const +{ + return m_mainwindow->vcsManager(); +} + +ModeManager *CoreImpl::modeManager() const +{ + return m_mainwindow->modeManager(); +} + +MimeDatabase *CoreImpl::mimeDatabase() const +{ + return m_mainwindow->mimeDatabase(); +} + +QSettings *CoreImpl::settings() const +{ + return m_mainwindow->settings(); +} + +QPrinter *CoreImpl::printer() const +{ + return m_mainwindow->printer(); +} + +QString CoreImpl::resourcePath() const +{ +#if defined(Q_OS_MAC) + return QDir::cleanPath(QCoreApplication::applicationDirPath()+QLatin1String("/../Resources")); +#else + return QDir::cleanPath(QCoreApplication::applicationDirPath()); +#endif +} + +QString CoreImpl::libraryPath() const +{ +#if defined(Q_OS_MAC) + return QDir::cleanPath(QCoreApplication::applicationDirPath()+QLatin1String("/../PlugIns")); +#else + return QDir::cleanPath(QCoreApplication::applicationDirPath()+QLatin1String("/../lib")); +#endif +} + +IContext *CoreImpl::currentContextObject() const +{ + return m_mainwindow->currentContextObject(); +} + + +QMainWindow *CoreImpl::mainWindow() const +{ + return m_mainwindow; +} + +QStatusBar *CoreImpl::statusBar() const +{ + return m_mainwindow->statusBar(); +} + + +// adds and removes additional active contexts, this context is appended to the +// currently active contexts. call updateContext after changing +void CoreImpl::addAdditionalContext(int context) +{ + m_mainwindow->addAdditionalContext(context); +} + +void CoreImpl::removeAdditionalContext(int context) +{ + m_mainwindow->removeAdditionalContext(context); +} + +bool CoreImpl::hasContext(int context) const +{ + return m_mainwindow->hasContext(context); +} + +void CoreImpl::addContextObject(IContext *context) +{ + m_mainwindow->addContextObject(context); +} + +void CoreImpl::removeContextObject(IContext *context) +{ + m_mainwindow->removeContextObject(context); +} + +void CoreImpl::updateContext() +{ + return m_mainwindow->updateContext(); +} + +void CoreImpl::openFiles(const QStringList &arguments) +{ + m_mainwindow->openFiles(arguments); +} diff --git a/src/plugins/coreplugin/coreimpl.h b/src/plugins/coreplugin/coreimpl.h new file mode 100644 index 00000000000..b7eaf185a4d --- /dev/null +++ b/src/plugins/coreplugin/coreimpl.h @@ -0,0 +1,105 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef COREIMPL_H +#define COREIMPL_H + +#include "icore.h" +#include "mainwindow.h" + +namespace Core { +namespace Internal { + +class CoreImpl : public ICore +{ + Q_OBJECT + +public: + static CoreImpl *instance(); + + CoreImpl(MainWindow *mainwindow); + ~CoreImpl() {} + + QStringList showNewItemDialog(const QString &title, + const QList<IWizard *> &wizards, + const QString &defaultLocation = QString()); + void showOptionsDialog(const QString &group = QString(), + const QString &page = QString()); + + ActionManagerInterface *actionManager() const; + FileManager *fileManager() const ; + UniqueIDManager *uniqueIDManager() const; + MessageManager *messageManager() const; + ViewManagerInterface *viewManager() const; + ExtensionSystem::PluginManager *pluginManager() const; + EditorManager *editorManager() const; + ProgressManagerInterface *progressManager() const; + ScriptManagerInterface *scriptManager() const; + VariableManager *variableManager() const; + VCSManager *vcsManager() const; + ModeManager *modeManager() const; + MimeDatabase *mimeDatabase() const; + + QSettings *settings() const; + QPrinter *printer() const; + + QString resourcePath() const; + QString libraryPath() const; + + IContext *currentContextObject() const; + + QMainWindow *mainWindow() const; + QStatusBar *statusBar() const; + + // adds and removes additional active contexts, this context is appended to the + // currently active contexts. call updateContext after changing + void addAdditionalContext(int context); + void removeAdditionalContext(int context); + bool hasContext(int context) const; + void addContextObject(IContext *contex); + void removeContextObject(IContext *contex); + + void updateContext(); + + void openFiles(const QStringList &fileNames); + +private: + MainWindow *m_mainwindow; + friend class MainWindow; + + static CoreImpl *m_instance; +}; + +} // namespace Internal +} // namespace Core + +#endif // COREIMPL_H diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp new file mode 100644 index 00000000000..070edd9a6a7 --- /dev/null +++ b/src/plugins/coreplugin/coreplugin.cpp @@ -0,0 +1,111 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "coreplugin.h" +#include "welcomemode.h" +#include "editmode.h" +#include "editormanager.h" +#include "mainwindow.h" +#include "modemanager.h" +#include "viewmanager.h" +#include "fileiconprovider.h" + +#include <QtCore/qplugin.h> +#ifdef QT_WEBKIT +#include <QtGui/QApplication> +#include <QtWebKit/QWebSettings> +#endif + +using namespace Core::Internal; + +CorePlugin::CorePlugin() : + m_mainWindow(new MainWindow), m_welcomeMode(0), m_editMode(0), m_pm(0) +{ +} + +CorePlugin::~CorePlugin() +{ + if (m_welcomeMode) { + removeObject(m_welcomeMode); + delete m_welcomeMode; + } + if (m_editMode) { + removeObject(m_editMode); + delete m_editMode; + } + + // delete FileIconProvider singleton + delete FileIconProvider::instance(); + + delete m_mainWindow; +} + +bool CorePlugin::initialize(const QStringList & /*arguments*/, QString *error_message) +{ + m_pm = ExtensionSystem::PluginManager::instance(); + const bool success = m_mainWindow->init(m_pm, error_message); + if (success) { +#ifdef QT_WEBKIT + QWebSettings *webSettings = QWebSettings::globalSettings(); + const QFont applicationFont = QApplication::font(); + webSettings->setFontFamily(QWebSettings::StandardFont, applicationFont.family()); + //webSettings->setFontSize(QWebSettings::DefaultFontSize, applicationFont.pointSize()); +#endif + m_welcomeMode = new WelcomeMode; + addObject(m_welcomeMode); + + EditorManager *editorManager = qobject_cast<EditorManager*>(m_mainWindow->editorManager()); + m_editMode = new EditMode(editorManager); + addObject(m_editMode); + } + return success; +} + +void CorePlugin::extensionsInitialized() +{ + m_mainWindow->modeManager()->activateMode(m_welcomeMode->uniqueModeName()); + m_mainWindow->extensionsInitialized(); +} + +void CorePlugin::remoteArgument(const QString& arg) +{ + // An empty argument is sent to trigger activation + // of the window via QtSingleApplication. It should be + // the last of a sequence. + if (arg.isEmpty()) { + m_mainWindow->activateWindow(); + } else { + m_mainWindow->openFiles(QStringList(arg)); + } +} + +Q_EXPORT_PLUGIN(CorePlugin) diff --git a/src/plugins/coreplugin/coreplugin.h b/src/plugins/coreplugin/coreplugin.h new file mode 100644 index 00000000000..6afd9c7f248 --- /dev/null +++ b/src/plugins/coreplugin/coreplugin.h @@ -0,0 +1,70 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QWORKBENCHPLUGIN_H +#define QWORKBENCHPLUGIN_H + +#include <extensionsystem/iplugin.h> + +namespace Core { +namespace Internal { + +class WelcomeMode; +class EditMode; +class MainWindow; + +class CorePlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + CorePlugin(); + ~CorePlugin(); + + bool initialize(const QStringList &arguments, QString *error_message = 0); + void extensionsInitialized(); + +public slots: + void remoteArgument(const QString&); + +private: + MainWindow *m_mainWindow; + WelcomeMode *m_welcomeMode; + EditMode *m_editMode; + + ExtensionSystem::PluginManager *m_pm; +}; + +} // namespace Internal +} // namespace Core + +#endif // QWORKBENCHPLUGIN_H diff --git a/src/plugins/coreplugin/coreplugin.pri b/src/plugins/coreplugin/coreplugin.pri new file mode 100644 index 00000000000..c2b2a3fa2cf --- /dev/null +++ b/src/plugins/coreplugin/coreplugin.pri @@ -0,0 +1,3 @@ +include(coreplugin_dependencies.pri) + +LIBS *= -l$$qtLibraryTarget(Core) diff --git a/src/plugins/coreplugin/coreplugin.pro b/src/plugins/coreplugin/coreplugin.pro new file mode 100644 index 00000000000..f0c474d4f8f --- /dev/null +++ b/src/plugins/coreplugin/coreplugin.pro @@ -0,0 +1,169 @@ +TEMPLATE = lib +TARGET = Core +DEFINES += CORE_LIBRARY +QT += xml \ + script \ + svg + +include(../../qworkbenchplugin.pri) +include(../../libs/utils/utils.pri) +include(../../../shared/scriptwrapper/scriptwrapper.pri) +include(coreplugin_dependencies.pri) +INCLUDEPATH += dialogs \ + actionmanager \ + editormanager \ + progressmanager \ + scriptmanager +DEPENDPATH += dialogs \ + actionmanager \ + editormanager \ + scriptmanager +SOURCES += mainwindow.cpp \ + welcomemode.cpp \ + editmode.cpp \ + tabpositionindicator.cpp \ + fancyactionbar.cpp \ + fancytabwidget.cpp \ + flowlayout.cpp \ + generalsettings.cpp \ + filemanager.cpp \ + uniqueidmanager.cpp \ + messagemanager.cpp \ + messageoutputwindow.cpp \ + outputpane.cpp \ + vcsmanager.cpp \ + viewmanager.cpp \ + versiondialog.cpp \ + editormanager/editorgroup.cpp \ + editormanager/editormanager.cpp \ + editormanager/stackededitorgroup.cpp \ + editormanager/editorsplitter.cpp \ + editormanager/openeditorsview.cpp \ + editormanager/openeditorswindow.cpp \ + actionmanager/actionmanager.cpp \ + actionmanager/command.cpp \ + actionmanager/actioncontainer.cpp \ + actionmanager/commandsfile.cpp \ + dialogs/saveitemsdialog.cpp \ + dialogs/newdialog.cpp \ + dialogs/settingsdialog.cpp \ + dialogs/shortcutsettings.cpp \ + dialogs/openwithdialog.cpp \ + progressmanager/progressmanager.cpp \ + progressmanager/progressview.cpp \ + progressmanager/progresspie.cpp \ + progressmanager/futureprogress.cpp \ + scriptmanager/scriptmanager.cpp \ + scriptmanager/qworkbench_wrapper.cpp \ + basemode.cpp \ + baseview.cpp \ + coreplugin.cpp \ + variablemanager.cpp \ + modemanager.cpp \ + coreimpl.cpp \ + basefilewizard.cpp \ + plugindialog.cpp \ + stylehelper.cpp \ + inavigationwidgetfactory.cpp \ + navigationwidget.cpp \ + manhattanstyle.cpp \ + minisplitter.cpp \ + styleanimator.cpp \ + findplaceholder.cpp \ + rightpane.cpp \ + sidebar.cpp \ + fileiconprovider.cpp \ + mimedatabase.cpp +HEADERS += mainwindow.h \ + welcomemode.h \ + editmode.h \ + tabpositionindicator.h \ + fancyactionbar.h \ + fancytabwidget.h \ + flowlayout.h \ + generalsettings.h \ + filemanager.h \ + uniqueidmanager.h \ + messagemanager.h \ + messageoutputwindow.h \ + outputpane.h \ + vcsmanager.h \ + viewmanager.h \ + editormanager/editorgroup.h \ + editormanager/editormanager.h \ + editormanager/stackededitorgroup.h \ + editormanager/editorsplitter.h \ + editormanager/openeditorsview.h \ + editormanager/openeditorswindow.h \ + editormanager/ieditor.h \ + editormanager/ieditorfactory.h \ + actionmanager/iactioncontainer.h \ + actionmanager/actionmanagerinterface.h \ + actionmanager/icommand.h \ + actionmanager/actionmanager.h \ + actionmanager/command.h \ + actionmanager/actioncontainer.h \ + actionmanager/commandsfile.h \ + dialogs/saveitemsdialog.h \ + dialogs/newdialog.h \ + dialogs/settingsdialog.h \ + dialogs/shortcutsettings.h \ + dialogs/openwithdialog.h \ + dialogs/iwizard.h \ + dialogs/ioptionspage.h \ + progressmanager/progressmanager.h \ + progressmanager/progressview.h \ + progressmanager/progresspie.h \ + progressmanager/futureprogress.h \ + progressmanager/progressmanagerinterface.h \ + icontext.h \ + icore.h \ + ifile.h \ + ifilefactory.h \ + imode.h \ + ioutputpane.h \ + coreconstants.h \ + iversioncontrol.h \ + iview.h \ + ifilewizardextension.h \ + viewmanagerinterface.h \ + icorelistener.h \ + versiondialog.h \ + scriptmanager/metatypedeclarations.h \ + scriptmanager/qworkbench_wrapper.h \ + scriptmanager/scriptmanagerinterface.h \ + scriptmanager/scriptmanager.h \ + core_global.h \ + basemode.h \ + baseview.h \ + coreplugin.h \ + variablemanager.h \ + modemanager.h \ + coreimpl.h \ + basefilewizard.h \ + plugindialog.h \ + stylehelper.h \ + inavigationwidgetfactory.h \ + navigationwidget.h \ + manhattanstyle.h \ + minisplitter.h \ + styleanimator.h \ + findplaceholder.h \ + rightpane.h \ + sidebar.h \ + fileiconprovider.h \ + mimedatabase.h +FORMS += dialogs/newdialog.ui \ + dialogs/settingsdialog.ui \ + dialogs/shortcutsettings.ui \ + dialogs/saveitemsdialog.ui \ + dialogs/openwithdialog.ui \ + editormanager/openeditorsview.ui \ + generalsettings.ui +RESOURCES += core.qrc \ + fancyactionbar.qrc + +contains(QT_CONFIG, webkit): { + QT += webkit + DEFINES += QT_WEBKIT +} diff --git a/src/plugins/coreplugin/coreplugin_dependencies.pri b/src/plugins/coreplugin/coreplugin_dependencies.pri new file mode 100644 index 00000000000..8b548e71796 --- /dev/null +++ b/src/plugins/coreplugin/coreplugin_dependencies.pri @@ -0,0 +1,2 @@ +include(../../libs/extensionsystem/extensionsystem.pri) +include(../../libs/utils/utils.pri) diff --git a/src/plugins/coreplugin/dialogs/ioptionspage.h b/src/plugins/coreplugin/dialogs/ioptionspage.h new file mode 100644 index 00000000000..4aa38543851 --- /dev/null +++ b/src/plugins/coreplugin/dialogs/ioptionspage.h @@ -0,0 +1,61 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IOPTIONSPAGE_H +#define IOPTIONSPAGE_H + +#include <coreplugin/core_global.h> + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtGui/QWidget> + +namespace Core { + +class CORE_EXPORT IOptionsPage : public QObject +{ + Q_OBJECT +public: + IOptionsPage(QObject *parent = 0) : QObject(parent) {} + virtual ~IOptionsPage() {} + + virtual QString name() const = 0; + virtual QString category() const = 0; + virtual QString trCategory() const = 0; + + virtual QWidget *createPage(QWidget *parent) = 0; + virtual void finished(bool accepted) = 0; +}; + +} // namespace Core + +#endif // IOPTIONSPAGE_H diff --git a/src/plugins/coreplugin/dialogs/iwizard.h b/src/plugins/coreplugin/dialogs/iwizard.h new file mode 100644 index 00000000000..37457ba39b9 --- /dev/null +++ b/src/plugins/coreplugin/dialogs/iwizard.h @@ -0,0 +1,70 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IWIZARD_H +#define IWIZARD_H + +#include <coreplugin/core_global.h> +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE +class QIcon; +QT_END_NAMESPACE + +namespace Core { + +class CORE_EXPORT IWizard + : public QObject +{ + Q_OBJECT +public: + enum Kind { + FileWizard, + ClassWizard, + ProjectWizard + }; + + IWizard(QObject *parent = 0) : QObject(parent) {} + + virtual Kind kind() const = 0; + virtual QIcon icon() const = 0; + virtual QString description() const = 0; + virtual QString name() const = 0; + + virtual QString category() const = 0; + virtual QString trCategory() const = 0; + + virtual QStringList runWizard(const QString &path, QWidget *parent) = 0; +}; +} // namespace Core + +#endif // IWIZARD_H diff --git a/src/plugins/coreplugin/dialogs/newdialog.cpp b/src/plugins/coreplugin/dialogs/newdialog.cpp new file mode 100644 index 00000000000..e344dc34a05 --- /dev/null +++ b/src/plugins/coreplugin/dialogs/newdialog.cpp @@ -0,0 +1,150 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "newdialog.h" +#include "ui_newdialog.h" +#include "basefilewizard.h" + +#include <coreplugin/coreconstants.h> +#include <coreplugin/dialogs/iwizard.h> + + +#include <QtGui/QHeaderView> +#include <QtGui/QPushButton> + +Q_DECLARE_METATYPE(Core::IWizard*) + +static inline Core::IWizard *wizardOfItem(const QTreeWidgetItem *item = 0) +{ + if (!item) + return 0; + return qVariantValue<Core::IWizard*>(item->data(0, Qt::UserRole)); +} + +using namespace Core; +using namespace Core::Internal; + +NewDialog::NewDialog(QWidget *parent) : + QDialog(parent), + m_ui(new Core::Internal::Ui::NewDialog), + m_okButton(0) +{ + typedef QMap<QString, QTreeWidgetItem *> CategoryItemMap; + m_ui->setupUi(this); + m_okButton = m_ui->buttonBox->button(QDialogButtonBox::Ok); + m_okButton->setDefault(true); + + m_ui->watermark->setPixmap(BaseFileWizard::watermark()); + + m_ui->templatesTree->header()->hide(); + connect(m_ui->templatesTree, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), + this, SLOT(currentItemChanged(QTreeWidgetItem*))); + connect(m_ui->templatesTree, SIGNAL(itemActivated(QTreeWidgetItem*,int)), m_okButton, SLOT(animateClick())); + + connect(m_okButton, SIGNAL(clicked()), this, SLOT(okButtonClicked())); + connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); +} + +void NewDialog::setWizards(const QList<IWizard*> wizards) +{ + typedef QMap<QString, QTreeWidgetItem *> CategoryItemMap; + + CategoryItemMap categories; + QVariant wizardPtr; + + m_ui->templatesTree->clear(); + foreach (IWizard *wizard, wizards) { + // ensure category root + const QString categoryName = wizard->category(); + CategoryItemMap::iterator cit = categories.find(categoryName); + if (cit == categories.end()) { + QTreeWidgetItem *categoryItem = new QTreeWidgetItem(m_ui->templatesTree); + categoryItem->setFlags(Qt::ItemIsEnabled); + categoryItem->setText(0, wizard->trCategory()); + qVariantSetValue<IWizard*>(wizardPtr, 0); + categoryItem->setData(0, Qt::UserRole, wizardPtr); + cit = categories.insert(categoryName, categoryItem); + } + // add item + QTreeWidgetItem *wizardItem = new QTreeWidgetItem(cit.value(), QStringList(wizard->name())); + wizardItem->setIcon(0, wizard->icon()); + qVariantSetValue<IWizard*>(wizardPtr, wizard); + wizardItem->setData(0, Qt::UserRole, wizardPtr); + wizardItem->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable); + } +} + +Core::IWizard *NewDialog::showDialog() +{ + m_ui->templatesTree->expandAll(); + if (QTreeWidgetItem *rootItem = m_ui->templatesTree->topLevelItem(0)) { + m_ui->templatesTree->scrollToItem(rootItem); + if (rootItem->childCount()) + m_ui->templatesTree->setCurrentItem(rootItem->child(0)); + } + updateOkButton(); + if (exec() != Accepted) + return 0; + return currentWizard(); +} + +NewDialog::~NewDialog() +{ + delete m_ui; +} + +IWizard *NewDialog::currentWizard() const +{ + return wizardOfItem(m_ui->templatesTree->currentItem()); +} + +void NewDialog::currentItemChanged(QTreeWidgetItem *cat) +{ + + if (const IWizard *wizard = wizardOfItem(cat)) + m_ui->descLabel->setText(wizard->description()); + else + m_ui->descLabel->setText(QString()); + updateOkButton(); +} + +void NewDialog::okButtonClicked() +{ + if (m_ui->templatesTree->currentItem()) + accept(); +} + + +void NewDialog::updateOkButton() +{ + m_okButton->setEnabled(currentWizard() != 0); +} diff --git a/src/plugins/coreplugin/dialogs/newdialog.h b/src/plugins/coreplugin/dialogs/newdialog.h new file mode 100644 index 00000000000..84059b71a17 --- /dev/null +++ b/src/plugins/coreplugin/dialogs/newdialog.h @@ -0,0 +1,82 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef NEWDIALOG_H +#define NEWDIALOG_H + +#include <QtGui/QDialog> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE +class QPushButton; +class QTreeWidgetItem; +class QStringList; +QT_END_NAMESPACE + +namespace Core { + +class IWizard; + +namespace Internal { + +namespace Ui { + class NewDialog; +} + +class NewDialog : public QDialog +{ + Q_OBJECT + +public: + explicit NewDialog(QWidget *parent); + virtual ~NewDialog(); + + void setWizards(const QList<IWizard*> wizards); + + Core::IWizard *showDialog(); + +private slots: + void currentItemChanged(QTreeWidgetItem *cat); + void okButtonClicked(); + void updateOkButton(); + +private: + Core::IWizard *currentWizard() const; + + Ui::NewDialog *m_ui; + QPushButton *m_okButton; +}; + +} // namespace Internal +} // namespace Core + +#endif //NEWDIALOG_H diff --git a/src/plugins/coreplugin/dialogs/newdialog.ui b/src/plugins/coreplugin/dialogs/newdialog.ui new file mode 100644 index 00000000000..39412407687 --- /dev/null +++ b/src/plugins/coreplugin/dialogs/newdialog.ui @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Core::Internal::NewDialog</class> + <widget class="QDialog" name="Core::Internal::NewDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>490</width> + <height>390</height> + </rect> + </property> + <property name="windowTitle"> + <string>New Project</string> + </property> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>9</number> + </property> + <item> + <layout class="QGridLayout"> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item row="0" column="1"> + <widget class="QTreeWidget" name="templatesTree"> + <property name="minimumSize"> + <size> + <width>400</width> + <height>301</height> + </size> + </property> + <column> + <property name="text"> + <string>1</string> + </property> + </column> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="descLabel"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="watermark"> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <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> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/coreplugin/dialogs/openwithdialog.cpp b/src/plugins/coreplugin/dialogs/openwithdialog.cpp new file mode 100644 index 00000000000..b3882184744 --- /dev/null +++ b/src/plugins/coreplugin/dialogs/openwithdialog.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "openwithdialog.h" + +#include <QtGui/QListWidget> +#include <QtGui/QPushButton> +#include <QtCore/QFileInfo> + +using namespace Core; +using namespace Core::Internal; + +OpenWithDialog::OpenWithDialog(const QString &fileName, QWidget *parent) : + QDialog(parent) +{ + setupUi(this); + label->setText(tr("Open file '%1' with:").arg(QFileInfo(fileName).fileName())); + buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + + connect(buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), + this, SLOT(accept())); + connect(buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), + this, SLOT(reject())); + connect(editorListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem *)), + this, SLOT(accept())); + connect(editorListWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), + this, SLOT(currentItemChanged(QListWidgetItem*,QListWidgetItem*))); + + setOkButtonEnabled(false); +} + +void OpenWithDialog::setOkButtonEnabled(bool v) +{ + buttonBox->button(QDialogButtonBox::Ok)->setEnabled(v); +} + +void OpenWithDialog::setEditors(const QStringList &editors) +{ + foreach (const QString &e, editors) + editorListWidget->addItem(e); +} + +QString OpenWithDialog::editor() const +{ + if (const QListWidgetItem *item = editorListWidget->currentItem()) + return item->text(); + return QString(); +} + +void OpenWithDialog::setCurrentEditor(int index) +{ + editorListWidget->setCurrentRow(index); +} + +void OpenWithDialog::currentItemChanged(QListWidgetItem *current, QListWidgetItem *) +{ + setOkButtonEnabled(current); +} diff --git a/src/plugins/coreplugin/dialogs/openwithdialog.h b/src/plugins/coreplugin/dialogs/openwithdialog.h new file mode 100644 index 00000000000..569f5eac7d7 --- /dev/null +++ b/src/plugins/coreplugin/dialogs/openwithdialog.h @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef OPENWITHDIALOG_H +#define OPENWITHDIALOG_H + +#include <QtGui/QDialog> +#include "ui_openwithdialog.h" + +namespace Core { + +class ICore; + +namespace Internal { + +// Present the user with a file name and a list of available +// editor kinds to choose from. +class OpenWithDialog : public QDialog, public Ui::OpenWithDialog +{ + Q_OBJECT + +public: + OpenWithDialog(const QString &fileName, QWidget *parent); + + void setEditors(const QStringList &); + QString editor() const; + + void setCurrentEditor(int index); + +private slots: + void currentItemChanged(QListWidgetItem *, QListWidgetItem *); + +private: + void setOkButtonEnabled(bool); +}; + +} // namespace Internal +} // namespace Core + +#endif diff --git a/src/plugins/coreplugin/dialogs/openwithdialog.ui b/src/plugins/coreplugin/dialogs/openwithdialog.ui new file mode 100644 index 00000000000..1ec069d598c --- /dev/null +++ b/src/plugins/coreplugin/dialogs/openwithdialog.ui @@ -0,0 +1,46 @@ +<ui version="4.0" > + <class>OpenWithDialog</class> + <widget class="QWidget" name="OpenWithDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>358</width> + <height>199</height> + </rect> + </property> + <property name="windowTitle" > + <string>Open File With...</string> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="label" > + <property name="text" > + <string>Open file extension with:</string> + </property> + </widget> + </item> + <item> + <widget class="QListWidget" name="editorListWidget" /> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp new file mode 100644 index 00000000000..7c06c786fd5 --- /dev/null +++ b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp @@ -0,0 +1,207 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "saveitemsdialog.h" +#include "mainwindow.h" +#include "vcsmanager.h" + +#include <coreplugin/ifile.h> + +#include <QtCore/QFileInfo> +#include <QtGui/QPushButton> +#include <QtGui/QTreeWidget> +#include <QtGui/QHeaderView> +#include <QtGui/QCheckBox> +#include <QtGui/QPushButton> + +using namespace Core; +using namespace Core::Internal; + +FileItem::FileItem(QTreeWidget *tree, bool supportOpen, bool open, const QString &text) + : QTreeWidgetItem(tree) +{ + m_saveCheckBox = createCheckBox(tree, 0); + m_saveCheckBox->setChecked(true); + + QFileInfo fi(text); + QString name = fi.fileName(); + if (open) + name.append(tr(" [ReadOnly]")); + + if (supportOpen) { + m_sccCheckBox = createCheckBox(tree, 1); + m_sccCheckBox->setEnabled(open); + m_sccCheckBox->setChecked(open); + connect(m_saveCheckBox, SIGNAL(stateChanged(int)), + this, SLOT(updateSCCCheckBox())); + setText(2, name); + setToolTip(2, text); + } else { + m_sccCheckBox = 0; + setText(2, name); + setToolTip(2, text); + } +} + +QCheckBox *FileItem::createCheckBox(QTreeWidget *tree, int column) +{ + QWidget *w = new QWidget(); + QHBoxLayout *l = new QHBoxLayout(w); + l->setMargin(0); + l->setSpacing(0); + QCheckBox *box = new QCheckBox(w); + l->addWidget(box); + l->setAlignment(box, Qt::AlignCenter); + w->setLayout(l); + tree->setItemWidget(this, column, w); + return box; +} + +void FileItem::updateSCCCheckBox() +{ + if (!m_saveCheckBox->isChecked()) { + m_sccCheckBox->setEnabled(false); + m_sccCheckBox->setChecked(false); + } else { + m_sccCheckBox->setEnabled(true); + } +} + +bool FileItem::shouldBeSaved() const +{ + return m_saveCheckBox->isChecked(); +} + +void FileItem::setShouldBeSaved(bool s) +{ + m_saveCheckBox->setChecked(s); +} + +bool FileItem::shouldBeOpened() const +{ + if (m_sccCheckBox) + return m_sccCheckBox->isChecked(); + return false; +} + + + +SaveItemsDialog::SaveItemsDialog(MainWindow *mainWindow, + QMap<IFile*, QString> items) + : QDialog(mainWindow) +{ + m_ui.setupUi(this); + QPushButton *uncheckButton = m_ui.buttonBox->addButton(tr("Uncheck All"), + QDialogButtonBox::ActionRole); + QPushButton *discardButton = m_ui.buttonBox->addButton(tr("Discard All"), + QDialogButtonBox::DestructiveRole); + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setFocus(Qt::TabFocusReason); + + m_ui.treeWidget->header()->setMovable(false); + m_ui.treeWidget->setRootIsDecorated(false); + m_ui.treeWidget->setColumnCount(2); + + QStringList headers; + headers << tr("Save") << tr("File Name"); + + const bool hasVersionControl = true; + if (hasVersionControl) { + m_ui.treeWidget->setColumnCount(3); + headers.insert(1, tr("Open with SCC")); + } + m_ui.treeWidget->setHeaderLabels(headers); + + FileItem *itm; + QMap<IFile*, QString>::const_iterator it = items.constBegin(); + while (it != items.constEnd()) { + QString directory = QFileInfo(it.key()->fileName()).absolutePath(); + bool fileHasVersionControl = mainWindow->vcsManager()->findVersionControlForDirectory(directory) != 0; + itm = new FileItem(m_ui.treeWidget, fileHasVersionControl, + it.key()->isReadOnly(), it.value()); + m_itemMap.insert(itm, it.key()); + ++it; + } + + m_ui.treeWidget->resizeColumnToContents(0); + if (hasVersionControl) + m_ui.treeWidget->resizeColumnToContents(1); + + connect(m_ui.buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), + this, SLOT(collectItemsToSave())); + connect(uncheckButton, SIGNAL(clicked()), this, SLOT(uncheckAll())); + connect(discardButton, SIGNAL(clicked()), this, SLOT(discardAll())); +} + +void SaveItemsDialog::setMessage(const QString &msg) +{ + m_ui.msgLabel->setText(msg); +} + +void SaveItemsDialog::collectItemsToSave() +{ + m_itemsToSave.clear(); + m_itemsToOpen.clear(); + QMap<FileItem*, IFile*>::const_iterator it = m_itemMap.constBegin(); + while (it != m_itemMap.constEnd()) { + if (it.key()->shouldBeSaved()) + m_itemsToSave << it.value(); + if (it.key()->shouldBeOpened()) + m_itemsToOpen.insert(it.value()); + ++it; + } + accept(); +} + +void SaveItemsDialog::discardAll() +{ + uncheckAll(); + collectItemsToSave(); +} + +QList<IFile*> SaveItemsDialog::itemsToSave() const +{ + return m_itemsToSave; +} + +QSet<IFile*> SaveItemsDialog::itemsToOpen() const +{ + return m_itemsToOpen; +} + +void SaveItemsDialog::uncheckAll() +{ + for (int i=0; i<m_ui.treeWidget->topLevelItemCount(); ++i) { + FileItem *item = static_cast<FileItem*>(m_ui.treeWidget->topLevelItem(i)); + item->setShouldBeSaved(false); + } +} diff --git a/src/plugins/coreplugin/dialogs/saveitemsdialog.h b/src/plugins/coreplugin/dialogs/saveitemsdialog.h new file mode 100644 index 00000000000..52e43aab0a6 --- /dev/null +++ b/src/plugins/coreplugin/dialogs/saveitemsdialog.h @@ -0,0 +1,102 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SAVEITEMSDIALOG_H +#define SAVEITEMSDIALOG_H + +#include <QtCore/QMap> +#include <QtGui/QDialog> + +#include "ui_saveitemsdialog.h" + +QT_BEGIN_NAMESPACE +class QCheckBox; +QT_END_NAMESPACE + +namespace Core { + +class IFile; +class EditorManager; + +namespace Internal { + +class MainWindow; + +class FileItem : public QObject, public QTreeWidgetItem +{ + Q_OBJECT + +public: + FileItem(QTreeWidget *tree, bool supportOpen, + bool open, const QString &text); + bool shouldBeSaved() const; + void setShouldBeSaved(bool s); + bool shouldBeOpened() const; + +private slots: + void updateSCCCheckBox(); + +private: + QCheckBox *createCheckBox(QTreeWidget *tree, int column); + QCheckBox *m_saveCheckBox; + QCheckBox *m_sccCheckBox; +}; + +class SaveItemsDialog : public QDialog +{ + Q_OBJECT + +public: + SaveItemsDialog(MainWindow *mainWindow, + QMap<Core::IFile*, QString> items); + + void setMessage(const QString &msg); + + QList<Core::IFile*> itemsToSave() const; + QSet<Core::IFile*> itemsToOpen() const; + +private slots: + void collectItemsToSave(); + void uncheckAll(); + void discardAll(); + +private: + Ui::SaveItemsDialog m_ui; + QMap<FileItem*, Core::IFile*> m_itemMap; + QList<Core::IFile*> m_itemsToSave; + QSet<Core::IFile*> m_itemsToOpen; +}; + +} // namespace Internal +} // namespace Core + +#endif // SAVEITEMSDIALOG_H diff --git a/src/plugins/coreplugin/dialogs/saveitemsdialog.ui b/src/plugins/coreplugin/dialogs/saveitemsdialog.ui new file mode 100644 index 00000000000..85931a1daba --- /dev/null +++ b/src/plugins/coreplugin/dialogs/saveitemsdialog.ui @@ -0,0 +1,66 @@ +<ui version="4.0" > + <class>SaveItemsDialog</class> + <widget class="QDialog" name="SaveItemsDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>200</height> + </rect> + </property> + <property name="windowTitle" > + <string>Save Changes</string> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="msgLabel" > + <property name="text" > + <string>Save the changes of the following items:</string> + </property> + </widget> + </item> + <item> + <widget class="QTreeWidget" name="treeWidget" /> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>treeWidget</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>SaveItemsDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>199</x> + <y>174</y> + </hint> + <hint type="destinationlabel" > + <x>199</x> + <y>99</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/coreplugin/dialogs/settingsdialog.cpp b/src/plugins/coreplugin/dialogs/settingsdialog.cpp new file mode 100644 index 00000000000..78d839bf1aa --- /dev/null +++ b/src/plugins/coreplugin/dialogs/settingsdialog.cpp @@ -0,0 +1,138 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "settingsdialog.h" +#include "coreimpl.h" + +#include <QtGui/QHeaderView> +#include <QtGui/QPushButton> + +using namespace Core; +using namespace Core::Internal; + +SettingsDialog::SettingsDialog(QWidget *parent, const QString &initialCategory, + const QString &initialPage) + : QDialog(parent) +{ + setupUi(this); + buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + + pageTree->header()->setVisible(false); + + connect(pageTree, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + this, SLOT(pageSelected(QTreeWidgetItem *))); + + QMap<QString, QTreeWidgetItem *> categories; + + QList<IOptionsPage*> pages = + CoreImpl::instance()->pluginManager()->getObjects<IOptionsPage>(); + + int index = 0; + foreach(IOptionsPage *page, pages) { + QTreeWidgetItem *item = new QTreeWidgetItem(); + item->setText(0, page->name()); + item->setData(0, Qt::UserRole, index); + + QStringList categoriesId = page->category().split(QLatin1Char('|')); + QStringList trCategories = page->trCategory().split(QLatin1Char('|')); + QString currentCategory = categoriesId.at(0); + + QTreeWidgetItem *treeitem; + if (!categories.contains(currentCategory)) { + treeitem = new QTreeWidgetItem(pageTree); + treeitem->setText(0, trCategories.at(0)); + treeitem->setData(0, Qt::UserRole, index); + categories.insert(currentCategory, treeitem); + } + + int catCount = 1; + while (catCount < categoriesId.count()) { + if(!categories.contains(currentCategory + QLatin1Char('|') + categoriesId.at(catCount))) { + treeitem = new QTreeWidgetItem(categories.value(currentCategory)); + currentCategory += QLatin1Char('|') + categoriesId.at(catCount); + treeitem->setText(0, trCategories.at(catCount)); + treeitem->setData(0, Qt::UserRole, index); + categories.insert(currentCategory, treeitem); + } else { + currentCategory += QLatin1Char('|') + categoriesId.at(catCount); + } + ++catCount; + } + + categories.value(currentCategory)->addChild(item); + + m_pages.append(page); + stackedPages->addWidget(page->createPage(stackedPages)); + + if (page->name() == initialPage && currentCategory == initialCategory) { + stackedPages->setCurrentIndex(stackedPages->count()); + pageTree->setCurrentItem(item); + } + + index++; + } + + QList<int> sizes; + sizes << 150 << 300; + splitter->setSizes(sizes); + + splitter->setStretchFactor(splitter->indexOf(pageTree), 0); + splitter->setStretchFactor(splitter->indexOf(layoutWidget), 1); +} + +SettingsDialog::~SettingsDialog() +{ + +} + +void SettingsDialog::pageSelected(QTreeWidgetItem *) +{ + QTreeWidgetItem *item = pageTree->currentItem(); + int index = item->data(0, Qt::UserRole).toInt(); + stackedPages->setCurrentIndex(index); +} + +void SettingsDialog::accept() +{ + foreach(IOptionsPage *page, m_pages) { + page->finished(true); + } + done(QDialog::Accepted); +} + +void SettingsDialog::reject() +{ + foreach(IOptionsPage *page, m_pages) { + page->finished(false); + } + done(QDialog::Rejected); +} diff --git a/src/plugins/coreplugin/dialogs/settingsdialog.h b/src/plugins/coreplugin/dialogs/settingsdialog.h new file mode 100644 index 00000000000..2d80aa643c2 --- /dev/null +++ b/src/plugins/coreplugin/dialogs/settingsdialog.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SETTINGSDIALOG_H +#define SETTINGSDIALOG_H + +#include "ui_settingsdialog.h" + +#include <QtCore/QList> + +#include "coreplugin/dialogs/ioptionspage.h" + +namespace Core { +namespace Internal { + +class SettingsDialog : public QDialog, public ::Ui::SettingsDialog +{ + Q_OBJECT + +public: + SettingsDialog(QWidget *parent, + const QString &initialCategory = QString(), + const QString &initialPage = QString()); + ~SettingsDialog(); + +private slots: + void pageSelected(QTreeWidgetItem *cat); + void accept(); + void reject(); + +private: + QList<Core::IOptionsPage*> m_pages; +}; + +} // namespace Internal +} // namespace Core + +#endif // SETTINGSDIALOG_H diff --git a/src/plugins/coreplugin/dialogs/settingsdialog.ui b/src/plugins/coreplugin/dialogs/settingsdialog.ui new file mode 100644 index 00000000000..9d2475536c0 --- /dev/null +++ b/src/plugins/coreplugin/dialogs/settingsdialog.ui @@ -0,0 +1,121 @@ +<ui version="4.0" > + <class>SettingsDialog</class> + <widget class="QDialog" name="SettingsDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>697</width> + <height>476</height> + </rect> + </property> + <property name="windowTitle" > + <string>Options</string> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QSplitter" name="splitter" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <widget class="QTreeWidget" name="pageTree" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="columnCount" > + <number>1</number> + </property> + <column> + <property name="text" > + <string>0</string> + </property> + </column> + </widget> + <widget class="QWidget" name="layoutWidget" > + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QStackedWidget" name="stackedPages" > + <property name="minimumSize" > + <size> + <width>350</width> + <height>250</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="Line" name="line" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>SettingsDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>297</x> + <y>361</y> + </hint> + <hint type="destinationlabel" > + <x>297</x> + <y>193</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>SettingsDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>297</x> + <y>361</y> + </hint> + <hint type="destinationlabel" > + <x>297</x> + <y>193</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp new file mode 100644 index 00000000000..aeefebdf24c --- /dev/null +++ b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp @@ -0,0 +1,379 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "shortcutsettings.h" +#include "ui_shortcutsettings.h" +#include "actionmanager.h" +#include "command.h" +#include "coreconstants.h" +#include "coreimpl.h" +#include "commandsfile.h" +#include "filemanager.h" + +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/icommand.h> + +#include <QtGui/QKeyEvent> +#include <QtGui/QShortcut> +#include <QtGui/QHeaderView> +#include <QtGui/QFileDialog> +#include <QtDebug> + +Q_DECLARE_METATYPE(Core::Internal::ShortcutItem*); + +using namespace Core; +using namespace Core::Internal; + +ShortcutSettings::ShortcutSettings(QObject *parent) + : IOptionsPage(parent) +{ +} + +ShortcutSettings::~ShortcutSettings() +{ +} + +// IOptionsPage +QString ShortcutSettings::name() const +{ + return tr("Keyboard"); +} + +QString ShortcutSettings::category() const +{ + return QLatin1String("Environment"); +} + +QString ShortcutSettings::trCategory() const +{ + return tr("Environment"); +} + +QWidget *ShortcutSettings::createPage(QWidget *parent) +{ + m_keyNum = m_key[0] = m_key[1] = m_key[2] = m_key[3] = 0; + + m_page = new Ui_ShortcutSettings(); + QWidget *w = new QWidget(parent); + m_page->setupUi(w); + + m_page->resetButton->setIcon(QIcon(Constants::ICON_RESET)); + m_page->shortcutEdit->installEventFilter(this); + + connect(m_page->resetButton, SIGNAL(clicked()), + this, SLOT(resetKeySequence())); + connect(m_page->removeButton, SIGNAL(clicked()), + this, SLOT(removeKeySequence())); + connect(m_page->exportButton, SIGNAL(clicked()), + this, SLOT(exportAction())); + connect(m_page->importButton, SIGNAL(clicked()), + this, SLOT(importAction())); + connect(m_page->defaultButton, SIGNAL(clicked()), + this, SLOT(defaultAction())); + + initialize(); + + m_page->commandList->sortByColumn(0, Qt::AscendingOrder); + + connect(m_page->filterEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); + connect(m_page->commandList, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + this, SLOT(commandChanged(QTreeWidgetItem *))); + connect(m_page->shortcutEdit, SIGNAL(textChanged(QString)), this, SLOT(keyChanged())); + + QHeaderView *hv = m_page->commandList->header(); + hv->resizeSection(0, 210); + hv->resizeSection(1, 110); + hv->setStretchLastSection(true); + + commandChanged(0); + + return w; +} + +void ShortcutSettings::finished(bool accepted) +{ + if (accepted) { + foreach(ShortcutItem *item, m_scitems) { + item->m_cmd->setKeySequence(item->m_key); + } + } + + qDeleteAll(m_scitems); + m_scitems.clear(); +} + +bool ShortcutSettings::eventFilter(QObject *o, QEvent *e) +{ + Q_UNUSED(o); + + if ( e->type() == QEvent::KeyPress ) { + QKeyEvent *k = static_cast<QKeyEvent*>(e); + handleKeyEvent(k); + return true; + } + + if ( e->type() == QEvent::Shortcut || + e->type() == QEvent::ShortcutOverride || + e->type() == QEvent::KeyRelease ) + return true; + + return false; +} + +void ShortcutSettings::commandChanged(QTreeWidgetItem *current) +{ + if (!current || !current->data(0, Qt::UserRole).isValid()) { + m_page->shortcutEdit->setText(""); + m_page->seqGrp->setEnabled(false); + return; + } + m_page->seqGrp->setEnabled(true); + ShortcutItem *scitem = qVariantValue<ShortcutItem *>(current->data(0, Qt::UserRole)); + setKeySequence(scitem->m_key); +} + +void ShortcutSettings::filterChanged(const QString &f) +{ + for (int i=0; i<m_page->commandList->topLevelItemCount(); ++i) { + QTreeWidgetItem *item = m_page->commandList->topLevelItem(i); + item->setHidden(filter(f, item)); + } +} + +void ShortcutSettings::keyChanged() +{ + QTreeWidgetItem *current = m_page->commandList->currentItem(); + if (current && current->data(0, Qt::UserRole).isValid()) { + ShortcutItem *scitem = qVariantValue<ShortcutItem *>(current->data(0, Qt::UserRole)); + scitem->m_key = QKeySequence(m_key[0], m_key[1], m_key[2], m_key[3]); + current->setText(2, scitem->m_key); + } +} + +void ShortcutSettings::setKeySequence(const QKeySequence &key) +{ + m_keyNum = m_key[0] = m_key[1] = m_key[2] = m_key[3] = 0; + m_keyNum = key.count(); + for (int i = 0; i < m_keyNum; ++i) { + m_key[i] = key[i]; + } + m_page->shortcutEdit->setText(key); +} + +bool ShortcutSettings::filter(const QString &f, const QTreeWidgetItem *item) +{ + if (item->childCount() == 0) { + if (f.isEmpty()) + return false; + for (int i = 0; i < item->columnCount(); ++i) { + if(item->text(i).contains(f, Qt::CaseInsensitive)) + return false; + } + return true; + } + + bool found = false; + for (int i = 0; i < item->childCount(); ++i) { + QTreeWidgetItem *citem = item->child(i); + if (filter(f, citem)) { + citem->setHidden(true); + } else { + citem->setHidden(false); + found = true; + } + } + return !found; +} + +void ShortcutSettings::resetKeySequence() +{ + QTreeWidgetItem *current = m_page->commandList->currentItem(); + if (current && current->data(0, Qt::UserRole).isValid()) { + ShortcutItem *scitem = qVariantValue<ShortcutItem *>(current->data(0, Qt::UserRole)); + setKeySequence(scitem->m_cmd->defaultKeySequence()); + } +} + +void ShortcutSettings::removeKeySequence() +{ + m_keyNum = m_key[0] = m_key[1] = m_key[2] = m_key[3] = 0; + m_page->shortcutEdit->clear(); +} + +void ShortcutSettings::importAction() +{ + UniqueIDManager *uidm = + CoreImpl::instance()->uniqueIDManager(); + + QString fileName = QFileDialog::getOpenFileName(0, tr("Import Keyboard Mapping Scheme"), + CoreImpl::instance()->resourcePath() + "/schemes/", + tr("Keyboard Mapping Scheme (*.kms)")); + if (!fileName.isEmpty()) { + CommandsFile cf(fileName); + QMap<QString, QKeySequence> mapping = cf.importCommands(); + + foreach(ShortcutItem *item, m_scitems) { + QString sid = uidm->stringForUniqueIdentifier(item->m_cmd->id()); + if (mapping.contains(sid)) { + item->m_key = mapping.value(sid); + item->m_item->setText(2, item->m_key); + if (item->m_item == m_page->commandList->currentItem()) + commandChanged(item->m_item); + } + } + } +} + +void ShortcutSettings::defaultAction() +{ + foreach(ShortcutItem *item, m_scitems) { + item->m_key = item->m_cmd->defaultKeySequence(); + item->m_item->setText(2, item->m_key); + if (item->m_item == m_page->commandList->currentItem()) + commandChanged(item->m_item); + } +} + +void ShortcutSettings::exportAction() +{ + QString fileName = CoreImpl::instance()->fileManager()->getSaveFileNameWithExtension( + tr("Export Keyboard Mapping Scheme"), + CoreImpl::instance()->resourcePath() + "/schemes/", + tr("Keyboard Mapping Scheme (*.kms)"), + ".kms"); + if (!fileName.isEmpty()) { + CommandsFile cf(fileName); + cf.exportCommands(m_scitems); + } +} + +void ShortcutSettings::initialize() +{ + QMap<QString, QTreeWidgetItem *> categories; + + m_am = ActionManager::instance(); + UniqueIDManager *uidm = + CoreImpl::instance()->uniqueIDManager(); + + QList<Command *> cmds = m_am->commands(); + for (int i = 0; i < cmds.size(); ++i) { + Command *c = cmds.at(i); + if (c->hasAttribute(Command::CA_NonConfigureable)) + continue; + if (c->action() && c->action()->isSeparator()) + continue; + + QTreeWidgetItem *item = 0; + ShortcutItem *s = new ShortcutItem; + m_scitems << s; + if (c->category().isEmpty()) { + item = new QTreeWidgetItem(m_page->commandList); + } else { + if (!categories.contains(c->category())) { + QTreeWidgetItem *cat = new QTreeWidgetItem(m_page->commandList); + cat->setText(0, c->category()); + categories.insert(c->category(), cat); + cat->setExpanded(true); + } + item = new QTreeWidgetItem(categories.value(c->category())); + } + s->m_cmd = c; + s->m_item = item; + + item->setText(0, uidm->stringForUniqueIdentifier(c->id())); + + if (c->action()) { + QString text = c->hasAttribute(Command::CA_UpdateText) && !c->defaultText().isNull() ? c->defaultText() : c->action()->text(); + s->m_key = c->action()->shortcut(); + item->setText(1, text); + } else { + s->m_key = c->shortcut()->key(); + item->setText(1, c->shortcut()->whatsThis()); + } + + item->setText(2, s->m_key); + item->setData(0, Qt::UserRole, qVariantFromValue(s)); + } +} + +void ShortcutSettings::handleKeyEvent(QKeyEvent *e) +{ + int nextKey = e->key(); + if ( m_keyNum > 3 || + nextKey == Qt::Key_Control || + nextKey == Qt::Key_Shift || + nextKey == Qt::Key_Meta || + nextKey == Qt::Key_Alt ) + return; + + nextKey |= translateModifiers(e->modifiers(), e->text()); + switch (m_keyNum) { + case 0: + m_key[0] = nextKey; + break; + case 1: + m_key[1] = nextKey; + break; + case 2: + m_key[2] = nextKey; + break; + case 3: + m_key[3] = nextKey; + break; + default: + break; + } + m_keyNum++; + QKeySequence ks(m_key[0], m_key[1], m_key[2], m_key[3]); + m_page->shortcutEdit->setText(ks); + e->accept(); +} + +int ShortcutSettings::translateModifiers(Qt::KeyboardModifiers state, + const QString &text) +{ + int result = 0; + // The shift modifier only counts when it is not used to type a symbol + // that is only reachable using the shift key anyway + if ((state & Qt::ShiftModifier) && (text.size() == 0 + || !text.at(0).isPrint() + || text.at(0).isLetter() + || text.at(0).isSpace())) + result |= Qt::SHIFT; + if (state & Qt::ControlModifier) + result |= Qt::CTRL; + if (state & Qt::MetaModifier) + result |= Qt::META; + if (state & Qt::AltModifier) + result |= Qt::ALT; + return result; +} diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.h b/src/plugins/coreplugin/dialogs/shortcutsettings.h new file mode 100644 index 00000000000..38dfb888948 --- /dev/null +++ b/src/plugins/coreplugin/dialogs/shortcutsettings.h @@ -0,0 +1,110 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SHORTCUTSETTINGS_H +#define SHORTCUTSETTINGS_H + +#include <coreplugin/dialogs/ioptionspage.h> + +#include <QtCore/QObject> +#include <QtGui/QKeySequence> +#include <QtGui/QTreeWidgetItem> +#include <QtGui/QKeyEvent> + +QT_BEGIN_NAMESPACE +class Ui_ShortcutSettings; +QT_END_NAMESPACE + +namespace Core { + +class ICommand; + +namespace Internal { + +class ActionManager; +class Command; +class MainWindow; + +struct ShortcutItem { + ICommand *m_cmd; + QKeySequence m_key; + QTreeWidgetItem *m_item; +}; + + +class ShortcutSettings : public Core::IOptionsPage +{ + Q_OBJECT + +public: + ShortcutSettings(QObject *parent = 0); + ~ShortcutSettings(); + + // IOptionsPage + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + +protected: + bool eventFilter(QObject *o, QEvent *e); + +private slots: + void commandChanged(QTreeWidgetItem *current); + void filterChanged(const QString &f); + void keyChanged(); + void resetKeySequence(); + void removeKeySequence(); + void importAction(); + void exportAction(); + void defaultAction(); + +private: + void setKeySequence(const QKeySequence &key); + bool filter(const QString &f, const QTreeWidgetItem *item); + void initialize(); + + void handleKeyEvent(QKeyEvent *e); + int translateModifiers(Qt::KeyboardModifiers state, const QString &text); + + QList<ShortcutItem *> m_scitems; + ActionManager *m_am; + int m_key[4], m_keyNum; + Ui_ShortcutSettings *m_page; +}; + +} // namespace Internal +} // namespace Core + +#endif // SHORTCUTSETTINGS_H diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.ui b/src/plugins/coreplugin/dialogs/shortcutsettings.ui new file mode 100644 index 00000000000..4faaec4b32e --- /dev/null +++ b/src/plugins/coreplugin/dialogs/shortcutsettings.ui @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ShortcutSettings</class> + <widget class="QWidget" name="ShortcutSettings"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>568</width> + <height>451</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Keyboard Shortcuts</string> + </property> + <layout class="QVBoxLayout"> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QLabel" name="filterLabel"> + <property name="text"> + <string>Filter:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="filterEdit"/> + </item> + </layout> + </item> + <item> + <widget class="QTreeWidget" name="commandList"> + <property name="uniformRowHeights"> + <bool>true</bool> + </property> + <property name="sortingEnabled"> + <bool>true</bool> + </property> + <property name="columnCount"> + <number>3</number> + </property> + <column> + <property name="text"> + <string>Command</string> + </property> + </column> + <column> + <property name="text"> + <string>Label</string> + </property> + </column> + <column> + <property name="text"> + <string>Shortcut</string> + </property> + </column> + </widget> + </item> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QPushButton" name="defaultButton"> + <property name="text"> + <string>Defaults</string> + </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> + <item> + <widget class="QPushButton" name="importButton"> + <property name="text"> + <string>Import...</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="exportButton"> + <property name="text"> + <string>Export...</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="seqGrp"> + <property name="title"> + <string>Key Sequence</string> + </property> + <layout class="QVBoxLayout"> + <item> + <layout class="QHBoxLayout"> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Shortcut:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="shortcutEdit"/> + </item> + <item> + <widget class="QToolButton" name="resetButton"> + <property name="toolTip"> + <string>Reset</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../core.qrc"> + <normaloff>:/qworkbench/images/reset.png</normaloff>:/qworkbench/images/reset.png</iconset> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="removeButton"> + <property name="toolTip"> + <string>Remove</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../core.qrc"> + <normaloff>:/qworkbench/images/clear.png</normaloff>:/qworkbench/images/clear.png</iconset> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="infoLabel"> + <property name="textFormat"> + <enum>Qt::RichText</enum> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources> + <include location="../core.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/plugins/coreplugin/editmode.cpp b/src/plugins/coreplugin/editmode.cpp new file mode 100644 index 00000000000..088cd69f37d --- /dev/null +++ b/src/plugins/coreplugin/editmode.cpp @@ -0,0 +1,138 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "editmode.h" +#include "editormanager.h" +#include "coreconstants.h" +#include "coreimpl.h" +#include "modemanager.h" +#include "uniqueidmanager.h" +#include "minisplitter.h" +#include "findplaceholder.h" +#include "outputpane.h" +#include "navigationwidget.h" +#include "rightpane.h" + +#include <QtCore/QLatin1String> +#include <QtGui/QHBoxLayout> +#include <QtGui/QWidget> +#include <QtGui/QSplitter> + +using namespace Core; +using namespace Core::Internal; + +EditMode::EditMode(EditorManager *editorManager): + m_editorManager(editorManager), + m_splitter(new MiniSplitter), + m_rightSplitWidgetLayout(new QVBoxLayout) +{ + m_rightSplitWidgetLayout->setSpacing(0); + m_rightSplitWidgetLayout->setMargin(0); + QWidget *rightSplitWidget = new QWidget; + rightSplitWidget->setLayout(m_rightSplitWidgetLayout); + m_rightSplitWidgetLayout->insertWidget(0, new Core::EditorManagerPlaceHolder(this)); + m_rightSplitWidgetLayout->addWidget(new Core::FindToolBarPlaceHolder(this)); + + MiniSplitter *rightPaneSplitter = new MiniSplitter; + rightPaneSplitter->insertWidget(0, rightSplitWidget); + rightPaneSplitter->insertWidget(1, new RightPanePlaceHolder(this)); + rightPaneSplitter->setStretchFactor(0, 1); + rightPaneSplitter->setStretchFactor(1, 0); + + MiniSplitter *splitter = new MiniSplitter; + splitter->setOrientation(Qt::Vertical); + splitter->insertWidget(0, rightPaneSplitter); + splitter->insertWidget(1, new Core::OutputPanePlaceHolder(this)); + splitter->setStretchFactor(0, 3); + splitter->setStretchFactor(1, 0); + + m_splitter->insertWidget(0, new NavigationWidgetPlaceHolder(this)); + m_splitter->insertWidget(1, splitter); + m_splitter->setStretchFactor(0, 0); + m_splitter->setStretchFactor(1, 1); + + ModeManager *modeManager = ModeManager::instance(); + connect(modeManager, SIGNAL(currentModeChanged(Core::IMode*)), + this, SLOT(grabEditorManager(Core::IMode*))); + m_splitter->setFocusProxy(m_editorManager); +} + +EditMode::~EditMode() +{ + // Make sure the editor manager does not get deleted + m_editorManager->setParent(0); + delete m_splitter; +} + +QString EditMode::name() const +{ + return QLatin1String("Edit"); +} + +QIcon EditMode::icon() const +{ + return QIcon(QLatin1String(":/fancyactionbar/images/mode_Edit.png")); +} + +int EditMode::priority() const +{ + return Constants::P_MODE_EDIT; +} + +QWidget* EditMode::widget() +{ + return m_splitter; +} + +const char* EditMode::uniqueModeName() const +{ + return Constants::MODE_EDIT; +} + +QList<int> EditMode::context() const +{ + static QList<int> contexts = QList<int>() << + CoreImpl::instance()->uniqueIDManager()->uniqueIdentifier(Constants::C_EDIT_MODE) << + CoreImpl::instance()->uniqueIDManager()->uniqueIdentifier(Constants::C_EDITORMANAGER) << + CoreImpl::instance()->uniqueIDManager()->uniqueIdentifier(Constants::C_NAVIGATION_PANE); + return contexts; +} + +void EditMode::grabEditorManager(Core::IMode *mode) +{ + if (mode != this) + return; + + if (m_editorManager->currentEditor()) + m_editorManager->currentEditor()->widget()->setFocus(); +} diff --git a/src/plugins/coreplugin/editmode.h b/src/plugins/coreplugin/editmode.h new file mode 100644 index 00000000000..234c4aab9e9 --- /dev/null +++ b/src/plugins/coreplugin/editmode.h @@ -0,0 +1,80 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef EDITMODE_H +#define EDITMODE_H + +#include <coreplugin/imode.h> + +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE +class QSplitter; +class QWidget; +class QVBoxLayout; +QT_END_NAMESPACE + +namespace Core { + +class EditorManager; + +namespace Internal { + +class EditMode : public Core::IMode +{ + Q_OBJECT + +public: + EditMode(EditorManager *editorManager); + ~EditMode(); + + // IMode + QString name() const; + QIcon icon() const; + int priority() const; + QWidget* widget(); + const char* uniqueModeName() const; + QList<int> context() const; + +private slots: + void grabEditorManager(Core::IMode *mode); + +private: + EditorManager *m_editorManager; + QSplitter *m_splitter; + QVBoxLayout *m_rightSplitWidgetLayout; +}; + +} // namespace Internal +} // namespace Core + +#endif // EDITMODE_H diff --git a/src/plugins/coreplugin/editormanager/editorgroup.cpp b/src/plugins/coreplugin/editormanager/editorgroup.cpp new file mode 100644 index 00000000000..53b61c74fde --- /dev/null +++ b/src/plugins/coreplugin/editormanager/editorgroup.cpp @@ -0,0 +1,337 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "editorgroup.h" +#include "editormanager.h" + +#include <coreplugin/coreconstants.h> + +#include <QtCore/QDir> +#include <QtGui/QPainter> +#include <QtGui/QStyle> +#include <QtGui/QStyleOption> +#include <QtCore/QtDebug> +#ifdef Q_WS_MAC +#include <QtGui/QMacStyle> +#endif + +Q_DECLARE_METATYPE(Core::IEditor*) + +using namespace Core; +using namespace Core::Internal; + +namespace Core { +namespace Internal { +class EditorList; +} +} + +QDataStream &operator<<(QDataStream &out, const Core::Internal::EditorList &list); +QDataStream &operator>>(QDataStream &in, Core::Internal::EditorList &list); + +namespace Core { +namespace Internal { + +class EditorList +{ +public: + quint32 currentEditorIndex; + void append(Core::IEditor *editor); + QString fileNameAt(int index); + QString editorKindAt(int index); + int count(); + +private: + QList<QPair<QString,QString> > editorinfo; + + friend QDataStream &::operator<<(QDataStream &out, const EditorList &list); + friend QDataStream &::operator>>(QDataStream &in, EditorList &list); +}; + +} // namespace Internal +} // namespace Core + +//================EditorModel==================== +int EditorModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 1; +} + +int EditorModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return m_editors.count(); +} + +QModelIndex EditorModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0 || row < 0 || row >= m_editors.count()) + return QModelIndex(); + return createIndex(row, column); +} + +QVariant EditorModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + IEditor *editor = m_editors.at(index.row()); + Q_ASSERT(editor); + switch (role) { + case Qt::DisplayRole: + return editor->file()->isModified() + ?editor->displayName()+QLatin1String("*") + :editor->displayName(); + case Qt::DecorationRole: + return editor->file()->isReadOnly() + ?QIcon(QLatin1String(":/qworkbench/images/locked.png")) + :QIcon(); + case Qt::ToolTipRole: + return editor->file()->fileName().isEmpty() + ?editor->displayName() + :QDir::toNativeSeparators(editor->file()->fileName()); + case Qt::UserRole: + return qVariantFromValue(editor); + default: + return QVariant(); + } + return QVariant(); +} + +QModelIndex EditorModel::indexOf(IEditor *editor) const +{ + int idx = m_editors.indexOf(editor); + if (idx < 0) + return QModelIndex(); + return createIndex(idx, 0); +} + +//================EditorGroupContext=============== + +EditorGroupContext::EditorGroupContext(EditorGroup *editorGroup) + : IContext(editorGroup), + m_context(QList<int>() << Constants::C_GLOBAL_ID), + m_editorGroup(editorGroup) +{ +} +QList<int> EditorGroupContext::context() const +{ + return m_context; +} + +QWidget *EditorGroupContext::widget() +{ + return m_editorGroup; +} + +EditorGroup *EditorGroupContext::editorGroup() +{ + return m_editorGroup; +} + +//================EditorGroup================= + +EditorGroup::EditorGroup(QWidget *parent) + : QFrame(parent), + m_contextObject(new EditorGroupContext(this)) +{ + setFocusPolicy(Qt::StrongFocus); + + m_model = new EditorModel(this); +} + +QSize EditorGroup::minimumSizeHint() const +{ + return QSize(10, 10); +} + +void EditorGroup::focusInEvent(QFocusEvent *) +{ + update(); +} + +void EditorGroup::focusOutEvent(QFocusEvent *) +{ + update(); +} + +void EditorGroup::paintEvent(QPaintEvent *e) +{ + QFrame::paintEvent(e); + if (editorCount() == 0) { + QPainter painter(this); + + // Discreet indication where an editor would be + painter.setRenderHint(QPainter::Antialiasing, true); + painter.setPen(Qt::NoPen); + QColor shadeBrush(Qt::black); + shadeBrush.setAlpha(10); + painter.setBrush(shadeBrush); + const int r = 3; + painter.drawRoundedRect(rect().adjusted(r, r, -r, -r), r * 2, r * 2); + + if (hasFocus()) { +#ifdef Q_WS_MAC + // With QMacStyle, we have to draw our own focus rect, since I didn't find + // a way to draw the nice mac focus rect _inside_ this widget + if (qobject_cast<QMacStyle *>(style())) { + painter.setPen(Qt::DotLine); + painter.setBrush(Qt::NoBrush); + painter.setOpacity(0.75); + painter.drawRect(rect()); + } else { +#endif + QStyleOptionFocusRect option; + option.initFrom(this); + option.backgroundColor = palette().color(QPalette::Background); + + // Some styles require a certain state flag in order to draw the focus rect + option.state |= QStyle::State_KeyboardFocusChange; + + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter); +#ifdef Q_WS_MAC + } +#endif + } + } +} + +void EditorGroup::moveEditorsFromGroup(EditorGroup *group) +{ + foreach (IEditor *editor, group->editors()) { + group->removeEditor(editor); + addEditor(editor); + } +} + +void EditorGroup::moveEditorFromGroup(EditorGroup *group, IEditor *editor) +{ + group->removeEditor(editor); + addEditor(editor); +} + +QByteArray EditorGroup::saveState() const +{ + QByteArray bytes; + QDataStream stream(&bytes, QIODevice::WriteOnly); + EditorList editorinfo; + IEditor *curr = currentEditor(); + QList<IEditor *> editors = editorsInNaturalOrder(); + for (int j = 0; j < editors.count(); ++j) { + IEditor *editor = editors.at(j); + if (editor == curr) + editorinfo.currentEditorIndex = j; + editorinfo.append(editor); + } + stream << editorinfo; + return bytes; +} + +bool EditorGroup::restoreState(const QByteArray &state) +{ + QDataStream in(state); + EditorManager *em = EditorManager::instance(); + EditorList editors; + in >> editors; + IEditor *currentEditor = 0; + IEditor *editor; + int savedIndex = editors.currentEditorIndex; + for (int j = 0; j < editors.count(); ++j) { + editor = em->restoreEditor(editors.fileNameAt(j), editors.editorKindAt(j), this); + if (j == savedIndex) + currentEditor = editor; + } + if (currentEditor) + setCurrentEditor(currentEditor); + return true; +} + +void EditorGroup::addEditor(IEditor *editor) +{ + m_model->addEditor(editor); +} + +void EditorGroup::insertEditor(int i, IEditor *editor) +{ + m_model->insertEditor(i, editor); +} + +void EditorGroup::removeEditor(IEditor *editor) +{ + m_model->removeEditor(editor); +} + +void EditorGroup::showEditorInfoBar(const QString &, const QString &, const QString &, QObject *, const char *) +{ +} + +void EditorGroup::hideEditorInfoBar(const QString &) +{ +} + +void EditorList::append(IEditor *editor) +{ + if (editor->file()->fileName().isEmpty()) + return; + editorinfo << qMakePair(editor->file()->fileName(), QString(editor->kind())); +} + +QDataStream &operator<<(QDataStream &out, const EditorList &list) +{ + //todo: versioning + out << list.currentEditorIndex << list.editorinfo; + return out; +} + +QDataStream &operator>>(QDataStream &in, EditorList &list) +{ + //todo: versioning + in >> list.currentEditorIndex; + in >> list.editorinfo; + return in; +} + +QString EditorList::fileNameAt(int index) +{ + return editorinfo.at(index).first; +} + +QString EditorList::editorKindAt(int index) +{ + return editorinfo.at(index).second; +} + +int EditorList::count() +{ + return editorinfo.count(); +} diff --git a/src/plugins/coreplugin/editormanager/editorgroup.h b/src/plugins/coreplugin/editormanager/editorgroup.h new file mode 100644 index 00000000000..eca333c4df7 --- /dev/null +++ b/src/plugins/coreplugin/editormanager/editorgroup.h @@ -0,0 +1,165 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef EDITORGROUP_H +#define EDITORGROUP_H + +#include <coreplugin/icontext.h> + +#include <QtCore/QEvent> +#include <QtCore/QAbstractListModel> + +#include <QtGui/QFrame> +#include <QtGui/QAbstractButton> +#include <QtGui/QPen> + +namespace Core { + +class IEditor; +class EditorGroup; + +namespace Internal { + +class EditorModel; + +// Also used by the EditorManager +class EditorGroupContext : public IContext +{ + Q_OBJECT + +public: + EditorGroupContext(EditorGroup *editorGroup); + EditorGroup *editorGroup(); + // IContext + QList<int> context() const; + QWidget *widget(); +private: + QList<int> m_context; + EditorGroup *m_editorGroup; +}; + +} // namespace Internal + +class CORE_EXPORT EditorGroup : public QFrame +{ + Q_OBJECT + +public: + EditorGroup(QWidget *parent); + virtual ~EditorGroup() {}; + + virtual IContext *contextObject() { return m_contextObject; } + virtual QWidget *widget() { return this; } + + virtual int editorCount() const = 0; + virtual void addEditor(IEditor *editor); + virtual void insertEditor(int i, IEditor *editor); + virtual void removeEditor(IEditor *editor); + virtual QList<IEditor*> editors() const = 0; + + virtual IEditor *currentEditor() const = 0; + virtual void setCurrentEditor(IEditor *editor) = 0; + + virtual void moveEditorsFromGroup(EditorGroup *group); + virtual void moveEditorFromGroup(EditorGroup *group, IEditor *editor); + + virtual QByteArray saveState() const; + virtual bool restoreState(const QByteArray &state); + + virtual void showEditorInfoBar(const QString &kind, + const QString &infoText, + const QString &buttonText, + QObject *object, const char *member); + + virtual void hideEditorInfoBar(const QString &kind); + + QSize minimumSizeHint() const; + void focusInEvent(QFocusEvent *e); + void focusOutEvent(QFocusEvent *e); + void paintEvent(QPaintEvent *e); + +signals: + void closeRequested(Core::IEditor *editor); + void editorRemoved(Core::IEditor *editor); + void editorAdded(Core::IEditor *editor); + +protected: + virtual QList<IEditor *> editorsInNaturalOrder() const { return editors(); } + Internal::EditorModel *model() const { return m_model; } + +private: + Internal::EditorGroupContext *m_contextObject; + Internal::EditorModel *m_model; +}; + +namespace Internal { + +// Also used by StackedEditorGroup +class EditorModel : public QAbstractItemModel +{ +public: + EditorModel(QObject *parent) : QAbstractItemModel(parent) {} + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QModelIndex parent(const QModelIndex &/*index*/) const { return QModelIndex(); } + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + + void addEditor(IEditor *editor) { insertEditor(rowCount(), editor); } + void insertEditor(int index, IEditor *editor) { + beginInsertRows(QModelIndex(), index, index); + m_editors.insert(index, editor); + endInsertRows(); + } + void removeEditor(IEditor *editor) { + int index = m_editors.indexOf(editor); + beginRemoveRows(QModelIndex(), index, index); + m_editors.removeAt(index); + endRemoveRows(); + } + + void emitDataChanged(IEditor *editor) { + int idx = m_editors.indexOf(editor); + QModelIndex mindex = index(idx, 0); + emit dataChanged(mindex, mindex); + } + + QList<IEditor *> editors() const { return m_editors; } + QModelIndex indexOf(IEditor *editor) const; +private: + QList<IEditor *> m_editors; +}; + +} // namespace Internal +} // namespace Core + +#endif // EDITORGROUP_H diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp new file mode 100644 index 00000000000..7b2e0b23627 --- /dev/null +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -0,0 +1,1576 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "editormanager.h" +#include "editorsplitter.h" +#include "openeditorswindow.h" +#include "openwithdialog.h" +#include "filemanager.h" +#include "tabpositionindicator.h" +#include "saveitemsdialog.h" +#include "vcsmanager.h" +#include "iversioncontrol.h" +#include "openeditorsview.h" +#include "editorgroup.h" +#include "mimedatabase.h" + +#include <coreplugin/coreimpl.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/ieditorfactory.h> +#include <coreplugin/baseview.h> +#include <coreplugin/imode.h> + +#include <QtCore/QFileInfo> +#include <QtCore/QSettings> +#include <QtCore/QMap> +#include <QtCore/QSet> +#include <QtCore/QProcess> +#include <QtCore/QDebug> + +#include <QtGui/QAction> +#include <QtGui/QLayout> +#include <QtGui/QApplication> +#include <QtGui/QSplitter> +#include <QtGui/QFileDialog> +#include <QtGui/QMenu> +#include <QtGui/QMessageBox> +#include <QtGui/QPushButton> + +using namespace Core; +using namespace Core::Internal; + +enum { debugEditorManager=0 }; + +//===================EditorManager===================== + +EditorManagerPlaceHolder *EditorManagerPlaceHolder::m_current = 0; + +EditorManagerPlaceHolder::EditorManagerPlaceHolder(Core::IMode *mode, QWidget *parent) + : QWidget(parent), m_mode(mode) +{ + setLayout(new QVBoxLayout); + layout()->setMargin(0); + connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode *)), + this, SLOT(currentModeChanged(Core::IMode *))); +} + +EditorManagerPlaceHolder::~EditorManagerPlaceHolder() +{ + if (m_current == this) { + EditorManager::instance()->setParent(0); + EditorManager::instance()->hide(); + } +} + +void EditorManagerPlaceHolder::currentModeChanged(Core::IMode *mode) +{ + if (m_current == this) { + m_current = 0; + EditorManager::instance()->setParent(0); + EditorManager::instance()->hide(); + } + if (m_mode == mode) { + m_current = this; + layout()->addWidget(EditorManager::instance()); + EditorManager::instance()->show(); + } +} + +EditorManagerPlaceHolder* EditorManagerPlaceHolder::current() +{ + return m_current; +} + +// ---------------- EditorManager + +struct Core::EditorManagerPrivate { + struct EditLocation { + QPointer<IEditor> editor; + QString fileName; + QString kind; + QVariant state; + }; + explicit EditorManagerPrivate(ICore *core, QWidget *parent); + ~EditorManagerPrivate(); + Internal::EditorSplitter *m_splitter; + ICore *m_core; + + bool m_suppressEditorChanges; + + // actions + QAction *m_revertToSavedAction; + QAction *m_saveAction; + QAction *m_saveAsAction; + QAction *m_closeCurrentEditorAction; + QAction *m_closeAllEditorsAction; + QAction *m_gotoNextDocHistoryAction; + QAction *m_gotoPreviousDocHistoryAction; + QAction *m_duplicateAction; + QAction *m_goBackAction; + QAction *m_goForwardAction; + QAction *m_openInExternalEditorAction; + + QList<IEditor *> m_editorHistory; + QList<EditLocation *> m_navigationHistory; + int currentNavigationHistoryPosition; + Internal::OpenEditorsWindow *m_windowPopup; + Core::BaseView *m_openEditorsView; + Internal::EditorClosingCoreListener *m_coreListener; + + typedef QMap<IEditor *, QList<IEditor *> *> DuplicateMap; + DuplicateMap m_duplicates; + + QMap<QString, QVariant> m_editorStates; + Internal::OpenEditorsViewFactory *m_openEditorsFactory; + + QString fileFilters; + QString selectedFilter; + + QString m_externalEditor; +}; + +EditorManagerPrivate::EditorManagerPrivate(ICore *core, QWidget *parent) : + m_splitter(0), + m_core(core), + m_suppressEditorChanges(false), + m_revertToSavedAction(new QAction(EditorManager::tr("Revert to Saved"), parent)), + m_saveAction(new QAction(parent)), + m_saveAsAction(new QAction(parent)), + m_closeCurrentEditorAction(new QAction(EditorManager::tr("Close"), parent)), + m_closeAllEditorsAction(new QAction(EditorManager::tr("Close All"), parent)), + m_gotoNextDocHistoryAction(new QAction(EditorManager::tr("Next Document in History"), parent)), + m_gotoPreviousDocHistoryAction(new QAction(EditorManager::tr("Previous Document in History"), parent)), + m_duplicateAction(new QAction(EditorManager::tr("Duplicate Document"), parent)), + m_goBackAction(new QAction(EditorManager::tr("Go back"), parent)), + m_goForwardAction(new QAction(EditorManager::tr("Go forward"), parent)), + m_openInExternalEditorAction(new QAction(EditorManager::tr("Open in External Editor"), parent)), + currentNavigationHistoryPosition(-1), + m_windowPopup(0), + m_coreListener(0) +{ + +} + +EditorManagerPrivate::~EditorManagerPrivate() +{ + qDeleteAll(m_navigationHistory); + m_navigationHistory.clear(); +} + +EditorManager *EditorManager::m_instance = 0; + +EditorManager::EditorManager(ICore *core, QWidget *parent) : + QWidget(parent), + m_d(new EditorManagerPrivate(core, parent)) +{ + m_instance = this; + + connect(m_d->m_core, SIGNAL(contextAboutToChange(Core::IContext *)), + this, SLOT(updateCurrentEditorAndGroup(Core::IContext *))); + + const QList<int> gc = QList<int>() << Constants::C_GLOBAL_ID; + const QList<int> editManagerContext = + QList<int>() << m_d->m_core->uniqueIDManager()->uniqueIdentifier(Constants::C_EDITORMANAGER); + + ActionManagerInterface *am = m_d->m_core->actionManager(); + IActionContainer *mfile = am->actionContainer(Constants::M_FILE); + + //Revert to saved + ICommand *cmd = am->registerAction(m_d->m_revertToSavedAction, + Constants::REVERTTOSAVED, editManagerContext); + cmd->setAttribute(ICommand::CA_UpdateText); + cmd->setDefaultText(tr("Revert File to Saved")); + mfile->addAction(cmd, Constants::G_FILE_SAVE); + connect(m_d->m_revertToSavedAction, SIGNAL(triggered()), this, SLOT(revertToSaved())); + + //Save Action + am->registerAction(m_d->m_saveAction, Constants::SAVE, editManagerContext); + connect(m_d->m_saveAction, SIGNAL(triggered()), this, SLOT(saveFile())); + + //Save As Action + am->registerAction(m_d->m_saveAsAction, Constants::SAVEAS, editManagerContext); + connect(m_d->m_saveAsAction, SIGNAL(triggered()), this, SLOT(saveFileAs())); + + //Window Menu + IActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW); + + //Window menu separators + QAction *tmpaction = new QAction(this); + tmpaction->setSeparator(true); + cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Window.Sep.Split"), editManagerContext); + mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT); + + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Window.Sep.Close"), editManagerContext); + mwindow->addAction(cmd, Constants::G_WINDOW_CLOSE); + + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Window.Sep.Navigate"), editManagerContext); + mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE); + + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Window.Sep.Navigate.Groups"), editManagerContext); + mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE_GROUPS); + + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Window.Sep.Bottom"), editManagerContext); + mwindow->addAction(cmd, Constants::G_WINDOW_LIST); + + //Close Action + cmd = am->registerAction(m_d->m_closeCurrentEditorAction, Constants::CLOSE, editManagerContext); + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+W"))); + cmd->setAttribute(Core::ICommand::CA_UpdateText); + cmd->setDefaultText(m_d->m_closeCurrentEditorAction->text()); + mfile->addAction(cmd, Constants::G_FILE_CLOSE); + connect(m_d->m_closeCurrentEditorAction, SIGNAL(triggered()), this, SLOT(closeEditor())); + + //Close All Action + cmd = am->registerAction(m_d->m_closeAllEditorsAction, Constants::CLOSEALL, editManagerContext); + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+W"))); + mfile->addAction(cmd, Constants::G_FILE_CLOSE); + connect(m_d->m_closeAllEditorsAction, SIGNAL(triggered()), this, SLOT(closeAllEditors())); + + //Duplicate Action + cmd = am->registerAction(m_d->m_duplicateAction, Constants::DUPLICATEDOCUMENT, editManagerContext); + mwindow->addAction(cmd, Constants::G_WINDOW_CLOSE); + connect(m_d->m_duplicateAction, SIGNAL(triggered()), this, SLOT(duplicateEditor())); + + // Goto Previous In History Action + cmd = am->registerAction(m_d->m_gotoPreviousDocHistoryAction, Constants::GOTOPREVINHISTORY, editManagerContext); +#ifdef Q_WS_MAC + cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Tab"))); +#else + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Tab"))); +#endif + mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE); + connect(m_d->m_gotoPreviousDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoPreviousDocHistory())); + + // Goto Next In History Action + cmd = am->registerAction(m_d->m_gotoNextDocHistoryAction, Constants::GOTONEXTINHISTORY, editManagerContext); +#ifdef Q_WS_MAC + cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Shift+Tab"))); +#else + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+Tab"))); +#endif + mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE); + connect(m_d->m_gotoNextDocHistoryAction, SIGNAL(triggered()), this, SLOT(gotoNextDocHistory())); + + // Go back in navigation history + cmd = am->registerAction(m_d->m_goBackAction, Constants::GO_BACK, editManagerContext); +#ifdef Q_WS_MAC + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Left"))); +#else + cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Left"))); +#endif + mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE); + connect(m_d->m_goBackAction, SIGNAL(triggered()), this, SLOT(goBackInNavigationHistory())); + + // Go forward in navigation history + cmd = am->registerAction(m_d->m_goForwardAction, Constants::GO_FORWARD, editManagerContext); +#ifdef Q_WS_MAC + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Right"))); +#else + cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Right"))); +#endif + mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE); + connect(m_d->m_goForwardAction, SIGNAL(triggered()), this, SLOT(goForwardInNavigationHistory())); + + + IActionContainer *medit = am->actionContainer(Constants::M_EDIT); + IActionContainer *advancedMenu = am->createMenu(Constants::M_EDIT_ADVANCED); + medit->addMenu(advancedMenu, Constants::G_EDIT_FORMAT); + advancedMenu->menu()->setTitle(tr("&Advanced")); + cmd = am->registerAction(m_d->m_openInExternalEditorAction, Constants::OPEN_IN_EXTERNAL_EDITOR, editManagerContext); + cmd->setDefaultKeySequence(QKeySequence(tr("Alt+V,Alt+I"))); + advancedMenu->addAction(cmd); + connect(m_d->m_openInExternalEditorAction, SIGNAL(triggered()), this, SLOT(openInExternalEditor())); + + + // other setup + connect(this, SIGNAL(currentEditorChanged(Core::IEditor*)), + this, SLOT(updateActions())); + connect(this, SIGNAL(currentEditorChanged(Core::IEditor*)), + this, SLOT(updateEditorHistory())); + m_d->m_splitter = new EditorSplitter(m_d->m_core); + connect(m_d->m_splitter, SIGNAL(closeRequested(Core::IEditor *)), + this, SLOT(closeEditor(Core::IEditor *))); + connect(m_d->m_splitter, SIGNAL(editorGroupsChanged()), + this, SIGNAL(editorGroupsChanged())); + + QHBoxLayout *l = new QHBoxLayout(this); + l->setSpacing(0); + l->setMargin(0); + l->addWidget(m_d->m_splitter); + + updateActions(); + + m_d->m_windowPopup = new OpenEditorsWindow(this); + +#ifdef Q_OS_MAC + m_d->m_externalEditor = m_d->m_core->resourcePath() + +QLatin1String("/runInTerminal.command vi %f +%l"); +#elif defined(Q_OS_UNIX) + m_d->m_externalEditor = QLatin1String("xterm -geom %Wx%H+%x+%y -e vi %f +%l +\"normal %c|\""); +#endif +} + +EditorManager::~EditorManager() +{ + if (m_d->m_core) { + if (m_d->m_coreListener) { + m_d->m_core->pluginManager()->removeObject(m_d->m_coreListener); + delete m_d->m_coreListener; + } + m_d->m_core->pluginManager()->removeObject(m_d->m_openEditorsFactory); + delete m_d->m_openEditorsFactory; + } + delete m_d; +} + +void EditorManager::init() +{ + QList<int> context; + context << m_d->m_core->uniqueIDManager()->uniqueIdentifier("QtCreator.OpenDocumentsView"); + + m_d->m_coreListener = new EditorClosingCoreListener(this); + m_d->m_core->pluginManager()->addObject(m_d->m_coreListener); + + m_d->m_openEditorsFactory = new OpenEditorsViewFactory(); + m_d->m_core->pluginManager()->addObject(m_d->m_openEditorsFactory); +} + +QSize EditorManager::minimumSizeHint() const +{ + return QSize(400, 300); +} + +EditorSplitter *EditorManager::editorSplitter() const +{ + return m_d->m_splitter; +} + +void EditorManager::updateEditorHistory() +{ + IEditor *editor = currentEditor(); + if (!editor) + return; + m_d->m_editorHistory.removeAll(editor); + m_d->m_editorHistory.prepend(editor); +} + +bool EditorManager::registerEditor(IEditor *editor) +{ + if (editor) { + if (!hasDuplicate(editor)) { + m_d->m_core->fileManager()->addFile(editor->file()); + m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName()); + } + m_d->m_editorHistory.removeAll(editor); + m_d->m_editorHistory.prepend(editor); + return true; + } + return false; +} + +bool EditorManager::unregisterEditor(IEditor *editor) +{ + if (editor) { + if (!hasDuplicate(editor)) + m_d->m_core->fileManager()->removeFile(editor->file()); + m_d->m_editorHistory.removeAll(editor); + return true; + } + return false; +} + +void EditorManager::updateCurrentEditorAndGroup(IContext *context) +{ + if (debugEditorManager) + qDebug() << Q_FUNC_INFO; + EditorGroupContext *groupContext = context ? qobject_cast<EditorGroupContext*>(context) : 0; + IEditor *editor = context ? qobject_cast<IEditor*>(context) : 0; + if (groupContext) { + m_d->m_splitter->setCurrentGroup(groupContext->editorGroup()); + setCurrentEditor(0); + updateActions(); + } else if (editor) { + setCurrentEditor(editor); + } else { + updateActions(); + } + if (debugEditorManager) + qDebug() << "leaving method" << Q_FUNC_INFO; +} + +void EditorManager::setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory) +{ + if (debugEditorManager) + qDebug() << Q_FUNC_INFO << currentEditor() << "-->" << editor + << (m_d->m_suppressEditorChanges?"suppressed":"") + << "ignore history?" << ignoreNavigationHistory; + if (m_d->m_suppressEditorChanges) + return; + if (editor) { + bool addToHistory = (!ignoreNavigationHistory && editor != currentEditor()); + if (debugEditorManager) + qDebug() << Q_FUNC_INFO << (addToHistory ? "adding to history" : "not adding to history"); + if (addToHistory) + addCurrentPositionToNavigationHistory(true); + EditorGroup *group = groupOfEditor(editor); + if (!group) + return; + m_d->m_suppressEditorChanges = true; + m_d->m_splitter->setCurrentGroup(group); + group->setCurrentEditor(editor); + m_d->m_suppressEditorChanges = false; + if (addToHistory) + addCurrentPositionToNavigationHistory(); + } + editorChanged(editor); +} + +void EditorManager::editorChanged(IEditor *toEditor) +{ + emit currentEditorChanged(toEditor); +} + +EditorGroup *EditorManager::groupOfEditor(IEditor *editor) const +{ + foreach (EditorGroup *group, m_d->m_splitter->groups()) { + if (group->editors().contains(editor)) + return group; + } + return 0; +} + +QList<IEditor *> EditorManager::editorsForFileName(const QString &filename) const +{ + QList<IEditor *> found; + QString fixedname = FileManager::fixFileName(filename); + foreach (IEditor *editor, openedEditors()) { + if (fixedname == FileManager::fixFileName(editor->file()->fileName())) + found << editor; + } + return found; +} + +IEditor *EditorManager::currentEditor() const +{ + return m_d->m_splitter->currentGroup()->currentEditor(); +} + +EditorGroup *EditorManager::currentEditorGroup() const +{ + return m_d->m_splitter->currentGroup(); +} + +void EditorManager::duplicateEditor() +{ + IEditor *curEditor = currentEditor(); + if (!curEditor || !curEditor->duplicateSupported()) + return; + IEditor *editor = curEditor->duplicate(this); + registerDuplicate(curEditor, editor); + insertEditor(editor); +} + +// SLOT connected to action +// since this is potentially called in the event handler of the editor +// we simply postpone it with a single shot timer +void EditorManager::closeEditor() +{ + static bool postpone = true; + if (postpone) { + QTimer::singleShot(0, this, SLOT(closeEditor())); + postpone = false; + } else { + closeEditor(currentEditor()); + postpone = true; + } + +} + +void EditorManager::closeEditor(IEditor *editor) +{ + if (!editor) + editor = currentEditor(); + if (!editor) + return; + closeEditors(QList<IEditor *>() << editor); +} + +QList<IEditor*> + EditorManager::editorsForFiles(QList<IFile*> files) const +{ + const QList<IEditor *> editors = openedEditors(); + QSet<IEditor *> found; + foreach (IFile *file, files) { + foreach (IEditor *editor, editors) { + if (editor->file() == file && !found.contains(editor)) { + if (hasDuplicate(editor)) { + foreach (IEditor *duplicate, duplicates(editor)) { + found << duplicate; + } + } else { + found << editor; + } + } + } + } + return found.toList(); +} + +QList<IFile *> + EditorManager::filesForEditors(QList<IEditor *> editors) const +{ + QSet<IEditor *> handledEditors; + QList<IFile *> files; + foreach (IEditor *editor, editors) { + if (!handledEditors.contains(editor)) { + files << editor->file(); + if (hasDuplicate(editor)) { + foreach (IEditor *duplicate, duplicates(editor)) { + handledEditors << duplicate; + } + } else { + handledEditors.insert(editor); + } + } + } + return files; +} + +bool EditorManager::closeAllEditors(bool askAboutModifiedEditors) +{ + return closeEditors(openedEditors(), askAboutModifiedEditors); +} + +bool EditorManager::closeEditors(const QList<IEditor*> editorsToClose, bool askAboutModifiedEditors) +{ + if (editorsToClose.isEmpty()) + return true; + bool closingFailed = false; + QList<IEditor*> acceptedEditors; + //ask all core listeners to check whether the editor can be closed + const QList<ICoreListener *> listeners = + m_d->m_core->pluginManager()->getObjects<ICoreListener>(); + foreach (IEditor *editor, editorsToClose) { + bool editorAccepted = true; + foreach (ICoreListener *listener, listeners) { + if (!listener->editorAboutToClose(editor)) { + editorAccepted = false; + closingFailed = false; + break; + } + } + if (editorAccepted) + acceptedEditors.append(editor); + } + if (acceptedEditors.isEmpty()) + return false; + //ask whether to save modified files + if (askAboutModifiedEditors) { + bool cancelled = false; + QList<IFile*> list = CoreImpl::instance()->fileManager()-> + saveModifiedFiles(filesForEditors(acceptedEditors), &cancelled); + if (cancelled) + return false; + if (!list.isEmpty()) { + QSet<IEditor*> skipSet = editorsForFiles(list).toSet(); + acceptedEditors = acceptedEditors.toSet().subtract(skipSet).toList(); + closingFailed = false; + } + } + if (acceptedEditors.isEmpty()) + return false; + bool currentEditorRemoved = false; + IEditor *current = currentEditor(); + if (current) + addCurrentPositionToNavigationHistory(true); + // remove current editor last, for optimization + if (acceptedEditors.contains(current)) { + currentEditorRemoved = true; + acceptedEditors.removeAll(current); + acceptedEditors.append(current); + } + // remove the editors + foreach (IEditor *editor, acceptedEditors) { + emit editorAboutToClose(editor); + if (!editor->file()->fileName().isEmpty()) { + QByteArray state = editor->saveState(); + if (!state.isEmpty()) + m_d->m_editorStates.insert(editor->file()->fileName(), QVariant(state)); + } + unregisterEditor(editor); + if (hasDuplicate(editor)) + unregisterDuplicate(editor); + m_d->m_core->removeContextObject(editor); + EditorGroup *group = groupOfEditor(editor); + const bool suppress = m_d->m_suppressEditorChanges; + m_d->m_suppressEditorChanges = true; + if (group) + group->removeEditor(editor); + m_d->m_suppressEditorChanges = suppress; + } + emit editorsClosed(acceptedEditors); + foreach (IEditor *editor, acceptedEditors) { + delete editor; + } + if (currentEditorRemoved) { + if (m_d->m_editorHistory.count() > 0) { + setCurrentEditor(m_d->m_editorHistory.first(), true); + } else { + editorChanged(currentEditor()); + } + } + if (currentEditor()) + addCurrentPositionToNavigationHistory(); + updateActions(); + + return !closingFailed; +} + + +/* Find editors for a mimetype, best matching at the front + * of the list. Recurse over the parent classes of the mimetype to + * find them. */ +static void mimeTypeFactoryRecursion(const MimeDatabase *db, + const MimeType &mimeType, + const QList<IEditorFactory*> &allFactories, + bool firstMatchOnly, + QList<IEditorFactory*> *list) +{ + typedef QList<IEditorFactory*> EditorFactoryList; + // Loop factories to find type + const QString type = mimeType.type(); + const EditorFactoryList::const_iterator fcend = allFactories.constEnd(); + for (EditorFactoryList::const_iterator fit = allFactories.constBegin(); fit != fcend; ++fit) { + // Exclude duplicates when recursing over xml or C++ -> C -> text. + IEditorFactory *factory = *fit; + if (!list->contains(factory) && factory->mimeTypes().contains(type)) { + list->push_back(*fit); + if (firstMatchOnly) + return; + break; + } + } + // Any parent classes? -> recurse + QStringList parentTypes = mimeType.subClassesOf(); + if (parentTypes.empty()) + return; + const QStringList::const_iterator pcend = parentTypes .constEnd(); + for (QStringList::const_iterator pit = parentTypes .constBegin(); pit != pcend; ++pit) { + if (const MimeType parent = db->findByType(*pit)) + mimeTypeFactoryRecursion(db, parent, allFactories, firstMatchOnly, list); + } +} + +EditorManager::EditorFactoryList + EditorManager::editorFactories(const MimeType &mimeType, bool bestMatchOnly) const +{ + EditorFactoryList rc; + const EditorFactoryList allFactories = m_d->m_core->pluginManager()->getObjects<IEditorFactory>(); + mimeTypeFactoryRecursion(m_d->m_core->mimeDatabase(), mimeType, allFactories, bestMatchOnly, &rc); + if (debugEditorManager) + qDebug() << Q_FUNC_INFO << mimeType.type() << " returns " << rc; + return rc; +} + +IEditor *EditorManager::createEditor(const QString &editorKind, + const QString &fileName) +{ + typedef QList<IEditorFactory*> FactoryList; + if (debugEditorManager) + qDebug() << Q_FUNC_INFO << editorKind << fileName; + + + EditorFactoryList factories; + if (editorKind.isEmpty()) { + // Find by mime type + const MimeType mimeType = m_d->m_core->mimeDatabase()->findByFile(QFileInfo(fileName)); + if (!mimeType) { + qWarning("%s unable to determine mime type of %s/%s.", + Q_FUNC_INFO, fileName.toUtf8().constData(), editorKind.toUtf8().constData()); + return 0; + } + factories = editorFactories(mimeType, true); + } else { + // Find by editor kind + const EditorFactoryList allFactories = m_d->m_core->pluginManager()->getObjects<IEditorFactory>(); + const EditorFactoryList::const_iterator acend = allFactories.constEnd(); + for (EditorFactoryList::const_iterator ait = allFactories.constBegin(); ait != acend; ++ait) { + if (editorKind == (*ait)->kind()) { + factories.push_back(*ait); + break; + } + } + } + if (factories.empty()) { + qWarning("%s: unable to find an editor factory for the file '%s', editor kind '%s'.", + Q_FUNC_INFO, fileName.toUtf8().constData(), editorKind.toUtf8().constData()); + return 0; + } + + IEditor *editor = factories.front()->createEditor(this); + if (editor) + connect(editor, SIGNAL(changed()), this, SLOT(updateActions())); + if (editor) + emit editorCreated(editor, fileName); + return editor; +} + +void EditorManager::insertEditor(IEditor *editor, + bool ignoreNavigationHistory, + EditorGroup *group) +{ + if (!editor) + return; + m_d->m_core->addContextObject(editor); + registerEditor(editor); + if (group) + group->addEditor(editor); + else + m_d->m_splitter->currentGroup()->addEditor(editor); + + setCurrentEditor(editor, ignoreNavigationHistory); + emit editorOpened(editor); +} + +// Run the OpenWithDialog and return the editor kind +// selected by the user. +QString EditorManager::getOpenWithEditorKind(const QString &fileName) const +{ + QStringList editorKinds; + // Collect editors that can open the file + if (const MimeType mt = m_d->m_core->mimeDatabase()->findByFile(fileName)) { + const EditorFactoryList editors = editorFactories(mt, false); + const int size = editors.size(); + for (int i = 0; i < size; i++) { + editorKinds.push_back(editors.at(i)->kind()); + } + } + if (editorKinds.empty()) + return QString(); + + // Run dialog. + OpenWithDialog dialog(fileName, m_d->m_core->mainWindow()); + dialog.setEditors(editorKinds); + dialog.setCurrentEditor(0); + if (dialog.exec() != QDialog::Accepted) + return QString(); + return dialog.editor(); +} + +static QString formatFileFilters(const Core::ICore *core, QString *selectedFilter) +{ + QString rc; + // Compile list of filter strings. If we find a glob matching all files, + // put it last and set it as default selectedFilter. + QStringList filters = core->mimeDatabase()->filterStrings(); + filters.sort(); + selectedFilter->clear(); + if (filters.empty()) + return rc; + const QString filterSeparator = QLatin1String(";;"); + bool hasAllFilter = false; + const int size = filters.size(); + for (int i = 0; i < size; i++) { + const QString &filterString = filters.at(i); + if (filterString.isEmpty()) { // binary editor + hasAllFilter = true; + } else { + if (!rc.isEmpty()) + rc += filterSeparator; + rc += filterString; + } + } + if (hasAllFilter) { + // prepend all files filter + // prepending instead of appending to work around a but in Qt/Mac + QString allFilesFilter = QLatin1String("All Files (*)"); + if (!rc.isEmpty()) + allFilesFilter += filterSeparator; + rc.prepend(allFilesFilter); + *selectedFilter = allFilesFilter; + } else { + *selectedFilter = filters.front(); + } + return rc; +} + +IEditor *EditorManager::openEditor(const QString &fileName, const QString &editorKind, + bool ignoreNavigationHistory) +{ + if (debugEditorManager) + qDebug() << Q_FUNC_INFO << fileName << editorKind; + + if (fileName.isEmpty()) + return 0; + + const QList<IEditor *> editors = editorsForFileName(fileName); + if (!editors.isEmpty()) { + setCurrentEditor(editors.first(), ignoreNavigationHistory); + return editors.first(); + } + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + IEditor *editor = createEditor(editorKind, fileName); + if (!editor || !editor->open(fileName)) { + QApplication::restoreOverrideCursor(); + QMessageBox::critical(m_d->m_core->mainWindow(), tr("Opening File"), tr("Cannot open file %1!").arg(fileName)); + delete editor; + editor = 0; + return 0; + } + insertEditor(editor, ignoreNavigationHistory); + restoreEditorState(editor); + QApplication::restoreOverrideCursor(); + ensureEditorManagerVisible(); + return editor; +} + +QStringList EditorManager::getOpenFileNames() const +{ + QString dir; + if (m_d->fileFilters.isEmpty()) + m_d->fileFilters = formatFileFilters(m_d->m_core, &m_d->selectedFilter); + + if (IEditor *curEditor = currentEditor()) { + const QFileInfo fi(curEditor->file()->fileName()); + dir = fi.absolutePath(); + } + + return QFileDialog::getOpenFileNames(m_d->m_core->mainWindow(), tr("Open File"), + dir, m_d->fileFilters, &m_d->selectedFilter); +} + +void EditorManager::ensureEditorManagerVisible() +{ + if (!isVisible()) { + m_d->m_core->modeManager()->activateMode(Constants::MODE_EDIT); + } +} + +IEditor *EditorManager::newFile(const QString &editorKind, + QString *titlePattern, + const QString &contents) +{ + if (debugEditorManager) + qDebug() << Q_FUNC_INFO << editorKind << titlePattern << contents; + + if (editorKind.isEmpty()) + return 0; + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + IEditor *edt = createEditor(editorKind); + if (!edt) + return 0; + + if (!edt || !edt->createNew(contents)) { + QApplication::restoreOverrideCursor(); + delete edt; + edt = 0; + return 0; + } + + QString title = edt->displayName(); + + if (title.isEmpty() && titlePattern) { + const QChar dollar = QLatin1Char('$'); + const QChar dot = QLatin1Char('.'); + + QString base = *titlePattern; + if (base.isEmpty()) + base = QLatin1String("unnamed$"); + if (base.contains(dollar)) { + int i = 1; + QSet<QString> docnames; + foreach (IEditor *editor, openedEditors()) { + QString name = editor->file()->fileName(); + if (name.isEmpty()) { + name = editor->displayName(); + name.remove(QLatin1Char('*')); + } else { + name = QFileInfo(name).completeBaseName(); + } + docnames << name; + } + + do { + title = base; + title.replace(QString(dollar), QString::number(i++)); + } while (docnames.contains(title)); + } else { + title = *titlePattern; + } + } + *titlePattern = title; + edt->setDisplayName(title); + insertEditor(edt); + QApplication::restoreOverrideCursor(); + return edt; +} + +bool EditorManager::hasEditor(const QString &fileName) const +{ + return !editorsForFileName(fileName).isEmpty(); +} + +void EditorManager::restoreEditorState(IEditor *editor) +{ + Q_ASSERT(editor); + QString fileName = editor->file()->fileName(); + if (m_d->m_editorStates.contains(fileName)) { + editor->restoreState(m_d->m_editorStates.value(fileName).toByteArray()); + } +} + +bool EditorManager::saveEditor(IEditor *editor) +{ + return saveFile(editor); +} + +bool EditorManager::saveFile(IEditor *editor) +{ + if (!editor) + editor = currentEditor(); + if (!editor) + return false; + + IFile *file = editor->file(); + const QString &fileName = file->fileName(); + if (!fileName.isEmpty() && file->isReadOnly()) { + MakeWritableResult answer = + makeEditorWritable(editor); + if (answer == Failed) + return false; + if (answer == SavedAs) + return true; + } + + if (file->isReadOnly() || fileName.isEmpty()) { + return saveFileAs(editor); + } + + m_d->m_core->fileManager()->blockFileChange(file); + const bool success = file->save(fileName); + m_d->m_core->fileManager()->unblockFileChange(file); + if (success) + m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName()); + return success; +} + +namespace { + enum ReadOnlyAction { RO_Cancel, RO_OpenSCC, RO_MakeWriteable, RO_SaveAs }; +} + +static ReadOnlyAction promptReadOnly(const QString &fileName, bool hasSCC, QWidget *parent) +{ + QMessageBox msgBox(QMessageBox::Question, QObject::tr("File is Read Only"), + QObject::tr("The file %1 is read only.").arg(fileName), + QMessageBox::Cancel, parent); + + QPushButton *sccButton = 0; + if (hasSCC) + sccButton = msgBox.addButton(QObject::tr("Open with SCC"), QMessageBox::AcceptRole); + QPushButton *makeWritableButton = msgBox.addButton(QObject::tr("Make writable"), QMessageBox::AcceptRole); + QPushButton *saveAsButton = msgBox.addButton(QObject::tr("Save as ..."), QMessageBox::ActionRole); + if (hasSCC) + msgBox.setDefaultButton(sccButton); + else + msgBox.setDefaultButton(makeWritableButton); + msgBox.exec(); + QAbstractButton *clickedButton = msgBox.clickedButton(); + if (clickedButton == sccButton) + return RO_OpenSCC; + if (clickedButton == makeWritableButton) + return RO_MakeWriteable; + if (clickedButton == saveAsButton) + return RO_SaveAs; + return RO_Cancel; +} + + +MakeWritableResult +EditorManager::makeEditorWritable(IEditor *editor) +{ + QString directory = QFileInfo(editor->file()->fileName()).absolutePath(); + IVersionControl *versionControl = m_d->m_core->vcsManager()->findVersionControlForDirectory(directory); + IFile *file = editor->file(); + const QString &fileName = file->fileName(); + + switch (promptReadOnly(fileName, versionControl, m_d->m_core->mainWindow())) { + case RO_OpenSCC: + if (!versionControl->vcsOpen(fileName)) { + QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not open the file for edit with SCC.")); + return Failed; + } + return OpenedWithVersionControl; + case RO_MakeWriteable: { + const bool permsOk = QFile::setPermissions(fileName, QFile::permissions(fileName) | QFile::WriteUser); + if (!permsOk) { + QMessageBox::warning(m_d->m_core->mainWindow(), tr("Failed!"), tr("Could not set permissions to writable.")); + return Failed; + } + } + return MadeWritable; + case RO_SaveAs : + return saveFileAs(editor) ? SavedAs : Failed; + case RO_Cancel: + break; + } + return Failed; +} + +bool EditorManager::saveFileAs(IEditor *editor) +{ + if (!editor) + editor = currentEditor(); + if (!editor) + return false; + + QString absoluteFilePath = m_d->m_core->fileManager()->getSaveAsFileName(editor->file()); + if (absoluteFilePath.isEmpty()) + return false; + if (absoluteFilePath != editor->file()->fileName()) { + const QList<IEditor *> existList = editorsForFileName(absoluteFilePath); + if (!existList.isEmpty()) { + closeEditors(existList, false); + } + } + + m_d->m_core->fileManager()->blockFileChange(editor->file()); + const bool success = editor->file()->save(absoluteFilePath); + m_d->m_core->fileManager()->unblockFileChange(editor->file()); + + if(success) + m_d->m_core->fileManager()->addToRecentFiles(editor->file()->fileName()); + + updateActions(); + return success; +} + +void EditorManager::gotoNextDocHistory() +{ + OpenEditorsWindow *dialog = windowPopup(); + dialog->setMode(OpenEditorsWindow::HistoryMode); + dialog->selectNextEditor(); + showWindowPopup(); +} + +void EditorManager::gotoPreviousDocHistory() +{ + OpenEditorsWindow *dialog = windowPopup(); + dialog->setMode(OpenEditorsWindow::HistoryMode); + dialog->selectPreviousEditor(); + showWindowPopup(); +} + +void EditorManager::makeCurrentEditorWritable() +{ + if (IEditor* curEditor = currentEditor()) + makeEditorWritable(curEditor); +} + +void EditorManager::updateActions() +{ + QString fName; + IEditor *curEditor = currentEditor(); + int openedCount = openedEditors().count(); + if (curEditor) { + if (!curEditor->file()->fileName().isEmpty()) { + QFileInfo fi(curEditor->file()->fileName()); + fName = fi.fileName(); + } else { + fName = curEditor->displayName(); + } + + + if (curEditor->file()->isModified() && curEditor->file()->isReadOnly()) { + // we are about to change a read-only file, warn user + showEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable"), + tr("<b>Warning:</b> You are changing a read-only file."), + tr("Make writable"), this, SLOT(makeCurrentEditorWritable())); + } else { + hideEditorInfoBar(QLatin1String("Core.EditorManager.MakeWritable")); + } + } + + m_d->m_saveAction->setEnabled(curEditor != 0 && curEditor->file()->isModified()); + m_d->m_saveAsAction->setEnabled(curEditor != 0 && curEditor->file()->isSaveAsAllowed()); + m_d->m_revertToSavedAction->setEnabled(curEditor != 0 + && !curEditor->file()->fileName().isEmpty() && curEditor->file()->isModified()); + + m_d->m_saveAsAction->setText(tr("Save %1 As...").arg(fName)); + m_d->m_saveAction->setText(tr("&Save %1").arg(fName)); + m_d->m_revertToSavedAction->setText(tr("Revert %1 to Saved").arg(fName)); + + + m_d->m_closeCurrentEditorAction->setEnabled(m_d->m_splitter->currentGroup()->editorCount() > 0); + m_d->m_closeCurrentEditorAction->setText(tr("Close %1").arg(fName)); + m_d->m_closeAllEditorsAction->setEnabled(openedCount > 0); + + m_d->m_gotoNextDocHistoryAction->setEnabled(m_d->m_editorHistory.count() > 0); + m_d->m_gotoPreviousDocHistoryAction->setEnabled(m_d->m_editorHistory.count() > 0); + m_d->m_goBackAction->setEnabled(m_d->currentNavigationHistoryPosition > 0); + m_d->m_goForwardAction->setEnabled(m_d->currentNavigationHistoryPosition < m_d->m_navigationHistory.size()-1); + + m_d->m_duplicateAction->setEnabled(curEditor != 0 && curEditor->duplicateSupported()); + + m_d->m_openInExternalEditorAction->setEnabled(curEditor != 0); +} + +QList<IEditor*> EditorManager::openedEditors() const +{ + QList<IEditor*> editors; + const QList<EditorGroup*> groups = m_d->m_splitter->groups(); + foreach (EditorGroup *group, groups) { + editors += group->editors(); + } + return editors; +} + +QList<EditorGroup *> EditorManager::editorGroups() const +{ + return m_d->m_splitter->groups(); +} + +QList<IEditor*> EditorManager::editorHistory() const +{ + return m_d->m_editorHistory; +} + +void EditorManager::addCurrentPositionToNavigationHistory(bool compress) +{ + IEditor *editor = currentEditor(); + if (!editor) + return; + if (!editor->file()) + return; + QString fileName = editor->file()->fileName(); + QByteArray state = editor->saveState(); + // cut existing + int firstIndexToRemove; + if (compress && m_d->currentNavigationHistoryPosition >= 0) { + EditorManagerPrivate::EditLocation *previousLocation = + m_d->m_navigationHistory.at(m_d->currentNavigationHistoryPosition); + if ((previousLocation->editor && editor == previousLocation->editor) + || (!fileName.isEmpty() && previousLocation->fileName == fileName)) { + firstIndexToRemove = m_d->currentNavigationHistoryPosition; + } + } else { + firstIndexToRemove = m_d->currentNavigationHistoryPosition+1; + } + if (firstIndexToRemove >= 0) { + for (int i = m_d->m_navigationHistory.size()-1; i >= firstIndexToRemove; --i) { + delete m_d->m_navigationHistory.takeLast(); + } + } + while (m_d->m_navigationHistory.size() >= 30) { + delete m_d->m_navigationHistory.takeFirst(); + } + EditorManagerPrivate::EditLocation *location = new EditorManagerPrivate::EditLocation; + location->editor = editor; + location->fileName = editor->file()->fileName(); + location->kind = editor->kind(); + location->state = QVariant(state); + m_d->m_navigationHistory.append(location); + m_d->currentNavigationHistoryPosition = m_d->m_navigationHistory.size()-1; + updateActions(); +} + +void EditorManager::goBackInNavigationHistory() +{ + while (m_d->currentNavigationHistoryPosition > 0) { + --m_d->currentNavigationHistoryPosition; + EditorManagerPrivate::EditLocation *location = m_d->m_navigationHistory.at(m_d->currentNavigationHistoryPosition); + IEditor *editor; + if (location->editor) { + editor = location->editor; + setCurrentEditor(location->editor, true); + } else { + editor = openEditor(location->fileName, location->kind, true); + if (!editor) { + delete m_d->m_navigationHistory.takeAt(m_d->currentNavigationHistoryPosition); + continue; + } + } + editor->restoreState(location->state.toByteArray()); + updateActions(); + ensureEditorManagerVisible(); + return; + } +} + +void EditorManager::goForwardInNavigationHistory() +{ + if (m_d->currentNavigationHistoryPosition >= m_d->m_navigationHistory.size()-1) + return; + ++m_d->currentNavigationHistoryPosition; + EditorManagerPrivate::EditLocation *location = m_d->m_navigationHistory.at(m_d->currentNavigationHistoryPosition); + IEditor *editor; + if (location->editor) { + editor = location->editor; + setCurrentEditor(location->editor, true); + } else { + editor = openEditor(location->fileName, location->kind, true); + if (!editor) { + //TODO + qDebug() << Q_FUNC_INFO << "can't open file" << location->fileName; + return; + } + } + editor->restoreState(location->state.toByteArray()); + updateActions(); + ensureEditorManagerVisible(); +} + +OpenEditorsWindow *EditorManager::windowPopup() const +{ + return m_d->m_windowPopup; +} + +void EditorManager::showWindowPopup() const +{ + const QPoint p(mapToGlobal(QPoint(0, 0))); + m_d->m_windowPopup->move((width()-m_d->m_windowPopup->width())/2 + p.x(), + (height()-m_d->m_windowPopup->height())/2 + p.y()); + m_d->m_windowPopup->setVisible(true); +} + +void EditorManager::registerDuplicate(IEditor *original, + IEditor *duplicate) +{ + QList<IEditor *> *duplicateList; + if (m_d->m_duplicates.contains(original)) { + duplicateList = m_d->m_duplicates.value(original); + } else { + duplicateList = new QList<IEditor *>; + duplicateList->append(original); + m_d->m_duplicates.insert(original, duplicateList); + } + duplicateList->append(duplicate); + m_d->m_duplicates.insert(duplicate, duplicateList); +} + +void EditorManager::unregisterDuplicate(IEditor *editor) +{ + if (!m_d->m_duplicates.contains(editor)) + return; + QList<IEditor *> *duplicateList = m_d->m_duplicates.value(editor); + duplicateList->removeAll(editor); + m_d->m_duplicates.remove(editor); + if (duplicateList->count() < 2) { + foreach (IEditor *other, *duplicateList) { + m_d->m_duplicates.remove(other); + } + delete duplicateList; + } +} + +bool EditorManager::hasDuplicate(IEditor *editor) const +{ + return m_d->m_duplicates.contains(editor); +} + +QList<IEditor *> + EditorManager::duplicates(IEditor *editor) const +{ + if (m_d->m_duplicates.contains(editor)) + return *m_d->m_duplicates.value(editor); + return QList<IEditor *>() << editor; +} + +QByteArray EditorManager::saveState() const +{ + //todo: versioning + QByteArray bytes; + QDataStream stream(&bytes, QIODevice::WriteOnly); + stream << m_d->m_splitter->saveState(); + stream << saveOpenEditorList(); + stream << m_d->m_editorStates; + return bytes; +} + +bool EditorManager::restoreState(const QByteArray &state) +{ + closeAllEditors(true); + //todo: versioning + QDataStream stream(state); + QByteArray data; + QMap<QString, QVariant> editorstates; + stream >> data; + const bool success = m_d->m_splitter->restoreState(data); + if (!success) + return false; + + bool editorChangesSuppressed = m_d->m_suppressEditorChanges; + m_d->m_suppressEditorChanges = true; + + stream >> data; + restoreOpenEditorList(data); + stream >> editorstates; + QMapIterator<QString, QVariant> i(editorstates); + while (i.hasNext()) { + i.next(); + m_d->m_editorStates.insert(i.key(), i.value()); + } + + m_d->m_suppressEditorChanges = editorChangesSuppressed; + if (currentEditor()) + setCurrentEditor(currentEditor());// looks like a null-op but is not + + return true; +} + +void EditorManager::saveSettings(QSettings *settings) +{ + m_d->m_splitter->saveSettings(settings); + settings->setValue(QLatin1String("EditorManager/DocumentStates"), + m_d->m_editorStates); + settings->setValue(QLatin1String("EditorManager/ExternalEditor"), + m_d->m_externalEditor); +} + +void EditorManager::readSettings(QSettings *settings) +{ + m_d->m_splitter->readSettings(settings); + if (settings->contains(QLatin1String("EditorManager/DocumentStates"))) + m_d->m_editorStates = settings->value(QLatin1String("EditorManager/DocumentStates")) + .value<QMap<QString, QVariant> >(); + if (settings->contains(QLatin1String("EditorManager/ExternalEditor"))) + m_d->m_externalEditor = settings->value(QLatin1String("EditorManager/ExternalEditor")).toString(); +} + +QByteArray EditorManager::saveOpenEditorList() const +{ + QByteArray bytes; + QDataStream stream(&bytes, QIODevice::WriteOnly); + QMap<QString, QByteArray> outlist; + QMapIterator<QString, EditorGroup *> i(m_d->m_splitter->pathGroupMap()); + while (i.hasNext()) { + i.next(); + outlist.insert(i.key(), i.value()->saveState()); + } + stream << outlist; + return bytes; +} + +void EditorManager::restoreOpenEditorList(const QByteArray &state) +{ + QDataStream in(state); + QMap<QString, EditorGroup *> pathGroupMap = m_d->m_splitter->pathGroupMap(); + QMap<QString, QByteArray> inlist; + in >> inlist; + QMapIterator<QString, QByteArray> i(inlist); + while (i.hasNext()) { + i.next(); + EditorGroup *group = pathGroupMap.value(i.key()); + if (!group) + continue; + group->restoreState(i.value()); + } +} + +IEditor *EditorManager::restoreEditor(QString fileName, QString editorKind, EditorGroup *group) +{ + IEditor *editor; + QList<IEditor *> existing = + editorsForFileName(fileName); + if (!existing.isEmpty()) { + IEditor *first = existing.first(); + if (!first->duplicateSupported()) + return 0; + editor = first->duplicate(this); + registerDuplicate(first, editor); + } else { + editor = createEditor(editorKind, fileName); + if (!editor || !editor->open(fileName)) + return 0; + } + insertEditor(editor, false, group); + restoreEditorState(editor); + return editor; +} + +void EditorManager::revertToSaved() +{ + IEditor *currEditor = currentEditor(); + if (!currEditor) + return; + const QString fileName = currEditor->file()->fileName(); + if (fileName.isEmpty()) + return; + if (currEditor->file()->isModified()) { + QMessageBox msgBox(QMessageBox::Question, tr("Revert to Saved"), + tr("You will lose your current changes if you proceed reverting %1.").arg(fileName), + QMessageBox::Yes|QMessageBox::No, m_d->m_core->mainWindow()); + msgBox.button(QMessageBox::Yes)->setText(tr("Proceed")); + msgBox.button(QMessageBox::No)->setText(tr("Cancel")); + msgBox.setDefaultButton(QMessageBox::No); + msgBox.setEscapeButton(QMessageBox::No); + if (msgBox.exec() == QMessageBox::No) + return; + + } + IFile::ReloadBehavior temp = IFile::ReloadAll; + currEditor->file()->modified(&temp); +} + + +void EditorManager::showEditorInfoBar(const QString &kind, + const QString &infoText, + const QString &buttonText, + QObject *object, const char *member) +{ + m_d->m_splitter->currentGroup()->showEditorInfoBar(kind, infoText, buttonText, object, member); +} + + +void EditorManager::hideEditorInfoBar(const QString &kind) +{ + m_d->m_splitter->currentGroup()->hideEditorInfoBar(kind); +} + +QString EditorManager::externalEditorHelpText() const +{ + QString help = tr( + "<table border=1 cellspacing=0 cellpadding=3>" + "<tr><th>Variable</th><th>Expands to</th></tr>" + "<tr><td>%f</td><td>file name</td></tr>" + "<tr><td>%l</td><td>current line number</td></tr>" + "<tr><td>%c</td><td>current column number</td></tr>" + "<tr><td>%x</td><td>editor's x position on screen</td></tr>" + "<tr><td>%y</td><td>editor's y position on screen</td></tr>" + "<tr><td>%w</td><td>editor's width in pixels</td></tr>" + "<tr><td>%h</td><td>editor's height in pixels</td></tr>" + "<tr><td>%W</td><td>editor's width in characters</td></tr>" + "<tr><td>%H</td><td>editor's height in characters</td></tr>" + "<tr><td>%%</td><td>%</td></tr>" + "</table>"); + return help; +} + +void EditorManager::openInExternalEditor() +{ + IEditor *editor = currentEditor(); + if (!editor) + return; + if (editor->file()->isModified()) { + bool cancelled = false; + QList<IFile*> list = CoreImpl::instance()->fileManager()-> + saveModifiedFiles(QList<IFile*>() << editor->file(), &cancelled); + if (cancelled) + return; + } + + + QRect rect = editor->widget()->rect(); + QFont font = editor->widget()->font(); + QFontMetrics fm(font); + rect.moveTo(editor->widget()->mapToGlobal(QPoint(0,0))); + + QString pre = m_d->m_externalEditor; + QString cmd; + for (int i = 0; i < pre.size(); ++i) { + QChar c = pre.at(i); + if (c == QLatin1Char('%') && i < pre.size()-1) { + c = pre.at(++i); + QString s; + if (c == QLatin1Char('f')) + s = editor->file()->fileName(); + else if (c == QLatin1Char('l')) + s = QString::number(editor->currentLine()); + else if (c == QLatin1Char('c')) + s = QString::number(editor->currentColumn()); + else if (c == QLatin1Char('x')) + s = QString::number(rect.x()); + else if (c == QLatin1Char('y')) + s = QString::number(rect.y()); + else if (c == QLatin1Char('w')) + s = QString::number(rect.width()); + else if (c == QLatin1Char('h')) + s = QString::number(rect.height()); + else if (c == QLatin1Char('W')) + s = QString::number(rect.width() / fm.width(QLatin1Char('x'))); + else if (c == QLatin1Char('H')) + s = QString::number(rect.height() / fm.lineSpacing()); + else if (c == QLatin1Char('%')) + s = c; + else { + s = QLatin1Char('%'); + cmd += c; + } + cmd += s; + continue; + + } + cmd += c; + } + + QProcess::startDetached(cmd); +} + +void EditorManager::setExternalEditor(const QString &editor) +{ + m_d->m_externalEditor = editor; +} + +QString EditorManager::externalEditor() const +{ + return m_d->m_externalEditor; +} + +//===================EditorClosingCoreListener====================== + +EditorClosingCoreListener::EditorClosingCoreListener(EditorManager *em) + : m_em(em) +{ +} + +bool EditorClosingCoreListener::editorAboutToClose(IEditor *) +{ + return true; +} + +bool EditorClosingCoreListener::coreAboutToClose() +{ + // Do not ask for files to save. + // MainWindow::closeEvent has already done that. + return m_em->closeAllEditors(false); +} diff --git a/src/plugins/coreplugin/editormanager/editormanager.h b/src/plugins/coreplugin/editormanager/editormanager.h new file mode 100644 index 00000000000..750e35b0cb0 --- /dev/null +++ b/src/plugins/coreplugin/editormanager/editormanager.h @@ -0,0 +1,229 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef EDITORMANAGER_H +#define EDITORMANAGER_H + +#include "../core_global.h" + +#include <coreplugin/icorelistener.h> + +#include <QtGui/QWidget> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace Core { + +class EditorGroup; +class IContext; +class ICore; +class IEditor; +class IEditorFactory; +class MimeType; +class IFile; +class IMode; + +enum MakeWritableResult { + OpenedWithVersionControl, + MadeWritable, + SavedAs, + Failed +}; + +struct EditorManagerPrivate; +namespace Internal { +class OpenEditorsWindow; +class EditorSplitter; + +class EditorClosingCoreListener; +class OpenEditorsViewFactory; +} // namespace Internal + +class CORE_EXPORT EditorManagerPlaceHolder : public QWidget +{ + Q_OBJECT +public: + EditorManagerPlaceHolder(Core::IMode *mode, QWidget *parent = 0); + ~EditorManagerPlaceHolder(); + static EditorManagerPlaceHolder* current(); +private slots: + void currentModeChanged(Core::IMode *); +private: + Core::IMode *m_mode; + static EditorManagerPlaceHolder* m_current; +}; + +class CORE_EXPORT EditorManager : public QWidget +{ + Q_OBJECT + +public: + typedef QList<IEditorFactory*> EditorFactoryList; + + explicit EditorManager(ICore *core, QWidget *parent); + virtual ~EditorManager(); + void init(); + static EditorManager *instance() { return m_instance; } + + IEditor *openEditor(const QString &fileName, + const QString &editorKind = QString(), + bool ignoreNavigationHistory = false); + QStringList getOpenFileNames() const; + QString getOpenWithEditorKind(const QString &fileName) const; + + + void ensureEditorManagerVisible(); + IEditor *newFile(const QString &editorKind, + QString *titlePattern = 0, + const QString &contents = QString()); + bool hasEditor(const QString &fileName) const; + QList<IEditor *> editorsForFileName(const QString &filename) const; + + void setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory = false); + IEditor *currentEditor() const; + EditorGroup *currentEditorGroup() const; + + QList<IEditor*> openedEditors() const; + QList<IEditor*> editorsForFiles(QList<IFile*> files) const; + QList<EditorGroup *> editorGroups() const; + QList<IEditor*> editorHistory() const; + void addCurrentPositionToNavigationHistory(bool compress = false); + + bool hasDuplicate(IEditor *editor) const; + + bool saveEditor(IEditor *editor); + + bool closeEditors(const QList<IEditor *> editorsToClose, bool askAboutModifiedEditors = true); + + MakeWritableResult makeEditorWritable(IEditor *editor); + + QByteArray saveState() const; + bool restoreState(const QByteArray &state); + + IEditor *restoreEditor(QString fileName, QString editorKind, EditorGroup *group); + + void saveSettings(QSettings *settings); + void readSettings(QSettings *settings); + + QSize minimumSizeHint() const; + + Internal::OpenEditorsWindow *windowPopup() const; + void showWindowPopup() const; + + Internal::EditorSplitter *editorSplitter() const; + + void showEditorInfoBar(const QString &kind, + const QString &infoText, + const QString &buttonText = QString(), + QObject *object = 0, const char *member = 0); + + void hideEditorInfoBar(const QString &kind); + + EditorFactoryList editorFactories(const MimeType &mimeType, bool bestMatchOnly = true) const; + + void setExternalEditor(const QString &); + QString externalEditor() const; + QString externalEditorHelpText() const; + +signals: + void currentEditorChanged(Core::IEditor *editor); + void editorCreated(Core::IEditor *editor, const QString &fileName); + void editorOpened(Core::IEditor *editor); + void editorAboutToClose(Core::IEditor *editor); + void editorsClosed(QList<Core::IEditor *> editors); + void editorGroupsChanged(); + +public slots: + bool closeAllEditors(bool askAboutModifiedEditors = true); + void openInExternalEditor(); + +private slots: + bool saveFile(Core::IEditor *editor = 0); + bool saveFileAs(Core::IEditor *editor = 0); + void closeEditor(); + void closeEditor(Core::IEditor *editor); + void gotoNextDocHistory(); + void gotoPreviousDocHistory(); + void updateCurrentEditorAndGroup(Core::IContext *context); + void updateEditorHistory(); + void updateActions(); + void duplicateEditor(); + void revertToSaved(); + void goBackInNavigationHistory(); + void goForwardInNavigationHistory(); + void makeCurrentEditorWritable(); + +private: + QList<IFile *> filesForEditors(QList<IEditor *> editors) const; + IEditor *createEditor(const QString &mimeType = QString(), + const QString &fileName = QString()); + void insertEditor(IEditor *editor, bool ignoreNavigationHistory = false, EditorGroup *group = 0); + bool registerEditor(IEditor *editor); + bool unregisterEditor(IEditor *editor); + EditorGroup *groupOfEditor(IEditor *editor) const; + void editorChanged(IEditor *editor); + void registerDuplicate(IEditor *original, + IEditor *duplicate); + void unregisterDuplicate(IEditor *editor); + QList<IEditor *> duplicates(IEditor *editor) const; + + QByteArray saveOpenEditorList() const; + void restoreOpenEditorList(const QByteArray &state); + void restoreEditorState(IEditor *editor); + + static EditorManager *m_instance; + EditorManagerPrivate *m_d; +}; + +//===================EditorClosingCoreListener====================== + +namespace Internal { + +class EditorClosingCoreListener : public ICoreListener { + Q_OBJECT + +public: + EditorClosingCoreListener(EditorManager *em); + bool editorAboutToClose(IEditor *editor); + bool coreAboutToClose(); + +private: + EditorManager *m_em; +}; + +} // namespace Internal +} // namespace Core + +#endif // EDITORMANAGER_H diff --git a/src/plugins/coreplugin/editormanager/editorsplitter.cpp b/src/plugins/coreplugin/editormanager/editorsplitter.cpp new file mode 100644 index 00000000000..82710e7e930 --- /dev/null +++ b/src/plugins/coreplugin/editormanager/editorsplitter.cpp @@ -0,0 +1,664 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "editorsplitter.h" +#include "editormanager.h" +#include "openeditorswindow.h" +#include "stackededitorgroup.h" +#include "minisplitter.h" + +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> + +#include <QtGui/QHBoxLayout> +#include <QtGui/QMenu> +#include <QtGui/QApplication> + +using namespace Core; +using namespace Core::Internal; + +EditorSplitter::EditorSplitter(ICore *core, QWidget *parent) + : QWidget(parent), + m_curGroup(0), + m_core(core) +{ + registerActions(); + createRootGroup(); + updateActions(); +} + +EditorSplitter::~EditorSplitter() +{ +} + +void EditorSplitter::registerActions() +{ + QList<int> gc = QList<int>() << Constants::C_GLOBAL_ID; + const QList<int> editorManagerContext = + QList<int>() << m_core->uniqueIDManager()->uniqueIdentifier(Constants::C_EDITORMANAGER); + + ActionManagerInterface *am = m_core->actionManager(); + IActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW); + ICommand *cmd; + + //Horizontal Action + m_horizontalSplitAction = new QAction(tr("Split Left/Right"), this); + cmd = am->registerAction(m_horizontalSplitAction, Constants::HORIZONTAL, editorManagerContext); + mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT); + connect(m_horizontalSplitAction, SIGNAL(triggered()), + this, SLOT(splitHorizontal())); + + //Vertical Action + m_verticalSplitAction = new QAction(tr("Split Top/Bottom"), this); + cmd = am->registerAction(m_verticalSplitAction, Constants::VERTICAL, editorManagerContext); + mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT); + connect(m_verticalSplitAction, SIGNAL(triggered()), + this, SLOT(splitVertical())); + + //Unsplit Action + m_unsplitAction = new QAction(tr("Unsplit"), this); + cmd = am->registerAction(m_unsplitAction, Constants::REMOVE, editorManagerContext); + mwindow->addAction(cmd, Constants::G_WINDOW_SPLIT); + connect(m_unsplitAction, SIGNAL(triggered()), + this, SLOT(unsplit())); + + //Default Layout menu + IActionContainer *mLayout = am->createMenu("QtCreator.Menu.Window.Layout"); + mwindow->addMenu(mLayout, Constants::G_WINDOW_SPLIT); + mLayout->menu()->setTitle(tr("Default Splitter Layout")); + + //Set Current As Default + m_currentAsDefault = new QAction(tr("Save Current as Default"), this); + cmd = am->registerAction(m_currentAsDefault, Constants::SAVEASDEFAULT, editorManagerContext); + mLayout->addAction(cmd); + connect(m_currentAsDefault, SIGNAL(triggered()), + this, SLOT(saveCurrentLayout())); + + //Restore Default + m_restoreDefault = new QAction(tr("Restore Default Layout"), this); + cmd = am->registerAction(m_restoreDefault, Constants::RESTOREDEFAULT, editorManagerContext); + mLayout->addAction(cmd); + connect(m_restoreDefault, SIGNAL(triggered()), + this, SLOT(restoreDefaultLayout())); + + // TODO: The previous and next actions are removed, to be reenabled when they + // navigate according to code navigation history. And they need different shortcuts on the mac + // since Alt+Left/Right is jumping wordwise in editors +#if 0 + // Goto Previous Action + m_gotoPreviousEditorAction = new QAction(QIcon(Constants::ICON_PREV), tr("Previous Document"), this); + cmd = am->registerAction(m_gotoPreviousEditorAction, Constants::GOTOPREV, editorManagerContext); + cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Left"))); + mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE); + connect(m_gotoPreviousEditorAction, SIGNAL(triggered()), this, SLOT(gotoPreviousEditor())); + + // Goto Next Action + m_gotoNextEditorAction = new QAction(QIcon(Constants::ICON_NEXT), tr("Next Document"), this); + cmd = am->registerAction(m_gotoNextEditorAction, Constants::GOTONEXT, editorManagerContext); + cmd->setDefaultKeySequence(QKeySequence(tr("Alt+Right"))); + mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE); + connect(m_gotoNextEditorAction, SIGNAL(triggered()), this, SLOT(gotoNextEditor())); +#endif + + // Previous Group Action + m_gotoPreviousGroupAction = new QAction(tr("Previous Group"), this); + cmd = am->registerAction(m_gotoPreviousGroupAction, Constants::GOTOPREVIOUSGROUP, editorManagerContext); + mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE_GROUPS); + connect(m_gotoPreviousGroupAction, SIGNAL(triggered()), this, SLOT(selectPreviousGroup())); + + // Next Group Action + m_gotoNextGroupAction = new QAction(tr("Next Group"), this); + cmd = am->registerAction(m_gotoNextGroupAction, Constants::GOTONEXTGROUP, editorManagerContext); + mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE_GROUPS); + connect(m_gotoNextGroupAction, SIGNAL(triggered()), this, SLOT(selectNextGroup())); + + // Move to Previous Group + m_moveDocToPreviousGroupAction = new QAction(tr("Move Document to Previous Group"), this); + cmd = am->registerAction(m_moveDocToPreviousGroupAction, "QtCreator.DocumentToPreviousGroup", editorManagerContext); + mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE_GROUPS); + connect(m_moveDocToPreviousGroupAction, SIGNAL(triggered()), this, SLOT(moveDocToPreviousGroup())); + + // Move to Next Group + m_moveDocToNextGroupAction = new QAction(tr("Move Document to Next Group"), this); + cmd = am->registerAction(m_moveDocToNextGroupAction, "QtCreator.DocumentToNextGroup", editorManagerContext); + mwindow->addAction(cmd, Constants::G_WINDOW_NAVIGATE_GROUPS); + connect(m_moveDocToNextGroupAction, SIGNAL(triggered()), this, SLOT(moveDocToNextGroup())); +} + +void EditorSplitter::updateActions() +{ + const bool hasMultipleGroups = (qobject_cast<QSplitter*>(m_root) != 0); + Q_ASSERT(currentGroup()); + const bool hasEditors = (currentGroup()->editorCount() != 0); + m_unsplitAction->setEnabled(hasMultipleGroups); +#if 0 + const bool hasMultipleEditors = (editorCount() > 1); + m_gotoNextEditorAction->setEnabled(hasMultipleEditors); + m_gotoPreviousEditorAction->setEnabled(hasMultipleEditors); +#endif + m_gotoPreviousGroupAction->setEnabled(hasMultipleGroups); + m_gotoNextGroupAction->setEnabled(hasMultipleGroups); + m_moveDocToPreviousGroupAction->setEnabled(hasEditors && hasMultipleGroups); + m_moveDocToNextGroupAction->setEnabled(hasEditors && hasMultipleGroups); +} + +int EditorSplitter::editorCount() const +{ + int count = 0; + foreach (EditorGroup *group, groups()) { + count += group->editorCount(); + } + return count; +} + +void EditorSplitter::createRootGroup() +{ + QHBoxLayout *l = new QHBoxLayout(this); + l->setMargin(0); + l->setSpacing(0); + EditorGroup *rootGroup = createGroup(); + m_root = rootGroup->widget(); + l->addWidget(m_root); + setCurrentGroup(rootGroup); +} + +void EditorSplitter::splitHorizontal() +{ + split(Qt::Horizontal); +} + +void EditorSplitter::splitVertical() +{ + split(Qt::Vertical); +} + +void EditorSplitter::gotoNextEditor() +{ + OpenEditorsWindow *dialog = EditorManager::instance()->windowPopup(); + dialog->setMode(OpenEditorsWindow::ListMode); + dialog->selectNextEditor(); + EditorManager::instance()->showWindowPopup(); +} + +void EditorSplitter::gotoPreviousEditor() +{ + OpenEditorsWindow *dialog = EditorManager::instance()->windowPopup(); + dialog->setMode(OpenEditorsWindow::ListMode); + dialog->selectPreviousEditor(); + EditorManager::instance()->showWindowPopup(); +} + +void EditorSplitter::setCurrentGroup(EditorGroup *group) +{ + if (!group || group == m_curGroup) + return; + m_curGroup = group; + if (m_curGroup->widget()->focusWidget() != QApplication::focusWidget()) + m_curGroup->widget()->setFocus(); + updateActions(); +} + +QList<EditorGroup*> EditorSplitter::groups() const +{ + QList<EditorGroup*> grps; + collectGroups(m_root, grps); + return grps; +} + +void EditorSplitter::collectGroups(QWidget *widget, QList<EditorGroup*> &groups) const +{ + EditorGroup *group = qobject_cast<EditorGroup *>(widget); + if (group) { + groups += group; + return; + } + QSplitter *splitter = qobject_cast<QSplitter*>(widget); + Q_ASSERT(splitter); + collectGroups(splitter->widget(LEFT), groups); + collectGroups(splitter->widget(RIGHT), groups); +} + + +EditorGroup *EditorSplitter::currentGroup() const +{ + return m_curGroup; +} + +void EditorSplitter::split(Qt::Orientation orientation) +{ + EditorGroup *curGroup = currentGroup(); + IEditor *editor = curGroup->currentEditor(); + split(orientation, 0); + if (editor) { + EditorGroup *otherGroup = currentGroup(); + if (curGroup != otherGroup) { + curGroup->removeEditor(editor); + otherGroup->addEditor(editor); + } + } + emit editorGroupsChanged(); +} + +QSplitter *EditorSplitter::split(Qt::Orientation orientation, EditorGroup *group) +{ + EditorGroup *curGroup = group; + if (!curGroup) + curGroup = currentGroup(); + if (!curGroup) + return 0; + + int oldSibling1 = -1; + int oldSibling2 = -1; + int idx = 1; + QWidget *curGroupWidget = curGroup->widget(); + QSplitter *parentSplitter = qobject_cast<QSplitter*>(curGroupWidget->parentWidget()); + if (parentSplitter) { + if (parentSplitter->orientation() == Qt::Vertical) { + oldSibling1 = parentSplitter->widget(LEFT)->height(); + oldSibling2 = parentSplitter->widget(RIGHT)->height(); + } else { + oldSibling1 = parentSplitter->widget(LEFT)->width(); + oldSibling2 = parentSplitter->widget(RIGHT)->width(); + } + idx = parentSplitter->indexOf(curGroupWidget); + } + + QLayout *l = curGroupWidget->parentWidget()->layout(); + curGroupWidget->setParent(0); + + QSplitter *splitter = new MiniSplitter(0); + splitter->setOrientation(orientation); + EditorGroup *eg = createGroup(); + + splitter->addWidget(curGroupWidget); + splitter->addWidget(eg->widget()); + + if (curGroupWidget == m_root) { + l->addWidget(splitter); + m_root = splitter; + } else { + parentSplitter->insertWidget(idx, splitter); + } + + if (parentSplitter) + parentSplitter->setSizes(QList<int>() << oldSibling1 << oldSibling2); + if (orientation == Qt::Vertical) + splitter->setSizes(QList<int>() << curGroupWidget->height()/2 + << curGroupWidget->height()/2); + else + splitter->setSizes(QList<int>() << curGroupWidget->width()/2 + << curGroupWidget->width()/2); + setCurrentGroup(eg); + return splitter; +} + +void EditorSplitter::unsplit() +{ + EditorGroup *curGroup = currentGroup(); + if (!curGroup) + return; + QWidget *curGroupWidget = curGroup->widget(); + Q_ASSERT(curGroupWidget); + IEditor *selectedEditor = curGroup->currentEditor(); + + QSplitter *parentSplitter = qobject_cast<QSplitter*>(curGroupWidget->parentWidget()); + if (parentSplitter) { + int oldSibling1 = -1; + int oldSibling2 = -1; + + EditorGroup *e1 = qobject_cast<EditorGroup *>(parentSplitter->widget(LEFT)); + EditorGroup *e2 = qobject_cast<EditorGroup *>(parentSplitter->widget(RIGHT)); + + QWidget *w = parentSplitter->parentWidget(); + QSplitter *grandParentSplitter = qobject_cast<QSplitter*>(w); + int idx = 0; + if (grandParentSplitter) { + idx = grandParentSplitter->indexOf(parentSplitter); + if (grandParentSplitter->orientation() == Qt::Vertical) { + oldSibling1 = grandParentSplitter->widget(LEFT)->height(); + oldSibling2 = grandParentSplitter->widget(RIGHT)->height(); + } else { + oldSibling1 = grandParentSplitter->widget(LEFT)->width(); + oldSibling2 = grandParentSplitter->widget(RIGHT)->width(); + } + } + if (e1 && e2) { // we are unsplitting a split that contains of groups directly not one or more additional splits + e1->moveEditorsFromGroup(e2); + parentSplitter->setParent(0); + if (grandParentSplitter) { + grandParentSplitter->insertWidget(idx, e1->widget()); + } else { + w->layout()->addWidget(e1->widget()); + m_root = e1->widget(); + } + setCurrentGroup(e1); + deleteGroup(e2); + e2 = 0; + delete parentSplitter; + e1->setCurrentEditor(selectedEditor); + } else if (e1 || e2) { + parentSplitter->setParent(0); + QSplitter *s = 0; + if (e1) { + s = qobject_cast<QSplitter*>(parentSplitter->widget(RIGHT)); + } else { + s = qobject_cast<QSplitter*>(parentSplitter->widget(LEFT)); + e1 = e2; + } + + if (grandParentSplitter) { + grandParentSplitter->insertWidget(idx, s); + } else { + w->layout()->addWidget(s); + m_root = s; + } + EditorGroup *leftMost = groupFarthestOnSide(s, LEFT); + leftMost->moveEditorsFromGroup(e1); + leftMost->setCurrentEditor(selectedEditor); + setCurrentGroup(leftMost); + deleteGroup(e1); + } + if (grandParentSplitter) + grandParentSplitter->setSizes(QList<int>() << oldSibling1 << oldSibling2); + emit editorGroupsChanged(); + } + updateActions(); +} + +void EditorSplitter::unsplitAll() +{ + QSplitter *rootSplit = qobject_cast<QSplitter *>(m_root); + if (!rootSplit) + return; + // first create and set the new root, then kill the original stuff + // this way we can avoid unnecessary signals/ context changes + rootSplit->setParent(0); + EditorGroup *rootGroup = createGroup(); + QLayout *l = layout(); + l->addWidget(rootGroup->widget()); + m_root = rootGroup->widget(); + setCurrentGroup(rootGroup); + // kill old hierarchy + QList<IEditor *> editors; + unsplitAll(rootSplit->widget(1), editors); + unsplitAll(rootSplit->widget(0), editors); + delete rootSplit; + rootSplit = 0; + foreach (IEditor *editor, editors) { + rootGroup->addEditor(editor); + } +} + +void EditorSplitter::unsplitAll(QWidget *node, QList<IEditor*> &editors) +{ + QSplitter *splitter = qobject_cast<QSplitter *>(node); + if (splitter) { + unsplitAll(splitter->widget(1), editors); + unsplitAll(splitter->widget(0), editors); + delete splitter; + splitter = 0; + } else { + EditorGroup *group = qobject_cast<EditorGroup *>(node); + editors << group->editors(); + bool blocking = group->widget()->blockSignals(true); + foreach (IEditor *editor, group->editors()) { + group->removeEditor(editor); + } + group->widget()->blockSignals(blocking); + deleteGroup(group); + } +} + +EditorGroup *EditorSplitter::groupFarthestOnSide(QWidget *node, Side side) const +{ + QWidget *current = node; + QSplitter *split = 0; + while ((split = qobject_cast<QSplitter*>(current))) { + current = split->widget(side); + } + return qobject_cast<EditorGroup *>(current); +} + +void EditorSplitter::selectNextGroup() +{ + EditorGroup *curGroup = currentGroup(); + Q_ASSERT(curGroup); + setCurrentGroup(nextGroup(curGroup, RIGHT)); +} + +void EditorSplitter::selectPreviousGroup() +{ + EditorGroup *curGroup = currentGroup(); + Q_ASSERT(curGroup); + setCurrentGroup(nextGroup(curGroup, LEFT)); +} + +EditorGroup *EditorSplitter::nextGroup(EditorGroup *curGroup, Side side) const +{ + Q_ASSERT(curGroup); + QWidget *curWidget = curGroup->widget(); + QWidget *parent = curWidget->parentWidget(); + while (curWidget != m_root) { + QSplitter *splitter = qobject_cast<QSplitter *>(parent); + Q_ASSERT(splitter); + if (splitter->widget(side) != curWidget) { + curWidget = splitter->widget(side); + break; + } + curWidget = parent; + parent = curWidget->parentWidget(); + } + return groupFarthestOnSide(curWidget, side==LEFT ? RIGHT : LEFT); +} + +void EditorSplitter::moveDocToAdjacentGroup(Side side) +{ + EditorGroup *curGroup = currentGroup(); + Q_ASSERT(curGroup); + IEditor *editor = curGroup->currentEditor(); + if (!editor) + return; + EditorGroup *next = nextGroup(curGroup, side); + next->moveEditorFromGroup(curGroup, editor); + setCurrentGroup(next); +} + +void EditorSplitter::moveDocToNextGroup() +{ + moveDocToAdjacentGroup(RIGHT); +} + +void EditorSplitter::moveDocToPreviousGroup() +{ + moveDocToAdjacentGroup(LEFT); +} + +QWidget *EditorSplitter::recreateGroupTree(QWidget *node) +{ + QSplitter *splitter = qobject_cast<QSplitter *>(node); + if (!splitter) { + EditorGroup *group = qobject_cast<EditorGroup *>(node); + Q_ASSERT(group); + IEditor *currentEditor = group->currentEditor(); + EditorGroup *newGroup = createGroup(); + bool block = newGroup->widget()->blockSignals(true); + foreach (IEditor *editor, group->editors()) { + newGroup->addEditor(editor); + } + newGroup->setCurrentEditor(currentEditor); + deleteGroup(group); + newGroup->widget()->blockSignals(block); + return newGroup->widget(); + } else { + QByteArray splitterState = splitter->saveState(); + QWidget *orig0 = splitter->widget(0); + QWidget *orig1 = splitter->widget(1); + QWidget *g0 = recreateGroupTree(orig0); + QWidget *g1 = recreateGroupTree(orig1); + splitter->insertWidget(0, g0); + splitter->insertWidget(1, g1); + splitter->restoreState(splitterState); + return node; + } +} + +void EditorSplitter::saveCurrentLayout() +{ + QSettings *settings = m_core->settings(); + settings->setValue("EditorManager/Splitting", saveState()); +} + +void EditorSplitter::restoreDefaultLayout() +{ + QSettings *settings = m_core->settings(); + if (settings->contains("EditorManager/Splitting")) + restoreState(settings->value("EditorManager/Splitting").toByteArray()); +} + +void EditorSplitter::saveSettings(QSettings * /*settings*/) const +{ +} + +void EditorSplitter::readSettings(QSettings * /*settings*/) +{ + restoreDefaultLayout(); +} + +QByteArray EditorSplitter::saveState() const +{ + //todo: versioning + QByteArray bytes; + QDataStream stream(&bytes, QIODevice::WriteOnly); + saveState(m_root, stream); + return bytes; +} + +bool EditorSplitter::restoreState(const QByteArray &state) +{ + unsplitAll(); + //todo: versioning + QDataStream stream(state); + EditorGroup *curGroup = + restoreState(qobject_cast<EditorGroup *>(m_root), stream); + setCurrentGroup(curGroup); + return true; +} + +void EditorSplitter::saveState(QWidget *current, QDataStream &stream) const +{ + QSplitter *splitter = qobject_cast<QSplitter *>(current); + quint8 type; + if (splitter) { + type = 0; + stream << type; + stream << splitter->saveState(); + saveState(splitter->widget(0), stream); + saveState(splitter->widget(1), stream); + } else { + EditorGroup *group = qobject_cast<EditorGroup *>(current); + Q_ASSERT(group); + if (group != currentGroup()) + type = 1; + else + type = 2; + stream << type; + } +} + +EditorGroup *EditorSplitter::restoreState(EditorGroup *current, + QDataStream &stream) +{ + EditorGroup *curGroup = 0; + EditorGroup *group; + quint8 type; + stream >> type; + if (type == 0) { + QSplitter *splitter = split(Qt::Horizontal, current); + QByteArray splitterState; + stream >> splitterState; + splitter->restoreState(splitterState); + group = restoreState(qobject_cast<EditorGroup *>(splitter->widget(0)), + stream); + if (group) + curGroup = group; + group = restoreState(qobject_cast<EditorGroup *>(splitter->widget(1)), + stream); + if (group) + curGroup = group; + } else { + if (type == 2) + return current; + } + return curGroup; +} + +QMap<QString,EditorGroup *> EditorSplitter::pathGroupMap() +{ + QMap<QString,EditorGroup *> map; + fillPathGroupMap(m_root, "", map); + return map; +} + +void EditorSplitter::fillPathGroupMap(QWidget *current, QString currentPath, + QMap<QString,EditorGroup *> &map) +{ + EditorGroup *group = qobject_cast<EditorGroup *>(current); + if (group) { + map.insert(currentPath, group); + } else { + QSplitter *splitter = qobject_cast<QSplitter *>(current); + Q_ASSERT(splitter); + fillPathGroupMap(splitter->widget(0), currentPath+"0", map); + fillPathGroupMap(splitter->widget(1), currentPath+"1", map); + } +} + +EditorGroup *EditorSplitter::createGroup() +{ + EditorGroup *group = new StackedEditorGroup(this); + connect(group, SIGNAL(closeRequested(Core::IEditor *)), + this, SIGNAL(closeRequested(Core::IEditor *))); + connect(group, SIGNAL(editorRemoved(Core::IEditor *)), + this, SLOT(updateActions())); + connect(group, SIGNAL(editorAdded(Core::IEditor *)), + this, SLOT(updateActions())); + m_core->addContextObject(group->contextObject()); + return group; +} + +void EditorSplitter::deleteGroup(EditorGroup *group) +{ + m_core->removeContextObject(group->contextObject()); + delete group; +} diff --git a/src/plugins/coreplugin/editormanager/editorsplitter.h b/src/plugins/coreplugin/editormanager/editorsplitter.h new file mode 100644 index 00000000000..12faab68da1 --- /dev/null +++ b/src/plugins/coreplugin/editormanager/editorsplitter.h @@ -0,0 +1,136 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef EDITORSPLITTER_H +#define EDITORSPLITTER_H + +#include <QtCore/QMap> +#include <QtCore/QList> +#include <QtCore/QString> +#include <QtCore/QSettings> +#include <QtGui/QWidget> +#include <QtGui/QAction> +#include <QtGui/QSplitter> + +namespace Core { + +class EditorGroup; +class ICore; +class IEditor; + +namespace Internal { + +class EditorSplitter : public QWidget +{ + Q_OBJECT + +public: + EditorSplitter(ICore *core, QWidget *parent = 0); + ~EditorSplitter(); + + void setCurrentGroup(Core::EditorGroup *group); + EditorGroup *currentGroup() const; + QList<EditorGroup*> groups() const; + + void split(Qt::Orientation orientation); + + void saveSettings(QSettings *settings) const; + void readSettings(QSettings *settings); + QByteArray saveState() const; + bool restoreState(const QByteArray &state); + + QMap<QString,EditorGroup *> pathGroupMap(); + +public slots: + void unsplit(); + +signals: + void closeRequested(Core::IEditor *editor); + void editorGroupsChanged(); + +private slots: + void splitHorizontal(); + void splitVertical(); + void gotoNextEditor(); + void gotoPreviousEditor(); + void updateActions(); + void selectNextGroup(); + void selectPreviousGroup(); + void moveDocToNextGroup(); + void moveDocToPreviousGroup(); + void saveCurrentLayout(); + void restoreDefaultLayout(); + +private: + enum Side {LEFT = 0, RIGHT = 1}; + + void registerActions(); + void createRootGroup(); + EditorGroup *createGroup(); + void deleteGroup(EditorGroup *group); + void collectGroups(QWidget *widget, QList<EditorGroup*> &groups) const; + EditorGroup *groupFarthestOnSide(QWidget *node, Side side) const; + EditorGroup *nextGroup(EditorGroup *curGroup, Side side) const; + void moveDocToAdjacentGroup(Side side); + void saveState(QWidget *current, QDataStream &stream) const; + EditorGroup *restoreState(EditorGroup *current, QDataStream &stream); + QSplitter *split(Qt::Orientation orientation, EditorGroup *group); + void fillPathGroupMap(QWidget *current, QString currentPath, + QMap<QString,EditorGroup *> &map); + void unsplitAll(); + void unsplitAll(QWidget *node, QList<IEditor *> &editors); + QWidget *recreateGroupTree(QWidget *node); + int editorCount() const; + + QWidget *m_root; + EditorGroup *m_curGroup; + ICore *m_core; + + QAction *m_horizontalSplitAction; + QAction *m_verticalSplitAction; + QAction *m_unsplitAction; +#if 0 + QAction *m_gotoNextEditorAction; + QAction *m_gotoPreviousEditorAction; +#endif + QAction *m_gotoNextGroupAction; + QAction *m_gotoPreviousGroupAction; + QAction *m_moveDocToNextGroupAction; + QAction *m_moveDocToPreviousGroupAction; + QAction *m_currentAsDefault; + QAction *m_restoreDefault; +}; + +} // namespace Internal +} // namespace Core + +#endif // EDITORSPLITTER_H diff --git a/src/plugins/coreplugin/editormanager/ieditor.h b/src/plugins/coreplugin/editormanager/ieditor.h new file mode 100644 index 00000000000..66a9fdca14f --- /dev/null +++ b/src/plugins/coreplugin/editormanager/ieditor.h @@ -0,0 +1,102 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IEDITOR_H +#define IEDITOR_H + +#include <coreplugin/core_global.h> +#include <coreplugin/icontext.h> +#include <coreplugin/ifile.h> + +QT_BEGIN_NAMESPACE +class QToolBar; +QT_END_NAMESPACE + +namespace Core { + +/*! + \class Core::IEditor + \brief The IEditor is an interface for providing different editors for different file types. + + Classes that implement this interface are for example the editors for + C++ files, ui-files and resource files. + + Whenever a user wants to edit or create a file, the EditorManager scans all + EditorFactoryInterfaces for suitable editors. The selected EditorFactory + is then asked to create an editor, which must implement this interface. + + Guidelines for implementing: + \list + \o displayName() is used as a user visible description of the document (usually filename w/o path). + \o kind() must be the same value as the kind() of the corresponding EditorFactory. + \o The changed() signal should be emitted when the modified state of the document changes + (so /bold{not} every time the document changes, but /bold{only once}). + \o If duplication is supported, you need to ensure that all duplicates + return the same file(). + \endlist + + \sa Core::EditorFactoryInterface Core::IContext + +*/ + +class CORE_EXPORT IEditor : public IContext +{ + Q_OBJECT +public: + IEditor(QObject *parent = 0) : IContext(parent) {} + virtual ~IEditor() {} + + virtual bool createNew(const QString &contents = QString()) = 0; + virtual bool open(const QString &fileName = QString()) = 0; + virtual IFile *file() = 0; + virtual const char *kind() const = 0; + virtual QString displayName() const = 0; + virtual void setDisplayName(const QString &title) = 0; + + virtual bool duplicateSupported() const = 0; + virtual IEditor *duplicate(QWidget *parent) = 0; + + virtual QByteArray saveState() const = 0; + virtual bool restoreState(const QByteArray &state) = 0; + + virtual int currentLine() const { return 0; }; + virtual int currentColumn() const { return 0; }; + + virtual QToolBar *toolBar() = 0; + +signals: + void changed(); +}; + +} // namespace Core + +#endif //IEDITOR_H diff --git a/src/plugins/coreplugin/editormanager/ieditorfactory.h b/src/plugins/coreplugin/editormanager/ieditorfactory.h new file mode 100644 index 00000000000..2b70ef95b99 --- /dev/null +++ b/src/plugins/coreplugin/editormanager/ieditorfactory.h @@ -0,0 +1,59 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IEDITORFACTORY_H +#define IEDITORFACTORY_H + +#include <coreplugin/ifilefactory.h> +#include <coreplugin/editormanager/ieditor.h> + +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +namespace Core { + +class CORE_EXPORT IEditorFactory : public Core::IFileFactory +{ + Q_OBJECT +public: + IEditorFactory(QObject *parent = 0) : IFileFactory(parent) {} + virtual ~IEditorFactory() {} + + virtual IEditor *createEditor(QWidget *parent) = 0; +}; + +} // namespace Core + +#endif // IEDITORFACTORY_H diff --git a/src/plugins/coreplugin/editormanager/openeditorsview.cpp b/src/plugins/coreplugin/editormanager/openeditorsview.cpp new file mode 100644 index 00000000000..b2400853ec5 --- /dev/null +++ b/src/plugins/coreplugin/editormanager/openeditorsview.cpp @@ -0,0 +1,310 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "openeditorsview.h" +#include "editorgroup.h" +#include "editormanager.h" +#include "coreimpl.h" + +#include <coreplugin/coreconstants.h> +#include <coreplugin/filemanager.h> +#include <coreplugin/uniqueidmanager.h> + +#include <QtCore/QTimer> +#include <QtGui/QMenu> +#include <QtGui/QPainter> +#include <QtGui/QStyle> +#include <QtGui/QStyleOption> +#include <QtGui/QHeaderView> +#include <QtGui/QKeyEvent> +#ifdef Q_WS_MAC +#include <qmacstyle_mac.h> +#endif + +Q_DECLARE_METATYPE(Core::IEditor*) + +using namespace Core; +using namespace Core::Internal; + +OpenEditorsWidget::OpenEditorsWidget() +{ + m_ui.setupUi(this); + setWindowTitle(tr("Open Documents")); + setWindowIcon(QIcon(Constants::ICON_DIR)); + setFocusProxy(m_ui.editorList); + m_ui.editorList->setColumnCount(1); + m_ui.editorList->header()->hide(); + m_ui.editorList->setIndentation(0); + m_ui.editorList->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_ui.editorList->setTextElideMode(Qt::ElideMiddle); + m_ui.editorList->installEventFilter(this); + m_ui.editorList->setFrameStyle(QFrame::NoFrame); + EditorManager *em = EditorManager::instance(); + foreach(IEditor *editor, em->openedEditors()) { + registerEditor(editor); + } + connect(em, SIGNAL(editorOpened(Core::IEditor*)), + this, SLOT(registerEditor(Core::IEditor*))); + connect(em, SIGNAL(editorsClosed(QList<Core::IEditor*>)), + this, SLOT(unregisterEditors(QList<Core::IEditor*>))); + connect(em, SIGNAL(editorGroupsChanged()), + this, SLOT(updateEditorList())); + connect(em, SIGNAL(currentEditorChanged(Core::IEditor*)), + this, SLOT(updateCurrentItem())); + connect(m_ui.editorList, SIGNAL(itemActivated(QTreeWidgetItem*, int)), + this, SLOT(selectEditor(QTreeWidgetItem*))); + updateEditorList(); +} + +OpenEditorsWidget::~OpenEditorsWidget() +{ + +} + +void OpenEditorsWidget::registerEditor(IEditor *editor) +{ + connect(editor, SIGNAL(changed()), this, SLOT(updateEditor())); + updateEditorList(); +} + +void OpenEditorsWidget::unregisterEditors(QList<IEditor *> editors) +{ + foreach (IEditor *editor, editors) + disconnect(editor, SIGNAL(changed()), this, SLOT(updateEditor())); + updateEditorList(); +} + +void OpenEditorsWidget::updateEditorList() +{ + EditorManager *em = EditorManager::instance(); + QList<EditorGroup *> groups = em->editorGroups(); + IEditor *curEditor = em->currentEditor(); + int oldNum = m_ui.editorList->topLevelItemCount(); + QTreeWidgetItem *currentItem = 0; + int currItemIndex = 0; + for (int i = 0; i < groups.count(); ++i) { + QTreeWidgetItem *item; + if (groups.count() > 1) { + if (currItemIndex < oldNum) { + item = m_ui.editorList->topLevelItem(currItemIndex); + } else { + item = new QTreeWidgetItem(QStringList()<<""); + m_ui.editorList->addTopLevelItem(item); + } + currItemIndex++; + item->setIcon(0, QIcon()); + item->setText(0, tr("---Group %1---").arg(i)); + item->setFlags(0); + item->setToolTip(0, ""); + item->setData(0, Qt::UserRole, QVariant()); + item->setTextAlignment(0, Qt::AlignLeft); + } + foreach (IEditor *editor, groups.at(i)->editors()) { + if (currItemIndex < oldNum) { + item = m_ui.editorList->topLevelItem(currItemIndex); + } else { + item = new QTreeWidgetItem(QStringList()<<""); + m_ui.editorList->addTopLevelItem(item); + } + currItemIndex++; + updateItem(item, editor); + if (editor == curEditor) + currentItem = item; + } + } + for (int i = oldNum-1; i >= currItemIndex; --i) { + delete m_ui.editorList->takeTopLevelItem(i); + } + updateCurrentItem(currentItem); +} + +void OpenEditorsWidget::updateCurrentItem(QTreeWidgetItem *currentItem) +{ + EditorManager *em = EditorManager::instance(); + IEditor *curEditor = em->currentEditor(); + m_ui.editorList->clearSelection(); + if (!currentItem && curEditor) { + int count = m_ui.editorList->topLevelItemCount(); + for (int i = 0; i < count; ++i) { + if (m_ui.editorList->topLevelItem(i)->data(0, Qt::UserRole).value<IEditor *>() + == curEditor) { + currentItem = m_ui.editorList->topLevelItem(i); + break; + } + } + } + m_ui.editorList->setCurrentItem(currentItem); + if (currentItem) + m_ui.editorList->scrollTo(m_ui.editorList->currentIndex()); +} + +//todo: this is almost duplicated in openeditorswindow +void OpenEditorsWidget::updateItem(QTreeWidgetItem *item, IEditor *editor) +{ + static const QIcon lockedIcon(QLatin1String(":/qworkbench/images/locked.png")); + static const QIcon emptyIcon(QLatin1String(":/qworkbench/images/empty14.png")); + QString title = editor->displayName(); + if (editor->file()->isModified()) + title += tr("*"); + item->setIcon(0, editor->file()->isReadOnly() ? lockedIcon : emptyIcon); + item->setText(0, title); + item->setToolTip(0, editor->file()->fileName()); + item->setData(0, Qt::UserRole, QVariant::fromValue(editor)); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + item->setTextAlignment(0, Qt::AlignLeft); +} + +void OpenEditorsWidget::selectEditor(QTreeWidgetItem *item) +{ + if (item == 0) + item = m_ui.editorList->currentItem(); + if (item == 0) + return; + IEditor *editor = item->data(0, Qt::UserRole).value<IEditor*>(); + EditorManager::instance()->setCurrentEditor(editor); +} + +void OpenEditorsWidget::updateEditor() +{ + IEditor *editor = qobject_cast<IEditor *>(sender()); + Q_ASSERT(editor); + int num = m_ui.editorList->topLevelItemCount(); + for (int i = 0; i < num; ++i) { + QTreeWidgetItem *item = m_ui.editorList->topLevelItem(i); + if (item->data(0, Qt::UserRole).value<IEditor *>() + == editor) { + updateItem(item, editor); + return; + } + } +} + +void OpenEditorsWidget::closeEditors() +{ + QList<IFile *> selectedFiles; + QList<IEditor *> selectedEditors; + foreach (QTreeWidgetItem *item, m_ui.editorList->selectedItems()) { + selectedEditors.append(item->data(0, Qt::UserRole).value<IEditor *>()); + selectedFiles.append(item->data(0, Qt::UserRole).value<IEditor *>()->file()); + } + ICore *core = CoreImpl::instance(); + bool cancelled = false; + core->fileManager()->saveModifiedFiles(selectedFiles, &cancelled); + if (cancelled) + return; + core->editorManager()-> + closeEditors(selectedEditors); + updateEditorList(); +} + +void OpenEditorsWidget::closeAllEditors() +{ + m_ui.editorList->selectAll(); + closeEditors(); +} + + +bool OpenEditorsWidget::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == m_ui.editorList) { + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); + switch (keyEvent->key()) { + case Qt::Key_Return: + selectEditor(m_ui.editorList->currentItem()); + return true; + case Qt::Key_Delete: //fall through + case Qt::Key_Backspace: + if (keyEvent->modifiers() == Qt::NoModifier) { + closeEditors(); + return true; + } + break; + default: + break; + } + } + if (event->type() == QEvent::ContextMenu) { + QContextMenuEvent *contextMenuEvent = static_cast<QContextMenuEvent *>(event); + QMenu menu; + menu.addAction(tr("&Select"), this, SLOT(selectEditor())); + menu.addAction(tr("&Close"), this, SLOT(closeEditors())); + menu.addAction(tr("Close &All"), this, SLOT(closeAllEditors())); + if (m_ui.editorList->selectedItems().isEmpty()) + menu.setEnabled(false); + menu.exec(contextMenuEvent->globalPos()); + return true; + } + } else if (obj == m_widget) { + if (event->type() == QEvent::FocusIn) { + QFocusEvent *e = static_cast<QFocusEvent *>(event); + if (e->reason() != Qt::MouseFocusReason) { + // we should not set the focus in a event filter for a focus event, + // so do it when the focus event is processed + QTimer::singleShot(0, this, SLOT(putFocusToEditorList())); + } + } + } + return false; +} + +void OpenEditorsWidget::putFocusToEditorList() +{ + m_ui.editorList->setFocus(); +} + +NavigationView OpenEditorsViewFactory::createWidget() +{ + NavigationView n; + n.widget = new OpenEditorsWidget(); + return n; +} + +QString OpenEditorsViewFactory::displayName() +{ + return "Open Documents"; +} + +QKeySequence OpenEditorsViewFactory::activationSequence() +{ + return QKeySequence(Qt::ALT + Qt::Key_O); +} + +OpenEditorsViewFactory::OpenEditorsViewFactory() +{ + +} + +OpenEditorsViewFactory::~OpenEditorsViewFactory() +{ + +} diff --git a/src/plugins/coreplugin/editormanager/openeditorsview.h b/src/plugins/coreplugin/editormanager/openeditorsview.h new file mode 100644 index 00000000000..d4c20a4a24e --- /dev/null +++ b/src/plugins/coreplugin/editormanager/openeditorsview.h @@ -0,0 +1,91 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef OPENEDITORSVIEW_H +#define OPENEDITORSVIEW_H + +#include "ui_openeditorsview.h" + +#include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/inavigationwidgetfactory.h> + +#include <QtCore/QList> +#include <QtGui/QWidget> +#include <QtGui/QKeySequence> +#include <QtGui/QAbstractButton> +#include <QtGui/QTreeWidgetItem> + +namespace Core { +namespace Internal { + +class OpenEditorsWidget : public QWidget +{ + Q_OBJECT + +public: + OpenEditorsWidget(); + ~OpenEditorsWidget(); + + bool eventFilter(QObject *obj, QEvent *event); + +private slots: + void registerEditor(Core::IEditor *editor); + void unregisterEditors(QList<Core::IEditor *> editors); + void updateEditorList(); + void selectEditor(QTreeWidgetItem *item = 0); + void updateEditor(); + void closeEditors(); + void closeAllEditors(); + void updateCurrentItem(QTreeWidgetItem *currentItem = 0); + void putFocusToEditorList(); + +private: + static void updateItem(QTreeWidgetItem *item, Core::IEditor *editor); + + Ui::OpenEditorsView m_ui; + QWidget *m_widget; +}; + +class OpenEditorsViewFactory : public Core::INavigationWidgetFactory +{ +public: + OpenEditorsViewFactory(); + virtual ~OpenEditorsViewFactory(); + QString displayName(); + virtual QKeySequence activationSequence(); + Core::NavigationView createWidget(); +}; + +} // namespace Internal +} // namespace Core + +#endif diff --git a/src/plugins/coreplugin/editormanager/openeditorsview.ui b/src/plugins/coreplugin/editormanager/openeditorsview.ui new file mode 100644 index 00000000000..7676ba39677 --- /dev/null +++ b/src/plugins/coreplugin/editormanager/openeditorsview.ui @@ -0,0 +1,39 @@ +<ui version="4.0" > + <class>OpenEditorsView</class> + <widget class="QWidget" name="OpenEditorsView" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>263</width> + <height>217</height> + </rect> + </property> + <property name="minimumSize" > + <size> + <width>200</width> + <height>100</height> + </size> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>0</number> + </property> + <item row="0" column="0" > + <widget class="QTreeWidget" name="editorList" > + <property name="uniformRowHeights" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/coreplugin/editormanager/openeditorswindow.cpp b/src/plugins/coreplugin/editormanager/openeditorswindow.cpp new file mode 100644 index 00000000000..2e97d444f78 --- /dev/null +++ b/src/plugins/coreplugin/editormanager/openeditorswindow.cpp @@ -0,0 +1,351 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "openeditorswindow.h" +#include "editorgroup.h" +#include "editormanager.h" + +#include <QtGui/QHeaderView> + +Q_DECLARE_METATYPE(Core::IEditor *) + +using namespace Core; +using namespace Core::Internal; + +const int OpenEditorsWindow::WIDTH = 300; +const int OpenEditorsWindow::HEIGHT = 200; +const int OpenEditorsWindow::MARGIN = 4; + +OpenEditorsWindow::OpenEditorsWindow(QWidget *parent) : + QWidget(parent, Qt::Popup), + m_editorList(new QTreeWidget(this)), + m_mode(HistoryMode), + m_current(0) +{ + resize(QSize(WIDTH, HEIGHT)); + m_editorList->setColumnCount(1); + m_editorList->header()->hide(); + m_editorList->setIndentation(0); + m_editorList->setSelectionMode(QAbstractItemView::SingleSelection); + m_editorList->setSelectionBehavior(QAbstractItemView::SelectItems); + m_editorList->setTextElideMode(Qt::ElideMiddle); +#ifdef Q_WS_MAC + m_editorList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); +#endif + m_editorList->installEventFilter(this); + m_editorList->setGeometry(MARGIN, MARGIN, WIDTH-2*MARGIN, HEIGHT-2*MARGIN); + + connect(m_editorList, SIGNAL(itemClicked(QTreeWidgetItem*, int)), + this, SLOT(editorClicked(QTreeWidgetItem*))); + + m_autoHide.setSingleShot(true); + connect(&m_autoHide, SIGNAL(timeout()), this, SLOT(selectAndHide())); + EditorManager *em = EditorManager::instance(); + connect(em, SIGNAL(editorOpened(Core::IEditor *)), + this, SLOT(updateEditorList())); + connect(em, SIGNAL(editorsClosed(QList<Core::IEditor *>)), + this, SLOT(updateEditorList())); + connect(em, SIGNAL(editorGroupsChanged()), + this, SLOT(updateEditorList())); + connect(em, SIGNAL(currentEditorChanged(Core::IEditor*)), + this, SLOT(updateEditorList())); +} + +void OpenEditorsWindow::selectAndHide() +{ + selectEditor(m_editorList->currentItem()); + setVisible(false); +} + +void OpenEditorsWindow::setVisible(bool visible) +{ + QWidget::setVisible(visible); + if (visible) { + updateEditorList(m_current); + m_autoHide.start(600); + setFocus(); + } +} + +bool OpenEditorsWindow::isCentering() +{ + if (m_mode == OpenEditorsWindow::HistoryMode || m_editorList->topLevelItemCount() < 3) + return false; + int internalMargin = m_editorList->viewport()->mapTo(m_editorList, QPoint(0,0)).y(); + QRect rect0 = m_editorList->visualItemRect(m_editorList->topLevelItem(0)); + QRect rect1 = m_editorList->visualItemRect(m_editorList->topLevelItem(m_editorList->topLevelItemCount()-1)); + int height = rect1.y() + rect1.height() - rect0.y(); + height += 2*internalMargin + 2*MARGIN; + if (height > HEIGHT) + return true; + return false; +} + +void OpenEditorsWindow::setMode(Mode mode) +{ + m_mode = mode; + updateEditorList(m_current); +} + +bool OpenEditorsWindow::event(QEvent *e) { + if (e->type() == QEvent::KeyRelease) { + QKeyEvent *ke = static_cast<QKeyEvent*>(e); + m_autoHide.stop(); + if (ke->modifiers() == 0 + /*HACK this is to overcome some event inconsistencies between platforms*/ + || (ke->modifiers() == Qt::AltModifier && (ke->key() == Qt::Key_Alt || ke->key() == -1))) { + selectAndHide(); + } + } + return QWidget::event(e); +} + +bool OpenEditorsWindow::eventFilter(QObject *obj, QEvent *e) +{ + if (obj == m_editorList) { + if (e->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast<QKeyEvent*>(e); + if (ke->key() == Qt::Key_Escape) { + m_current = EditorManager::instance()->currentEditor(); + updateSelectedEditor(); + setVisible(false); + return true; + } + if (ke->key() == Qt::Key_Return) { + selectEditor(m_editorList->currentItem()); + return true; + } + } + } + return QWidget::eventFilter(obj, e); +} + +void OpenEditorsWindow::focusInEvent(QFocusEvent *) +{ + m_editorList->setFocus(); +} + +void OpenEditorsWindow::selectUpDown(bool up) +{ + int itemCount = m_editorList->topLevelItemCount(); + if (itemCount < 2) + return; + int index = m_editorList->indexOfTopLevelItem(m_editorList->currentItem()); + if (index < 0) + return; + IEditor *editor = 0; + int count = 0; + while (!editor && count < itemCount) { + if (up) { + index--; + if (index < 0) + index = itemCount-1; + } else { + index++; + if (index >= itemCount) + index = 0; + } + editor = m_editorList->topLevelItem(index) + ->data(0, Qt::UserRole).value<IEditor *>(); + count++; + } + if (editor) + updateEditorList(editor); +} + +void OpenEditorsWindow::selectPreviousEditor() +{ + selectUpDown(m_mode == ListMode); +} + +void OpenEditorsWindow::selectNextEditor() +{ + selectUpDown(m_mode != ListMode); +} + +void OpenEditorsWindow::updateEditorList(IEditor *editor) +{ + if (!editor) + editor = EditorManager::instance()->currentEditor(); + m_current = editor; + if (m_mode == ListMode) + updateList(); + else if (m_mode == HistoryMode) + updateHistory(); +} + +void OpenEditorsWindow::updateHistory() +{ + EditorManager *em = EditorManager::instance(); + QList<IEditor *> history = em->editorHistory(); + int oldNum = m_editorList->topLevelItemCount(); + int num = history.count(); + int common = qMin(oldNum, num); + int selectedIndex = -1; + QTreeWidgetItem *item; + for (int i = 0; i < common; ++i) { + item = m_editorList->topLevelItem(i); + updateItem(item, history.at(i)); + if (history.at(i) == m_current) + selectedIndex = i; + } + for (int i = common; i < num; ++i) { + item = new QTreeWidgetItem(QStringList() << ""); + updateItem(item, history.at(i)); + m_editorList->addTopLevelItem(item); + if (history.at(i) == m_current) + selectedIndex = i; + } + for (int i = oldNum-1; i >= common; --i) { + delete m_editorList->takeTopLevelItem(i); + } + if (isCentering()) + centerOnItem(selectedIndex); + updateSelectedEditor(); +} + +void OpenEditorsWindow::updateList() +{ + EditorManager *em = EditorManager::instance(); + QList<EditorGroup *> groups = em->editorGroups(); + int oldNum = m_editorList->topLevelItemCount(); + int curItem = 0; + int selectedIndex = -1; + QTreeWidgetItem *item; + for (int i = 0; i < groups.count(); ++i) { + if (groups.count() > 1) { + if (curItem < oldNum) { + item = m_editorList->topLevelItem(curItem); + } else { + item = new QTreeWidgetItem(QStringList()<<""); + m_editorList->addTopLevelItem(item); + } + curItem++; + item->setText(0, tr("---Group %1---").arg(i)); + item->setFlags(0); + item->setData(0, Qt::UserRole, QVariant()); + } + foreach (IEditor *editor, groups.at(i)->editors()) { + if (curItem < oldNum) { + item = m_editorList->topLevelItem(curItem); + } else { + item = new QTreeWidgetItem(QStringList()<<""); + m_editorList->addTopLevelItem(item); + } + updateItem(item, editor); + if (editor == m_current) { + m_editorList->setCurrentItem(item); + selectedIndex = curItem; + } + curItem++; + } + } + for (int i = oldNum-1; i >= curItem; --i) { + delete m_editorList->takeTopLevelItem(i); + } + if (isCentering()) + centerOnItem(selectedIndex); + if (m_current == 0 && m_editorList->currentItem()) + m_editorList->currentItem()->setSelected(false); + m_editorList->scrollTo(m_editorList->currentIndex(), QAbstractItemView::PositionAtCenter); +} + +void OpenEditorsWindow::centerOnItem(int selectedIndex) +{ + if (selectedIndex >= 0) { + QTreeWidgetItem *item; + int num = m_editorList->topLevelItemCount(); + int rotate = selectedIndex-(num-1)/2; + for (int i = 0; i < rotate; ++i) { + item = m_editorList->takeTopLevelItem(0); + m_editorList->addTopLevelItem(item); + } + rotate = -rotate; + for (int i = 0; i < rotate; ++i) { + item = m_editorList->takeTopLevelItem(num-1); + m_editorList->insertTopLevelItem(0, item); + } + } +} + +void OpenEditorsWindow::updateItem(QTreeWidgetItem *item, IEditor *editor) +{ + static const QIcon lockedIcon(QLatin1String(":/qworkbench/images/locked.png")); + static const QIcon emptyIcon(QLatin1String(":/qworkbench/images/empty14.png")); + + QString title = editor->displayName(); + if (editor->file()->isModified()) + title += tr("*"); + item->setIcon(0, editor->file()->isReadOnly() ? lockedIcon : emptyIcon); + item->setText(0, title); + item->setToolTip(0, editor->file()->fileName()); + item->setData(0, Qt::UserRole, QVariant::fromValue(editor)); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + item->setTextAlignment(0, Qt::AlignLeft); +} + +void OpenEditorsWindow::selectEditor(QTreeWidgetItem *item) +{ + IEditor *editor = 0; + if (item) + editor = item->data(0, Qt::UserRole).value<IEditor*>(); + EditorManager::instance()->setCurrentEditor(editor); +} + +void OpenEditorsWindow::editorClicked(QTreeWidgetItem *item) +{ + selectEditor(item); + setFocus(); +} + +void OpenEditorsWindow::updateSelectedEditor() +{ + if (m_current == 0 && m_editorList->currentItem()) { + m_editorList->currentItem()->setSelected(false); + return; + } + int num = m_editorList->topLevelItemCount(); + for (int i = 0; i < num; ++i) { + IEditor *editor = m_editorList->topLevelItem(i) + ->data(0, Qt::UserRole).value<IEditor *>(); + if (editor == m_current) { + m_editorList->setCurrentItem(m_editorList->topLevelItem(i)); + break; + } + } + m_editorList->scrollTo(m_editorList->currentIndex(), QAbstractItemView::PositionAtCenter); +} + +void OpenEditorsWindow::setSelectedEditor(IEditor *editor) +{ + updateEditorList(editor); +} diff --git a/src/plugins/coreplugin/editormanager/openeditorswindow.h b/src/plugins/coreplugin/editormanager/openeditorswindow.h new file mode 100644 index 00000000000..fcefe42eff9 --- /dev/null +++ b/src/plugins/coreplugin/editormanager/openeditorswindow.h @@ -0,0 +1,99 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef OPENEDITORSWINDOW_H +#define OPENEDITORSWINDOW_H + +#include <QtCore/QTimer> +#include <QtGui/QWidget> +#include <QtGui/QKeyEvent> +#include <QtGui/QFocusEvent> +#include <QtGui/QTreeWidget> +#include <QtDebug> + +namespace Core { + +class IEditor; + +namespace Internal { + +class OpenEditorsWindow : public QWidget +{ + Q_OBJECT + +public: + enum Mode {ListMode, HistoryMode }; + + OpenEditorsWindow(QWidget *parent = 0); + ~OpenEditorsWindow() {} + + void setMode(Mode mode); + Mode mode() const { return m_mode; } + + bool event(QEvent *e); + bool eventFilter(QObject *src, QEvent *e); + void focusInEvent(QFocusEvent *); + void setVisible(bool visible); + void setSelectedEditor(IEditor *editor); + void selectNextEditor(); + void selectPreviousEditor(); + IEditor *selectedEditor() const { return m_current; } + +private slots: + void updateEditorList(IEditor *current = 0); + void editorClicked(QTreeWidgetItem *item); + void selectEditor(QTreeWidgetItem *item); + void selectAndHide(); + +private: + static const int WIDTH; + static const int HEIGHT; + static const int MARGIN; + + static void updateItem(QTreeWidgetItem *item, IEditor *editor); + void updateList(); + void updateHistory(); + void updateSelectedEditor(); + bool isCentering(); + void centerOnItem(int selectedIndex); + void selectUpDown(bool up); + + QTreeWidget *m_editorList; + Mode m_mode; + QTimer m_autoHide; + IEditor *m_current; +}; + +} // namespace Internal +} // namespace Core + +#endif // OPENEDITORSWINDOW_H diff --git a/src/plugins/coreplugin/editormanager/stackededitorgroup.cpp b/src/plugins/coreplugin/editormanager/stackededitorgroup.cpp new file mode 100644 index 00000000000..ffb0b147432 --- /dev/null +++ b/src/plugins/coreplugin/editormanager/stackededitorgroup.cpp @@ -0,0 +1,375 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "stackededitorgroup.h" +#include "editormanager.h" +#include "coreimpl.h" + +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QMimeData> +#include <QtGui/QComboBox> +#include <QtGui/QHBoxLayout> +#include <QtGui/QPainter> +#include <QtGui/QStyle> +#include <QtGui/QStyleOption> +#include <QtGui/QMouseEvent> +#include <QtGui/QApplication> +#include <QtGui/QToolBar> +#include <QtGui/QToolButton> +#include <QtGui/QLabel> +#include <QtGui/QStackedWidget> +#include <QtDebug> +#ifdef Q_WS_MAC +#include <qmacstyle_mac.h> +#endif + +Q_DECLARE_METATYPE(Core::IEditor *) + +using namespace Core; +using namespace Core::Internal; + +StackedEditorGroup::StackedEditorGroup(QWidget *parent) : + EditorGroup(parent), + m_toplevel(new QWidget), + m_toolBar(new QWidget), + m_container(new QStackedWidget(this)), + m_editorList(new QComboBox), + m_closeButton(new QToolButton), + m_lockButton(new QToolButton), + m_defaultToolBar(new QToolBar(this)), + m_infoWidget(new QFrame(this)), + m_editorForInfoWidget(0) +{ + QVBoxLayout *tl = new QVBoxLayout(m_toplevel); + tl->setSpacing(0); + tl->setMargin(0); + { + m_editorList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_editorList->setMinimumContentsLength(20); + m_proxyModel.setSourceModel(model()); + m_proxyModel.sort(0); + m_editorList->setModel(&m_proxyModel); + m_editorList->setMaxVisibleItems(40); + + QToolBar *editorListToolBar = new QToolBar; + + editorListToolBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored); + editorListToolBar->addWidget(m_editorList); + + m_defaultToolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + m_activeToolBar = m_defaultToolBar; + + QHBoxLayout *toolBarLayout = new QHBoxLayout; + toolBarLayout->setMargin(0); + toolBarLayout->setSpacing(0); + toolBarLayout->addWidget(m_defaultToolBar); + m_toolBar->setLayout(toolBarLayout); + + m_lockButton->setAutoRaise(true); + m_lockButton->setProperty("type", QLatin1String("dockbutton")); + + m_closeButton->setAutoRaise(true); + m_closeButton->setIcon(QIcon(":/qworkbench/images/closebutton.png")); + m_closeButton->setProperty("type", QLatin1String("dockbutton")); + + QToolBar *rightToolBar = new QToolBar; + rightToolBar->setLayoutDirection(Qt::RightToLeft); + rightToolBar->addWidget(m_closeButton); + rightToolBar->addWidget(m_lockButton); + + QHBoxLayout *toplayout = new QHBoxLayout; + toplayout->setSpacing(0); + toplayout->setMargin(0); + toplayout->addWidget(editorListToolBar); + toplayout->addWidget(m_toolBar, 1); // Custom toolbar stretches + toplayout->addWidget(rightToolBar); + + QWidget *top = new QWidget; + QVBoxLayout *vlayout = new QVBoxLayout(top); + vlayout->setSpacing(0); + vlayout->setMargin(0); + vlayout->addLayout(toplayout); + tl->addWidget(top); + + connect(m_editorList, SIGNAL(currentIndexChanged(int)), this, SLOT(listSelectionChanged(int))); + connect(m_lockButton, SIGNAL(clicked()), this, SLOT(makeEditorWritable())); + connect(m_closeButton, SIGNAL(clicked()), this, SLOT(sendCloseRequest())); + } + { + m_infoWidget->setFrameStyle(QFrame::Panel | QFrame::Raised); + m_infoWidget->setLineWidth(1); + m_infoWidget->setForegroundRole(QPalette::ToolTipText); + m_infoWidget->setBackgroundRole(QPalette::ToolTipBase); + m_infoWidget->setAutoFillBackground(true); + + + QHBoxLayout *hbox = new QHBoxLayout(m_infoWidget); + hbox->setMargin(2); + m_infoWidgetLabel = new QLabel("Placeholder"); + m_infoWidgetLabel->setForegroundRole(QPalette::ToolTipText); + hbox->addWidget(m_infoWidgetLabel); + hbox->addStretch(1); + + m_infoWidgetButton = new QToolButton; + m_infoWidgetButton->setText(tr("Placeholder")); + hbox->addWidget(m_infoWidgetButton); + + QToolButton *closeButton = new QToolButton; + closeButton->setAutoRaise(true); + closeButton->setIcon(QIcon(":/qworkbench/images/clear.png")); + closeButton->setToolTip(tr("Close")); + connect(closeButton, SIGNAL(clicked()), m_infoWidget, SLOT(hide())); + + hbox->addWidget(closeButton); + + + tl->addWidget(m_infoWidget); + } + tl->addWidget(m_container); + + QHBoxLayout *l = new QHBoxLayout; + l->setSpacing(0); + l->setMargin(0); + l->addWidget(m_toplevel); + setLayout(l); + + m_toplevel->setVisible(false); +} + +void StackedEditorGroup::showEditorInfoBar(const QString &kind, + const QString &infoText, + const QString &buttonText, + QObject *object, const char *member) +{ + m_infoWidgetKind = kind; + m_infoWidgetLabel->setText(infoText); + m_infoWidgetButton->setText(buttonText); + m_infoWidgetButton->disconnect(); + if (object && member) + connect(m_infoWidgetButton, SIGNAL(clicked()), object, member); + m_infoWidget->setVisible(true); + m_editorForInfoWidget = currentEditor(); +} + +void StackedEditorGroup::hideEditorInfoBar(const QString &kind) +{ + if (kind == m_infoWidgetKind) + m_infoWidget->setVisible(false); +} + + +StackedEditorGroup::~StackedEditorGroup() +{ +} + +void StackedEditorGroup::focusInEvent(QFocusEvent *e) +{ + if (m_container->count() > 0) { + setEditorFocus(m_container->currentIndex()); + } else { + EditorGroup::focusInEvent(e); + } +} + +void StackedEditorGroup::setEditorFocus(int index) +{ + QWidget *w = m_container->widget(index); + w->setFocus(); +} + +void StackedEditorGroup::addEditor(IEditor *editor) +{ + insertEditor(editorCount(), editor); +} + +void StackedEditorGroup::insertEditor(int index, IEditor *editor) +{ + EditorGroup::insertEditor(index, editor); + if (m_container->indexOf(editor->widget()) != -1) + return; + + m_container->insertWidget(index, editor->widget()); + m_widgetEditorMap.insert(editor->widget(), editor); + + QToolBar *toolBar = editor->toolBar(); + if (toolBar) + m_toolBar->layout()->addWidget(toolBar); + connect(editor, SIGNAL(changed()), this, SLOT(updateEditorStatus())); + + updateEditorStatus(editor); + updateToolBar(editor); + + emit editorAdded(editor); +} + +void StackedEditorGroup::sendCloseRequest() +{ + emit closeRequested(currentEditor()); +} + +void StackedEditorGroup::removeEditor(IEditor *editor) +{ + Q_ASSERT(editor); + EditorGroup::removeEditor(editor); + const int index = m_container->indexOf(editor->widget()); + if (index != -1) { + m_container->removeWidget(editor->widget()); + m_widgetEditorMap.remove(editor->widget()); + editor->widget()->setParent(0); + disconnect(editor, SIGNAL(changed()), this, SLOT(updateEditorStatus())); + QToolBar *toolBar = editor->toolBar(); + if (toolBar != 0) { + if (m_activeToolBar == toolBar) { + m_activeToolBar = m_defaultToolBar; + m_activeToolBar->setVisible(true); + } + m_toolBar->layout()->removeWidget(toolBar); + toolBar->setVisible(false); + toolBar->setParent(0); + } + if (m_container->count() == 0) { + m_toplevel->setVisible(false); + setFocus(); + } + emit editorRemoved(editor); + } +} + +IEditor *StackedEditorGroup::currentEditor() const +{ + if (m_container->count() > 0) + return m_widgetEditorMap.value(m_container->currentWidget()); + return 0; +} + +void StackedEditorGroup::setCurrentEditor(IEditor *editor) +{ + if (!editor || m_container->count() <= 0 + || m_container->indexOf(editor->widget()) == -1) + return; + m_toplevel->setVisible(true); + const int idx = m_container->indexOf(editor->widget()); + Q_ASSERT(idx >= 0); + if (m_container->currentIndex() != idx) { + m_container->setCurrentIndex(idx); + + const bool block = m_editorList->blockSignals(true); + m_editorList->setCurrentIndex(indexOf(editor)); + m_editorList->blockSignals(block); + + updateEditorStatus(editor); + updateToolBar(editor); + } + setEditorFocus(idx); + if (editor != m_editorForInfoWidget) { + m_infoWidget->hide(); + m_editorForInfoWidget = 0; + } +} + +void StackedEditorGroup::updateEditorStatus(IEditor *editor) { + if (!editor) + editor = qobject_cast<IEditor *>(sender()); + Q_ASSERT(editor); + + static const QIcon lockedIcon(QLatin1String(":/qworkbench/images/locked.png")); + static const QIcon unlockedIcon(QLatin1String(":/qworkbench/images/unlocked.png")); + + if (editor->file()->isReadOnly()) { + m_lockButton->setIcon(lockedIcon); + m_lockButton->setEnabled(!editor->file()->fileName().isEmpty()); + m_lockButton->setToolTip(tr("Make writable")); + } else { + m_lockButton->setIcon(unlockedIcon); + m_lockButton->setEnabled(false); + m_lockButton->setToolTip(tr("File is writable")); + } + if (currentEditor() == editor) + m_editorList->setToolTip(model()->data(model()->indexOf(editor), Qt::ToolTipRole).toString()); + model()->emitDataChanged(editor); +} + +void StackedEditorGroup::updateToolBar(IEditor *editor) +{ + QToolBar *toolBar = editor->toolBar(); + if (!toolBar) + toolBar = m_defaultToolBar; + if (m_activeToolBar == toolBar) + return; + m_activeToolBar->setVisible(false); + toolBar->setVisible(true); + m_activeToolBar = toolBar; +} + +int StackedEditorGroup::editorCount() const +{ + return model()->editors().count(); +} + +QList<IEditor *> StackedEditorGroup::editors() const +{ + QAbstractItemModel *model = m_editorList->model(); + QList<IEditor*> output; + int rows = model->rowCount(); + for (int i = 0; i < rows; ++i) + output.append(model->data(model->index(i, 0), Qt::UserRole).value<IEditor*>()); + return output; +} + +QList<IEditor *> StackedEditorGroup::editorsInNaturalOrder() const +{ + return model()->editors(); +} + +void StackedEditorGroup::makeEditorWritable() +{ + CoreImpl::instance()->editorManager()->makeEditorWritable(currentEditor()); +} + +void StackedEditorGroup::listSelectionChanged(int index) +{ + QAbstractItemModel *model = m_editorList->model(); + setCurrentEditor(model->data(model->index(index, 0), Qt::UserRole).value<IEditor*>()); +} + +int StackedEditorGroup::indexOf(IEditor *editor) +{ + QAbstractItemModel *model = m_editorList->model(); + int rows = model->rowCount(); + for (int i = 0; i < rows; ++i) { + if (editor == model->data(model->index(i, 0), Qt::UserRole).value<IEditor*>()) + return i; + } + Q_ASSERT(false); + return 0; +} diff --git a/src/plugins/coreplugin/editormanager/stackededitorgroup.h b/src/plugins/coreplugin/editormanager/stackededitorgroup.h new file mode 100644 index 00000000000..5b7cfd5298a --- /dev/null +++ b/src/plugins/coreplugin/editormanager/stackededitorgroup.h @@ -0,0 +1,111 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef STACKEDEDITORGROUP_H +#define STACKEDEDITORGROUP_H + +#include "editorgroup.h" + +#include <QtCore/QMap> +#include <QtGui/QSortFilterProxyModel> + +QT_BEGIN_NAMESPACE +class QComboBox; +class QToolBar; +class QToolButton; +class QLabel; +class QStackedWidget; +QT_END_NAMESPACE + +namespace Core { +namespace Internal { + +class StackedEditorGroup : public EditorGroup +{ + Q_OBJECT + +public: + StackedEditorGroup(QWidget *parent = 0); + virtual ~StackedEditorGroup(); + + //EditorGroup + int editorCount() const; + void addEditor(IEditor *editor); + void insertEditor(int i, IEditor *editor); + void removeEditor(IEditor *editor); + IEditor *currentEditor() const; + void setCurrentEditor(IEditor *editor); + QList<IEditor *> editors() const; + void showEditorInfoBar(const QString &kind, + const QString &infoText, + const QString &buttonText, + QObject *object, const char *member); + void hideEditorInfoBar(const QString &kind); + + void focusInEvent(QFocusEvent *e); + +protected: + QList<IEditor *> editorsInNaturalOrder() const; + +private slots: + void sendCloseRequest(); + void updateEditorStatus(Core::IEditor *editor = 0); + void setEditorFocus(int index); + void makeEditorWritable(); + void listSelectionChanged(int index); + +private: + void updateToolBar(IEditor *editor); + int indexOf(IEditor *editor); + void checkProjectLoaded(IEditor *editor); + + QWidget *m_toplevel; + QWidget *m_toolBar; + QToolBar *m_activeToolBar; + QStackedWidget *m_container; + QComboBox *m_editorList; + QToolButton *m_closeButton; + QToolButton *m_lockButton; + QToolBar *m_defaultToolBar; + QString m_infoWidgetKind; + QFrame *m_infoWidget; + QLabel *m_infoWidgetLabel; + QToolButton *m_infoWidgetButton; + IEditor *m_editorForInfoWidget; + QSortFilterProxyModel m_proxyModel; + QMap<QWidget *, IEditor *> m_widgetEditorMap; +}; + +} // namespace Internal +} // namespace Core + +#endif // STACKEDEDITORGROUP_H diff --git a/src/plugins/coreplugin/fancyactionbar.cpp b/src/plugins/coreplugin/fancyactionbar.cpp new file mode 100644 index 00000000000..08ad842eed2 --- /dev/null +++ b/src/plugins/coreplugin/fancyactionbar.cpp @@ -0,0 +1,137 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "fancyactionbar.h" + +#include <QtGui/QHBoxLayout> +#include <QtGui/QPainter> +#include <QtGui/QPicture> +#include <QtGui/QVBoxLayout> +#include <QtSvg/QSvgRenderer> + +using namespace Core; +using namespace Internal; + +static const char* const svgIdButtonBase = "ButtonBase"; +static const char* const svgIdButtonNormalBase = "ButtonNormalBase"; +static const char* const svgIdButtonNormalOverlay = "ButtonNormalOverlay"; +static const char* const svgIdButtonPressedBase = "ButtonPressedBase"; +static const char* const svgIdButtonPressedOverlay = "ButtonPressedOverlay"; +static const char* const svgIdButtonDisabledOverlay = "ButtonDisabledOverlay"; +static const char* const svgIdButtonHoverOverlay = "ButtonHoverOverlay"; + +static const char* const elementsSvgIds[] = { + svgIdButtonBase, + svgIdButtonNormalBase, + svgIdButtonNormalOverlay, + svgIdButtonPressedBase, + svgIdButtonPressedOverlay, + svgIdButtonDisabledOverlay, + svgIdButtonHoverOverlay +}; + +const QMap<QString, QPicture> &buttonElementsMap() +{ + static QMap<QString, QPicture> result; + if (result.isEmpty()) { + QSvgRenderer renderer(QLatin1String(":/fancyactionbar/images/fancytoolbutton.svg")); + for (size_t i = 0; i < sizeof(elementsSvgIds)/sizeof(elementsSvgIds[0]); i++) { + QString elementId(elementsSvgIds[i]); + QPicture elementPicture; + QPainter elementPainter(&elementPicture); + renderer.render(&elementPainter, elementId); + result.insert(elementId, elementPicture); + } + } + return result; +} + +FancyToolButton::FancyToolButton(QWidget *parent) + : QToolButton(parent) + , m_buttonElements(buttonElementsMap()) +{ + setAttribute(Qt::WA_Hover, true); +} + +void FancyToolButton::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + QPainter p(this); + p.drawPicture(0, 0, m_buttonElements.value(svgIdButtonBase)); + p.drawPicture(0, 0, m_buttonElements.value(isDown() ? svgIdButtonPressedBase : svgIdButtonNormalBase)); +#ifndef Q_WS_MAC // Mac UI's dont usually do hover + if (underMouse() && isEnabled()) + p.drawPicture(0, 0, m_buttonElements.value(svgIdButtonHoverOverlay)); +#endif + if (!icon().isNull()) { + icon().paint(&p, rect()); + } else { + const int margin = 4; + p.drawText(rect().adjusted(margin, margin, -margin, -margin), Qt::AlignCenter | Qt::TextWordWrap, text()); + } + if (!isEnabled()) + p.drawPicture(0, 0, m_buttonElements.value(svgIdButtonDisabledOverlay)); + p.drawPicture(0, 0, m_buttonElements.value(isDown() ? svgIdButtonPressedOverlay : svgIdButtonNormalOverlay)); +} + +void FancyActionBar::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) +} + +QSize FancyToolButton::sizeHint() const +{ + return m_buttonElements.value(svgIdButtonBase).boundingRect().size(); +} + +FancyActionBar::FancyActionBar(QWidget *parent) + : QWidget(parent) +{ + m_actionsLayout = new QVBoxLayout; + + QHBoxLayout *centeringLayout = new QHBoxLayout; + centeringLayout->addStretch(); + centeringLayout->addLayout(m_actionsLayout); + centeringLayout->addStretch(); + setLayout(centeringLayout); +} + +void FancyActionBar::insertAction(int index, QAction *action, QMenu *menu) +{ + FancyToolButton *toolButton = new FancyToolButton(this); + toolButton->setDefaultAction(action); + if (menu) { + toolButton->setMenu(menu); + toolButton->setPopupMode(QToolButton::DelayedPopup); + } + m_actionsLayout->insertWidget(index, toolButton); +} diff --git a/src/plugins/coreplugin/fancyactionbar.h b/src/plugins/coreplugin/fancyactionbar.h new file mode 100644 index 00000000000..701e834ad60 --- /dev/null +++ b/src/plugins/coreplugin/fancyactionbar.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FANCYACTIONBAR_H +#define FANCYACTIONBAR_H + +#include <QtCore/QMap> +#include <QtGui/QToolButton> + +QT_BEGIN_NAMESPACE +class QMenu; +class QVBoxLayout; +QT_END_NAMESPACE + +namespace Core { +namespace Internal { + +class FancyToolButton : public QToolButton +{ +public: + FancyToolButton(QWidget *parent = 0); + + void paintEvent(QPaintEvent *event); + QSize sizeHint() const; + +private: + const QMap<QString, QPicture> &m_buttonElements; +}; + +class FancyActionBar : public QWidget +{ + Q_OBJECT + +public: + FancyActionBar(QWidget *parent = 0); + + void paintEvent(QPaintEvent *event); + void insertAction(int index, QAction *action, QMenu *menu = 0); + +private: + QVBoxLayout *m_actionsLayout; +}; + +} // namespace Internal +} // namespace Core + +#endif // FANCYACTIONBAR_H diff --git a/src/plugins/coreplugin/fancyactionbar.qrc b/src/plugins/coreplugin/fancyactionbar.qrc new file mode 100644 index 00000000000..e54a3c578cd --- /dev/null +++ b/src/plugins/coreplugin/fancyactionbar.qrc @@ -0,0 +1,10 @@ +<RCC> + <qresource prefix="/fancyactionbar" > + <file>images/fancytoolbutton.svg</file> + <file>images/mode_Debug.png</file> + <file>images/mode_Edit.png</file> + <file>images/mode_Output.png</file> + <file>images/mode_Project.png</file> + <file>images/mode_Reference.png</file> + </qresource> +</RCC> diff --git a/src/plugins/coreplugin/fancytabwidget.cpp b/src/plugins/coreplugin/fancytabwidget.cpp new file mode 100644 index 00000000000..ed57c27b921 --- /dev/null +++ b/src/plugins/coreplugin/fancytabwidget.cpp @@ -0,0 +1,373 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "fancytabwidget.h" +#include "stylehelper.h" + +#include <QDebug> + +#include <QtGui/QColorDialog> +#include <QtGui/QHBoxLayout> +#include <QtGui/QVBoxLayout> +#include <QtGui/QMouseEvent> +#include <QtGui/QWindowsStyle> +#include <QtGui/QPainter> +#include <QtGui/QSplitter> +#include <QtGui/QStackedLayout> +#include <QtGui/QStatusBar> +#include <QtGui/QToolBar> +#include <QtGui/QToolButton> + +using namespace Core; +using namespace Internal; + +const int FancyTabBar::m_rounding = 22; +const int FancyTabBar::m_textPadding = 4; + +FancyTabBar::FancyTabBar(QWidget *parent) + : QTabBar(parent) +{ + setStyle(new QWindowsStyle); + setDrawBase(false); + setElideMode(Qt::ElideNone); + setMinimumWidth(qMax(2 * m_rounding, 40)); + setAttribute(Qt::WA_Hover, true); + setFocusPolicy(Qt::NoFocus); + m_hoverControl.setFrameRange(0, 20); + m_hoverControl.setDuration(130); + m_hoverControl.setCurveShape(QTimeLine::EaseInCurve); + connect(&m_hoverControl, SIGNAL(frameChanged(int)), this, SLOT(updateHover())); + setMouseTracking(true); // Needed for hover events + setExpanding(false); +} + +FancyTabBar::~FancyTabBar() +{ + delete style(); +} + +QSize FancyTabBar::tabSizeHint(int index) const +{ + QFont boldFont(font()); + boldFont.setPointSizeF(StyleHelper::sidebarFontSize()); + boldFont.setBold(true); + QFontMetrics fm(boldFont); + int spacing = 6; + int width = 60 + spacing + 2; + return QSize(width, tabIcon(index).actualSize(QSize(64, 64)).height() + spacing + fm.height()); +} + +void FancyTabBar::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + QPainter p(this); + + for (int i = 0; i < count(); ++i) + if (i != currentIndex()) + paintTab(&p, i); + + // paint active tab last, since it overlaps the neighbors + paintTab(&p, currentIndex()); + + +} + +// Handle hover events for mouse fade ins +void FancyTabBar::mouseMoveEvent(QMouseEvent *e) +{ + if (!m_hoverRect.contains(e->pos())) { + for (int i = 0; i < count(); ++i) { + QRect area = tabRect(i); + if (area.contains(e->pos())) { + QRect oldHoverRect = m_hoverRect; + m_hoverRect = area; + update(oldHoverRect); + m_hoverControl.stop(); + m_hoverControl.start(); + break; + } + } + } +} + +void FancyTabBar::updateHover() +{ + update(m_hoverRect); +} + +// Resets hover animation on mouse enter +void FancyTabBar::enterEvent(QEvent *e) +{ + Q_UNUSED(e); + m_hoverRect = QRect(); +} + +// Resets hover animation on mouse enter +void FancyTabBar::leaveEvent(QEvent *e) +{ + Q_UNUSED(e); + + m_hoverControl.stop(); + m_hoverControl.start(); +} + +void FancyTabBar::paintTab(QPainter *painter, int tabIndex) const +{ + QStyleOptionTabV2 tab; + initStyleOption(&tab, tabIndex); + QRect rect = tab.rect; + painter->save(); + + + bool selected = tab.state & QStyle::State_Selected; + bool hover = tab.state & QStyle::State_MouseOver; + +#ifdef Q_WS_MAC + hover = false; // Dont hover on Mac +#endif + + QColor background = QColor(0, 0, 0, 10); + QColor hoverColor; + + if (hover) { + hoverColor = QColor(255, 255, 255, m_hoverControl.currentFrame()*2); + } + + QColor light = QColor(255, 255, 255, 40); + QColor dark = QColor(0, 0, 0, 60); + + if (selected) { + QLinearGradient selectedGradient(rect.topLeft(), QPoint(rect.center().x(), rect.bottom())); + selectedGradient.setColorAt(0, Qt::white); + selectedGradient.setColorAt(0.3, Qt::white); + selectedGradient.setColorAt(0.7, QColor(230, 230, 230)); + + painter->fillRect(rect, selectedGradient); + painter->setPen(QColor(200, 200, 200)); + painter->drawLine(rect.topLeft(), rect.topRight()); + painter->setPen(QColor(150, 160, 200)); + painter->drawLine(rect.bottomLeft(), rect.bottomRight()); + } else { + painter->fillRect(tab.rect, background); + if (hover) + painter->fillRect(tab.rect, hoverColor); + painter->setPen(QPen(light, 0)); + painter->drawLine(rect.topLeft(), rect.topRight()); + painter->setPen(QPen(dark, 0)); + painter->drawLine(rect.bottomLeft(), rect.bottomRight()); + } + + QString tabText(tab.text); + QRect tabTextRect(tab.rect); + QRect tabIconRect(tab.rect); + QFont boldFont(painter->font()); + boldFont.setPointSizeF(StyleHelper::sidebarFontSize()); + boldFont.setBold(true); + painter->setFont(boldFont); + painter->setPen(selected ? StyleHelper::panelTextColor() : QColor(30, 30, 30, 80)); + int textFlags = Qt::AlignCenter | Qt::AlignBottom | Qt::ElideRight | Qt::TextWordWrap; + painter->drawText(tabTextRect, textFlags, tabText); + painter->setPen(selected ? QColor(60, 60, 60) : StyleHelper::panelTextColor()); + int textHeight = painter->fontMetrics().boundingRect(QRect(0, 0, width(), height()), Qt::TextWordWrap, tabText).height(); + tabIconRect.adjust(0, 4, 0, -textHeight); + style()->drawItemPixmap(painter, tabIconRect, Qt::AlignCenter | Qt::AlignVCenter, + tab.icon.pixmap(QSize(64, 64))); + painter->translate(0, -1); + painter->drawText(tabTextRect, textFlags, tabText); + painter->restore(); +} + +void FancyTabBar::tabInserted(int index) +{ + Q_UNUSED(index) +} + +void FancyTabBar::tabRemoved(int index) +{ + Q_UNUSED(index) +} + +////// +// FancyColorButton +////// + +class FancyColorButton : public QWidget +{ +public: + FancyColorButton(QWidget *parent) + : m_parent(parent) + { + setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + } + + void mousePressEvent(QMouseEvent *ev) + { + if (ev->modifiers() & Qt::ShiftModifier) + StyleHelper::setBaseColor(QColorDialog::getColor(StyleHelper::baseColor(), m_parent)); + } +private: + QWidget *m_parent; +}; + +////// +// FancyTabWidget +////// + +FancyTabWidget::FancyTabWidget(QWidget *parent) + : QWidget(parent) +{ + m_tabBar = new FancyTabBar(this); + m_tabBar->setShape(QTabBar::RoundedEast); + m_tabBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + m_tabBar->setUsesScrollButtons(false); + + m_selectionWidget = new QWidget(this); + QVBoxLayout *selectionLayout = new QVBoxLayout; + selectionLayout->setSpacing(0); + selectionLayout->setMargin(0); + + QToolBar *bar = new QToolBar; + bar->addWidget(new FancyColorButton(this)); + bar->setFixedHeight(StyleHelper::navigationWidgetHeight()); + selectionLayout->addWidget(bar); + + selectionLayout->addWidget(m_tabBar, 1); + m_selectionWidget->setLayout(selectionLayout); + m_selectionWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding); + + m_cornerWidgetContainer = new QWidget(this); + m_cornerWidgetContainer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + m_cornerWidgetContainer->setAutoFillBackground(false); + + QVBoxLayout *cornerWidgetLayout = new QVBoxLayout; + cornerWidgetLayout->setSpacing(0); + cornerWidgetLayout->setMargin(0); + cornerWidgetLayout->addStretch(); + m_cornerWidgetContainer->setLayout(cornerWidgetLayout); + + selectionLayout->addWidget(m_cornerWidgetContainer, 0); + + m_modesStack = new QStackedLayout; + m_statusBar = new QStatusBar; + m_statusBar->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->setMargin(0); + vlayout->setSpacing(0); + vlayout->addLayout(m_modesStack); + vlayout->addWidget(m_statusBar); + + QHBoxLayout *layout = new QHBoxLayout; + layout->setMargin(0); + layout->setSpacing(1); + layout->addWidget(m_selectionWidget); + layout->addLayout(vlayout); + setLayout(layout); + + connect(m_tabBar, SIGNAL(currentChanged(int)), this, SLOT(showWidget(int))); +} + +void FancyTabWidget::insertTab(int index, QWidget *tab, const QIcon &icon, const QString &label) +{ + m_modesStack->insertWidget(index, tab); + m_tabBar->insertTab(index, icon, label); +} + +void FancyTabWidget::removeTab(int index) +{ + m_modesStack->removeWidget(m_modesStack->widget(index)); + m_tabBar->removeTab(index); +} + +void FancyTabWidget::setBackgroundBrush(const QBrush &brush) +{ + QPalette pal = m_tabBar->palette(); + pal.setBrush(QPalette::Mid, brush); + m_tabBar->setPalette(pal); + pal = m_cornerWidgetContainer->palette(); + pal.setBrush(QPalette::Mid, brush); + m_cornerWidgetContainer->setPalette(pal); +} + +void FancyTabWidget::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + QPainter p(this); + + QRect rect = m_selectionWidget->rect().adjusted(0, 0, 1, 0); + StyleHelper::verticalGradient(&p, rect, rect); + p.setPen(StyleHelper::borderColor()); + p.drawLine(rect.topRight(), rect.bottomRight()); +} + +void FancyTabWidget::insertCornerWidget(int pos, QWidget *widget) +{ + QVBoxLayout *layout = static_cast<QVBoxLayout *>(m_cornerWidgetContainer->layout()); + layout->insertWidget(pos, widget); +} + +int FancyTabWidget::cornerWidgetCount() const +{ + return m_cornerWidgetContainer->layout()->count(); +} + +void FancyTabWidget::addCornerWidget(QWidget *widget) +{ + m_cornerWidgetContainer->layout()->addWidget(widget); +} + +int FancyTabWidget::currentIndex() const +{ + return m_tabBar->currentIndex(); +} + +QStatusBar *FancyTabWidget::statusBar() const +{ + return m_statusBar; +} + +void FancyTabWidget::setCurrentIndex(int index) +{ + m_tabBar->setCurrentIndex(index); +} + +void FancyTabWidget::showWidget(int index) +{ + emit currentAboutToShow(index); + m_modesStack->setCurrentIndex(index); + emit currentChanged(index); +} + +void FancyTabWidget::setTabToolTip(int index, const QString &toolTip) +{ + m_tabBar->setTabToolTip(index, toolTip); +} diff --git a/src/plugins/coreplugin/fancytabwidget.h b/src/plugins/coreplugin/fancytabwidget.h new file mode 100644 index 00000000000..19fd1731102 --- /dev/null +++ b/src/plugins/coreplugin/fancytabwidget.h @@ -0,0 +1,118 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FANCYTABWIDGET_H +#define FANCYTABWIDGET_H + +#include <QtGui/QPushButton> +#include <QtGui/QTabBar> +#include <QtGui/QStyleOptionTabV2> +#include <QtCore/QTimeLine> + +QT_BEGIN_NAMESPACE +class QPainter; +class QStackedLayout; +class QStatusBar; +QT_END_NAMESPACE + +namespace Core { +namespace Internal { + +class FancyTabBar : public QTabBar +{ + Q_OBJECT + +public: + FancyTabBar(QWidget *parent = 0); + ~FancyTabBar(); + + QSize tabSizeHint(int index) const; + void paintEvent(QPaintEvent *event); + void paintTab(QPainter *painter, int tabIndex) const; + void mouseMoveEvent(QMouseEvent *); + void enterEvent(QEvent *); + void leaveEvent(QEvent *); + void tabInserted(int index); + void tabRemoved(int index); + +public slots: + void updateHover(); + +private: + static const int m_rounding; + static const int m_textPadding; + QTimeLine m_hoverControl; + QRect m_hoverRect; +}; + +class FancyTabWidget : public QWidget +{ + Q_OBJECT + +public: + FancyTabWidget(QWidget *parent = 0); + + void insertTab(int index, QWidget *tab, const QIcon &icon, const QString &label); + void removeTab(int index); + void setBackgroundBrush(const QBrush &brush); + void addCornerWidget(QWidget *widget); + void insertCornerWidget(int pos, QWidget *widget); + int cornerWidgetCount() const; + void setTabToolTip(int index, const QString &toolTip); + + void paintEvent(QPaintEvent *event); + + int currentIndex() const; + QStatusBar *statusBar() const; + +signals: + void currentAboutToShow(int index); + void currentChanged(int index); + +public slots: + void setCurrentIndex(int index); + +private slots: + void showWidget(int index); + +private: + FancyTabBar *m_tabBar; + QWidget *m_cornerWidgetContainer; + QStackedLayout *m_modesStack; + QWidget *m_selectionWidget; + QStatusBar *m_statusBar; +}; + +} // namespace Internal +} // namespace Core + +#endif diff --git a/src/plugins/coreplugin/fileiconprovider.cpp b/src/plugins/coreplugin/fileiconprovider.cpp new file mode 100644 index 00000000000..8e01ecbb8d7 --- /dev/null +++ b/src/plugins/coreplugin/fileiconprovider.cpp @@ -0,0 +1,138 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "fileiconprovider.h" + +using namespace Core; + +/*! + \class FileIconProvider + + Provides icons based on file suffixes. + + The class is a singleton: It's instance can be accessed via the static instance() method. + Plugins can register custom icons via registerIconSuffix(), and retrieve icons via the icon() + method. + */ + +FileIconProvider *FileIconProvider::m_instance = 0; + +FileIconProvider::FileIconProvider(): + m_unknownFileIcon(QLatin1String(":/qworkbench/images/unknownfile.png")) +{ +} + +FileIconProvider::~FileIconProvider() +{ + m_instance = 0; +} + +/*! + Returns the icon associated with the file suffix in fileInfo. If there is none, + the default icon of the operating system is returned. + */ +QIcon FileIconProvider::icon(const QFileInfo &fileInfo) +{ + const QString suffix = fileInfo.suffix(); + QIcon icon = iconForSuffix(suffix); + + if (icon.isNull()) { + // Get icon from OS and store it in the cache + + // Disabled since for now we'll make sure that all icons fit with our + // own custom icons by returning an empty one if we don't know it. +#if 0 + // This is incorrect if the OS does not always return the same icon for the + // same suffix (Mac OS X), but should speed up the retrieval a lot ... + icon = m_systemIconProvider.icon(fileInfo); + if (!suffix.isEmpty()) + registerIconForSuffix(icon, suffix); +#else + if (fileInfo.isDir()) { + icon = m_systemIconProvider.icon(fileInfo); + } else { + icon = m_unknownFileIcon; + } +#endif + } + + return icon; +} + +/*! + Registers an icon for a given suffix, overriding any existing icon. + */ +void FileIconProvider::registerIconForSuffix(const QIcon &icon, const QString &suffix) +{ + // delete old icon, if it exists + QList<QPair<QString,QIcon> >::iterator iter = m_cache.begin(); + for(; iter != m_cache.end(); ++iter) { + if ((*iter).first == suffix) { + iter = m_cache.erase(iter); + break; + } + } + + QPair<QString,QIcon> newEntry(suffix, icon); + m_cache.append(newEntry); +} + +/*! + Returns an icon for the given suffix, or an empty one if none registered. + */ +QIcon FileIconProvider::iconForSuffix(const QString &suffix) const +{ + QIcon icon; + + if (suffix.isEmpty()) + return icon; + + QList<QPair<QString,QIcon> >::const_iterator iter = m_cache.constBegin(); + for(; iter != m_cache.constEnd(); ++iter) { + if ((*iter).first == suffix) { + icon = (*iter).second; + break; + } + } + + return icon; +} + +/*! + Returns the sole instance of FileIconProvider. + */ +FileIconProvider *FileIconProvider::instance() +{ + if (!m_instance) + m_instance = new FileIconProvider; + return m_instance; +} diff --git a/src/plugins/coreplugin/fileiconprovider.h b/src/plugins/coreplugin/fileiconprovider.h new file mode 100644 index 00000000000..4124eeb43d4 --- /dev/null +++ b/src/plugins/coreplugin/fileiconprovider.h @@ -0,0 +1,71 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FILEICONPROVIDER_H +#define FILEICONPROVIDER_H + +#include <coreplugin/core_global.h> + +#include <QtCore/QFileInfo> +#include <QtCore/QPair> +#include <QtGui/QFileIconProvider> +#include <QtGui/QIcon> + +namespace Core { + +class CORE_EXPORT FileIconProvider { +public: + ~FileIconProvider(); // used to clear the cache + QIcon icon(const QFileInfo &fileInfo); + + void registerIconForSuffix(const QIcon &icon, const QString &suffix); + + static FileIconProvider *instance(); + +private: + QIcon iconForSuffix(const QString &suffix) const; + + // mapping of file ending to icon + // TODO: Check if this is really faster than a QHash + mutable QList<QPair<QString, QIcon> > m_cache; + + QFileIconProvider m_systemIconProvider; + QIcon m_unknownFileIcon; + + // singleton pattern + FileIconProvider(); + static FileIconProvider *m_instance; +}; + +} // namespace Core + +#endif // FILEICONPROVIDER_H diff --git a/src/plugins/coreplugin/filemanager.cpp b/src/plugins/coreplugin/filemanager.cpp new file mode 100644 index 00000000000..299bba4ff90 --- /dev/null +++ b/src/plugins/coreplugin/filemanager.cpp @@ -0,0 +1,592 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "filemanager.h" +#include "ifile.h" +#include "mainwindow.h" +#include "saveitemsdialog.h" +#include "vcsmanager.h" +#include "editormanager.h" +#include "mimedatabase.h" +#include "iversioncontrol.h" + +#include <QtCore/QDebug> +#include <QtCore/QSettings> +#include <QtCore/QFileInfo> +#include <QtCore/QFile> +#include <QtCore/QDir> +#include <QtCore/QTimer> +#include <QtCore/QFileSystemWatcher> +#include <QtGui/QFileDialog> +#include <QtGui/QMessageBox> + +using namespace Core; +using namespace Core::Internal; + +/*! + \class FileManager + \mainclass + \ingroup qwb + \inheaderfile filemanager.h + \brief Manages a set of IFile objects. + + The FileManager service monitors a set of IFile's. Plugins should register + files they work with at the service. The files the IFile's point to will be + monitored at filesystem level. If a file changes, the status of the IFile's + will be adjusted accordingly. Furthermore, on application exit the user will + be asked to save all modified files. + + Different IFile objects in the set can point to the same file in the + filesystem. The monitoring of a file can be blocked by blockFileChange(), and + enabled again by unblockFileChange(). + + The FileManager service also provides two convenience methods for saving + files: saveModifiedFiles() and saveModifiedFilesSilently(). Both take a list + of FileInterfaces as an argument, and return the list of files which were + _not_ saved. + + The service also manages the list of recent files to be shown to the user + (see addToRecentFiles() and recentFiles()). + */ + +static const char *settingsGroup = "RecentFiles"; +static const char *filesKey = "Files"; + +FileManager::FileManager(Core::ICore *core, MainWindow *mw) : + QObject(mw), + m_core(core), + m_mainWindow(mw), + m_fileWatcher(new QFileSystemWatcher(this)), + m_blockActivated(false) +{ + connect(m_fileWatcher, SIGNAL(fileChanged(const QString&)), + this, SLOT(changedFile(const QString&))); + connect(m_mainWindow, SIGNAL(windowActivated()), + this, SLOT(mainWindowActivated())); + connect(m_core, SIGNAL(contextChanged(Core::IContext*)), + this, SLOT(syncWithEditor(Core::IContext*))); + + QSettings *s = m_mainWindow->settings(); + s->beginGroup(QLatin1String(settingsGroup)); + m_recentFiles = s->value(QLatin1String(filesKey), QStringList()).toStringList(); + s->endGroup(); + for (QStringList::iterator it = m_recentFiles.begin(); it != m_recentFiles.end(); ) { + if (QFileInfo(*it).isFile()) { + ++it; + } else { + it = m_recentFiles.erase(it); + } + } +} + +/*! + \fn bool FileManager::addFiles(const QList<IFile *> &files) + + Adds a list of IFile's to the collection. + + Returns true if the file specified by \a files have not been yet part of the file list. +*/ +bool FileManager::addFiles(const QList<IFile *> &files) +{ + bool filesAdded = false; + foreach (IFile *file, files) { + if (!file || m_managedFiles.contains(file)) + continue; + connect(file, SIGNAL(changed()), this, SLOT(checkForNewFileName())); + connect(file, SIGNAL(destroyed(QObject *)), this, SLOT(fileDestroyed(QObject *))); + filesAdded = true; + addWatch(fixFileName(file->fileName())); + updateFileInfo(file); + } + return filesAdded; +} + +/*! + \fn bool FileManager::addFile(IFile *files) + + Adds a IFile object to the collection. + + Returns true if the file specified by \a file has not been yet part of the file list. +*/ +bool FileManager::addFile(IFile *file) +{ + return addFiles(QList<IFile *>() << file); +} + +void FileManager::fileDestroyed(QObject *obj) +{ + // we can't use qobject_cast here, because meta data is already destroyed + IFile *file = static_cast<IFile*>(obj); + const QString filename = m_managedFiles.value(file).fileName; + m_managedFiles.remove(file); + removeWatch(filename); +} + +/*! + \fn bool FileManager::removeFile(IFile *file) + + Removes a IFile object from the collection. + + Returns true if the file specified by \a file has been part of the file list. +*/ +bool FileManager::removeFile(IFile *file) +{ + if (!file) + return false; + + disconnect(file, SIGNAL(changed()), this, SLOT(checkForNewFileName())); + disconnect(file, SIGNAL(destroyed(QObject *)), this, SLOT(fileDestroyed(QObject *))); + + if (!m_managedFiles.contains(file)) + return false; + const FileInfo info = m_managedFiles.take(file); + const QString filename = info.fileName; + removeWatch(filename); + return true; +} + +void FileManager::addWatch(const QString &filename) +{ + if (!filename.isEmpty() && managedFiles(filename).isEmpty()) { + m_fileWatcher->addPath(filename); + } +} + +void FileManager::removeWatch(const QString &filename) +{ + if (!filename.isEmpty() && managedFiles(filename).isEmpty()) { + m_fileWatcher->removePath(filename); + } +} + +void FileManager::checkForNewFileName() +{ + IFile *file = qobject_cast<IFile *>(sender()); + Q_ASSERT(file); + const QString newfilename = fixFileName(file->fileName()); + const QString oldfilename = m_managedFiles.value(file).fileName; + if (!newfilename.isEmpty() && newfilename != oldfilename) { + m_managedFiles[file].fileName = newfilename; + removeWatch(oldfilename); + addWatch(newfilename); + } +} + +// TODO Rename to nativeFileName +QString FileManager::fixFileName(const QString &fileName) +{ + QString s = fileName; +#ifdef Q_OS_WIN + s = s.toLower(); +#endif + if (!QFile::exists(s)) + return QDir::toNativeSeparators(s); + return QFileInfo(QDir::toNativeSeparators(s)).canonicalFilePath(); +} + +/*! + \fn bool FileManager::isFileManaged(const QString &fileName) const + + Returns true if at least one IFile in the set points to \a fileName. +*/ +bool FileManager::isFileManaged(const QString &fileName) const +{ + if (fileName.isEmpty()) + return false; + + return !managedFiles(fixFileName(fileName)).isEmpty(); +} + +/*! + \fn QList<IFile*> FileManager::modifiedFiles() const + + Returns the list of IFile's that have been modified. +*/ +QList<IFile *> FileManager::modifiedFiles() const +{ + QList<IFile *> modifiedFiles; + + const QMap<IFile*, FileInfo>::const_iterator cend = m_managedFiles.constEnd(); + for (QMap<IFile*, FileInfo>::const_iterator i = m_managedFiles.constBegin(); i != cend; ++i) { + IFile *fi = i.key(); + if (fi->isModified()) + modifiedFiles << fi; + } + return modifiedFiles; +} + +/*! + \fn void FileManager::blockFileChange(IFile *file) + + Blocks the monitoring of the file the \a file argument points to. +*/ +void FileManager::blockFileChange(IFile *file) +{ + if (!file->fileName().isEmpty()) + m_fileWatcher->removePath(file->fileName()); +} + +/*! + \fn void FileManager::unblockFileChange(IFile *file) + + Enables the monitoring of the file the \a file argument points to, and update the status of the corresponding IFile's. +*/ +void FileManager::unblockFileChange(IFile *file) +{ + foreach (IFile *managedFile, managedFiles(file->fileName())) + updateFileInfo(managedFile); + if (!file->fileName().isEmpty()) + m_fileWatcher->addPath(file->fileName()); +} + +void FileManager::updateFileInfo(IFile *file) +{ + const QString fixedname = fixFileName(file->fileName()); + const QFileInfo fi(file->fileName()); + FileInfo info; + info.fileName = fixedname; + info.modified = fi.lastModified(); + info.permissions = fi.permissions(); + m_managedFiles.insert(file, info); +} + +/*! + \fn QList<IFile*> FileManager::saveModifiedFilesSilently(const QList<IFile*> &files) + + Tries to save the files listed in \a files . Returns the files that could not be saved. +*/ +QList<IFile *> FileManager::saveModifiedFilesSilently(const QList<IFile *> &files) +{ + return saveModifiedFiles(files, 0, true, QString()); +} + +/*! + \fn QList<IFile*> FileManager::saveModifiedFiles(const QList<IFile*> &files, bool *cancelled, const QString &message) + + Asks the user whether to save the files listed in \a files . Returns the files that have not been saved. +*/ +QList<IFile *> FileManager::saveModifiedFiles(const QList<IFile *> &files, + bool *cancelled, const QString &message) +{ + return saveModifiedFiles(files, cancelled, false, message); +} + +static QMessageBox::StandardButton skipFailedPrompt(QWidget *parent, const QString &fileName) +{ + return QMessageBox::question(parent, + QObject::tr("Can't save file"), + QObject::tr("Can't save changes to '%1'. Do you want to continue and loose your changes?").arg(fileName), + QMessageBox::YesToAll| QMessageBox::Yes|QMessageBox::No, + QMessageBox::No); +} + +QList<IFile *> FileManager::saveModifiedFiles(const QList<IFile *> &files, + bool *cancelled, bool silently, const QString &message) +{ + if (cancelled) + (*cancelled) = false; + + QList<IFile *> notSaved; + QMap<IFile*, QString> modifiedFiles; + + foreach (IFile *file, files) { + if (file->isModified()) { + QString name = file->fileName(); + if (name.isEmpty()) + name = file->suggestedFileName(); + + // There can be several FileInterfaces pointing to the same file + // Select one that is not readonly. + if (!(modifiedFiles.values().contains(name) + && file->isReadOnly())) + modifiedFiles.insert(file, name); + } + } + + if (!modifiedFiles.isEmpty()) { + QList<IFile *> filesToSave; + QSet<IFile *> filesToOpen; + if (silently) { + filesToSave = modifiedFiles.keys(); + } else { + SaveItemsDialog dia(m_mainWindow, modifiedFiles); + if (!message.isEmpty()) + dia.setMessage(message); + if (dia.exec() != QDialog::Accepted) { + if (cancelled) + (*cancelled) = true; + notSaved = modifiedFiles.keys(); + return notSaved; + } + filesToSave = dia.itemsToSave(); + filesToOpen = dia.itemsToOpen(); + } + + bool yestoall = false; + foreach (IFile *file, filesToSave) { + if (file->isReadOnly() && filesToOpen.contains(file)) { + QString directory = QFileInfo(file->fileName()).absolutePath(); + IVersionControl *versionControl = m_mainWindow->vcsManager()->findVersionControlForDirectory(directory); + if (versionControl) + versionControl->vcsOpen(file->fileName()); + } + if (!file->isReadOnly() && !file->fileName().isEmpty()) { + blockFileChange(file); + const bool ok = file->save(); + unblockFileChange(file); + if (!ok) + notSaved.append(file); + } else if (QFile::exists(file->fileName()) && !file->isSaveAsAllowed()) { + if (yestoall) + continue; + const QFileInfo fi(file->fileName()); + switch (skipFailedPrompt(m_mainWindow, fi.fileName())) { + case QMessageBox::YesToAll: + yestoall = true; + break; + case QMessageBox::No: + if (cancelled) + *cancelled = true; + return filesToSave; + default: + break; + } + } else { + QString fileName = getSaveAsFileName(file); + bool ok = false; + if (!fileName.isEmpty()) { + blockFileChange(file); + ok = file->save(fileName); + unblockFileChange(file); + } + if (!ok) + notSaved.append(file); + } + } + } + return notSaved; +} + +QString FileManager::getSaveFileNameWithExtension(const QString &title, const QString &path, + const QString &fileFilter, const QString &extension) +{ + QString fileName; + bool repeat; + do { + repeat = false; + fileName = QFileDialog::getSaveFileName(m_mainWindow, title, path, fileFilter); + if (!fileName.isEmpty() && !extension.isEmpty() && !fileName.endsWith(extension)) { + fileName.append(extension); + if (QFile::exists(fileName)) { + if (QMessageBox::warning(m_mainWindow, tr("Overwrite?"), + tr("An item named '%1' already exists at this location. Do you want to overwrite it?").arg(fileName), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + repeat = true; + } + } + } while (repeat); + return fileName; +} + +/*! + \fn QString FileManager::getSaveAsFileName(IFile *file) + + Asks the user for a new file name (Save File As) for /arg file. +*/ +QString FileManager::getSaveAsFileName(IFile *file) +{ + if (!file) + return QLatin1String(""); + QString absoluteFilePath = file->fileName(); + const QFileInfo fi(absoluteFilePath); + QString fileName = fi.fileName(); + QString path = fi.absolutePath(); + if (absoluteFilePath.isEmpty()) { + fileName = file->suggestedFileName(); + const QString defaultPath = file->defaultPath(); + if (!defaultPath.isEmpty()) + path = defaultPath; + } + QString filterString; + QString preferredSuffix; + if (const MimeType mt = m_core->mimeDatabase()->findByFile(fi)) { + filterString = mt.filterString(); + preferredSuffix = mt.preferredSuffix(); + } + + absoluteFilePath = getSaveFileNameWithExtension(tr("Save File As"), + path + QDir::separator() + fileName, + filterString, + preferredSuffix); + return absoluteFilePath; +} + +void FileManager::changedFile(const QString &file) +{ + const bool wasempty = m_changedFiles.isEmpty(); + foreach (IFile *fileinterface, managedFiles(file)) + m_changedFiles << fileinterface; + if (wasempty && !m_changedFiles.isEmpty()) { + QTimer::singleShot (200, this, SLOT(checkForReload())); + } +} + +void FileManager::mainWindowActivated() +{ + checkForReload(); +} + +void FileManager::checkForReload() +{ + if (QApplication::activeWindow() == m_mainWindow && + !m_blockActivated && !m_changedFiles.isEmpty()) { + m_blockActivated = true; + const QList<QPointer<IFile> > changed = m_changedFiles; + m_changedFiles.clear(); + IFile::ReloadBehavior behavior = + IFile::AskForReload; + foreach (IFile *f, changed) { + if (!f) + continue; + QFileInfo fi(f->fileName()); + FileInfo info = m_managedFiles.value(f); + if (info.modified != fi.lastModified() + || info.permissions != fi.permissions()) { + if (info.modified != fi.lastModified()) + f->modified(&behavior); + else { + IFile::ReloadBehavior tempBeh = + IFile::ReloadPermissions; + f->modified(&tempBeh); + } + updateFileInfo(f); + + // the file system watchers loses inodes when a file is removed/renamed. Work around it. + m_fileWatcher->removePath(f->fileName()); + m_fileWatcher->addPath(f->fileName()); + } + } + m_blockActivated = false; + checkForReload(); + } +} + +void FileManager::syncWithEditor(Core::IContext *context) +{ + if (!context) + return; + + Core::IEditor *editor = m_core->editorManager()->currentEditor(); + if (editor && (editor->widget() == context->widget())) + setCurrentFile(editor->file()->fileName()); +} + +/*! + \fn void FileManager::addToRecentFiles(const QString &fileName) + + Adds the \a fileName to the list of recent files. +*/ +void FileManager::addToRecentFiles(const QString &fileName) +{ + if (fileName.isEmpty()) + return; + QString prettyFileName(QDir::toNativeSeparators(fileName)); + m_recentFiles.removeAll(prettyFileName); + if (m_recentFiles.count() > m_maxRecentFiles) + m_recentFiles.removeLast(); + m_recentFiles.prepend(prettyFileName); +} + +/*! + \fn QStringList FileManager::recentFiles() const + + Returns the list of recent files. +*/ +QStringList FileManager::recentFiles() const +{ + return m_recentFiles; +} + +void FileManager::saveRecentFiles() +{ + QSettings *s = m_mainWindow->settings(); + s->beginGroup(QLatin1String(settingsGroup)); + s->setValue(QLatin1String(filesKey), m_recentFiles); + s->endGroup(); +} + +/*! + + The current file is e.g. the file currently opened when an editor is active, + or the selected file in case a Project Explorer is active ... + + \see currentFile + */ +void FileManager::setCurrentFile(const QString &filePath) +{ + if (m_currentFile == filePath) + return; + m_currentFile = filePath; + emit currentFileChanged(m_currentFile); +} + +/*! + Returns the absolute path of the current file + + The current file is e.g. the file currently opened when an editor is active, + or the selected file in case a Project Explorer is active ... + + \see setCurrentFile + */ +QString FileManager::currentFile() const +{ + return m_currentFile; +} + +/*! + \fn QList<IFile*> FileManager::managedFiles(const QString &fileName) const + + Returns the list one IFile's in the set that point to \a fileName. +*/ +QList<IFile *> FileManager::managedFiles(const QString &fileName) const +{ + const QString fixedName = fixFileName(fileName); + QList<IFile *> result; + if (!fixedName.isEmpty()) { + const QMap<IFile*, FileInfo>::const_iterator cend = m_managedFiles.constEnd(); + for (QMap<IFile*, FileInfo>::const_iterator i = m_managedFiles.constBegin(); i != cend; ++i) { + if (i.value().fileName == fixedName) + result << i.key(); + } + } + return result; +} diff --git a/src/plugins/coreplugin/filemanager.h b/src/plugins/coreplugin/filemanager.h new file mode 100644 index 00000000000..c6759d5b45e --- /dev/null +++ b/src/plugins/coreplugin/filemanager.h @@ -0,0 +1,140 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FILEMANAGER_H +#define FILEMANAGER_H + +#include <coreplugin/core_global.h> + +#include <QtCore/QObject> +#include <QtCore/QMap> +#include <QtCore/QDateTime> +#include <QtCore/QFile> +#include <QtCore/QStringList> +#include <QtCore/QPointer> + +QT_BEGIN_NAMESPACE +class QFileSystemWatcher; +QT_END_NAMESPACE + +namespace Core { + +class ICore; +class IContext; +class IFile; + +namespace Internal { +class MainWindow; +} + +class CORE_EXPORT FileManager : public QObject +{ + Q_OBJECT + + struct FileInfo { + QString fileName; + QDateTime modified; + QFile::Permissions permissions; + }; + +public: + FileManager(Core::ICore *core, Internal::MainWindow *ew); + + // file pool to monitor + bool addFiles(const QList<IFile *> &files); + bool addFile(IFile *file); + bool removeFile(IFile *file); + bool isFileManaged(const QString &fileName) const; + QList<IFile *> managedFiles(const QString &fileName) const; + QList<IFile *> modifiedFiles() const; + + void blockFileChange(IFile *file); + void unblockFileChange(IFile *file); + + // recent files + void addToRecentFiles(const QString &fileName); + QStringList recentFiles() const; + void saveRecentFiles(); + + // current file + void setCurrentFile(const QString &filePath); + QString currentFile() const; + + // helper methods + static QString fixFileName(const QString &fileName); + + QString getSaveFileNameWithExtension(const QString &title, const QString &path, + const QString &fileFilter, const QString &extension); + QString getSaveAsFileName(IFile *file); + + QList<IFile *> saveModifiedFilesSilently(const QList<IFile *> &files); + QList<IFile *> saveModifiedFiles( + const QList<IFile *> &files, + bool *cancelled = 0, + const QString &message = QString()); + +signals: + void currentFileChanged(const QString &filePath); + +private slots: + void fileDestroyed(QObject *obj); + void checkForNewFileName(); + void checkForReload(); + void changedFile(const QString &file); + void mainWindowActivated(); + void syncWithEditor(Core::IContext *context); + +private: + void addWatch(const QString &filename); + void removeWatch(const QString &filename); + void updateFileInfo(IFile *file); + + QList<IFile *> saveModifiedFiles(const QList<IFile *> &files, + bool *cancelled, bool silently, const QString &message); + + QMap<IFile*, FileInfo> m_managedFiles; + + QStringList m_recentFiles; + static const int m_maxRecentFiles = 7; + + QString m_currentFile; + + Core::ICore *m_core; + Internal::MainWindow *m_mainWindow; + QFileSystemWatcher *m_fileWatcher; + QList<QPointer<IFile> > m_changedFiles; + bool m_blockActivated; +}; + +} // namespace Core + +#endif // FILEMANAGER_H diff --git a/src/plugins/coreplugin/findplaceholder.cpp b/src/plugins/coreplugin/findplaceholder.cpp new file mode 100644 index 00000000000..ec80b530b5d --- /dev/null +++ b/src/plugins/coreplugin/findplaceholder.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "findplaceholder.h" +#include "modemanager.h" + +#include <QtGui/QVBoxLayout> + + +using namespace Core; + +FindToolBarPlaceHolder *FindToolBarPlaceHolder::m_current = 0; + +FindToolBarPlaceHolder::FindToolBarPlaceHolder(Core::IMode *mode, QWidget *parent) + :QWidget(parent), m_mode(mode) +{ + setLayout(new QVBoxLayout); + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + layout()->setMargin(0); + connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode *)), + this, SLOT(currentModeChanged(Core::IMode *))); +} + +FindToolBarPlaceHolder::~FindToolBarPlaceHolder() +{ + +} + +void FindToolBarPlaceHolder::currentModeChanged(Core::IMode *mode) +{ + if (m_current == this) + m_current = 0; + if (m_mode == mode) + m_current = this; +} + +FindToolBarPlaceHolder *FindToolBarPlaceHolder::getCurrent() +{ + return m_current; +} diff --git a/src/plugins/coreplugin/findplaceholder.h b/src/plugins/coreplugin/findplaceholder.h new file mode 100644 index 00000000000..89850d812ea --- /dev/null +++ b/src/plugins/coreplugin/findplaceholder.h @@ -0,0 +1,60 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FINDPLACEHOLDER_H +#define FINDPLACEHOLDER_H + +#include "core_global.h" +#include <QtGui/QWidget> + +namespace Core { + +class IMode; + +class CORE_EXPORT FindToolBarPlaceHolder : public QWidget +{ + Q_OBJECT +public: + FindToolBarPlaceHolder(Core::IMode *mode, QWidget *parent = 0); + ~FindToolBarPlaceHolder(); + + static FindToolBarPlaceHolder *getCurrent(); +private slots: + void currentModeChanged(Core::IMode *); +private: + Core::IMode *m_mode; + static FindToolBarPlaceHolder *m_current; +}; + +} // namespace Core + +#endif // FINDPLACEHOLDER_H diff --git a/src/plugins/coreplugin/flowlayout.cpp b/src/plugins/coreplugin/flowlayout.cpp new file mode 100644 index 00000000000..1c43dead53c --- /dev/null +++ b/src/plugins/coreplugin/flowlayout.cpp @@ -0,0 +1,155 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/**************************************************************************** +** +** Copyright (C) 2004-$THISYEAR$ Trolltech AS. All rights reserved. +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include <QtGui> + +#include "flowlayout.h" + +using namespace Core::Internal; + +FlowLayout::FlowLayout(QWidget *parent, int margin, int spacing) + : QLayout(parent) +{ + setMargin(margin); + setSpacing(spacing); +} + +FlowLayout::FlowLayout(int spacing) +{ + setSpacing(spacing); +} + +FlowLayout::~FlowLayout() +{ + QLayoutItem *item; + while ((item = takeAt(0))) + delete item; +} + +void FlowLayout::addItem(QLayoutItem *item) +{ + itemList.append(item); +} + +int FlowLayout::count() const +{ + return itemList.size(); +} + +QLayoutItem *FlowLayout::itemAt(int index) const +{ + return itemList.value(index); +} + +QLayoutItem *FlowLayout::takeAt(int index) +{ + if (index >= 0 && index < itemList.size()) + return itemList.takeAt(index); + else + return 0; +} + +Qt::Orientations FlowLayout::expandingDirections() const +{ + return 0; +} + +bool FlowLayout::hasHeightForWidth() const +{ + return true; +} + +int FlowLayout::heightForWidth(int width) const +{ + int height = doLayout(QRect(0, 0, width, 0), true); + return height; +} + +void FlowLayout::setGeometry(const QRect &rect) +{ + QLayout::setGeometry(rect); + doLayout(rect, false); +} + +QSize FlowLayout::sizeHint() const +{ + return minimumSize(); +} + +QSize FlowLayout::minimumSize() const +{ + QSize size; + QLayoutItem *item; + foreach (item, itemList) + size = size.expandedTo(item->minimumSize()); + + size += QSize(2*margin(), 2*margin()); + return size; +} + +int FlowLayout::doLayout(const QRect &rect, bool testOnly) const +{ + int x = rect.x(); + int y = rect.y(); + int lineHeight = 0; + + QLayoutItem *item; + foreach (item, itemList) { + int nextX = x + item->sizeHint().width() + spacing(); + if (nextX - spacing() > rect.right() && lineHeight > 0) { + x = rect.x(); + y = y + lineHeight + spacing(); + nextX = x + item->sizeHint().width() + spacing(); + lineHeight = 0; + } + + if (!testOnly) + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + + x = nextX; + lineHeight = qMax(lineHeight, item->sizeHint().height()); + } + return y + lineHeight - rect.y() + margin(); +} diff --git a/src/plugins/coreplugin/flowlayout.h b/src/plugins/coreplugin/flowlayout.h new file mode 100644 index 00000000000..f0a7d4235b9 --- /dev/null +++ b/src/plugins/coreplugin/flowlayout.h @@ -0,0 +1,83 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/**************************************************************************** +** +** Copyright (C) 2004-$THISYEAR$ Trolltech AS. All rights reserved. +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef FLOWLAYOUT_H +#define FLOWLAYOUT_H + +#include <QLayout> +#include <QRect> +#include <QWidgetItem> + +namespace Core { +namespace Internal { + +class FlowLayout : public QLayout +{ +public: + FlowLayout(QWidget *parent, int margin = 0, int spacing = -1); + FlowLayout(int spacing = -1); + ~FlowLayout(); + + void addItem(QLayoutItem *item); + Qt::Orientations expandingDirections() const; + bool hasHeightForWidth() const; + int heightForWidth(int) const; + int count() const; + QLayoutItem *itemAt(int index) const; + QSize minimumSize() const; + void setGeometry(const QRect &rect); + QSize sizeHint() const; + QLayoutItem *takeAt(int index); + +private: + int doLayout(const QRect &rect, bool testOnly) const; + + QList<QLayoutItem *> itemList; +}; + +} // namespace Internal +} // namespace Core + +#endif diff --git a/src/plugins/coreplugin/generalsettings.cpp b/src/plugins/coreplugin/generalsettings.cpp new file mode 100644 index 00000000000..8595eb03ee4 --- /dev/null +++ b/src/plugins/coreplugin/generalsettings.cpp @@ -0,0 +1,114 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "generalsettings.h" + +#include "stylehelper.h" +#include "utils/qtcolorbutton.h" +#include <coreplugin/editormanager/editormanager.h> +#include <QtGui/QMessageBox> + +#include "ui_generalsettings.h" + +using namespace Core::Internal; + +GeneralSettings::GeneralSettings() +{ +} + +QString GeneralSettings::name() const +{ + return tr("General"); +} + +QString GeneralSettings::category() const +{ + return QLatin1String("Environment"); +} + +QString GeneralSettings::trCategory() const +{ + return tr("Environment"); +} + +QWidget* GeneralSettings::createPage(QWidget *parent) +{ + m_page = new Ui_GeneralSettings(); + QWidget *w = new QWidget(parent); + m_page->setupUi(w); + + m_page->colorButton->setColor(StyleHelper::baseColor()); + m_page->externalEditorEdit->setText(EditorManager::instance()->externalEditor()); + + connect(m_page->resetButton, SIGNAL(clicked()), + this, SLOT(resetInterfaceColor())); + connect(m_page->helpExternalEditorButton, SIGNAL(clicked()), + this, SLOT(showHelpForExternalEditor())); + + + return w; +} + +void GeneralSettings::finished(bool accepted) +{ + if (!accepted) + return; + + // Apply the new base color if accepted + StyleHelper::setBaseColor(m_page->colorButton->color()); + EditorManager::instance()->setExternalEditor(m_page->externalEditorEdit->text()); + +} + +void GeneralSettings::resetInterfaceColor() +{ + m_page->colorButton->setColor(0x666666); +} + + +void GeneralSettings::showHelpForExternalEditor() +{ + if (m_dialog) { + m_dialog->show(); + m_dialog->raise(); + m_dialog->activateWindow(); + return; + } + QMessageBox *mb = new QMessageBox(QMessageBox::Information, + tr("Variables"), + EditorManager::instance()->externalEditorHelpText(), + QMessageBox::Cancel, + m_page->helpExternalEditorButton); + mb->setWindowModality(Qt::NonModal); + m_dialog = mb; + mb->show(); +} diff --git a/src/plugins/coreplugin/generalsettings.h b/src/plugins/coreplugin/generalsettings.h new file mode 100644 index 00000000000..0cffc9caaf9 --- /dev/null +++ b/src/plugins/coreplugin/generalsettings.h @@ -0,0 +1,71 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GENERALSETTINGS_H +#define GENERALSETTINGS_H + +#include <coreplugin/dialogs/ioptionspage.h> +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE +class Ui_GeneralSettings; +QT_END_NAMESPACE + +namespace Core { +namespace Internal { + +class GeneralSettings : public IOptionsPage +{ + Q_OBJECT + +public: + GeneralSettings(); + + QString name() const; + QString category() const; + QString trCategory() const; + QWidget* createPage(QWidget *parent); + void finished(bool accepted); + +private slots: + void resetInterfaceColor(); + void showHelpForExternalEditor(); + +private: + Ui_GeneralSettings *m_page; + QWidget *m_dialog; +}; + +} // namespace Internal +} // namespace Core + +#endif // GENERALSETTINGS_H diff --git a/src/plugins/coreplugin/generalsettings.ui b/src/plugins/coreplugin/generalsettings.ui new file mode 100644 index 00000000000..1e5357d1547 --- /dev/null +++ b/src/plugins/coreplugin/generalsettings.ui @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>GeneralSettings</class> + <widget class="QWidget" name="GeneralSettings"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>540</width> + <height>236</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>General settings</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLabel" name="colorLabel"> + <property name="text"> + <string>User &interface color:</string> + </property> + <property name="buddy"> + <cstring>colorButton</cstring> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="Core::Utils::QtColorButton" name="colorButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>64</width> + <height>0</height> + </size> + </property> + <property name="alphaAllowed" stdset="0"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="resetButton"> + <property name="toolTip"> + <string>Reset to default</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="core.qrc"> + <normaloff>:/qworkbench/images/reset.png</normaloff>:/qworkbench/images/reset.png</iconset> + </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> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>External editor:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="externalEditorEdit"/> + </item> + <item> + <widget class="QToolButton" name="helpExternalEditorButton"> + <property name="text"> + <string>?</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>111</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Core::Utils::QtColorButton</class> + <extends>QToolButton</extends> + <header location="global">utils/qtcolorbutton.h</header> + </customwidget> + </customwidgets> + <resources> + <include location="core.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/plugins/coreplugin/html/images/bg_site_header_dark_grey.png b/src/plugins/coreplugin/html/images/bg_site_header_dark_grey.png Binary files differnew file mode 100644 index 00000000000..044c695a4dc --- /dev/null +++ b/src/plugins/coreplugin/html/images/bg_site_header_dark_grey.png diff --git a/src/plugins/coreplugin/html/images/body_bg_circles_bottom_right.png b/src/plugins/coreplugin/html/images/body_bg_circles_bottom_right.png Binary files differnew file mode 100644 index 00000000000..543e35071ff --- /dev/null +++ b/src/plugins/coreplugin/html/images/body_bg_circles_bottom_right.png diff --git a/src/plugins/coreplugin/html/images/body_bg_gradient.png b/src/plugins/coreplugin/html/images/body_bg_gradient.png Binary files differnew file mode 100644 index 00000000000..ebaa078c3e2 --- /dev/null +++ b/src/plugins/coreplugin/html/images/body_bg_gradient.png diff --git a/src/plugins/coreplugin/html/images/btn_getting_started.png b/src/plugins/coreplugin/html/images/btn_getting_started.png Binary files differnew file mode 100644 index 00000000000..affd4b25acb --- /dev/null +++ b/src/plugins/coreplugin/html/images/btn_getting_started.png diff --git a/src/plugins/coreplugin/html/images/btn_getting_started_hover.png b/src/plugins/coreplugin/html/images/btn_getting_started_hover.png Binary files differnew file mode 100644 index 00000000000..e6145de709e --- /dev/null +++ b/src/plugins/coreplugin/html/images/btn_getting_started_hover.png diff --git a/src/plugins/coreplugin/html/images/btn_restore_session.png b/src/plugins/coreplugin/html/images/btn_restore_session.png Binary files differnew file mode 100644 index 00000000000..4698c19a9c4 --- /dev/null +++ b/src/plugins/coreplugin/html/images/btn_restore_session.png diff --git a/src/plugins/coreplugin/html/images/btn_restore_session_hover.png b/src/plugins/coreplugin/html/images/btn_restore_session_hover.png Binary files differnew file mode 100644 index 00000000000..30c878207df --- /dev/null +++ b/src/plugins/coreplugin/html/images/btn_restore_session_hover.png diff --git a/src/plugins/coreplugin/html/images/list_bullet_arrow.png b/src/plugins/coreplugin/html/images/list_bullet_arrow.png Binary files differnew file mode 100644 index 00000000000..acf9f5da5ec --- /dev/null +++ b/src/plugins/coreplugin/html/images/list_bullet_arrow.png diff --git a/src/plugins/coreplugin/html/images/mode_Project.png b/src/plugins/coreplugin/html/images/mode_Project.png Binary files differnew file mode 100644 index 00000000000..05d7759cf1c --- /dev/null +++ b/src/plugins/coreplugin/html/images/mode_Project.png diff --git a/src/plugins/coreplugin/html/images/nokia_logo.png b/src/plugins/coreplugin/html/images/nokia_logo.png Binary files differnew file mode 100644 index 00000000000..c149f71b628 --- /dev/null +++ b/src/plugins/coreplugin/html/images/nokia_logo.png diff --git a/src/plugins/coreplugin/html/images/product_logo.png b/src/plugins/coreplugin/html/images/product_logo.png Binary files differnew file mode 100644 index 00000000000..7f8992e92c0 --- /dev/null +++ b/src/plugins/coreplugin/html/images/product_logo.png diff --git a/src/plugins/coreplugin/html/images/qt_logo.png b/src/plugins/coreplugin/html/images/qt_logo.png Binary files differnew file mode 100644 index 00000000000..c8755ec090f --- /dev/null +++ b/src/plugins/coreplugin/html/images/qt_logo.png diff --git a/src/plugins/coreplugin/html/images/rc_bottom_left.png b/src/plugins/coreplugin/html/images/rc_bottom_left.png Binary files differnew file mode 100644 index 00000000000..106f92f4627 --- /dev/null +++ b/src/plugins/coreplugin/html/images/rc_bottom_left.png diff --git a/src/plugins/coreplugin/html/images/rc_bottom_mid.png b/src/plugins/coreplugin/html/images/rc_bottom_mid.png Binary files differnew file mode 100644 index 00000000000..7c01362073c --- /dev/null +++ b/src/plugins/coreplugin/html/images/rc_bottom_mid.png diff --git a/src/plugins/coreplugin/html/images/rc_bottom_right.png b/src/plugins/coreplugin/html/images/rc_bottom_right.png Binary files differnew file mode 100644 index 00000000000..eed56c4f5c0 --- /dev/null +++ b/src/plugins/coreplugin/html/images/rc_bottom_right.png diff --git a/src/plugins/coreplugin/html/images/rc_mid_left.png b/src/plugins/coreplugin/html/images/rc_mid_left.png Binary files differnew file mode 100644 index 00000000000..d6dd8de8cf2 --- /dev/null +++ b/src/plugins/coreplugin/html/images/rc_mid_left.png diff --git a/src/plugins/coreplugin/html/images/rc_mid_mid.png b/src/plugins/coreplugin/html/images/rc_mid_mid.png Binary files differnew file mode 100644 index 00000000000..26040bdd975 --- /dev/null +++ b/src/plugins/coreplugin/html/images/rc_mid_mid.png diff --git a/src/plugins/coreplugin/html/images/rc_mid_right.png b/src/plugins/coreplugin/html/images/rc_mid_right.png Binary files differnew file mode 100644 index 00000000000..2714b81f17f --- /dev/null +++ b/src/plugins/coreplugin/html/images/rc_mid_right.png diff --git a/src/plugins/coreplugin/html/images/rc_top_left.png b/src/plugins/coreplugin/html/images/rc_top_left.png Binary files differnew file mode 100644 index 00000000000..ffed637a15f --- /dev/null +++ b/src/plugins/coreplugin/html/images/rc_top_left.png diff --git a/src/plugins/coreplugin/html/images/rc_top_mid.png b/src/plugins/coreplugin/html/images/rc_top_mid.png Binary files differnew file mode 100644 index 00000000000..10f5abd31c1 --- /dev/null +++ b/src/plugins/coreplugin/html/images/rc_top_mid.png diff --git a/src/plugins/coreplugin/html/images/rc_top_right.png b/src/plugins/coreplugin/html/images/rc_top_right.png Binary files differnew file mode 100644 index 00000000000..830bb46c5d0 --- /dev/null +++ b/src/plugins/coreplugin/html/images/rc_top_right.png diff --git a/src/plugins/coreplugin/html/qt.css b/src/plugins/coreplugin/html/qt.css new file mode 100644 index 00000000000..fa91bf8b931 --- /dev/null +++ b/src/plugins/coreplugin/html/qt.css @@ -0,0 +1,351 @@ +/************************************** +Nokia, QT Software CSS - IDE Startseite +**************************************/ + +/************************************** +Reset +**************************************/ +* { + + vertical-align: baseline; + font-weight: inherit; + font-family: inherit; + font-style: inherit; + padding: 0; + margin: 0; + +} + +/*image link*/ +a.img_link:link {text-decoration:none;} +a.img_link:visited {text-decoration:none} +a.img_link:active {text-decoration:none} +a.img_link:hover {text-decoration:none} + +/*regular link*/ +a:link {color:#5e5e5e; text-decoration:none;} +a:visited {color:#5e5e5e; text-decoration:none} +a:active {color:#5e5e5e; text-decoration:none} +a:hover {color:#5e5e5e; text-decoration:underline} + +p{ + + font-size:9pt; + + } + +strong{ + font-weight: bold; +} + +em { + font-style: italic; +} + +ul{ + + list-style-type: none; + margin:0px 0px; + padding:15px 0px; + + } + +li { + + background:url(images/list_bullet_arrow.png) top left no-repeat; + font-size:9pt; + padding:1px 0px 0px 22px; + margin:0px 0px 15px 0px; + } + +li:last-child{ + + margin:0px; + + } + + + + + +img { + + border: 0 none; + + } + +/*Welcome Headline*/ +h1{ + + font-size:2em; + font-weight:normal; + padding-bottom:22px; + + color:#4d4d4d; + + } + +/*Headlines Recent...*/ +h2{ + + font-size:18px; + font-weight:normal; + color:#7c92a1; + border-bottom:1px solid #a6b5c1; + padding:5px 0px 10px 0px; + + } + +.clear_float{ + + clear:both; + + } + +.clear_left{ + + clear:left; + + } + +.clear_right{ + + clear:right; + + } + +/************************************** +HTML / BODY +**************************************/ +html, body { + font-family: Arial, Trebuchet, Lucida, sans-serif; + color: #5e5e5e; + background:#d7d7d7 url(images/body_bg_gradient.png) top left repeat-x; + height:100%; +} + +/*Platzierung der Circles unten rechts*/ +.global_container { + position:relative; + width:100%; + min-height:100%; + min-width:550px; +} + + +.footer { + position:relative; + list-style-type:none; + margin:0; + padding:0; + width:100%; + height:100px; + margin-top:-100px; +} + +.left-logo { + float:left; + background:none; + margin:0; + padding:0; + content:url(images/qt_logo.png); + } + +.right-logo { + float:right; + background:none; + margin:0; + padding:0; + content:url(images/nokia_logo.png); + } + +/************************************** +Site Header +**************************************/ +.site_header{ + + height:21px; + background:#686868 url(images/bg_site_header_dark_grey.png) top left repeat-x; + + } + +.site_header p{ + + padding:3px 10px; + color:#fff; + + } + + +/************************************** +Content +**************************************/ +.content_container{ + + margin:0px auto; + + /*CUSTOMIZE WIDTH*/ + width:600px; + } + + +/************************************** +Layout TOP - Logo und Welcome Text +**************************************/ +.layout_top{ + + padding-top:50px; + min-height:250px; + + } + + +/*Product Logo*/ +.layout_top .product_logo{ + + float:left; + width:210px; + + } + +.layout_top .product_logo img{ + + + } + +/*Welcome Text*/ +.layout_top .welcome_text{ + + margin-left:210px; + padding-top:50px; + + + } + +.layout_top .welcome_text p{ + + font-size:9pt; + line-height:180%; + + } + +.layout_top .welcome_text .get_started_container{ + + border-top:1px solid #a8a8a8; + margin-top:20px; + padding:10px 0px; + + } + +/*Getting started Button inlusive CSS HOVER*/ +a.btn_getting_started{ + + float:right; + display:block; + background-image:url(images/btn_getting_started.png); + width:133px; + height:29px; + + } + +a.btn_getting_started:hover{ + + background-image:url(images/btn_getting_started_hover.png) + + }; + + +/************************************** +Layout BOTTOM - Recent Projects und Recent Sessions +**************************************/ + + +/*ROUNDED CORNER BOX - fluid layout compatible*/ +.rc_box{} + +.rc_box .top{height:8px;} + +.rc_box .top .left{float:left; height:8px; width:8px; background:url(images/rc_top_left.png) top left no-repeat;} + +.rc_box .top .mid{margin-left:8px; margin-right:8px; height:8px; background:url(images/rc_top_mid.png) top left repeat-x;} + +.rc_box .top .right{float:right; height:8px; width:8px; background:url(images/rc_top_right.png) top left no-repeat;} + + +.rc_box .mid{} + +.rc_box .mid .mid{padding:0px 20px 0px 10px; min-height:203px; background:#e6e6e6 url(images/rc_mid_mid.png) top left repeat-x; border-left:1px solid #a6b5c1; border-right:1px solid #a6b5c1;} + +.rc_box .icon{ + + width:35px; + float:left; + padding-top:3px; + + } + +.rc_box .box_content{ + + margin-left:35px; + + } + + +.rc_box .bottom{height:8px;} + +.rc_box .bottom .left{float:left; height:8px; width:8px; background:url(images/rc_bottom_left.png) top left no-repeat;} + +.rc_box .bottom .mid{margin-left:8px; margin-right:8px; height:8px; background:url(images/rc_bottom_mid.png) top left repeat-x;} + +.rc_box .bottom .right{float:right; height:8px; width:8px; background:url(images/rc_bottom_right.png) top left no-repeat;} + +/*Layout Bottom*/ +.layout_bottom{ padding:10px 0px 50px 0px; } + +/*Recent Projects*/ +.layout_bottom .rec_proj_container{ + + width:50%; + float:left; + + } + +.layout_bottom .rec_proj_container .rec_proj_box{ + + margin-right:10px; + + } + + + +/*Recent Sessions*/ +.layout_bottom .rec_sess_container{ + + margin-left:50%; + + } + +.layout_bottom .rec_sess_container .rec_sess_box{ + + margin-left:10px; + + } + +/*Restore Session Button inlusive CSS HOVER*/ +a.restore_session{ + + float:right; + display:block; + background-image:url(images/btn_restore_session.png); + width:137px; + height:27px; + + } + +a.restore_session:hover{ + + background-image:url(images/btn_restore_session_hover.png) + + }; + + diff --git a/src/plugins/coreplugin/html/recent_projects.html b/src/plugins/coreplugin/html/recent_projects.html new file mode 100644 index 00000000000..db1bab8bcff --- /dev/null +++ b/src/plugins/coreplugin/html/recent_projects.html @@ -0,0 +1,41 @@ + <div class="rc_box rec_proj_container"> + <div class="rec_proj_box"> + <!-- top --> + <div class="top"> + <div class="left"></div> + <div class="right"></div> + <div class="mid"></div> + </div> + + <!-- mid --> + <div class="mid"> + + <div class="mid"> + + <div class="icon"> + <img src="images/mode_Project.png"></img> + </div> + <div class="box_content"> + <h2>Recent Projects</h2> + <ul> + <!-- RECENT PROJECTS LIST --> + <!--<li><p><a href="#">Project 1</a></p></li> + <li><p><a href="#">Project 2</a></p></li> + <li><p><a href="#">Project 3</a></p></li> + <li><p><a href="#">Project 4</a></p></li> + <li><p><a href="#">Project 5</a></p></li>--> + </ul> + </div> + + </div> + + </div> + + <!-- bottom --> + <div class="bottom"> + <div class="left"></div> + <div class="right"></div> + <div class="mid"></div> + </div> + </div> + </div> diff --git a/src/plugins/coreplugin/html/recent_sessions.html b/src/plugins/coreplugin/html/recent_sessions.html new file mode 100644 index 00000000000..d2f6f2a5967 --- /dev/null +++ b/src/plugins/coreplugin/html/recent_sessions.html @@ -0,0 +1,43 @@ + <div class="rc_box rec_sess_container"> + <div class="rec_sess_box"> + <!-- top --> + <div class="top"> + <div class="left"></div> + <div class="right"></div> + <div class="mid"></div> + </div> + + <!-- mid --> + <div class="mid"> + + <div class="mid"> + + <div class="icon"> + <img src="images/mode_Project.png"></img> + </div> + <div class="box_content"> + <h2>Recent Sessions</h2> + <ul> + <!-- RECENT SESSIONS LIST --> + <!--<li><p><a href="#">Session 1</a></p></li> + <li><p><a href="#">Session 2</a></p></li> + <li><p><a href="#">Session 3</a></p></li> + <li><p><a href="#">Session 4</a></p></li>--> + </ul> + + <a class="restore_session img_link" href="gh-session:LAST_SESSION"> </a> + <div class="clear_right"></div> + </div> + + </div> + + </div> + + <!-- bottom --> + <div class="bottom"> + <div class="left"></div> + <div class="right"></div> + <div class="mid"></div> + </div> + </div> + </div> diff --git a/src/plugins/coreplugin/html/welcome.html b/src/plugins/coreplugin/html/welcome.html new file mode 100644 index 00000000000..40cdafe8122 --- /dev/null +++ b/src/plugins/coreplugin/html/welcome.html @@ -0,0 +1,76 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + + <title>Nokia, Qt Software</title> + <!--<meta name="expires" content="0">--> + + <meta http-equiv="content-type" content="text/html;charset=utf-8" /> + + <link rel="stylesheet" type="text/css" href="qt.css"> + <script type="text/javascript"> + var preloadImg01 = new Image(); + preloadImg01.src = "images/btn_getting_started_hover.png"; + var preloadImg02 = new Image(); + preloadImg02.src = "images/btn_restore_session_hover.png"; + </script> +</head> +<body> + <div class="global_container"> + <!-- Obere dunkelgraue Zeile "Welcome" --> + <!-- <div class="site_header"> + <p>Welcome</p> + </div> + --> + + <!-- Content START --> + <!-- Zentrierung des Contents --> + <div class="content_container"> + <!-- TOP - Logo und Welcome Text --> + <div class="layout_top"> + <div class="product_logo"> + <img src="images/product_logo.png" alt="product_logo"></img> + </div> + <div class="welcome_text"> + <h1>Welcome</h1> + <p>Qt Creator is an intuitive, modern cross platform IDE that enables + developers to create graphically appealing applications for desktop, + embedded, and mobile devices. Click on <strong>Getting Started</strong> to + begin developing with Qt Creator.</p> + <!--<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p> + <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p>--> + <div class="get_started_container"> + + <a class="btn_getting_started img_link" href="gh:getting-started"> </a> + <div class="clear_right"></div> + + </div> + </div> + </div> + + <!-- BOTTOM - Recent Projects und Recent Sessions --> + <div class="layout_bottom"> + + <!-- RECENT PROJECTS --> + + + <!-- RECENT SESSIONS --> + + + <!-- clear floating of sessions and projects --> + <div class="clear_left"></div> + + </div> + <!-- Layout Bottom END --> + + </div> + + <!-- Content END --> + + </div> + <ul class="footer"> + <li class="right-logo"/> + <li class="left-logo"/> + </ul> +</body> diff --git a/src/plugins/coreplugin/icontext.h b/src/plugins/coreplugin/icontext.h new file mode 100644 index 00000000000..a873afb4c5b --- /dev/null +++ b/src/plugins/coreplugin/icontext.h @@ -0,0 +1,59 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ICONTEXT_H +#define ICONTEXT_H + +#include <coreplugin/core_global.h> +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +namespace Core { + +class CORE_EXPORT IContext : public QObject +{ + Q_OBJECT +public: + IContext(QObject *parent = 0) : QObject(parent) {} + virtual ~IContext() {} + + virtual QList<int> context() const = 0; + virtual QWidget *widget() = 0; + virtual QString contextHelpId() const { return QString(); } +}; + +} // namespace Core + +#endif //ICONTEXT_H diff --git a/src/plugins/coreplugin/icore.h b/src/plugins/coreplugin/icore.h new file mode 100644 index 00000000000..ad85e4be188 --- /dev/null +++ b/src/plugins/coreplugin/icore.h @@ -0,0 +1,134 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#ifndef ICORE_H +#define ICORE_H + +#include "core_global.h" +#include <extensionsystem/pluginmanager.h> +#include <QtCore/QObject> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE +class QSettings; +class QStatusBar; +class QFocusEvent; +class QMainWindow; +class QPrinter; +QT_END_NAMESPACE + +namespace Core { + +// forward declarations +class ActionManagerInterface; +class IFile; +class FileManager; +class MessageManager; +class IEditor; +class UniqueIDManager; +class ViewManagerInterface; +class EditorManager; +class ProgressManagerInterface; +class ScriptManagerInterface; +class VariableManager; +class IContext; +class VCSManager; +class ModeManager; +class IWizard; +class MimeDatabase; + +class CORE_EXPORT ICore : public QObject +{ + Q_OBJECT + +public: + + ICore() {} + virtual ~ICore() {} + + virtual QStringList showNewItemDialog(const QString &title, + const QList<IWizard *> &wizards, + const QString &defaultLocation = QString()) = 0; + + virtual void showOptionsDialog(const QString &group = QString(), + const QString &page = QString()) = 0; + + virtual ActionManagerInterface *actionManager() const = 0; + virtual FileManager *fileManager() const = 0; + virtual UniqueIDManager *uniqueIDManager() const = 0; + virtual MessageManager *messageManager() const = 0; + virtual ViewManagerInterface *viewManager() const = 0; + virtual ExtensionSystem::PluginManager *pluginManager() const = 0; + virtual EditorManager *editorManager() const = 0; + virtual ProgressManagerInterface *progressManager() const = 0; + virtual ScriptManagerInterface *scriptManager() const = 0; + virtual VariableManager *variableManager() const = 0; + virtual VCSManager *vcsManager() const = 0; + virtual ModeManager *modeManager() const = 0; + virtual MimeDatabase *mimeDatabase() const = 0; + + virtual QSettings *settings() const = 0; + virtual QPrinter *printer() const = 0; + + virtual QString resourcePath() const = 0; + virtual QString libraryPath() const = 0; + + virtual IContext *currentContextObject() const = 0; + + virtual QMainWindow *mainWindow() const = 0; + virtual QStatusBar *statusBar() const = 0; + + // adds and removes additional active contexts, this context is appended to the + // currently active contexts. call updateContext after changing + virtual void addAdditionalContext(int context) = 0; + virtual void removeAdditionalContext(int context) = 0; + virtual bool hasContext(int context) const = 0; + virtual void addContextObject(IContext *contex) = 0; + virtual void removeContextObject(IContext *contex) = 0; + + virtual void updateContext() = 0; + + virtual void openFiles(const QStringList &fileNames) = 0; + +signals: + void coreOpened(); + void saveSettingsRequested(); + void settingsDialogRequested(); + void coreAboutToClose(); + void contextAboutToChange(Core::IContext *context); + void contextChanged(Core::IContext *context); +}; + +} // namespace Core + +#endif //ICORE_H diff --git a/src/plugins/coreplugin/icorelistener.h b/src/plugins/coreplugin/icorelistener.h new file mode 100644 index 00000000000..3ff0a859a1f --- /dev/null +++ b/src/plugins/coreplugin/icorelistener.h @@ -0,0 +1,77 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ICORELISTENER_H +#define ICORELISTENER_H + +#include "core_global.h" +#include <coreplugin/editormanager/ieditor.h> +#include <QtCore/QObject> + +namespace Core { + +/*! + \class Core::ICoreListener + \brief Provides a hook for plugins to veto on certain events emitted from the core plugin. + + You implement this interface if you want to prevent certain events from occurring, e.g. + if you want to prevent the closing of the whole application or to prevent the closing + of an editor window under certain conditions. + + If e.g. the application window requests a close, then first + ICoreListener::coreAboutToClose() is called (in arbitrary order) + on all registered objects implementing this interface. If one if these calls returns + false, the process is aborted and the event is ignored. + If all calls return true, the corresponding signal is emitted and the event is accepted/performed. + + Guidelines for implementing: + \list + \o Return false from the implemented method if you want to prevent the event. + \o You need to add your implementing object to the plugin managers objects: + ICore::pluginManager()->addObject(yourImplementingObject); + \o Don't forget to remove the object again at deconstruction (e.g. in the destructor of + your plugin). +*/ +class CORE_EXPORT ICoreListener : public QObject +{ + Q_OBJECT +public: + ICoreListener(QObject *parent = 0) : QObject(parent) {} + virtual ~ICoreListener() {} + + virtual bool editorAboutToClose(IEditor * /*editor*/) { return true; } + virtual bool coreAboutToClose() { return true; } +}; + +} // namespace Core + +#endif // ICORELISTENER_H diff --git a/src/plugins/coreplugin/ifile.h b/src/plugins/coreplugin/ifile.h new file mode 100644 index 00000000000..40a03b484e0 --- /dev/null +++ b/src/plugins/coreplugin/ifile.h @@ -0,0 +1,72 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IFILE_H +#define IFILE_H + +#include "core_global.h" +#include <QtCore/QObject> + +namespace Core { + +class MimeType; + +class CORE_EXPORT IFile : public QObject +{ + Q_OBJECT + +public: + enum ReloadBehavior { AskForReload, ReloadAll, ReloadPermissions, ReloadNone }; + + IFile(QObject *parent = 0) : QObject(parent) {} + virtual ~IFile() {} + + virtual bool save(const QString &fileName = QString()) = 0; + virtual QString fileName() const = 0; + + virtual QString defaultPath() const = 0; + virtual QString suggestedFileName() const = 0; + virtual QString mimeType() const = 0; + + virtual bool isModified() const = 0; + virtual bool isReadOnly() const = 0; + virtual bool isSaveAsAllowed() const = 0; + + virtual void modified(ReloadBehavior *behavior) = 0; + +signals: + void changed(); +}; + +} //namespace + +#endif //IFILE_H diff --git a/src/plugins/coreplugin/ifilefactory.h b/src/plugins/coreplugin/ifilefactory.h new file mode 100644 index 00000000000..e8dcf155e18 --- /dev/null +++ b/src/plugins/coreplugin/ifilefactory.h @@ -0,0 +1,63 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IFILEFACTORY_H +#define IFILEFACTORY_H + +#include "core_global.h" + +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE +class QStringList; +QT_END_NAMESPACE + +namespace Core { + +class IFile; + +class CORE_EXPORT IFileFactory : public QObject +{ + Q_OBJECT +public: + IFileFactory(QObject *parent = 0) : QObject(parent) {} + virtual ~IFileFactory() {} + + virtual QStringList mimeTypes() const = 0; + + virtual QString kind() const = 0; + virtual Core::IFile *open(const QString &fileName) = 0; +}; + +} // namespace Core + +#endif // IFILEFACTORY_H diff --git a/src/plugins/coreplugin/ifilewizardextension.h b/src/plugins/coreplugin/ifilewizardextension.h new file mode 100644 index 00000000000..238d628a78b --- /dev/null +++ b/src/plugins/coreplugin/ifilewizardextension.h @@ -0,0 +1,72 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IFILEWIZARDEXTENSION_H +#define IFILEWIZARDEXTENSION_H + +#include <coreplugin/core_global.h> + +#include <QtCore/QObject> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE +class QWizardPage; +QT_END_NAMESPACE + +namespace Core { + +class IWizard; +class GeneratedFile; + +/*! + Hook to add generic wizard pages to implementations of IWizard. + Used e.g. to add "Add to Project File/Add to version control" page + */ +class CORE_EXPORT IFileWizardExtension : public QObject +{ + Q_OBJECT +public: + /* Return a list of pages to be added to the Wizard (empty list if not + * applicable). */ + virtual QList<QWizardPage *> extensionPages(const IWizard *wizard) = 0; + + /* Process the files using the extension parameters */ + virtual bool process(const QList<GeneratedFile> &files, QString *errorMessage) = 0; + +public slots: + /* Notification about the first extension page being shown. */ + virtual void firstExtensionPageShown(const QList<GeneratedFile> &) {} +}; + +} // namespace Core + +#endif // IFILEWIZARDEXTENSION_H diff --git a/src/plugins/coreplugin/images/clean_pane_small.png b/src/plugins/coreplugin/images/clean_pane_small.png Binary files differnew file mode 100644 index 00000000000..341e23861a1 --- /dev/null +++ b/src/plugins/coreplugin/images/clean_pane_small.png diff --git a/src/plugins/coreplugin/images/clear.png b/src/plugins/coreplugin/images/clear.png Binary files differnew file mode 100644 index 00000000000..72279e1c694 --- /dev/null +++ b/src/plugins/coreplugin/images/clear.png diff --git a/src/plugins/coreplugin/images/closebutton.png b/src/plugins/coreplugin/images/closebutton.png Binary files differnew file mode 100644 index 00000000000..c978cf51aaa --- /dev/null +++ b/src/plugins/coreplugin/images/closebutton.png diff --git a/src/plugins/coreplugin/images/dir.png b/src/plugins/coreplugin/images/dir.png Binary files differnew file mode 100644 index 00000000000..57cec6bcd31 --- /dev/null +++ b/src/plugins/coreplugin/images/dir.png diff --git a/src/plugins/coreplugin/images/editcopy.png b/src/plugins/coreplugin/images/editcopy.png Binary files differnew file mode 100644 index 00000000000..ceb520e3051 --- /dev/null +++ b/src/plugins/coreplugin/images/editcopy.png diff --git a/src/plugins/coreplugin/images/editcut.png b/src/plugins/coreplugin/images/editcut.png Binary files differnew file mode 100644 index 00000000000..700ccb0cfde --- /dev/null +++ b/src/plugins/coreplugin/images/editcut.png diff --git a/src/plugins/coreplugin/images/editpaste.png b/src/plugins/coreplugin/images/editpaste.png Binary files differnew file mode 100644 index 00000000000..7238fae7f19 --- /dev/null +++ b/src/plugins/coreplugin/images/editpaste.png diff --git a/src/plugins/coreplugin/images/empty14.png b/src/plugins/coreplugin/images/empty14.png Binary files differnew file mode 100644 index 00000000000..7346e58082a --- /dev/null +++ b/src/plugins/coreplugin/images/empty14.png diff --git a/src/plugins/coreplugin/images/fancytoolbutton.svg b/src/plugins/coreplugin/images/fancytoolbutton.svg new file mode 100644 index 00000000000..8c9c0f1d623 --- /dev/null +++ b/src/plugins/coreplugin/images/fancytoolbutton.svg @@ -0,0 +1,539 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="64" + height="64" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.46" + version="1.0" + sodipodi:docname="fancytoolbutton.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:docbase="C:\depot\ide\research\modes\src\plugins\coreplugin\images" + style="display:inline"> + <defs + id="defs4"> + <linearGradient + id="linearGradient3213"> + <stop + style="stop-color:#ffffff;stop-opacity:0.21969697;" + offset="0" + id="stop3215" /> + <stop + style="stop-color:#ffffff;stop-opacity:0.0530303;" + offset="1" + id="stop3217" /> + </linearGradient> + <linearGradient + id="linearGradient3227"> + <stop + style="stop-color:#ffffff;stop-opacity:0.13559322;" + offset="0" + id="stop3229" /> + <stop + style="stop-color:#3a3a3a;stop-opacity:0.11016949;" + offset="1" + id="stop3231" /> + </linearGradient> + <linearGradient + id="linearGradient3284"> + <stop + style="stop-color:#ffffff;stop-opacity:0.62931037;" + offset="0" + id="stop3286" /> + <stop + style="stop-color:#a3a3a3;stop-opacity:0;" + offset="1" + id="stop3288" /> + </linearGradient> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 32 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="64 : 32 : 1" + inkscape:persp3d-origin="32 : 21.333333 : 1" + id="perspective62" /> + <linearGradient + id="linearGradient3299"> + <stop + id="stop3301" + offset="0" + style="stop-color:#ffffff;stop-opacity:0.31384614;" /> + <stop + id="stop3303" + offset="1" + style="stop-color:#ffffff;stop-opacity:0.15686275;" /> + </linearGradient> + <linearGradient + id="linearGradient3293"> + <stop + id="stop3295" + offset="0" + style="stop-color:#ffffff;stop-opacity:1;" /> + <stop + id="stop3297" + offset="1" + style="stop-color:#000000;stop-opacity:0.02157165;" /> + </linearGradient> + <linearGradient + id="linearGradient3218"> + <stop + style="stop-color:#ffffff;stop-opacity:0.78431374;" + offset="0" + id="stop3220" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop3222" /> + </linearGradient> + <linearGradient + id="linearGradient3204"> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="0" + id="stop3206" /> + <stop + style="stop-color:#ffffff;stop-opacity:0.25098041;" + offset="1" + id="stop3208" /> + </linearGradient> + <linearGradient + id="linearGradient3162"> + <stop + style="stop-color:#424242;stop-opacity:0.13793103;" + offset="0" + id="stop3164" /> + <stop + style="stop-color:#f0f0f0;stop-opacity:0;" + offset="1" + id="stop3166" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3204" + id="linearGradient3210" + x1="32" + y1="32" + x2="32" + y2="61" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3218" + id="linearGradient3224" + x1="24" + y1="1.9999999" + x2="24" + y2="31.571428" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3162" + id="linearGradient3267" + gradientUnits="userSpaceOnUse" + x1="32" + y1="64" + x2="32" + y2="0" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3218" + id="linearGradient3271" + gradientUnits="userSpaceOnUse" + x1="24" + y1="1.9999999" + x2="24" + y2="31.571428" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3299" + id="radialGradient3283" + cx="32" + cy="32" + fx="32" + fy="32" + r="29" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3213" + id="radialGradient2210" + gradientUnits="userSpaceOnUse" + cx="32" + cy="32" + fx="32" + fy="32" + r="29" + gradientTransform="matrix(0.962963,0,0,0.9444445,1.1851851,1.7777776)" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3218" + id="radialGradient4673" + cx="24" + cy="-2.0519459" + fx="24" + fy="-2.0519459" + r="18" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.8095239,-6.9383612e-8,5.1646454e-8,1.8333332,-19.428571,1.7619038)" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3218" + id="radialGradient4675" + cx="24" + cy="-0.72727227" + fx="24" + fy="-0.72727227" + r="18" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.8095238,0,0,1.8333332,-19.42857,-1.6666668)" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3227" + id="radialGradient3233" + cx="48.656292" + cy="51.519093" + fx="48.656292" + fy="51.519093" + r="29.444443" + gradientUnits="userSpaceOnUse" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#3e5e7e" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="1" + inkscape:pageshadow="2" + inkscape:zoom="5.6568542" + inkscape:cx="-11.541044" + inkscape:cy="27.695432" + inkscape:document-units="px" + inkscape:current-layer="LayerPressed" + showgrid="true" + inkscape:window-width="1551" + inkscape:window-height="972" + inkscape:window-x="103" + inkscape:window-y="25" + showguides="true" + inkscape:guide-bbox="true" + inkscape:grid-points="true" + inkscape:snap-nodes="true" + inkscape:snap-global="false" + inkscape:snap-intersection-grid-guide="false"> + <inkscape:grid + type="xygrid" + id="grid2388" + spacingx="0.5px" + spacingy="0.5px" + empspacing="4" + dotted="false" + enabled="true" + visible="false" /> + <sodipodi:guide + orientation="1,0" + position="32,44.910714" + id="guide3214" /> + <sodipodi:guide + orientation="horizontal" + position="32.880465,29.521708" + id="guide2214" /> + <inkscape:grid + id="GridFromPre046Settings" + type="xygrid" + originx="0px" + originy="0px" + spacingx="1px" + spacingy="1px" + color="#0000ff" + empcolor="#0000ff" + opacity="0.2" + empopacity="0.4" + empspacing="1" + visible="true" + enabled="false" /> + <sodipodi:guide + orientation="0,1" + position="45.749998,96.999996" + id="guide4677" /> + <sodipodi:guide + orientation="1,0" + position="69.999997,64.124997" + id="guide4679" /> + <sodipodi:guide + orientation="0,1" + position="14.437499,62" + id="guide4681" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + <dc:date /> + <dc:creator> + <cc:Agent> + <dc:title /> + </cc:Agent> + </dc:creator> + <dc:rights> + <cc:Agent> + <dc:title /> + </cc:Agent> + </dc:rights> + <dc:publisher> + <cc:Agent> + <dc:title /> + </cc:Agent> + </dc:publisher> + <dc:identifier /> + <dc:source /> + <dc:relation /> + <dc:language /> + <dc:subject> + <rdf:Bag /> + </dc:subject> + <dc:coverage /> + <dc:description /> + <dc:contributor> + <cc:Agent> + <dc:title /> + </cc:Agent> + </dc:contributor> + <cc:license + rdf:resource="" /> + </cc:Work> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:groupmode="layer" + id="LayerBase" + inkscape:label="LayerBase" + style="display:none"> + <g + style="fill:none;stroke:url(#radialGradient3233);stroke-width:2.17741656;stroke-miterlimit:4;stroke-dasharray:none;display:inline" + id="ButtonBase" + transform="matrix(0.6792453,0,0,0.6792453,10.26415,10.26415)"> + <path + d="M 61.444443,32 A 29.444443,29.444443 0 1 1 60.441148,24.379217" + sodipodi:ry="29.444443" + sodipodi:rx="29.444443" + sodipodi:cy="32" + sodipodi:cx="32" + id="path3257" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient3233);stroke-width:2.17741656;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:type="arc" + sodipodi:start="0" + sodipodi:end="6.0213859" + sodipodi:open="true" /> + <path + transform="matrix(0.9498207,0,0,0.9498207,1.6057361,1.6057361)" + d="M 63,32 A 31,31 0 1 1 1,32 A 31,31 0 1 1 63,32 z" + sodipodi:ry="31" + sodipodi:rx="31" + sodipodi:cy="32" + sodipodi:cx="32" + id="path3259" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient3233);stroke-width:2.7574501;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:type="arc" /> + <path + transform="matrix(0.9,0,0,0.9,3.1999999,3.1999999)" + d="M 62,32 A 30,30 0 1 1 2,32 A 30,30 0 1 1 62,32 z" + sodipodi:ry="30" + sodipodi:rx="30" + sodipodi:cy="32" + sodipodi:cx="32" + id="path3281" + style="opacity:1;fill:none;fill-opacity:0.62672813;fill-rule:evenodd;stroke:#2b2b2b;stroke-width:1.92861104;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.50228307" + sodipodi:type="arc" /> + </g> + </g> + <g + inkscape:groupmode="layer" + id="LayerPressed" + inkscape:label="LayerPressed" + style="display:inline"> + <g + id="ButtonPressedBase" + transform="matrix(0.6792453,0,0,0.6792453,10.26415,10.26415)"> + <rect + style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect4724" + width="64" + height="64" + x="0" + y="0" /> + <path + transform="matrix(0.9310345,0,0,0.9310345,2.2068967,2.2068968)" + d="M 61,32 A 29,29 0 1 1 3,32 A 29,29 0 1 1 61,32 z" + sodipodi:ry="29" + sodipodi:rx="29" + sodipodi:cy="32" + sodipodi:cx="32" + id="path3263" + style="opacity:1;fill:url(#radialGradient2210);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:0.10000000000000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:type="arc" /> + <path + transform="matrix(1.4166666,0,0,1.4027778,-1.9999995,3.1944446)" + d="M 42,20 A 18,18 0 1 1 6,20 A 18,18 0 1 1 42,20 z" + sodipodi:ry="18" + sodipodi:rx="18" + sodipodi:cy="20" + sodipodi:cx="24" + id="path3209" + style="opacity:1;fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" + sodipodi:type="arc" /> + </g> + <g + id="ButtonPressedOverlay" + transform="matrix(0.6792453,0,0,0.6792453,10.26415,12.739024)" + style="opacity:0.56682028"> + <rect + y="0" + x="0" + height="64" + width="64" + id="rect3182" + style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + sodipodi:type="arc" + style="opacity:0.47465436999999999;fill:url(#radialGradient4675);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.10000000000000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" + id="path3226" + sodipodi:cx="24" + sodipodi:cy="20" + sodipodi:rx="18" + sodipodi:ry="18" + d="M 42,20 A 18,18 0 1 1 6,20 A 18,18 0 1 1 42,20 z" + transform="matrix(1.1666668,0,0,1,3.9999975,4.9999993)" /> + </g> + </g> + <g + inkscape:label="LayerNormal" + inkscape:groupmode="layer" + id="LayerNormal" + style="display:none"> + <g + style="display:inline" + id="ButtonNormalBase" + transform="matrix(0.6792453,0,0,0.7011564,10.26415,10.26415)"> + <rect + style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect4730" + width="64" + height="64" + x="0" + y="0" + ry="0" /> + <path + transform="matrix(1.4722222,0,0,1.4444445,-3.3333336,2.1111101)" + d="M 42,20 A 18,18 0 1 1 6,20 A 18,18 0 1 1 42,20 z" + sodipodi:ry="18" + sodipodi:rx="18" + sodipodi:cy="20" + sodipodi:cx="24" + id="path4734" + style="opacity:1;fill:#ffffff;fill-opacity:0.1254902;fill-rule:evenodd;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline" + sodipodi:type="arc" /> + </g> + <g + id="ButtonNormalOverlay" + style="display:inline" + transform="matrix(0.6792453,0,0,0.6792453,10.528302,9.6981123)"> + <rect + ry="0" + y="0" + x="0" + height="64" + width="64" + id="rect2202" + style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:url(#radialGradient4673);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3188" + sodipodi:cx="24" + sodipodi:cy="20" + sodipodi:rx="18" + sodipodi:ry="18" + d="M 42,20 A 18,18 0 1 1 6,20 A 18,18 0 1 1 42,20 z" + transform="matrix(1.1666667,0,0,1.0000001,3.9999997,3.9999987)" /> + </g> + </g> + <g + inkscape:groupmode="layer" + id="LayerDisabled" + inkscape:label="LayerDisabled" + style="display:none"> + <g + id="ButtonDisabledOverlay" + transform="matrix(0.6792453,0,0,0.6792453,10.26415,10.26415)"> + <rect + style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect2247" + width="64" + height="64" + x="0" + y="0" + ry="0" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:#808080;fill-opacity:0.17351595;fill-rule:evenodd;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2250" + sodipodi:cx="32" + sodipodi:cy="32" + sodipodi:rx="30" + sodipodi:ry="30" + d="M 62,32 A 30,30 0 1 1 2,32 A 30,30 0 1 1 62,32 z" + transform="matrix(0.9,0,0,0.9,3.2,3.2)" /> + </g> + </g> + <g + inkscape:groupmode="layer" + id="LayerButtonHover" + inkscape:label="LayerButtonHover" + style="display:none"> + <g + id="ButtonHoverOverlay" + transform="matrix(0.6792453,0,0,0.6792453,10.26415,10.26415)"> + <rect + ry="0" + y="0" + x="0" + height="64" + width="64" + id="rect2209" + style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + transform="matrix(0.9,0,0,0.9,3.2,3.2)" + d="M 62,32 A 30,30 0 1 1 2,32 A 30,30 0 1 1 62,32 z" + sodipodi:ry="30" + sodipodi:rx="30" + sodipodi:cy="32" + sodipodi:cx="32" + id="path2211" + style="opacity:1;fill:#ffffff;fill-opacity:0.04020099;fill-rule:evenodd;stroke:none;stroke-width:0.1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:type="arc" /> + </g> + </g> +</svg> diff --git a/src/plugins/coreplugin/images/filenew.png b/src/plugins/coreplugin/images/filenew.png Binary files differnew file mode 100644 index 00000000000..dd795cfffc5 --- /dev/null +++ b/src/plugins/coreplugin/images/filenew.png diff --git a/src/plugins/coreplugin/images/fileopen.png b/src/plugins/coreplugin/images/fileopen.png Binary files differnew file mode 100644 index 00000000000..58d70149e9e --- /dev/null +++ b/src/plugins/coreplugin/images/fileopen.png diff --git a/src/plugins/coreplugin/images/filesave.png b/src/plugins/coreplugin/images/filesave.png Binary files differnew file mode 100644 index 00000000000..604ee3b8342 --- /dev/null +++ b/src/plugins/coreplugin/images/filesave.png diff --git a/src/plugins/coreplugin/images/find.png b/src/plugins/coreplugin/images/find.png Binary files differnew file mode 100644 index 00000000000..cbe2f31521a --- /dev/null +++ b/src/plugins/coreplugin/images/find.png diff --git a/src/plugins/coreplugin/images/findnext.png b/src/plugins/coreplugin/images/findnext.png Binary files differnew file mode 100644 index 00000000000..a2889e439fa --- /dev/null +++ b/src/plugins/coreplugin/images/findnext.png diff --git a/src/plugins/coreplugin/images/inputfield.png b/src/plugins/coreplugin/images/inputfield.png Binary files differnew file mode 100644 index 00000000000..8020c8f7319 --- /dev/null +++ b/src/plugins/coreplugin/images/inputfield.png diff --git a/src/plugins/coreplugin/images/inputfield_disabled.png b/src/plugins/coreplugin/images/inputfield_disabled.png Binary files differnew file mode 100644 index 00000000000..c69f082f087 --- /dev/null +++ b/src/plugins/coreplugin/images/inputfield_disabled.png diff --git a/src/plugins/coreplugin/images/linkicon.png b/src/plugins/coreplugin/images/linkicon.png Binary files differnew file mode 100644 index 00000000000..864f36f1747 --- /dev/null +++ b/src/plugins/coreplugin/images/linkicon.png diff --git a/src/plugins/coreplugin/images/locked.png b/src/plugins/coreplugin/images/locked.png Binary files differnew file mode 100644 index 00000000000..0ff602714e9 --- /dev/null +++ b/src/plugins/coreplugin/images/locked.png diff --git a/src/plugins/coreplugin/images/magnifier.png b/src/plugins/coreplugin/images/magnifier.png Binary files differnew file mode 100644 index 00000000000..0e652c945e4 --- /dev/null +++ b/src/plugins/coreplugin/images/magnifier.png diff --git a/src/plugins/coreplugin/images/minus.png b/src/plugins/coreplugin/images/minus.png Binary files differnew file mode 100644 index 00000000000..4466844662e --- /dev/null +++ b/src/plugins/coreplugin/images/minus.png diff --git a/src/plugins/coreplugin/images/mode_Debug.png b/src/plugins/coreplugin/images/mode_Debug.png Binary files differnew file mode 100644 index 00000000000..e6ab1e40e3e --- /dev/null +++ b/src/plugins/coreplugin/images/mode_Debug.png diff --git a/src/plugins/coreplugin/images/mode_Edit.png b/src/plugins/coreplugin/images/mode_Edit.png Binary files differnew file mode 100644 index 00000000000..c43fd4251a8 --- /dev/null +++ b/src/plugins/coreplugin/images/mode_Edit.png diff --git a/src/plugins/coreplugin/images/mode_Output.png b/src/plugins/coreplugin/images/mode_Output.png Binary files differnew file mode 100644 index 00000000000..25e403c7801 --- /dev/null +++ b/src/plugins/coreplugin/images/mode_Output.png diff --git a/src/plugins/coreplugin/images/mode_Project.png b/src/plugins/coreplugin/images/mode_Project.png Binary files differnew file mode 100644 index 00000000000..20f54e786b3 --- /dev/null +++ b/src/plugins/coreplugin/images/mode_Project.png diff --git a/src/plugins/coreplugin/images/mode_Reference.png b/src/plugins/coreplugin/images/mode_Reference.png Binary files differnew file mode 100644 index 00000000000..7d6a46a48ac --- /dev/null +++ b/src/plugins/coreplugin/images/mode_Reference.png diff --git a/src/plugins/coreplugin/images/next.png b/src/plugins/coreplugin/images/next.png Binary files differnew file mode 100644 index 00000000000..7700d6fce6b --- /dev/null +++ b/src/plugins/coreplugin/images/next.png diff --git a/src/plugins/coreplugin/images/panel_button.png b/src/plugins/coreplugin/images/panel_button.png Binary files differnew file mode 100644 index 00000000000..a101043f416 --- /dev/null +++ b/src/plugins/coreplugin/images/panel_button.png diff --git a/src/plugins/coreplugin/images/panel_button_checked.png b/src/plugins/coreplugin/images/panel_button_checked.png Binary files differnew file mode 100644 index 00000000000..fd7753bceb5 --- /dev/null +++ b/src/plugins/coreplugin/images/panel_button_checked.png diff --git a/src/plugins/coreplugin/images/panel_button_checked_hover.png b/src/plugins/coreplugin/images/panel_button_checked_hover.png Binary files differnew file mode 100644 index 00000000000..045c23aa027 --- /dev/null +++ b/src/plugins/coreplugin/images/panel_button_checked_hover.png diff --git a/src/plugins/coreplugin/images/panel_button_hover.png b/src/plugins/coreplugin/images/panel_button_hover.png Binary files differnew file mode 100644 index 00000000000..54e2d2b3a54 --- /dev/null +++ b/src/plugins/coreplugin/images/panel_button_hover.png diff --git a/src/plugins/coreplugin/images/panel_button_pressed.png b/src/plugins/coreplugin/images/panel_button_pressed.png Binary files differnew file mode 100644 index 00000000000..b92a1dda0cd --- /dev/null +++ b/src/plugins/coreplugin/images/panel_button_pressed.png diff --git a/src/plugins/coreplugin/images/plus.png b/src/plugins/coreplugin/images/plus.png Binary files differnew file mode 100644 index 00000000000..be8c961df1e --- /dev/null +++ b/src/plugins/coreplugin/images/plus.png diff --git a/src/plugins/coreplugin/images/prev.png b/src/plugins/coreplugin/images/prev.png Binary files differnew file mode 100644 index 00000000000..99dc8733c73 --- /dev/null +++ b/src/plugins/coreplugin/images/prev.png diff --git a/src/plugins/coreplugin/images/pushbutton.png b/src/plugins/coreplugin/images/pushbutton.png Binary files differnew file mode 100644 index 00000000000..a9efaf2522b --- /dev/null +++ b/src/plugins/coreplugin/images/pushbutton.png diff --git a/src/plugins/coreplugin/images/pushbutton_hover.png b/src/plugins/coreplugin/images/pushbutton_hover.png Binary files differnew file mode 100644 index 00000000000..910f6980b16 --- /dev/null +++ b/src/plugins/coreplugin/images/pushbutton_hover.png diff --git a/src/plugins/coreplugin/images/pushbutton_pressed.png b/src/plugins/coreplugin/images/pushbutton_pressed.png Binary files differnew file mode 100644 index 00000000000..7a5a4768ffc --- /dev/null +++ b/src/plugins/coreplugin/images/pushbutton_pressed.png diff --git a/src/plugins/coreplugin/images/qtcreator_logo_128.png b/src/plugins/coreplugin/images/qtcreator_logo_128.png Binary files differnew file mode 100644 index 00000000000..c3ddf74c74f --- /dev/null +++ b/src/plugins/coreplugin/images/qtcreator_logo_128.png diff --git a/src/plugins/coreplugin/images/qtcreator_logo_16.png b/src/plugins/coreplugin/images/qtcreator_logo_16.png Binary files differnew file mode 100644 index 00000000000..6e48e979b80 --- /dev/null +++ b/src/plugins/coreplugin/images/qtcreator_logo_16.png diff --git a/src/plugins/coreplugin/images/qtcreator_logo_24.png b/src/plugins/coreplugin/images/qtcreator_logo_24.png Binary files differnew file mode 100644 index 00000000000..797fc705a9e --- /dev/null +++ b/src/plugins/coreplugin/images/qtcreator_logo_24.png diff --git a/src/plugins/coreplugin/images/qtcreator_logo_32.png b/src/plugins/coreplugin/images/qtcreator_logo_32.png Binary files differnew file mode 100644 index 00000000000..992620b8ae2 --- /dev/null +++ b/src/plugins/coreplugin/images/qtcreator_logo_32.png diff --git a/src/plugins/coreplugin/images/qtcreator_logo_48.png b/src/plugins/coreplugin/images/qtcreator_logo_48.png Binary files differnew file mode 100644 index 00000000000..4bedd55219e --- /dev/null +++ b/src/plugins/coreplugin/images/qtcreator_logo_48.png diff --git a/src/plugins/coreplugin/images/qtcreator_logo_64.png b/src/plugins/coreplugin/images/qtcreator_logo_64.png Binary files differnew file mode 100644 index 00000000000..88a11abf6e6 --- /dev/null +++ b/src/plugins/coreplugin/images/qtcreator_logo_64.png diff --git a/src/plugins/coreplugin/images/qtwatermark.png b/src/plugins/coreplugin/images/qtwatermark.png Binary files differnew file mode 100644 index 00000000000..d5eec355dc2 --- /dev/null +++ b/src/plugins/coreplugin/images/qtwatermark.png diff --git a/src/plugins/coreplugin/images/redo.png b/src/plugins/coreplugin/images/redo.png Binary files differnew file mode 100644 index 00000000000..9d679fe6fcb --- /dev/null +++ b/src/plugins/coreplugin/images/redo.png diff --git a/src/plugins/coreplugin/images/replace.png b/src/plugins/coreplugin/images/replace.png Binary files differnew file mode 100644 index 00000000000..baa05997bc3 --- /dev/null +++ b/src/plugins/coreplugin/images/replace.png diff --git a/src/plugins/coreplugin/images/reset.png b/src/plugins/coreplugin/images/reset.png Binary files differnew file mode 100644 index 00000000000..cc0d6a26bd5 --- /dev/null +++ b/src/plugins/coreplugin/images/reset.png diff --git a/src/plugins/coreplugin/images/sidebaricon.png b/src/plugins/coreplugin/images/sidebaricon.png Binary files differnew file mode 100644 index 00000000000..8aab325d6ef --- /dev/null +++ b/src/plugins/coreplugin/images/sidebaricon.png diff --git a/src/plugins/coreplugin/images/splitbutton_horizontal.png b/src/plugins/coreplugin/images/splitbutton_horizontal.png Binary files differnew file mode 100644 index 00000000000..7b5f4937587 --- /dev/null +++ b/src/plugins/coreplugin/images/splitbutton_horizontal.png diff --git a/src/plugins/coreplugin/images/statusbar.png b/src/plugins/coreplugin/images/statusbar.png Binary files differnew file mode 100644 index 00000000000..dd426ef78fd --- /dev/null +++ b/src/plugins/coreplugin/images/statusbar.png diff --git a/src/plugins/coreplugin/images/undo.png b/src/plugins/coreplugin/images/undo.png Binary files differnew file mode 100644 index 00000000000..eee23d24a31 --- /dev/null +++ b/src/plugins/coreplugin/images/undo.png diff --git a/src/plugins/coreplugin/images/unknownfile.png b/src/plugins/coreplugin/images/unknownfile.png Binary files differnew file mode 100644 index 00000000000..88f77592d1f --- /dev/null +++ b/src/plugins/coreplugin/images/unknownfile.png diff --git a/src/plugins/coreplugin/images/unlocked.png b/src/plugins/coreplugin/images/unlocked.png Binary files differnew file mode 100644 index 00000000000..72f659d0981 --- /dev/null +++ b/src/plugins/coreplugin/images/unlocked.png diff --git a/src/plugins/coreplugin/imode.h b/src/plugins/coreplugin/imode.h new file mode 100644 index 00000000000..080b5d9be0f --- /dev/null +++ b/src/plugins/coreplugin/imode.h @@ -0,0 +1,62 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IMODE_H +#define IMODE_H + +#include "icontext.h" + +#include <coreplugin/core_global.h> + +#include <QtCore/QObject> +#include <QtGui/QIcon> +#include <QtGui/QKeySequence> +#include <QtGui/QLayout> + +namespace Core { + +class CORE_EXPORT IMode : public IContext +{ + Q_OBJECT +public: + IMode(QObject *parent = 0) : IContext(parent) {} + virtual ~IMode() {} + + virtual QString name() const = 0; + virtual QIcon icon() const = 0; + virtual int priority() const = 0; + virtual const char *uniqueModeName() const = 0; +}; + +} // namespace Core + +#endif // IMODE_H diff --git a/src/plugins/coreplugin/inavigationwidgetfactory.cpp b/src/plugins/coreplugin/inavigationwidgetfactory.cpp new file mode 100644 index 00000000000..74772c4cc91 --- /dev/null +++ b/src/plugins/coreplugin/inavigationwidgetfactory.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "inavigationwidgetfactory.h" + +using namespace Core; + +INavigationWidgetFactory::INavigationWidgetFactory() +{ + +} + +INavigationWidgetFactory::~INavigationWidgetFactory() +{ + +} + +QKeySequence INavigationWidgetFactory::activationSequence() +{ + return QKeySequence(); +} + diff --git a/src/plugins/coreplugin/inavigationwidgetfactory.h b/src/plugins/coreplugin/inavigationwidgetfactory.h new file mode 100644 index 00000000000..f182f3accaa --- /dev/null +++ b/src/plugins/coreplugin/inavigationwidgetfactory.h @@ -0,0 +1,70 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef INAVIGATIONWIDGET_H +#define INAVIGATIONWIDGET_H + +#include <coreplugin/core_global.h> +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QtGui/QKeySequence> + +QT_BEGIN_NAMESPACE +class QToolButton; +class QWidget; +QT_END_NAMESPACE + +namespace Core { + +struct NavigationView { + QWidget *widget; + QList<QToolButton *> doockToolBarWidgets; +}; + +class CORE_EXPORT INavigationWidgetFactory : public QObject +{ + Q_OBJECT +public: + INavigationWidgetFactory(); + virtual ~INavigationWidgetFactory(); + + virtual QString displayName() = 0; + virtual QKeySequence activationSequence(); + // This design is not optimal, think about it again once we need to extend it + // It could be implemented as returning an object which has both the widget + // and the docktoolbar widgets + // Similar to how IView + virtual NavigationView createWidget() = 0; +}; +} + +#endif // INAVIGATIONWIDGET_H diff --git a/src/plugins/coreplugin/ioutputpane.h b/src/plugins/coreplugin/ioutputpane.h new file mode 100644 index 00000000000..9847c1a1b25 --- /dev/null +++ b/src/plugins/coreplugin/ioutputpane.h @@ -0,0 +1,103 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IOUTPUTPANE_H +#define IOUTPUTPANE_H + +#include "core_global.h" + +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QtCore/QString> +#include <QtGui/QWidget> + +namespace Core { + +class CORE_EXPORT IOutputPane : public QObject +{ + Q_OBJECT +public: + IOutputPane(QObject *parent = 0) : QObject(parent) {} + virtual ~IOutputPane() {} + + virtual QWidget *outputWidget(QWidget *parent) = 0; + virtual QList<QWidget*> toolBarWidgets(void) const = 0; + virtual QString name() const = 0; + + // -1 don't show in statusBar + // 100...0 show at front...end + virtual int priorityInStatusBar() const = 0; + + virtual void clearContents() = 0; + virtual void visibilityChanged(bool visible) = 0; + + // This function is called to give the outputwindow focus + virtual void setFocus() = 0; + // Wheter the outputpane has focus + virtual bool hasFocus() = 0; + // Wheter the outputpane can be focused at the moment. + // (E.g. the search result window doesn't want to be focussed if the are no results.) + virtual bool canFocus() = 0; +public slots: + void popup() + { + popup(true); + } + void popup(bool withFocus) + { + emit showPage(withFocus); + } + + void hide() + { + emit hidePage(); + } + + void toggle() + { + toggle(true); + } + + void toggle(bool withFocusIfShown) + { + emit togglePage(withFocusIfShown); + } + +signals: + void showPage(bool withFocus); + void hidePage(); + void togglePage(bool withFocusIfShown); +}; + +} // namespace Core + +#endif // IOUTPUTPANE_H diff --git a/src/plugins/coreplugin/iversioncontrol.h b/src/plugins/coreplugin/iversioncontrol.h new file mode 100644 index 00000000000..e2d3de1ec38 --- /dev/null +++ b/src/plugins/coreplugin/iversioncontrol.h @@ -0,0 +1,96 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IVERSIONCONTROL_H +#define IVERSIONCONTROL_H + +#include "core_global.h" + +#include <QtCore/QObject> +#include <QtCore/QString> + +namespace Core { + +class CORE_EXPORT IVersionControl : public QObject +{ + Q_OBJECT +public: + IVersionControl(QObject *parent = 0) : QObject(parent) {} + virtual ~IVersionControl() {} + + // Returns wheter files in this directory should be managed with this + // version control. + virtual bool managesDirectory(const QString &filename) const = 0; + + // This function should return the topmost directory, for + // which this IVersionControl should be used. + // The VCSManager assumes that all files in the returned directory + // are managed by the same IVersionControl + // Note that this is used as an optimization, so that the VCSManager + // doesn't need to call managesDirectory(..) for each directory + // This function is called after finding out that the directory is managed by + // a specific version control + virtual QString findTopLevelForDirectory(const QString &directory) const = 0; + + // Called prior to save, if the file is read only. + // Should be implemented if the scc requires a operation before editing the file + // E.g. p4 edit + // Note: The EditorManager calls this for the editors + virtual bool vcsOpen(const QString &fileName) = 0; + + // Called after a file has been added to a project + // If the version control needs to know which files it needs to track + // you should reimplement this function + // E.g. p4 add, cvs add, svn add + // Note: This function should be called from IProject subclasses after files + // are added to the project + virtual bool vcsAdd(const QString &filename) = 0; + + // Called after a file has been removed from the project (if the user wants) + // E.g. p4 delete, svn delete + // You probably want to call SccManager::showDeleteDialog, which asks the user to + // confirm the deletion + virtual bool vcsDelete(const QString &filename) = 0; + + // TODO: ADD A WAY TO DETECT WHETHER A FILE IS MANAGED, e.g + // virtual bool sccManaged(const QStryng &filename) = 0; + + // TODO + // we probably want to have a function supports( enum Operation ) or + // something which describes which "kind" of revision control system it is. + // That is to check wheter a given operation is needed. + // But well I don't know yet how all different version control systems work +}; + +} // namespace Core + +#endif // IVERSIONCONTROL_H diff --git a/src/plugins/coreplugin/iview.h b/src/plugins/coreplugin/iview.h new file mode 100644 index 00000000000..dd0a222b59c --- /dev/null +++ b/src/plugins/coreplugin/iview.h @@ -0,0 +1,60 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IVIEW_H +#define IVIEW_H + +#include "core_global.h" + +#include <QtGui/QKeySequence> + +#include <coreplugin/icontext.h> + +namespace Core { + +class CORE_EXPORT IView + : public IContext +{ + Q_OBJECT +public: + enum ViewPosition { First=0, Second=1, Third=2 }; + + IView(QObject *parent = 0) : IContext(parent) {} + virtual ~IView() {} + + virtual const char *uniqueViewName() const = 0; + virtual ViewPosition defaultPosition() const = 0; +}; + +} // namespace Core + +#endif // IVIEW_H diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp new file mode 100644 index 00000000000..a90f1ff14e9 --- /dev/null +++ b/src/plugins/coreplugin/mainwindow.cpp @@ -0,0 +1,1102 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "mainwindow.h" +#include "actioncontainer.h" +#include "actionmanager.h" +#include "basemode.h" +#include "coreimpl.h" +#include "coreconstants.h" +#include "editorgroup.h" +#include "editormanager.h" +#include "fancytabwidget.h" +#include "filemanager.h" +#include "generalsettings.h" +#include "ifilefactory.h" +#include "messagemanager.h" +#include "modemanager.h" +#include "mimedatabase.h" +#include "newdialog.h" +#include "outputpane.h" +#include "plugindialog.h" +#include "progressmanager.h" +#include "progressview.h" +#include "shortcutsettings.h" +#include "vcsmanager.h" +#include "scriptmanager.h" +#include "settingsdialog.h" +#include "stylehelper.h" +#include "variablemanager.h" +#include "versiondialog.h" +#include "viewmanager.h" +#include "uniqueidmanager.h" +#include "manhattanstyle.h" +#include "dialogs/iwizard.h" +#include "navigationwidget.h" +#include "rightpane.h" +#include "editormanager/ieditorfactory.h" +#include "baseview.h" +#include "basefilewizard.h" + +#include <coreplugin/findplaceholder.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QDebug> +#include <QtCore/QSettings> +#include <QtCore/QTimer> +#include <QtCore/QFileInfo> + +#include <QtGui/QMenu> +#include <QtGui/QToolBar> +#include <QtGui/QApplication> +#include <QtGui/QPixmap> +#include <QtGui/QCloseEvent> +#include <QtGui/QShortcut> +#include <QtGui/QPrinter> +#include <QtGui/QWizard> +#include <QtGui/QStatusBar> + +/* +#ifdef Q_OS_UNIX +#include <signal.h> +extern "C" void handleSigInt(int sig) +{ + Q_UNUSED(sig); + Core::Internal::CoreImpl::instance()->exit(); + qDebug() << "SIGINT caught. Shutting down."; +} +#endif +*/ + +using namespace Core; +using namespace Core::Internal; + +namespace { + enum { debugMainWindow = 0 }; +} + + +MainWindow::MainWindow() : + QMainWindow(), + m_coreImpl(new CoreImpl(this)), + m_uniqueIDManager(new UniqueIDManager()), + m_globalContext(QList<int>() << Constants::C_GLOBAL_ID), + m_additionalContexts(m_globalContext), + m_settings(new QSettings(QSettings::IniFormat, QSettings::UserScope, QLatin1String("Nokia"), QLatin1String("QtCreator"), this)), + m_printer(0), + m_actionManager(new ActionManager(this, m_uniqueIDManager)), + m_editorManager(0), + m_fileManager(new FileManager(m_coreImpl, this)), + m_progressManager(new ProgressManager()), + m_scriptManager(new ScriptManager(this, m_coreImpl)), + m_variableManager(new VariableManager(this)), + m_vcsManager(new VCSManager()), + m_viewManager(0), + m_modeManager(0), + m_mimeDatabase(new MimeDatabase), + m_navigationWidget(0), + m_rightPaneWidget(0), + m_activeContext(0), + m_pluginManager(0), + m_outputPane(new OutputPane(m_globalContext)), + m_outputMode(0), + m_generalSettings(new GeneralSettings), + m_shortcutSettings(new ShortcutSettings), + m_focusToEditor(0), + m_newAction(0), + m_openAction(0), + m_openWithAction(0), + m_saveAllAction(0), + m_exitAction(0), + m_optionsAction(0), + m_toggleSideBarAction(0), +#ifdef Q_OS_MAC + m_minimizeAction(0), + m_zoomAction(0), +#endif + m_toggleSideBarButton(new QToolButton) +{ + setWindowTitle(tr("Qt Creator")); + qApp->setWindowIcon(QIcon(":/qworkbench/images/qtcreator_logo_128.png")); + setDockNestingEnabled(true); + + setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); + setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea); + + registerDefaultContainers(); + registerDefaultActions(); + + m_navigationWidget = new NavigationWidget(m_toggleSideBarAction); + m_rightPaneWidget = new RightPaneWidget(); + + m_modeStack = new FancyTabWidget(this); + m_modeManager = new ModeManager(this, m_modeStack); + m_modeManager->addWidget(m_progressManager->progressView()); + m_viewManager = new ViewManager(this); + m_messageManager = new MessageManager; + m_editorManager = new EditorManager(m_coreImpl, this); + m_editorManager->hide(); + setCentralWidget(m_modeStack); + + connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*,QWidget*)), + this, SLOT(updateFocusWidget(QWidget*,QWidget*))); + // Add a small Toolbutton for toggling the navigation widget + m_toggleSideBarButton->setProperty("type", QLatin1String("dockbutton")); + statusBar()->insertPermanentWidget(0, m_toggleSideBarButton); + +// setUnifiedTitleAndToolBarOnMac(true); +#ifdef Q_OS_UNIX + //signal(SIGINT, handleSigInt); +#endif + + QCoreApplication::setApplicationName(QLatin1String("QtCreator")); + QCoreApplication::setApplicationVersion(QLatin1String(Core::Constants::IDE_VERSION_LONG)); + QCoreApplication::setOrganizationName(QLatin1String("Nokia")); + QSettings::setDefaultFormat(QSettings::IniFormat); + QString baseName = qApp->style()->objectName(); + qApp->setStyle(new ManhattanStyle(baseName)); + statusBar()->setProperty("p_styled", true); +} + +void MainWindow::toggleNavigation() +{ + if (NavigationWidgetPlaceHolder::current()) { + if (m_navigationWidget->isSuppressed()) { + m_navigationWidget->setShown(true); + m_navigationWidget->setSuppressed(false); + } else { + m_navigationWidget->setShown(!m_navigationWidget->isShown()); + } + } +} + +void MainWindow::setSuppressNavigationWidget(bool suppress) +{ + if (NavigationWidgetPlaceHolder::current()) { + m_navigationWidget->setSuppressed(suppress); + } +} + +MainWindow::~MainWindow() +{ + hide(); + m_pluginManager->removeObject(m_shortcutSettings); + m_pluginManager->removeObject(m_generalSettings); + delete m_messageManager; + m_messageManager = 0; + delete m_shortcutSettings; + m_shortcutSettings = 0; + delete m_generalSettings; + m_generalSettings = 0; + delete m_settings; + m_settings = 0; + delete m_printer; + m_printer = 0; + delete m_uniqueIDManager; + m_uniqueIDManager = 0; + delete m_vcsManager; + m_vcsManager = 0; + m_pluginManager->removeObject(m_outputMode); + delete m_outputMode; + m_outputMode = 0; + //we need to delete editormanager and viewmanager explicitly before the end of the destructor, + //because they might trigger stuff that tries to access data from editorwindow, like removeContextWidget + + // All modes are now gone + delete OutputPane::instance(); + + // Now that the OutputPane is gone, is a good time to delete the view + m_pluginManager->removeObject(m_outputView); + delete m_outputView; + + delete m_editorManager; + m_editorManager = 0; + delete m_viewManager; + m_viewManager = 0; + delete m_progressManager; + m_progressManager = 0; + m_pluginManager->removeObject(m_coreImpl); + delete m_coreImpl; + m_coreImpl = 0; + + delete m_rightPaneWidget; + m_rightPaneWidget = 0; + + delete m_navigationWidget; + m_navigationWidget = 0; + + delete m_modeManager; + m_modeManager = 0; + delete m_mimeDatabase; + m_mimeDatabase = 0; +} + +bool MainWindow::init(ExtensionSystem::PluginManager *pm, QString *) +{ + m_pluginManager = pm; + m_pluginManager->addObject(m_coreImpl); + m_viewManager->init(); + m_modeManager->init(); + m_progressManager->init(); + QWidget *outputModeWidget = new QWidget; + outputModeWidget->setLayout(new QVBoxLayout); + outputModeWidget->layout()->setMargin(0); + outputModeWidget->layout()->setSpacing(0); + m_outputMode = new BaseMode(tr("Output"), + Constants::MODE_OUTPUT, + QIcon(QLatin1String(":/fancyactionbar/images/mode_Output.png")), + Constants::P_MODE_OUTPUT, + outputModeWidget); + OutputPanePlaceHolder *oph = new OutputPanePlaceHolder(m_outputMode); + oph->setVisible(true); + oph->setCloseable(false); + outputModeWidget->layout()->addWidget(oph); + outputModeWidget->layout()->addWidget(new Core::FindToolBarPlaceHolder(m_outputMode)); + outputModeWidget->setFocusProxy(oph); + + m_outputMode->setContext(m_outputPane->context()); + m_pluginManager->addObject(m_outputMode); + m_pluginManager->addObject(m_generalSettings); + m_pluginManager->addObject(m_shortcutSettings); + + // Add widget to the bottom, we create the view here instead of inside the OutputPane, since + // the ViewManager needs to be initilized before + m_outputView = new Core::BaseView("OutputWindow.Buttons", m_outputPane->buttonsWidget(), QList<int>(), Core::IView::Second); + m_pluginManager->addObject(m_outputView); + return true; +} + +void MainWindow::extensionsInitialized() +{ + m_editorManager->init(); + + m_viewManager->extensionsInitalized(); + + m_messageManager->init(m_pluginManager); + m_outputPane->init(m_coreImpl, m_pluginManager); + + m_actionManager->initialize(); + readSettings(); + updateContext(); + show(); + + emit m_coreImpl->coreOpened(); +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + emit m_coreImpl->saveSettingsRequested(); + + // Save opened files + bool cancelled; + fileManager()->saveModifiedFiles(fileManager()->modifiedFiles(), &cancelled); + if (cancelled) { + event->ignore(); + return; + } + + const QList<ICoreListener *> listeners = + pluginManager()->getObjects<ICoreListener>(); + foreach (ICoreListener *listener, listeners) { + if (!listener->coreAboutToClose()) { + event->ignore(); + return; + } + } + + emit m_coreImpl->coreAboutToClose(); + writeSettings(); + event->accept(); +} + +IContext *MainWindow::currentContextObject() const +{ + return m_activeContext; +} + +QStatusBar *MainWindow::statusBar() const +{ + return m_modeStack->statusBar(); +} + +void MainWindow::registerDefaultContainers() +{ + ActionManager *am = m_actionManager; + + IActionContainer *menubar = m_actionManager->createMenuBar(Constants::MENU_BAR); + +#ifndef Q_WS_MAC // System menu bar on Mac + setMenuBar(menubar->menuBar()); +#endif + menubar->appendGroup(Constants::G_FILE); + menubar->appendGroup(Constants::G_EDIT); + menubar->appendGroup(Constants::G_VIEW); + menubar->appendGroup(Constants::G_TOOLS); + menubar->appendGroup(Constants::G_WINDOW); + menubar->appendGroup(Constants::G_HELP); + + //File Menu + IActionContainer *filemenu = am->createMenu(Constants::M_FILE); + menubar->addMenu(filemenu, Constants::G_FILE); + filemenu->menu()->setTitle(tr("&File")); + filemenu->appendGroup(Constants::G_FILE_NEW); + filemenu->appendGroup(Constants::G_FILE_OPEN); + filemenu->appendGroup(Constants::G_FILE_PROJECT); + filemenu->appendGroup(Constants::G_FILE_SAVE); + filemenu->appendGroup(Constants::G_FILE_CLOSE); + filemenu->appendGroup(Constants::G_FILE_PRINT); + filemenu->appendGroup(Constants::G_FILE_OTHER); + connect(filemenu->menu(), SIGNAL(aboutToShow()), this, SLOT(aboutToShowRecentFiles())); + + + //Edit Menu + IActionContainer *medit = am->createMenu(Constants::M_EDIT); + menubar->addMenu(medit, Constants::G_EDIT); + medit->menu()->setTitle(tr("&Edit")); + medit->appendGroup(Constants::G_EDIT_UNDOREDO); + medit->appendGroup(Constants::G_EDIT_COPYPASTE); + medit->appendGroup(Constants::G_EDIT_SELECTALL); + medit->appendGroup(Constants::G_EDIT_FORMAT); + medit->appendGroup(Constants::G_EDIT_FIND, true); + medit->appendGroup(Constants::G_EDIT_OTHER); + + //Tools Menu + IActionContainer *ac = am->createMenu(Constants::M_TOOLS); + menubar->addMenu(ac, Constants::G_TOOLS); + ac->menu()->setTitle(tr("&Tools")); + + //Window Menu + IActionContainer *mwindow = am->createMenu(Constants::M_WINDOW); + menubar->addMenu(mwindow, Constants::G_WINDOW); + mwindow->menu()->setTitle(tr("&Window")); + mwindow->appendGroup(Constants::G_WINDOW_SIZE); + mwindow->appendGroup(Constants::G_WINDOW_PANES); + mwindow->appendGroup(Constants::G_WINDOW_SPLIT); + mwindow->appendGroup(Constants::G_WINDOW_CLOSE); + mwindow->appendGroup(Constants::G_WINDOW_NAVIGATE); + mwindow->appendGroup(Constants::G_WINDOW_NAVIGATE_GROUPS); + mwindow->appendGroup(Constants::G_WINDOW_OTHER); + mwindow->appendGroup(Constants::G_WINDOW_LIST, true); + + //Help Menu + ac = am->createMenu(Constants::M_HELP); + menubar->addMenu(ac, Constants::G_HELP); + ac->menu()->setTitle(tr("&Help")); + ac->appendGroup(Constants::G_HELP_HELP, true); + ac->appendGroup(Constants::G_HELP_ABOUT, true); +} + +static ICommand *createSeparator(ActionManager *am, QObject *parent, + const QString &name, + const QList<int> &context) +{ + QAction *tmpaction = new QAction(parent); + tmpaction->setSeparator(true); + ICommand *cmd = am->registerAction(tmpaction, name, context); + return cmd; +} + +void MainWindow::registerDefaultActions() +{ + ActionManager *am = m_actionManager; + IActionContainer *mfile = am->actionContainer(Constants::M_FILE); + IActionContainer *medit = am->actionContainer(Constants::M_EDIT); + IActionContainer *mtools = am->actionContainer(Constants::M_TOOLS); + IActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW); + Q_UNUSED(mwindow) + IActionContainer *mhelp = am->actionContainer(Constants::M_HELP); + + // File menu separators + ICommand *cmd = createSeparator(am, this, QLatin1String("QtCreator.File.Sep.Save"), m_globalContext); + mfile->addAction(cmd, Constants::G_FILE_SAVE); + + cmd = createSeparator(am, this, QLatin1String("QtCreator.File.Sep.Print"), m_globalContext); + mfile->addAction(cmd, Constants::G_FILE_PRINT); + + cmd = createSeparator(am, this, QLatin1String("QtCreator.File.Sep.Close"), m_globalContext); + mfile->addAction(cmd, Constants::G_FILE_CLOSE); + + cmd = createSeparator(am, this, QLatin1String("QtCreator.File.Sep.Other"), m_globalContext); + mfile->addAction(cmd, Constants::G_FILE_OTHER); + + // Edit menu separators + cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.CopyPaste"), m_globalContext); + medit->addAction(cmd, Constants::G_EDIT_COPYPASTE); + + cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.SelectAll"), m_globalContext); + medit->addAction(cmd, Constants::G_EDIT_SELECTALL); + + cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Find"), m_globalContext); + medit->addAction(cmd, Constants::G_EDIT_FIND); + + cmd = createSeparator(am, this, QLatin1String("QtCreator.Edit.Sep.Format"), m_globalContext); + medit->addAction(cmd, Constants::G_EDIT_FORMAT); + + //Tools menu separators + cmd = createSeparator(am, this, QLatin1String("QtCreator.Tools.Sep.Options"), m_globalContext); + mtools->addAction(cmd, Constants::G_DEFAULT_THREE); + + //Return to editor shortcut: Note this requires Qt to fix up + // handling of shortcut overrides in menus, item views, combos.... + m_focusToEditor = new QShortcut(this); + cmd = am->registerShortcut(m_focusToEditor, Constants::S_RETURNTOEDITOR, m_globalContext); + cmd->setDefaultKeySequence(QKeySequence(Qt::Key_Escape)); + connect(m_focusToEditor, SIGNAL(activated()), this, SLOT(setFocusToEditor())); + + //New File Action + m_newAction = new QAction(QIcon(Constants::ICON_NEWFILE), tr("&New..."), this); + cmd = am->registerAction(m_newAction, Constants::NEW, m_globalContext); + cmd->setDefaultKeySequence(QKeySequence::New); + mfile->addAction(cmd, Constants::G_FILE_NEW); + connect(m_newAction, SIGNAL(triggered()), this, SLOT(newFile())); + + //Open Action + m_openAction = new QAction(QIcon(Constants::ICON_OPENFILE), tr("&Open..."), this); + cmd = am->registerAction(m_openAction, Constants::OPEN, m_globalContext); + cmd->setDefaultKeySequence(QKeySequence::Open); + mfile->addAction(cmd, Constants::G_FILE_OPEN); + connect(m_openAction, SIGNAL(triggered()), this, SLOT(openFile())); + + //Open With Action + m_openWithAction = new QAction(tr("&Open With..."), this); + cmd = am->registerAction(m_openWithAction, Constants::OPEN_WITH, m_globalContext); + mfile->addAction(cmd, Constants::G_FILE_OPEN); + connect(m_openWithAction, SIGNAL(triggered()), this, SLOT(openFileWith())); + + //File->Recent Files Menu + IActionContainer *ac = am->createMenu(Constants::M_FILE_RECENTFILES); + mfile->addMenu(ac, Constants::G_FILE_OPEN); + ac->menu()->setTitle(tr("Recent Files")); + + //Save Action + QAction *tmpaction = new QAction(QIcon(Constants::ICON_SAVEFILE), tr("&Save"), this); + cmd = am->registerAction(tmpaction, Constants::SAVE); + cmd->setDefaultKeySequence(QKeySequence::Save); + cmd->setAttribute(ICommand::CA_UpdateText); + cmd->setDefaultText(tr("&Save")); + mfile->addAction(cmd, Constants::G_FILE_SAVE); + + //Save As Action + tmpaction = new QAction(tr("Save &As..."), this); + cmd = am->registerAction(tmpaction, Constants::SAVEAS); +#ifdef Q_OS_MAC + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+S"))); +#endif + cmd->setAttribute(ICommand::CA_UpdateText); + cmd->setDefaultText(tr("Save &As...")); + mfile->addAction(cmd, Constants::G_FILE_SAVE); + + //SaveAll Action + m_saveAllAction = new QAction(tr("Save A&ll"), this); + cmd = am->registerAction(m_saveAllAction, Constants::SAVEALL, m_globalContext); +#ifndef Q_OS_MAC + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+S"))); +#endif + mfile->addAction(cmd, Constants::G_FILE_SAVE); + connect(m_saveAllAction, SIGNAL(triggered()), this, SLOT(saveAll())); + + //Print Action + tmpaction = new QAction(tr("&Print..."), this); + cmd = am->registerAction(tmpaction, Constants::PRINT); + mfile->addAction(cmd, Constants::G_FILE_PRINT); + + //Exit Action + m_exitAction = new QAction(tr("E&xit"), this); + cmd = am->registerAction(m_exitAction, Constants::EXIT, m_globalContext); + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Q"))); + mfile->addAction(cmd, Constants::G_FILE_OTHER); + connect(m_exitAction, SIGNAL(triggered()), this, SLOT(exit())); + + //Undo Action + tmpaction = new QAction(QIcon(Constants::ICON_UNDO), tr("&Undo"), this); + cmd = am->registerAction(tmpaction, Constants::UNDO); + cmd->setDefaultKeySequence(QKeySequence::Undo); + cmd->setAttribute(ICommand::CA_UpdateText); + cmd->setDefaultText(tr("&Undo")); + medit->addAction(cmd, Constants::G_EDIT_UNDOREDO); + + //Redo Action + tmpaction = new QAction(QIcon(Constants::ICON_REDO), tr("&Redo"), this); + cmd = am->registerAction(tmpaction, Constants::REDO); + cmd->setDefaultKeySequence(QKeySequence::Redo); + cmd->setAttribute(ICommand::CA_UpdateText); + cmd->setDefaultText(tr("&Redo")); + medit->addAction(cmd, Constants::G_EDIT_UNDOREDO); + + //Cut Action + tmpaction = new QAction(QIcon(Constants::ICON_CUT), tr("Cu&t"), this); + cmd = am->registerAction(tmpaction, Constants::CUT); + cmd->setDefaultKeySequence(QKeySequence::Cut); + medit->addAction(cmd, Constants::G_EDIT_COPYPASTE); + + //Copy Action + tmpaction = new QAction(QIcon(Constants::ICON_COPY), tr("&Copy"), this); + cmd = am->registerAction(tmpaction, Constants::COPY); + cmd->setDefaultKeySequence(QKeySequence::Copy); + medit->addAction(cmd, Constants::G_EDIT_COPYPASTE); + + //Paste Action + tmpaction = new QAction(QIcon(Constants::ICON_PASTE), tr("&Paste"), this); + cmd = am->registerAction(tmpaction, Constants::PASTE); + cmd->setDefaultKeySequence(QKeySequence::Paste); + medit->addAction(cmd, Constants::G_EDIT_COPYPASTE); + + //Select All + tmpaction = new QAction(tr("&Select All"), this); + cmd = am->registerAction(tmpaction, Constants::SELECTALL); + cmd->setDefaultKeySequence(QKeySequence::SelectAll); + medit->addAction(cmd, Constants::G_EDIT_SELECTALL); + + //Goto Action + tmpaction = new QAction(tr("&Go To Line..."), this); + cmd = am->registerAction(tmpaction, Constants::GOTO); + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+L"))); + medit->addAction(cmd, Constants::G_EDIT_OTHER); + + //Options Action + m_optionsAction = new QAction(tr("&Options..."), this); + cmd = am->registerAction(m_optionsAction, Constants::OPTIONS, m_globalContext); +#ifdef Q_OS_MAC + cmd->setDefaultKeySequence(QKeySequence("Ctrl+,")); +#endif + mtools->addAction(cmd, Constants::G_DEFAULT_THREE); + connect(m_optionsAction, SIGNAL(triggered()), this, SLOT(showOptionsDialog())); + +#ifdef Q_OS_MAC + //Minimize Action + m_minimizeAction = new QAction(tr("Minimize"), this); + cmd = am->registerAction(m_minimizeAction, Constants::MINIMIZE_WINDOW, m_globalContext); + cmd->setDefaultKeySequence(QKeySequence("Ctrl+M")); + mwindow->addAction(cmd, Constants::G_WINDOW_SIZE); + connect(m_minimizeAction, SIGNAL(triggered()), this, SLOT(showMinimized())); + + //Zoom Action + m_zoomAction = new QAction(tr("Zoom"), this); + cmd = am->registerAction(m_zoomAction, Constants::ZOOM_WINDOW, m_globalContext); + mwindow->addAction(cmd, Constants::G_WINDOW_SIZE); + connect(m_zoomAction, SIGNAL(triggered()), this, SLOT(showMaximized())); + + //Window separator + cmd = createSeparator(am, this, QLatin1String("QtCreator.Window.Sep.Size"), m_globalContext); + mwindow->addAction(cmd, Constants::G_WINDOW_SIZE); +#endif + + // Toggle Sidebar Action + m_toggleSideBarAction = new QAction(QIcon(Constants::ICON_TOGGLE_SIDEBAR), + tr("Toggle Sidebar"), this); + cmd = am->registerAction(m_toggleSideBarAction, Constants::TOGGLE_SIDEBAR, m_globalContext); +#ifdef Q_OS_MAC + cmd->setDefaultKeySequence(QKeySequence("Ctrl+0")); +#else + cmd->setDefaultKeySequence(QKeySequence("Alt+0")); +#endif + connect(m_toggleSideBarAction, SIGNAL(triggered()), this, SLOT(toggleNavigation())); + m_toggleSideBarButton->setDefaultAction(cmd->action()); + mwindow->addAction(cmd, Constants::G_WINDOW_PANES); + m_toggleSideBarAction->setEnabled(false); + + //About IDE Action +#ifdef Q_OS_MAC + tmpaction = new QAction(tr("About &Qt Creator"), this); // it's convention not to add dots to the about menu +#else + tmpaction = new QAction(tr("About &Qt Creator..."), this); +#endif + cmd = am->registerAction(tmpaction, Constants:: ABOUT_WORKBENCH, m_globalContext); + mhelp->addAction(cmd, Constants::G_HELP_ABOUT); + tmpaction->setEnabled(true); + connect(tmpaction, SIGNAL(triggered()), this, SLOT(aboutQtCreator())); + //About Plugins Action + tmpaction = new QAction(tr("About &Plugins..."), this); + cmd = am->registerAction(tmpaction, Constants::ABOUT_PLUGINS, m_globalContext); + mhelp->addAction(cmd, Constants::G_HELP_ABOUT); + tmpaction->setEnabled(true); +#ifdef Q_OS_MAC + cmd->action()->setMenuRole(QAction::ApplicationSpecificRole); +#endif + connect(tmpaction, SIGNAL(triggered()), this, SLOT(aboutPlugins())); + //About Qt Action +// tmpaction = new QAction(tr("About &Qt..."), this); +// cmd = am->registerAction(tmpaction, Constants:: ABOUT_QT, m_globalContext); +// mhelp->addAction(cmd, Constants::G_HELP_ABOUT); +// tmpaction->setEnabled(true); +// connect(tmpaction, SIGNAL(triggered()), qApp, SLOT(aboutQt())); + // About sep + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + cmd = am->registerAction(tmpaction, QLatin1String("QtCreator.Help.Sep.About"), m_globalContext); + mhelp->addAction(cmd, Constants::G_HELP_ABOUT); +} + +void MainWindow::newFile() +{ + showNewItemDialog(tr("New", "Title of dialog"),BaseFileWizard::allWizards()); +} + +void MainWindow::openFile() +{ + openFiles(editorManager()->getOpenFileNames()); +} + +static QList<IFileFactory*> getNonEditorFileFactories() +{ + const ICore *core = CoreImpl::instance(); + const QList<IFileFactory*> allFileFactories = core->pluginManager()->getObjects<IFileFactory>(); + QList<IFileFactory*> nonEditorFileFactories; + foreach (IFileFactory *factory, allFileFactories) { + if (!qobject_cast<IEditorFactory *>(factory)) + nonEditorFileFactories.append(factory); + } + return nonEditorFileFactories; +} + +static IFileFactory *findFileFactory(const QList<IFileFactory*> &fileFactories, + const MimeDatabase *db, + const QFileInfo &fi) +{ + if (const MimeType mt = db->findByFile(fi)) { + const QString type = mt.type(); + foreach (IFileFactory *factory, fileFactories) { + if (factory->mimeTypes().contains(type)) + return factory; + } + } + return 0; +} + +// opens either an editor or loads a project +void MainWindow::openFiles(const QStringList &fileNames) +{ + bool needToSwitchToEditor = false; + QList<IFileFactory*> nonEditorFileFactories = getNonEditorFileFactories(); + + foreach (const QString &fileName, fileNames) { + const QFileInfo fi(fileName); + const QString absoluteFilePath = fi.absoluteFilePath(); + if (IFileFactory *fileFactory = findFileFactory(nonEditorFileFactories, mimeDatabase(), fi)) { + fileFactory->open(absoluteFilePath); + } else { + IEditor *editor = editorManager()->openEditor(absoluteFilePath); + if (editor) + needToSwitchToEditor = true; + } + } + if (needToSwitchToEditor) + editorManager()->ensureEditorManagerVisible(); +} + +void MainWindow::setFocusToEditor() +{ + QWidget *focusWidget = qApp->focusWidget(); + // ### Duplicated code from EditMode::makeSureEditorManagerVisible + IMode *currentMode = m_coreImpl->modeManager()->currentMode(); + if (currentMode && currentMode->uniqueModeName() != QLatin1String(Constants::MODE_EDIT) && + currentMode->uniqueModeName() != QLatin1String("GdbDebugger.Mode.Debug")) + { + m_coreImpl->modeManager()->activateMode(QLatin1String(Constants::MODE_EDIT)); + } + + EditorGroup *group = m_editorManager->currentEditorGroup(); + if (group && group->widget()) + group->widget()->setFocus(); + if (focusWidget && focusWidget == qApp->focusWidget()) { + if (FindToolBarPlaceHolder::getCurrent()) + FindToolBarPlaceHolder::getCurrent()->hide(); + m_outputPane->slotHide(); + RightPaneWidget::instance()->setShown(false); + } +} + +QStringList MainWindow::showNewItemDialog(const QString &title, + const QList<IWizard *> &wizards, + const QString &defaultLocation) +{ + QString defaultDir = defaultLocation; + if (defaultDir.isEmpty()) { + if (!m_coreImpl->fileManager()->currentFile().isEmpty()) + defaultDir = QFileInfo(m_coreImpl->fileManager()->currentFile()).absolutePath(); + } + + // Scan for wizards matching the filter and pick one. Don't show + // dialog if there is only one. + IWizard *wizard = 0; + switch (wizards.size()) { + case 0: + break; + case 1: + wizard = wizards.front(); + break; + default: { + NewDialog dlg(this); + dlg.setWizards(wizards); + dlg.setWindowTitle(title); + wizard = dlg.showDialog(); + } + break; + } + + if (!wizard) + return QStringList(); + return wizard->runWizard(defaultDir, this); +} + +void MainWindow::showOptionsDialog(const QString &category, const QString &page) +{ + emit m_coreImpl->settingsDialogRequested(); + SettingsDialog dlg(this, category, page); + dlg.exec(); +} + +void MainWindow::saveAll() +{ + m_fileManager->saveModifiedFiles(m_fileManager->modifiedFiles()); + emit m_coreImpl->saveSettingsRequested(); +} + +void MainWindow::exit() +{ + // this function is most likely called from a user action + // that is from an event handler of an object + // since on close we are going to delete everything + // so to prevent the deleting of that object we + // just append it + QTimer::singleShot(0, this, SLOT(close())); +} + +void MainWindow::openFileWith() +{ + QStringList fileNames = editorManager()->getOpenFileNames(); + foreach (const QString &fileName, fileNames) { + const QString editorKind = editorManager()->getOpenWithEditorKind(fileName); + if (editorKind.isEmpty()) + continue; + editorManager()->openEditor(fileName, editorKind); + } +} + +ActionManagerInterface *MainWindow::actionManager() const +{ + return m_actionManager; +} + +FileManager *MainWindow::fileManager() const +{ + return m_fileManager; +} + +UniqueIDManager *MainWindow::uniqueIDManager() const +{ + return m_uniqueIDManager; +} + +MessageManager *MainWindow::messageManager() const +{ + return m_messageManager; +} + +VCSManager *MainWindow::vcsManager() const +{ + return m_vcsManager; +} + +ViewManagerInterface *MainWindow::viewManager() const +{ + return m_viewManager; +} + +EditorManager *MainWindow::editorManager() const +{ + return m_editorManager; +} + +ProgressManagerInterface *MainWindow::progressManager() const +{ + return m_progressManager; +} + +ScriptManagerInterface *MainWindow::scriptManager() const +{ + return m_scriptManager; +} + +VariableManager *MainWindow::variableManager() const +{ + return m_variableManager; +} + +ModeManager *MainWindow::modeManager() const +{ + return m_modeManager; +} + +MimeDatabase *MainWindow::mimeDatabase() const +{ + return m_mimeDatabase; +} + +ExtensionSystem::PluginManager *MainWindow::pluginManager() const +{ + return m_pluginManager; +} + +IContext *MainWindow::contextObject(QWidget *widget) +{ + return m_contextWidgets.value(widget); +} + +void MainWindow::addContextObject(IContext *context) +{ + if (!context) + return; + QWidget *widget = context->widget(); + if (m_contextWidgets.contains(widget)) + return; + + m_contextWidgets.insert(widget, context); +} + +void MainWindow::removeContextObject(IContext *context) +{ + if (!context) + return; + + QWidget *widget = context->widget(); + if (!m_contextWidgets.contains(widget)) + return; + + m_contextWidgets.remove(widget); + if(m_activeContext == context) + updateContextObject(0); +} + +void MainWindow::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::ActivationChange) { + if (isActiveWindow()) { + if (debugMainWindow) + qDebug() << "main window activated"; + emit windowActivated(); + } +#ifdef Q_OS_MAC + } else if (e->type() == QEvent::WindowStateChange) { + bool minimized = isMinimized(); + if (debugMainWindow) + qDebug() << "main window state changed to minimized=" << minimized; + m_minimizeAction->setEnabled(!minimized); + m_zoomAction->setEnabled(!minimized); +#endif + } +} + +void MainWindow::updateFocusWidget(QWidget *old, QWidget *now) +{ + Q_UNUSED(old) + Q_UNUSED(now) + if (focusWidget()) { + IContext *context = 0; + QWidget *p = focusWidget(); + while (p) { + context = m_contextWidgets.value(p); + if (context) { + if (m_activeContext != context) + updateContextObject(context); + break; + } + p = p->parentWidget(); + } + } +} + +void MainWindow::updateContextObject(IContext *context) +{ + IContext *oldContext = m_activeContext; + m_activeContext = context; + if (!context || oldContext != m_activeContext) { + emit m_coreImpl->contextAboutToChange(context); + updateContext(); + if (debugMainWindow) + qDebug() << "new context object =" << context << (context ? context->widget() : 0) + << (context ? context->widget()->metaObject()->className() : 0); + emit m_coreImpl->contextChanged(context); + } +} + +void MainWindow::resetContext() +{ + updateContextObject(0); +} + +QMenu *MainWindow::createPopupMenu() { + QMenu *menu = new QMenu(this); + QList<ActionContainer *> containers = m_actionManager->containers(); + foreach(ActionContainer *c, containers) { + if (c->toolBar()) + menu->addAction(c->toolBar()->toggleViewAction()); + } + return menu; +} + +static const char *settingsGroup = "MainWindow"; +static const char *geometryKey = "Geometry"; +static const char *colorKey = "Color"; +static const char *maxKey = "Maximized"; + +void MainWindow::readSettings() +{ + m_settings->beginGroup(QLatin1String(settingsGroup)); + StyleHelper::setBaseColor(m_settings->value(QLatin1String(colorKey)).value<QColor>()); + const QVariant geom = m_settings->value(QLatin1String(geometryKey)); + if (geom.isValid()) { + setGeometry(geom.toRect()); + } else { + resize(1024, 700); + } + if (m_settings->value(QLatin1String(maxKey), false).toBool()) + setWindowState(Qt::WindowMaximized); + + m_settings->endGroup(); + m_editorManager->readSettings(m_settings); + m_navigationWidget->readSettings(m_settings); + m_rightPaneWidget->readSettings(m_settings); +} + +void MainWindow::writeSettings() +{ + m_settings->beginGroup(QLatin1String(settingsGroup)); + m_settings->setValue(colorKey, StyleHelper::baseColor()); + const QString maxSettingsKey = QLatin1String(maxKey); + if (windowState() & Qt::WindowMaximized) { + m_settings->setValue(maxSettingsKey, true); + } else { + m_settings->setValue(maxSettingsKey, false); + m_settings->setValue(QLatin1String(geometryKey), geometry()); + } + m_settings->endGroup(); + + m_fileManager->saveRecentFiles(); + m_viewManager->saveSettings(m_settings); + m_actionManager->saveSettings(m_settings); + m_editorManager->saveSettings(m_settings); + m_navigationWidget->saveSettings(m_settings); +} + +void MainWindow::addAdditionalContext(int context) +{ + if (context == 0) + return; + + if (!m_additionalContexts.contains(context)) + m_additionalContexts.prepend(context); +} + +void MainWindow::removeAdditionalContext(int context) +{ + if (context == 0) + return; + + int index = m_additionalContexts.indexOf(context); + if (index != -1) + m_additionalContexts.removeAt(index); +} + +bool MainWindow::hasContext(int context) const +{ + return m_actionManager->hasContext(context); +} + +void MainWindow::updateContext() +{ + QList<int> contexts; + + if (m_activeContext) + contexts += m_activeContext->context(); + IEditor *editor = m_editorManager->currentEditor(); + if (editor && (EditorManagerPlaceHolder::current() != 0)) { + contexts += editor->context(); + } + + contexts += m_additionalContexts; + + QList<int> uniquecontexts; + for (int i = 0; i < contexts.size(); ++i) { + const int c = contexts.at(i); + if (!uniquecontexts.contains(c)) + uniquecontexts << c; + } + + m_actionManager->setContext(uniquecontexts); +} + +void MainWindow::aboutToShowRecentFiles() +{ + IActionContainer *aci = + m_actionManager->actionContainer(Constants::M_FILE_RECENTFILES); + aci->menu()->clear(); + m_recentFilesActions.clear(); + + bool hasRecentFiles = false; + foreach (QString s, m_fileManager->recentFiles()) { + hasRecentFiles = true; + QAction *action = aci->menu()->addAction(s); + m_recentFilesActions.insert(action, s); + connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile())); + } + aci->menu()->setEnabled(hasRecentFiles); +} + +void MainWindow::openRecentFile() +{ + QAction *a = qobject_cast<QAction*>(sender()); + if (m_recentFilesActions.contains(a)) { + editorManager()->openEditor(m_recentFilesActions.value(a)); + editorManager()->ensureEditorManagerVisible(); + } +} + +void MainWindow::aboutQtCreator() +{ + VersionDialog versionDialog(this); + versionDialog.exec(); +} + +void MainWindow::aboutPlugins() +{ + PluginDialog dialog(ExtensionSystem::PluginManager::instance(), this); + dialog.exec(); +} + +QPrinter *MainWindow::printer() const +{ + if (!m_printer) + m_printer = new QPrinter(QPrinter::HighResolution); + return m_printer; +} diff --git a/src/plugins/coreplugin/mainwindow.h b/src/plugins/coreplugin/mainwindow.h new file mode 100644 index 00000000000..00721a1662e --- /dev/null +++ b/src/plugins/coreplugin/mainwindow.h @@ -0,0 +1,227 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include "icore.h" + +#include <QtGui/QMainWindow> +#include <QtCore/QMap> +#include <QtCore/QList> +#include <QtCore/QSet> +#include <QtCore/QPointer> +#include <QtGui/QPrinter> +#include <QtGui/QToolButton> + +QT_BEGIN_NAMESPACE +class QSettings; +class QShortcut; +QT_END_NAMESPACE + +namespace ExtensionSystem { +class PluginManager; +} + +namespace Core { + +class ActionManagerInterface; +class BaseMode; +class EditorManager; +class FileManager; +class IContext; +class MessageManager; +class ModeManager; +class MimeDatabase; +class ProgressManagerInterface; +class VCSManager; +class ScriptManagerInterface; +class UniqueIDManager; +class VariableManager; +class ViewManagerInterface; +class BaseView; +class RightPaneWidget; + +namespace Internal { + +class CoreImpl; +class ActionManager; +class ProgressManager; +class OutputPane; +class ViewManager; +class GeneralSettings; +class ShortcutSettings; +class FancyTabWidget; +class NavigationWidget; + +class CORE_EXPORT MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(); + ~MainWindow(); + + bool init(ExtensionSystem::PluginManager *pm, QString *error_message); + void extensionsInitialized(); + + IContext *contextObject(QWidget *widget); + void addContextObject(IContext *contex); + void removeContextObject(IContext *contex); + void resetContext(); + + void openFiles(const QStringList &fileNames); + + //ICore + inline ExtensionSystem::PluginManager *pluginManager() { return m_pluginManager; } + Core::ActionManagerInterface *actionManager() const; + Core::FileManager *fileManager() const; + Core::UniqueIDManager *uniqueIDManager() const; + Core::MessageManager *messageManager() const; + Core::ViewManagerInterface *viewManager() const; + ExtensionSystem::PluginManager *pluginManager() const; + Core::EditorManager *editorManager() const; + Core::ProgressManagerInterface *progressManager() const; + Core::ScriptManagerInterface *scriptManager() const; + Core::VariableManager *variableManager() const; + Core::ModeManager *modeManager() const; + Core::MimeDatabase *mimeDatabase() const; + + VCSManager *vcsManager() const; + inline QSettings *settings() const { return m_settings; } + virtual QPrinter *printer() const; + IContext * currentContextObject() const; + QStatusBar *statusBar() const; + void addAdditionalContext(int context); + void removeAdditionalContext(int context); + bool hasContext(int context) const; + + void updateContext(); + + QMenu *createPopupMenu(); + + + void setSuppressNavigationWidget(bool suppress); + +signals: + void windowActivated(); + +public slots: + void newFile(); + void openFileWith(); + void exit(); + + QStringList showNewItemDialog(const QString &title, + const QList<IWizard *> &wizards, + const QString &defaultLocation = QString()); + + void showOptionsDialog(const QString &category = QString(), const QString &page = QString()); + +protected: + void changeEvent(QEvent *e); + void closeEvent(QCloseEvent *event); + +private slots: + void openFile(); + void aboutToShowRecentFiles(); + void openRecentFile(); + void setFocusToEditor(); + void saveAll(); + void aboutQtCreator(); + void aboutPlugins(); + void updateFocusWidget(QWidget *old, QWidget *now); + void toggleNavigation(); + +private: + void updateContextObject(IContext *context); + void registerDefaultContainers(); + void registerDefaultActions(); + + void readSettings(); + void writeSettings(); + + CoreImpl *m_coreImpl; + UniqueIDManager *m_uniqueIDManager; + QList<int> m_globalContext; + QList<int> m_additionalContexts; + QSettings *m_settings; + mutable QPrinter *m_printer; + ActionManager *m_actionManager; + EditorManager *m_editorManager; + FileManager *m_fileManager; + MessageManager *m_messageManager; + ProgressManager *m_progressManager; + ScriptManagerInterface *m_scriptManager; + VariableManager *m_variableManager; + VCSManager *m_vcsManager; + ViewManager *m_viewManager; + ModeManager *m_modeManager; + MimeDatabase *m_mimeDatabase; + FancyTabWidget *m_modeStack; + NavigationWidget *m_navigationWidget; + RightPaneWidget *m_rightPaneWidget; + Core::BaseView *m_outputView; + + IContext * m_activeContext; + + QMap<QWidget *, IContext *> m_contextWidgets; + + ExtensionSystem::PluginManager *m_pluginManager; + + OutputPane *m_outputPane; + BaseMode *m_outputMode; + GeneralSettings *m_generalSettings; + ShortcutSettings *m_shortcutSettings; + + QMap<QAction*, QString> m_recentFilesActions; + + // actions + QShortcut *m_focusToEditor; + QAction *m_newAction; + QAction *m_openAction; + QAction *m_openWithAction; + QAction *m_saveAllAction; + QAction *m_exitAction; + QAction *m_optionsAction; + QAction *m_toggleSideBarAction; +#ifdef Q_OS_MAC + QAction *m_minimizeAction; + QAction *m_zoomAction; +#endif + + QToolButton *m_toggleSideBarButton; +}; + +} // namespace Internal +} // namespace Core + +#endif // MAINWINDOW_H diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp new file mode 100644 index 00000000000..d82059f00df --- /dev/null +++ b/src/plugins/coreplugin/manhattanstyle.cpp @@ -0,0 +1,942 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/**************************************************************************** +** +** Copyright (C) 2007-$THISYEAR$ $TROLLTECH$. All rights reserved. +** +** This file is part of the Manhattan Style project +** +** $TROLLTECH_GPL_LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "manhattanstyle.h" +#include <QStyleOption> +#include <QPainter> +#include <QScrollArea> +#include <QMainWindow> +#include <QDockWidget> +#include <QPixmapCache> +#include <QDialogButtonBox> +#include <QPixmap> +#include <QToolBar> +#include <QDialog> +#include <QLineEdit> +#include <QComboBox> +#include <QLibrary> +#include <QStatusBar> +#include <QApplication> +#include <QStyleFactory> +#include <QToolButton> +#include <QLabel> +#include <QPushButton> +#include <QSplitter> +#include <QMenuBar> +#include "stylehelper.h" +#include "styleanimator.h" +#include <qdebug.h> + +// We define a currently unused state for indicating animations +#define State_Animating 0x00000040 + +// Because designer needs to disable this for widget previews +// we have a custom property that is inherited +bool styleEnabled(const QWidget *widget) +{ + const QWidget *p = widget; + while (p) { + if (p->property("_q_custom_style_disabled").toBool()) + return false; + p = p->parentWidget(); + } + return true; +} + +// Consider making this a QStyle state +bool panelWidget(const QWidget *widget) +{ + const QWidget *p = widget; + + while (p) { + if (qobject_cast<const QToolBar *>(p) && styleEnabled(p)) + return true; + else if (qobject_cast<const QStatusBar *>(p) && styleEnabled(p)) + return true; + else if (qobject_cast<const QMenuBar *>(p) && styleEnabled(p)) + return true; + p = p->parentWidget(); + } + return false; +} + +class ManhattanStylePrivate +{ +public: + ManhattanStylePrivate(const QString &baseStyleName) + { + style = QStyleFactory::create(baseStyleName); + Q_ASSERT(style); + buttonImage_pressed = QImage(":/qworkbench/images/pushbutton_pressed.png"); + buttonImage = QImage(":/qworkbench/images/pushbutton.png"); + + lineeditImage = QImage(":/qworkbench/images/inputfield.png"); + lineeditImage_disabled = QImage(":/qworkbench/images/inputfield_disabled.png"); + } + + ~ManhattanStylePrivate() { + delete style; + style = 0; + } + + void init(); + QStyle *style; + QImage buttonImage; + QImage buttonImage_pressed; + QImage lineeditImage; + QImage lineeditImage_disabled; + + StyleAnimator animator; +}; + +ManhattanStyle::ManhattanStyle(const QString &baseStyleName) + : QWindowsStyle(), d(new ManhattanStylePrivate(baseStyleName)) +{ +} + +ManhattanStyle::~ManhattanStyle() +{ + delete d; + d = 0; +} + +// Draws a CSS-like border image where the defined borders are not stretched +void drawCornerImage(const QImage &img, QPainter *painter, QRect rect, + int left = 0, int top = 0, int right = 0, + int bottom = 0) +{ + QSize size = img.size(); + if (top > 0) { //top + painter->drawImage(QRect(rect.left() + left, rect.top(), rect.width() -right - left, top), img, + QRect(left, 0, size.width() -right - left, top)); + if(left > 0) //top-left + painter->drawImage(QRect(rect.left(), rect.top(), left, top), img, + QRect(0, 0, left, top)); + if (right > 0) //top-right + painter->drawImage(QRect(rect.left() + rect.width() - right, rect.top(), right, top), img, + QRect(size.width() - right, 0, right, top)); + } + //left + if (left > 0) + painter->drawImage(QRect(rect.left(), rect.top()+top, left, rect.height() - top - bottom), img, + QRect(0, top, left, size.height() - bottom - top)); + //center + painter->drawImage(QRect(rect.left() + left, rect.top()+top, rect.width() -right - left, + rect.height() - bottom - top), img, + QRect(left, top, size.width() -right -left, + size.height() - bottom - top)); + if (right > 0) //right + painter->drawImage(QRect(rect.left() +rect.width() - right, rect.top()+top, right, rect.height() - top - bottom), img, + QRect(size.width() - right, top, right, size.height() - bottom - top)); + if (bottom > 0) { //bottom + painter->drawImage(QRect(rect.left() +left, rect.top() + rect.height() - bottom, + rect.width() - right - left, bottom), img, + QRect(left, size.height() - bottom, + size.width() - right - left, bottom)); + if (left > 0) //bottom-left + painter->drawImage(QRect(rect.left(), rect.top() + rect.height() - bottom, left, bottom), img, + QRect(0, size.height() - bottom, left, bottom)); + if (right > 0) //bottom-right + painter->drawImage(QRect(rect.left() + rect.width() - right, rect.top() + rect.height() - bottom, right, bottom), img, + QRect(size.width() - right, size.height() - bottom, right, bottom)); + } +} + +QPixmap ManhattanStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *opt) const +{ + QPixmap result; + result = d->style->generatedIconPixmap(iconMode, pixmap, opt); + return result; +} + +int ManhattanStyle::layoutSpacingImplementation(QSizePolicy::ControlType control1, + QSizePolicy::ControlType control2, + Qt::Orientation orientation, + const QStyleOption * option , + const QWidget * widget ) const +{ + return d->style->layoutSpacing(control1, control2, orientation, option, widget); + +} + +QSize ManhattanStyle::sizeFromContents(ContentsType type, const QStyleOption *option, + const QSize &size, const QWidget *widget) const +{ + if (type == CT_Splitter && widget && widget->property("minisplitter").toBool()) + return QSize(1, 1); + return d->style->sizeFromContents(type, option, size, widget); +} + +QRect ManhattanStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const +{ + QRect rect; + rect = d->style->subElementRect(element, option, widget); + return rect; +} + +QRect ManhattanStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option, + SubControl subControl, const QWidget *widget) const +{ + QRect rect; + rect = d->style->subControlRect(control, option, subControl, widget); + return rect; +} + +QStyle::SubControl ManhattanStyle::hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option, + const QPoint &pos, const QWidget *widget) const +{ + SubControl result = QStyle::SC_None; + result = d->style->hitTestComplexControl(control, option, pos, widget); + return result; +} + +int ManhattanStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const +{ + int retval = 0; + retval = d->style->pixelMetric(metric, option, widget); + switch (metric) { + case PM_SplitterWidth: + if (widget && widget->property("minisplitter").toBool()) + retval = 1; + break; + case PM_ToolBarIconSize: + if (panelWidget(widget)) + retval = 16; + break; + case PM_MenuPanelWidth: + case PM_MenuBarHMargin: + case PM_MenuBarVMargin: + case PM_ToolBarFrameWidth: + if (panelWidget(widget)) + retval = 1; + break; + case PM_ButtonShiftVertical: + case PM_ButtonShiftHorizontal: + case PM_MenuBarPanelWidth: + case PM_ToolBarItemMargin: + case PM_ToolBarItemSpacing: + if (panelWidget(widget)) + retval = 0; + break; + case PM_DefaultFrameWidth: + if (qobject_cast<const QLineEdit*>(widget) && panelWidget(widget)) + return 1; + break; + default: + break; + } + return retval; +} + +QPalette ManhattanStyle::standardPalette() const +{ + QPalette result; + result = d->style->standardPalette(); + return result; +} + +void ManhattanStyle::polish(QApplication *app) +{ + d->style->polish(app); +} + +void ManhattanStyle::unpolish(QApplication *app) +{ + d->style->unpolish(app); +} + +QPalette panelPalette(const QPalette &oldPalette) +{ + QColor color = StyleHelper::panelTextColor(); + QPalette pal = oldPalette; + pal.setBrush(QPalette::All, QPalette::WindowText, color); + pal.setBrush(QPalette::All, QPalette::ButtonText, color); + pal.setBrush(QPalette::All, QPalette::Foreground, color); + color.setAlpha(100); + pal.setBrush(QPalette::Disabled, QPalette::WindowText, color); + pal.setBrush(QPalette::Disabled, QPalette::ButtonText, color); + pal.setBrush(QPalette::Disabled, QPalette::Foreground, color); + return pal; +} + +void ManhattanStyle::polish(QWidget *widget) +{ + d->style->polish(widget); + + // OxygenStyle forces a rounded widget mask on toolbars + if (d->style->inherits("OxygenStyle")) { + if (qobject_cast<QToolBar*>(widget)) + widget->removeEventFilter(d->style); + } + if (panelWidget(widget)) { + if (qobject_cast<QToolButton*>(widget)) { + widget->setAttribute(Qt::WA_Hover); + widget->setMaximumHeight(StyleHelper::navigationWidgetHeight() - 2); + } + else if (qobject_cast<QLineEdit*>(widget)) { + widget->setAttribute(Qt::WA_Hover); + widget->setMaximumHeight(StyleHelper::navigationWidgetHeight() - 2); + } + else if (qobject_cast<QLabel*>(widget)) + widget->setPalette(panelPalette(widget->palette())); + else if (qobject_cast<QToolBar*>(widget)) + widget->setMinimumHeight(StyleHelper::navigationWidgetHeight()); + else if (qobject_cast<QStatusBar*>(widget)) + widget->setFixedHeight(StyleHelper::navigationWidgetHeight() + 2); + else if (qobject_cast<QComboBox*>(widget)) + widget->setMaximumHeight(StyleHelper::navigationWidgetHeight() - 2); + } +} + +void ManhattanStyle::unpolish(QWidget *widget) +{ + d->style->unpolish(widget); + if (panelWidget(widget)) { + if (qobject_cast<QTabBar*>(widget)) + widget->setAttribute(Qt::WA_Hover, false); + else if (qobject_cast<QToolBar*>(widget)) + widget->setAttribute(Qt::WA_Hover, false); + } +} + +void ManhattanStyle::polish(QPalette &pal) +{ + d->style->polish(pal); +} + +QIcon ManhattanStyle::standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *option, + const QWidget *widget) const +{ + static const QIcon closeButton(":/qworkbench/images/closebutton.png"); + QIcon icon; + switch (standardIcon) { + case QStyle::SP_TitleBarCloseButton: + icon = closeButton; + break; + default: + icon = d->style->standardIcon(standardIcon, option, widget); + } + return icon; +} + +QPixmap ManhattanStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt, + const QWidget *widget) const +{ + static const QPixmap closeButton(":/qworkbench/images/closebutton.png"); + QPixmap pixmap; + switch (standardPixmap) { + case QStyle::SP_TitleBarCloseButton: + pixmap = closeButton; + break; + default: + pixmap = d->style->standardPixmap(standardPixmap, opt, widget); + } + return pixmap; +} + + +int ManhattanStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, + QStyleHintReturn *returnData) const +{ + int ret = 0; + switch (hint) { + case QStyle::SH_EtchDisabledText: + ret = false; // We really should only enforce this for panel widgets + break; + default: + ret = d->style->styleHint(hint, option, widget, returnData); + } + + return ret; +} + +void ManhattanStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, + QPainter *painter, const QWidget *widget) const +{ + if (!panelWidget(widget)) + return d->style->drawPrimitive(element, option, painter, widget); + + bool animating = (option->state & State_Animating); + int state = option->state; + QRect rect = option->rect; + QRect oldRect; + QRect newRect; + if (widget && (element == PE_PanelButtonTool) && !animating) { + QWidget *w = const_cast<QWidget *> (widget); + int oldState = w->property("_q_stylestate").toInt(); + oldRect = w->property("_q_stylerect").toRect(); + newRect = w->rect(); + w->setProperty("_q_stylestate", (int)option->state); + w->setProperty("_q_stylerect", w->rect()); + + // Determine the animated transition + bool doTransition = ((state & State_On) != (oldState & State_On) || + (state & State_MouseOver) != (oldState & State_MouseOver)); + if (oldRect != newRect) + { + doTransition = false; + d->animator.stopAnimation(widget); + } + + if (doTransition) { + QImage startImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied); + QImage endImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied); + Animation *anim = d->animator.widgetAnimation(widget); + QStyleOption opt = *option; + opt.state = (QStyle::State)oldState; + opt.state |= (State)State_Animating; + startImage.fill(0); + Transition *t = new Transition; + t->setWidget(w); + QPainter startPainter(&startImage); + if (!anim) { + drawPrimitive(element, &opt, &startPainter, widget); + } else { + anim->paint(&startPainter, &opt); + d->animator.stopAnimation(widget); + } + QStyleOption endOpt = *option; + endOpt.state |= (State)State_Animating; + t->setStartImage(startImage); + d->animator.startAnimation(t); + endImage.fill(0); + QPainter endPainter(&endImage); + drawPrimitive(element, &endOpt, &endPainter, widget); + t->setEndImage(endImage); + t->setDuration(130); + t->setStartTime(QTime::currentTime()); + } + } + + switch (element) { + case PE_PanelLineEdit: + { + painter->save(); + if (option->state & State_Enabled) + drawCornerImage(d->lineeditImage, painter, option->rect, 2, 2, 2, 2); + else + drawCornerImage(d->lineeditImage_disabled, painter, option->rect, 2, 2, 2, 2); + + if (option->state & State_HasFocus || option->state & State_MouseOver) { + QColor hover = StyleHelper::baseColor(); + if (state & State_HasFocus) + hover.setAlpha(100); + else + hover.setAlpha(50); + + painter->setPen(QPen(hover, 1)); + painter->drawRect(option->rect.adjusted(1, 1, -2 ,-2)); + } + painter->restore(); + } + break; + + case PE_FrameStatusBarItem: + break; + + case PE_PanelButtonTool: { + Animation *anim = d->animator.widgetAnimation(widget); + if (!animating && anim) { + anim->paint(painter, option); + } else { + bool pressed = option->state & State_Sunken || option->state & State_On; + QColor shadow(0, 0, 0, 30); + painter->setPen(shadow); + if (pressed) { + QColor shade(0, 0, 0, 50); + if (option->state & State_Sunken) + shade = QColor(0, 0, 0, 70); +#ifndef Q_WS_MAC + else if (option->state & State_MouseOver) + shade = QColor(255, 255, 255, 40); +#endif + else if (option->state & State_On) + shade = QColor(0, 0, 0, 50); + else + shade = QColor(0, 0, 0, 0); + painter->fillRect(rect.adjusted(1, 1, -1, -1), shade); + painter->drawLine(rect.topLeft(), rect.topRight()); + painter->drawLine(rect.topLeft(), rect.bottomLeft()); + QColor highlight(255, 255, 255, 30); + painter->setPen(highlight); + painter->drawLine(rect.topRight(), rect.bottomRight()); + painter->drawLine(rect.bottomLeft(), rect.bottomRight()); + } + #ifndef Q_WS_MAC + else if (option->state & State_MouseOver) { + QColor lighter(255, 255, 255, 100); + painter->fillRect(rect, lighter); + painter->drawLine(rect.topRight(), rect.bottomRight()); + } +#endif + } + } + break; + + case PE_PanelStatusBar: + { + painter->save(); + QLinearGradient grad(option->rect.topLeft(), QPoint(rect.center().x(), rect.bottom())); + QColor startColor = StyleHelper::shadowColor().darker(164); + QColor endColor = StyleHelper::baseColor().darker(130); + grad.setColorAt(0, startColor); + grad.setColorAt(1, endColor); + painter->fillRect(option->rect, grad); + painter->setPen(QColor(255, 255, 255, 60)); + painter->drawLine(rect.topLeft() + QPoint(0,1), + rect.topRight()+ QPoint(0,1)); + painter->setPen(StyleHelper::borderColor().darker(110)); + painter->drawLine(rect.topLeft(), rect.topRight()); + painter->restore(); + } + break; + + case PE_IndicatorToolBarSeparator: + { + QColor separatorColor = StyleHelper::borderColor(); + separatorColor.setAlpha(100); + painter->setPen(separatorColor); + const int margin = 6; + if (option->state & State_Horizontal) { + const int offset = rect.width()/2; + painter->drawLine(rect.bottomLeft().x() + offset, + rect.bottomLeft().y() - margin, + rect.topLeft().x() + offset, + rect.topLeft().y() + margin); + } else { //Draw vertical separator + const int offset = rect.height()/2; + painter->setPen(QPen(option->palette.background().color().darker(110))); + painter->drawLine(rect.topLeft().x() + margin , + rect.topLeft().y() + offset, + rect.topRight().x() - margin, + rect.topRight().y() + offset); + } + } + break; + + case PE_IndicatorToolBarHandle: + { + bool horizontal = option->state & State_Horizontal; + painter->save(); + QPainterPath path; + int x = option->rect.x() + horizontal ? 2 : 6; + int y = option->rect.y() + horizontal ? 6 : 2; + static const int RectHeight = 2; + if (horizontal) { + while (y < option->rect.height() - RectHeight - 6) { + path.moveTo(x, y); + path.addRect(x, y, RectHeight, RectHeight); + y += 6; + } + } else { + while (x < option->rect.width() - RectHeight - 6) { + path.moveTo(x, y); + path.addRect(x, y, RectHeight, RectHeight); + x += 6; + } + } + + painter->setPen(Qt::NoPen); + QColor dark = StyleHelper::borderColor(); + dark.setAlphaF(0.4); + + QColor light = StyleHelper::baseColor(); + light.setAlphaF(0.4); + + painter->fillPath(path, light); + painter->save(); + painter->translate(1, 1); + painter->fillPath(path, dark); + painter->restore(); + painter->translate(3, 3); + painter->fillPath(path, light); + painter->translate(1, 1); + painter->fillPath(path, dark); + painter->restore(); + } + break; + + default: + d->style->drawPrimitive(element, option, painter, widget); + break; + } +} + +void ManhattanStyle::drawControl(ControlElement element, const QStyleOption *option, + QPainter *painter, const QWidget *widget) const +{ + if (!panelWidget(widget)) + return d->style->drawControl(element, option, painter, widget); + + switch (element) { + case CE_MenuBarItem: + painter->save(); + if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { + QColor highlightOutline = StyleHelper::borderColor().lighter(120); + bool act = mbi->state & State_Selected && mbi->state & State_Sunken; + bool dis = !(mbi->state & State_Enabled); + StyleHelper::menuGradient(painter, option->rect, option->rect); + QStyleOptionMenuItem item = *mbi; + item.rect = mbi->rect; + QPalette pal = mbi->palette; + pal.setBrush(QPalette::ButtonText, dis ? Qt::gray : Qt::black); + item.palette = pal; + QCommonStyle::drawControl(element, &item, painter, widget); + QRect r = option->rect; + + if (act) { + // Fill| + QColor baseColor = StyleHelper::baseColor(); + QLinearGradient grad(option->rect.topLeft(), option->rect.bottomLeft()); + grad.setColorAt(0, baseColor.lighter(120)); + grad.setColorAt(1, baseColor.lighter(130)); + painter->fillRect(option->rect.adjusted(1, 1, -1, 0), grad); + + // Outline + painter->setPen(QPen(highlightOutline, 0)); + painter->drawLine(QPoint(r.left(), r.top() + 1), QPoint(r.left(), r.bottom())); + painter->drawLine(QPoint(r.right(), r.top() + 1), QPoint(r.right(), r.bottom())); + painter->drawLine(QPoint(r.left() + 1, r.top()), QPoint(r.right() - 1, r.top())); + highlightOutline.setAlpha(60); + painter->setPen(QPen(highlightOutline, 0)); + painter->drawPoint(r.topLeft()); + painter->drawPoint(r.topRight()); + + QPalette pal = mbi->palette; + uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; + if (!styleHint(SH_UnderlineShortcut, mbi, widget)) + alignment |= Qt::TextHideMnemonic; + pal.setBrush(QPalette::Text, dis ? Qt::gray : QColor(0, 0, 0, 60)); + drawItemText(painter, item.rect.translated(0, 1), alignment, pal, mbi->state & State_Enabled, mbi->text, QPalette::Text); + pal.setBrush(QPalette::Text, dis ? Qt::gray : Qt::white); + drawItemText(painter, item.rect, alignment, pal, mbi->state & State_Enabled, mbi->text, QPalette::Text); + } + } + painter->restore(); + break; + + case CE_ComboBoxLabel: + if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { + if (panelWidget(widget)) { + QRect editRect = subControlRect(CC_ComboBox, cb, SC_ComboBoxEditField, widget); + QPalette customPal = cb->palette; + + if (!cb->currentIcon.isNull()) { + QIcon::Mode mode = cb->state & State_Enabled ? QIcon::Normal + : QIcon::Disabled; + QPixmap pixmap = cb->currentIcon.pixmap(cb->iconSize, mode); + QRect iconRect(editRect); + iconRect.setWidth(cb->iconSize.width() + 4); + iconRect = alignedRect(cb->direction, + Qt::AlignLeft | Qt::AlignVCenter, + iconRect.size(), editRect); + if (cb->editable) + painter->fillRect(iconRect, customPal.brush(QPalette::Base)); + drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap); + + if (cb->direction == Qt::RightToLeft) + editRect.translate(-4 - cb->iconSize.width(), 0); + else + editRect.translate(cb->iconSize.width() + 4, 0); + } + + customPal.setBrush(QPalette::All, QPalette::ButtonText, QColor(0, 0, 0, 70)); + + QRect rect = editRect.adjusted(1, 0, -8, 0); + QString text = option->fontMetrics.elidedText(cb->currentText, Qt::ElideRight, rect.width()); + drawItemText(painter, rect.translated(0, 1), + visualAlignment(option->direction, Qt::AlignLeft | Qt::AlignVCenter), + customPal, cb->state & State_Enabled, text, QPalette::ButtonText); + customPal.setBrush(QPalette::All, QPalette::ButtonText, StyleHelper::panelTextColor()); + drawItemText(painter, rect, + visualAlignment(option->direction, Qt::AlignLeft | Qt::AlignVCenter), + customPal, cb->state & State_Enabled, text, QPalette::ButtonText); + } else { + d->style->drawControl(element, option, painter, widget); + } + } + break; + + case CE_SizeGrip: { + painter->save(); + QColor dark = Qt::white; + dark.setAlphaF(0.1); + int x, y, w, h; + option->rect.getRect(&x, &y, &w, &h); + int sw = qMin(h, w); + if (h > w) + painter->translate(0, h - w); + else + painter->translate(w - h, 0); + int sx = x; + int sy = y; + int s = 4; + painter->setPen(dark); + if (option->direction == Qt::RightToLeft) { + sx = x + sw; + for (int i = 0; i < 4; ++i) { + painter->drawLine(x, sy, sx, sw); + sx -= s; + sy += s; + } + } else { + for (int i = 0; i < 4; ++i) { + painter->drawLine(sx, sw, sw, sy); + sx += s; + sy += s; + } + } + painter->restore(); + } + break; + + case CE_MenuBarEmptyArea: { + StyleHelper::menuGradient(painter, option->rect, option->rect); + painter->save(); + painter->setPen(StyleHelper::borderColor()); + painter->drawLine(option->rect.bottomLeft(), option->rect.bottomRight()); + painter->restore(); + } + break; + + case CE_ToolBar: + { + QString key; + key.sprintf("mh_toolbar %d %d %d", option->rect.width(), option->rect.height(), StyleHelper::baseColor().rgb());; + + QPixmap pixmap; + QPainter *p = painter; + QRect rect = option->rect; + if (StyleHelper::usePixmapCache() && !QPixmapCache::find(key, pixmap)) { + pixmap = QPixmap(option->rect.size()); + p = new QPainter(&pixmap); + rect = QRect(0, 0, option->rect.width(), option->rect.height()); + } + + bool horizontal = option->state & State_Horizontal; + // Map offset for global window gradient + QPoint offset = widget->window()->mapToGlobal(option->rect.topLeft()) - + widget->mapToGlobal(option->rect.topLeft()); + QRect gradientSpan; + if (widget) { + gradientSpan = QRect(offset, widget->window()->size()); + } + if (horizontal) + StyleHelper::horizontalGradient(p, gradientSpan, rect); + else + StyleHelper::verticalGradient(p, gradientSpan, rect); + + painter->setPen(StyleHelper::borderColor()); + + if (horizontal) { + // Note: This is a hack to determine if we are the topmost + // toolbar and menu bar should draw the outline + QColor lighter(255, 255, 255, 40); + if (widget->mapToParent(rect.topLeft()).y()) { + p->drawLine(rect.topLeft(), rect.topRight()); + p->setPen(lighter); + p->drawLine(rect.topLeft() + QPoint(0, 1), rect.topRight() + QPoint(0, 1)); + } else { + p->drawLine(rect.bottomLeft(), rect.bottomRight()); + p->setPen(lighter); + p->drawLine(rect.topLeft(), rect.topRight()); + } + } else { + p->drawLine(rect.topLeft(), rect.bottomLeft()); + p->drawLine(rect.topRight(), rect.bottomRight()); + } + + if (StyleHelper::usePixmapCache() && !QPixmapCache::find(key, pixmap)) { + painter->drawPixmap(rect.topLeft(), pixmap); + p->end(); + delete p; + QPixmapCache::insert(key, pixmap); + } + } + break; + + default: + d->style->drawControl(element, option, painter, widget); + break; + } +} + +void ManhattanStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, + QPainter *painter, const QWidget *widget) const +{ + if (!panelWidget(widget)) + return d->style->drawComplexControl(control, option, painter, widget); + + QRect rect = option->rect; + switch (control) { + case CC_ToolButton: + if (const QStyleOptionToolButton *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(option)) { + QString buttonType = widget->property("type").toString(); + QRect button, menuarea; + button = subControlRect(control, toolbutton, SC_ToolButton, widget); + menuarea = subControlRect(control, toolbutton, SC_ToolButtonMenu, widget); + + State bflags = toolbutton->state; + if (bflags & State_AutoRaise) { + if (!(bflags & State_MouseOver)) { + bflags &= ~State_Raised; + } + } + + State mflags = bflags; + if (toolbutton->activeSubControls & SC_ToolButton) + bflags |= State_Sunken; + if (toolbutton->activeSubControls & SC_ToolButtonMenu) + mflags |= State_Sunken; + + QStyleOption tool(0); + tool.palette = toolbutton->palette; + if (toolbutton->subControls & SC_ToolButton) { + if (buttonType == "dockbutton") { + tool.rect = button; + tool.state = bflags; + drawPrimitive(PE_PanelButtonTool, &tool, painter, widget); + } else { // paint status bar button style + if (bflags & State_Sunken || bflags & State_On) + drawCornerImage(d->buttonImage_pressed, painter, option->rect, 2, 2, 2, 2); + else if (bflags & State_Enabled) { +#ifndef Q_WS_MAC + if (bflags & State_MouseOver) { + drawCornerImage(d->buttonImage, painter, option->rect, 2, 2, 2, 2); + QColor shade(255, 255, 255, 50); + painter->fillRect(button.adjusted(1, 1, -1, -1), shade); + } +#endif + } + + } + } + + if (toolbutton->state & State_HasFocus) { + QStyleOptionFocusRect fr; + fr.QStyleOption::operator=(*toolbutton); + fr.rect.adjust(3, 3, -3, -3); + if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup) + fr.rect.adjust(0, 0, -pixelMetric(QStyle::PM_MenuButtonIndicator, + toolbutton, widget), 0); + QPen oldPen = painter->pen(); + QColor focusColor = StyleHelper::panelTextColor(); + focusColor.setAlpha(120); + QPen outline(focusColor, 1); + outline.setStyle(Qt::DotLine); + painter->setPen(outline); + QRect r = option->rect.adjusted(2, 2, -2, -2); + painter->drawRect(r); + painter->setPen(oldPen); + } + + QStyleOptionToolButton label = *toolbutton; + label.palette = panelPalette(option->palette); + int fw = pixelMetric(PM_DefaultFrameWidth, option, widget); + label.rect = button.adjusted(fw, fw, -fw, -fw); + drawControl(CE_ToolButtonLabel, &label, painter, widget); + + if (toolbutton->subControls & SC_ToolButtonMenu) { + tool.state = mflags; + tool.rect = menuarea.adjusted(1, 1, -1, -1); + if (mflags & (State_Sunken | State_On | State_Raised)) { + painter->setPen(Qt::gray); + painter->drawLine(tool.rect.topLeft(), tool.rect.bottomLeft()); + if (mflags & (State_Sunken)) { + QColor shade(0, 0, 0, 50); + painter->fillRect(tool.rect.adjusted(0, -1, 1, 1), shade); + } +#ifndef Q_WS_MAC + else if (mflags & (State_MouseOver)) { + QColor shade(255, 255, 255, 50); + painter->fillRect(tool.rect.adjusted(0, -1, 1, 1), shade); + } +#endif + } + tool.rect = tool.rect.adjusted(2, 2, -2, -2); + drawPrimitive(PE_IndicatorArrowDown, &tool, painter, widget); + } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) { + int mbi = pixelMetric(PM_MenuButtonIndicator, toolbutton, widget); + QRect ir = toolbutton->rect.adjusted(1, 1, -1, -1); + QStyleOptionToolButton newBtn = *toolbutton; + newBtn.palette = panelPalette(option->palette); + newBtn.rect = QRect(ir.right() + 5 - mbi, ir.height() - mbi + 4, mbi - 6, mbi - 6); + QWindowsStyle::drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget); + } + } + break; + + case CC_ComboBox: + { + painter->save(); + + // Draw tool button + drawPrimitive(PE_PanelButtonTool, option, painter, widget); + + // Draw arrow + int menuButtonWidth = 16; + bool reverse = option->direction == Qt::RightToLeft; + int left = !reverse ? rect.right() - menuButtonWidth : rect.left(); + int right = !reverse ? rect.right() : rect.left() + menuButtonWidth; + QRect arrowRect((left + right) / 2 - 5, rect.center().y() - 3, 9, 9); + if (option->state & State_On) + arrowRect.translate(d->style->pixelMetric(PM_ButtonShiftHorizontal, option, widget), + d->style->pixelMetric(PM_ButtonShiftVertical, option, widget)); + QStyleOption arrowOpt = *option; + arrowOpt.rect = arrowRect; + QPalette pal = option->palette; + pal.setBrush(QPalette::All, QPalette::ButtonText, StyleHelper::panelTextColor()); + arrowOpt.palette = pal; + + QWindowsStyle::drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, painter, widget); + + painter->restore(); + } + break; + default: + d->style->drawComplexControl(control, option, painter, widget); + break; + } +} diff --git a/src/plugins/coreplugin/manhattanstyle.h b/src/plugins/coreplugin/manhattanstyle.h new file mode 100644 index 00000000000..685a35da663 --- /dev/null +++ b/src/plugins/coreplugin/manhattanstyle.h @@ -0,0 +1,106 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/**************************************************************************** +** +** Copyright (C) 2007-$THISYEAR$ $TROLLTECH$. All rights reserved. +** +** This file is part of the ManhattanStyle project on Trolltech Labs. +** +** $TROLLTECH_GPL_LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef MANHATTANSTYLE_H +#define MANHATTANSTYLE_H + +#include <QStyle> +#include <QWindowsStyle> + +QT_BEGIN_NAMESPACE +class QLinearGradient; +class QBrush; +QT_END_NAMESPACE + +class ManhattanStylePrivate; + +class ManhattanStyle : public QWindowsStyle +{ + Q_OBJECT; + +public: + ManhattanStyle(const QString &); + + ~ManhattanStyle(); + + void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = 0) const; + void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = 0) const; + void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget = 0) const; + + QSize sizeFromContents(ContentsType type, const QStyleOption *option, const QSize &size, const QWidget *widget) const; + QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const; + QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc, const QWidget *widget) const; + + SubControl hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option, const QPoint &pos, const QWidget *widget = 0) const; + QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt, const QWidget *widget = 0) const; + int styleHint(StyleHint hint, const QStyleOption *option = 0, const QWidget *widget = 0, QStyleHintReturn *returnData = 0) const; + QRect itemRect(QPainter *p, const QRect &r, int flags, bool enabled, const QPixmap *pixmap, const QString &text, int len = -1) const; + QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *opt) const; + + int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0) const; + + QPalette standardPalette() const; + + void polish(QWidget *widget); + void polish(QPalette &pal); + void polish(QApplication *app); + + void unpolish(QWidget *widget); + void unpolish(QApplication *app); + +protected Q_SLOTS: + QIcon standardIconImplementation(StandardPixmap standardIcon, const QStyleOption *option, const QWidget *widget) const; + int layoutSpacingImplementation(QSizePolicy::ControlType control1, + QSizePolicy::ControlType control2, + Qt::Orientation orientation, + const QStyleOption *option = 0, + const QWidget *widget = 0) const; + +private: + ManhattanStylePrivate *d; + Q_DISABLE_COPY(ManhattanStyle) +}; + +#endif // MANHATTANSTYLE_H diff --git a/src/plugins/coreplugin/messagemanager.cpp b/src/plugins/coreplugin/messagemanager.cpp new file mode 100644 index 00000000000..a6268fbd250 --- /dev/null +++ b/src/plugins/coreplugin/messagemanager.cpp @@ -0,0 +1,88 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "messagemanager.h" +#include "messageoutputwindow.h" + +#include <QtGui/QStatusBar> +#include <QtGui/QApplication> + +#include <extensionsystem/pluginmanager.h> + +using namespace Core; + +MessageManager *MessageManager::m_instance = 0; + +MessageManager::MessageManager(): + m_pm(0), + m_messageOutputWindow(0) +{ + m_instance = this; +} + +MessageManager::~MessageManager() +{ + if (m_pm && m_messageOutputWindow) { + m_pm->removeObject(m_messageOutputWindow); + delete m_messageOutputWindow; + } + + m_instance = 0; +} + +void MessageManager::init(ExtensionSystem::PluginManager *pm) +{ + m_pm = pm; + m_messageOutputWindow = new Internal::MessageOutputWindow; + pm->addObject(m_messageOutputWindow); +} + +void MessageManager::showOutputPane() +{ + if (m_messageOutputWindow) + m_messageOutputWindow->popup(false); +} + +void MessageManager::displayStatusBarMessage(const QString & /*text*/, int /*ms*/) +{ + // TODO: Currently broken, but noone really notices, so... + //m_mainWindow->statusBar()->showMessage(text, ms); +} + +void MessageManager::printToOutputPane(const QString &text, bool bringToForeground) +{ + if (!m_messageOutputWindow) + return; + if (bringToForeground) + m_messageOutputWindow->popup(false); + m_messageOutputWindow->append(text); +} diff --git a/src/plugins/coreplugin/messagemanager.h b/src/plugins/coreplugin/messagemanager.h new file mode 100644 index 00000000000..f2854108d69 --- /dev/null +++ b/src/plugins/coreplugin/messagemanager.h @@ -0,0 +1,74 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef MESSAGEMANAGER_H +#define MESSAGEMANAGER_H + +#include "core_global.h" +#include <QtCore/QObject> + +namespace ExtensionSystem { class PluginManager; } + +namespace Core { + +namespace Internal { +class MessageOutputWindow; +} + +class CORE_EXPORT MessageManager : public QObject +{ + Q_OBJECT + +public: + MessageManager(); + ~MessageManager(); + + void init(ExtensionSystem::PluginManager *pm); + + static MessageManager *instance() { return m_instance; } + + void displayStatusBarMessage(const QString &text, int ms = 0); + void showOutputPane(); + +public slots: + void printToOutputPane(const QString &text, bool bringToForeground = true); + +private: + ExtensionSystem::PluginManager *m_pm; + Internal::MessageOutputWindow *m_messageOutputWindow; + + static MessageManager *m_instance; +}; + +} // namespace Core + +#endif // MESSAGEMANAGER_H diff --git a/src/plugins/coreplugin/messageoutputwindow.cpp b/src/plugins/coreplugin/messageoutputwindow.cpp new file mode 100644 index 00000000000..2ffe82e9df6 --- /dev/null +++ b/src/plugins/coreplugin/messageoutputwindow.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "messageoutputwindow.h" + +#include <QtGui/QTextEdit> + +using namespace Core::Internal; + +MessageOutputWindow::MessageOutputWindow() +{ + m_widget = new QTextEdit; + m_widget->setReadOnly(true); + m_widget->setFrameStyle(QFrame::NoFrame); +} + +MessageOutputWindow::~MessageOutputWindow() +{ + delete m_widget; +} + +bool MessageOutputWindow::hasFocus() +{ + return m_widget->hasFocus(); +} + +bool MessageOutputWindow::canFocus() +{ + return true; +} + +void MessageOutputWindow::setFocus() +{ + m_widget->setFocus(); +} + +void MessageOutputWindow::clearContents() +{ + m_widget->clear(); +} + +QWidget *MessageOutputWindow::outputWidget(QWidget *parent) +{ + m_widget->setParent(parent); + return m_widget; +} + +QString MessageOutputWindow::name() const +{ + return tr("General"); +} + +void MessageOutputWindow::visibilityChanged(bool /*b*/) +{ +} + +void MessageOutputWindow::append(const QString &text) +{ + m_widget->append(text); +} + +int MessageOutputWindow::priorityInStatusBar() const +{ + return -1; +} diff --git a/src/plugins/coreplugin/messageoutputwindow.h b/src/plugins/coreplugin/messageoutputwindow.h new file mode 100644 index 00000000000..1373ea363d4 --- /dev/null +++ b/src/plugins/coreplugin/messageoutputwindow.h @@ -0,0 +1,73 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef MESSAGEOUTPUTWINDOW_H +#define MESSAGEOUTPUTWINDOW_H + +#include <coreplugin/ioutputpane.h> + +QT_BEGIN_NAMESPACE +class QTextEdit; +QT_END_NAMESPACE + +namespace Core { +namespace Internal { + +class MessageOutputWindow : public Core::IOutputPane +{ + Q_OBJECT + +public: + MessageOutputWindow(); + ~MessageOutputWindow(); + + QWidget *outputWidget(QWidget *parent); + QList<QWidget*> toolBarWidgets(void) const { return QList<QWidget *>(); } + + QString name() const; + int priorityInStatusBar() const; + void clearContents(); + void visibilityChanged(bool visible); + + void append(const QString &text); + bool canFocus(); + bool hasFocus(); + void setFocus(); + +private: + QTextEdit *m_widget; +}; + +} // namespace Internal +} // namespace Core + +#endif // MESSAGEOUTPUTWINDOW_H diff --git a/src/plugins/coreplugin/mimedatabase.cpp b/src/plugins/coreplugin/mimedatabase.cpp new file mode 100644 index 00000000000..d49d4728e6b --- /dev/null +++ b/src/plugins/coreplugin/mimedatabase.cpp @@ -0,0 +1,1178 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "mimedatabase.h" + +#include <QtCore/QStringList> +#include <QtCore/QFile> +#include <QtCore/QMap> +#include <QtCore/QMultiHash> +#include <QtCore/QDebug> +#include <QtCore/QRegExp> +#include <QtCore/QCoreApplication> +#include <QtCore/QFileInfo> +#include <QtCore/QByteArray> +#include <QtCore/QSharedData> +#include <QtCore/QSharedPointer> +#include <QtCore/QTextStream> +#include <QtCore/QLocale> +#include <QtXml/QXmlStreamReader> + +enum { debugMimeDB = 0 }; + +// XML tags in mime files +static const char *mimeInfoTagC = "mime-info"; +static const char *mimeTypeTagC = "mime-type"; +static const char *mimeTypeAttributeC = "type"; +static const char *subClassTagC = "sub-class-of"; +static const char *commentTagC = "comment"; +static const char *globTagC = "glob"; +static const char *aliasTagC = "alias"; +static const char *patternAttributeC = "pattern"; +static const char *localeAttributeC = "xml:lang"; + +static const char *magicTagC = "magic"; +static const char *priorityAttributeC = "priority"; +static const char *matchTagC = "match"; +static const char *matchValueAttributeC = "value"; +static const char *matchTypeAttributeC = "type"; +static const char *matchStringTypeValueC = "string"; +static const char *matchOffsetAttributeC = "offset"; + +// Types +static const char *textTypeC = "text/plain"; +static const char *binaryTypeC = "application/octet-stream"; + +// UTF16 byte order marks +static const char bigEndianByteOrderMarkC[] = "\xFE\xFF"; +static const char littleEndianByteOrderMarkC[] = "\xFF\xFE"; + +// Fallback priorities, must be low. +enum { BinaryMatchPriority = 1, TextMatchPriority = 2}; + +/* Parse sth like (<mime-info> being optional): + *\code +?xml version="1.0" encoding="UTF-8"?> +<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info"> + <!-- Mime types must match the desktop file associations --> + <mime-type type="application/vnd.nokia.qt.qmakeprofile"> + <comment xml:lang="en">Qt QMake Profile</comment> + <glob pattern="*.pro"/> + </mime-type> +</mime-info> + *\endcode +*/ + +namespace Core { + +namespace Internal { + +// FileMatchContext: Passed on to the mimetypes from the database +// when looking for a file match. It exists to enable reading the file +// contents "on demand" (as opposed to each mime type trying to open +// and read while checking). + +class FileMatchContext { + Q_DISABLE_COPY(FileMatchContext); +public: + // Max data to be read from a file + enum { MaxData = 2048 }; + + explicit FileMatchContext(const QFileInfo &fi); + + inline QString fileName() const { return m_fileName; } + // Return (cached) first MaxData bytes of file + QByteArray data(); + +private: + enum State { + // File cannot be read/does not exist + NoDataAvailable, + // Not read yet + DataNotRead, + // Available + DataRead }; + const QFileInfo m_fileInfo; + const QString m_fileName; + State m_state; + QByteArray m_data; +}; + +FileMatchContext::FileMatchContext(const QFileInfo &fi) : + m_fileInfo(fi), + m_fileName(fi.fileName()), + m_state(fi.isFile() && fi.isReadable() && fi.size() > 0 ? DataNotRead : NoDataAvailable) +{ +} + +QByteArray FileMatchContext::data() +{ + // Do we need to read? + if (m_state == DataNotRead) { + const QString fullName = m_fileInfo.absoluteFilePath(); + QFile file(fullName); + if (file.open(QIODevice::ReadOnly)) { + m_data = file.read(MaxData); + m_state = DataRead; + } else { + qWarning("%s failed to open %s: %s\n", Q_FUNC_INFO, fullName.toUtf8().constData(), file.errorString().toUtf8().constData()); + m_state = NoDataAvailable; + } + } + return m_data; +} + +// The binary fallback matcher for "application/octet-stream". +class BinaryMatcher : public IMagicMatcher { + Q_DISABLE_COPY(BinaryMatcher); +public: + BinaryMatcher() {} + virtual bool matches(const QByteArray & /*data*/) const { return true; } + virtual int priority() const { return BinaryMatchPriority; } +}; + +// A heuristic text file matcher: If the data do not contain any character +// below tab (9), detect as text. +class HeuristicTextMagicMatcher : public IMagicMatcher { + Q_DISABLE_COPY(HeuristicTextMagicMatcher); +public: + HeuristicTextMagicMatcher() {} + virtual bool matches(const QByteArray &data) const; + virtual int priority() const { return TextMatchPriority; } + + static bool isTextFile(const QByteArray &data); +}; + +bool HeuristicTextMagicMatcher::isTextFile(const QByteArray &data) +{ + const int size = data.size(); + for (int i = 0; i < size; i++) { + const char c = data.at(i); + if (c >= 0x01 && c < 0x09) // Sure-fire binary + return false; + if (c == 0) // Check for UTF16 + return data.startsWith(bigEndianByteOrderMarkC) || data.startsWith(littleEndianByteOrderMarkC); + } + return true; +} + +bool HeuristicTextMagicMatcher::matches(const QByteArray &data) const +{ + const bool rc = isTextFile(data); + if (debugMimeDB) + qDebug() << Q_FUNC_INFO << " on " << data.size() << " returns " << rc; + return rc; +} + +} // namespace Internal + +// MagicRule +MagicRule::MagicRule(const QByteArray &pattern, int startPos, int endPos) : + m_pattern(pattern), + m_startPos(startPos), + m_endPos(endPos) +{ +} + +bool MagicRule::matches(const QByteArray &data) const +{ + // Quick check + const int dataSize = data.size(); + if ((m_startPos + m_pattern.size()) >= dataSize) + return false; + // Most common: some string at position 0: + if (m_startPos == 0 && m_startPos == m_endPos) + return data.startsWith(m_pattern); + // Range + const int index = data.indexOf(m_pattern, m_startPos); + return index != -1 && index < m_endPos; +} + +MagicRule *MagicRule::createStringRule(const QString &c, int startPos, int endPos) +{ + return new MagicRule(c.toUtf8(), startPos, endPos); +} + +// List matcher +MagicRuleMatcher::MagicRuleMatcher() : + m_priority(65535) +{ +} + +void MagicRuleMatcher::add(const MagicRuleSharedPointer &rule) +{ + m_list.push_back(rule); +} + +bool MagicRuleMatcher::matches(const QByteArray &data) const +{ + const MagicRuleList::const_iterator cend = m_list.constEnd(); + for (MagicRuleList::const_iterator it = m_list.constBegin(); it != cend; ++it) + if ( (*it)->matches(data)) + return true; + return false; +} + +int MagicRuleMatcher::priority() const +{ + return m_priority; +} + +void MagicRuleMatcher::setPriority(int p) +{ + m_priority = p; +} + +// ---------- MimeTypeData +class MimeTypeData : public QSharedData { +public: + typedef QHash<QString,QString> LocaleHash; + void clear(); + void debug(QTextStream &str, int indent = 0) const; + + QString type; + QString comment; + + LocaleHash localeComments; + QStringList aliases; + QList<QRegExp> globPatterns; + QStringList subClassesOf; + QString preferredSuffix; + QStringList suffixes; + + typedef QSharedPointer<IMagicMatcher> IMagicMatcherSharedPointer; + typedef QList<IMagicMatcherSharedPointer> IMagicMatcherList; + IMagicMatcherList magicMatchers; +}; + +void MimeTypeData::clear() +{ + type.clear(); + comment.clear(); + aliases.clear(); + globPatterns.clear(); + subClassesOf.clear(); + preferredSuffix.clear(); + suffixes.clear(); + magicMatchers.clear(); +} + +void MimeTypeData::debug(QTextStream &str, int indent) const +{ + const QString indentS = QString(indent, QLatin1Char(' ')); + const QString comma = QString(1, QLatin1Char(',')); + str << indentS << "Type: " << type; + if (!aliases.empty()) + str << " Aliases: " << aliases.join(comma); + str << ", magic: " << magicMatchers.size() << '\n'; + str << indentS << "Comment: " << comment << '\n'; + if (!subClassesOf.empty()) + str << indentS << "SubClassesOf: " << subClassesOf.join(comma) << '\n'; + if (!globPatterns.empty()) { + str << indentS << "Glob: "; + foreach(const QRegExp &r, globPatterns) + str << r.pattern() << ' '; + str << '\n'; + if (!suffixes.empty()) { + str << indentS << "Suffixes: " << suffixes.join(comma) + << " preferred: " << preferredSuffix << '\n'; + } + } + str << '\n'; +} + +// ---------------- MimeType +MimeType::MimeType() : + m_d(new MimeTypeData) +{ +} + +MimeType::MimeType(const MimeType &rhs) : + m_d(rhs.m_d) +{ +} + +MimeType &MimeType::operator=(const MimeType &rhs) +{ + if (this != &rhs) + m_d = rhs.m_d; + return *this; +} + +MimeType::MimeType(const MimeTypeData &d) : + m_d(new MimeTypeData(d)) +{ +} + +MimeType::~MimeType() +{ +} + +void MimeType::clear() +{ + m_d->clear(); +} + +bool MimeType::isNull() const +{ + return m_d->type.isEmpty(); +} + +MimeType::operator bool() const +{ + return !isNull(); +} + +bool MimeType::isTopLevel() const +{ + return m_d->subClassesOf.empty(); +} + +QString MimeType::type() const +{ + return m_d->type; +} + +void MimeType::setType(const QString &type) +{ + m_d->type = type; +} + +QString MimeType::comment() const +{ + return m_d->comment; +} + +void MimeType::setComment(const QString &comment) +{ + m_d->comment = comment; +} + +// Return "en", "de", etc. derived from "en_US", de_DE". +static inline QString systemLanguage() +{ + QString name = QLocale::system().name(); + const int underScorePos = name.indexOf(QLatin1Char('_')); + if (underScorePos != -1) + name.truncate(underScorePos); + return name; +} + +QString MimeType::localeComment(const QString &localeArg) const +{ + const QString locale = localeArg.isEmpty() ? systemLanguage() : localeArg; + const MimeTypeData::LocaleHash::const_iterator it = m_d->localeComments.constFind(locale); + if (it == m_d->localeComments.constEnd()) + return m_d->comment; + return it.value(); +} + +void MimeType::setLocaleComment(const QString &locale, const QString &comment) +{ + m_d->localeComments[locale] = comment; +} + +QStringList MimeType::aliases() const +{ + return m_d->aliases; +} + +void MimeType::setAliases(const QStringList &a) +{ + m_d->aliases = a; +} + +QList<QRegExp> MimeType::globPatterns() const +{ + return m_d->globPatterns; +} + +void MimeType::setGlobPatterns(const QList<QRegExp> &g) +{ + m_d->globPatterns = g; +} + +QStringList MimeType::subClassesOf() const +{ + return m_d->subClassesOf; +} + +void MimeType::setSubClassesOf(const QStringList &s) +{ + m_d->subClassesOf = s; +} + +QString MimeType::preferredSuffix() const +{ + return m_d->preferredSuffix; +} + +bool MimeType::setPreferredSuffix(const QString &s) +{ + if (!m_d->suffixes.contains(s)) { + qWarning("%s: Attempt to set preferred suffix to '%s', which is not in the list of suffixes: %s.", + m_d->type.toUtf8().constData(), + s.toUtf8().constData(), + m_d->suffixes.join(QLatin1String(",")).toUtf8().constData()); + return false; + } + m_d->preferredSuffix = s; + return true; +} + +static QString formatFilterString(const QString &description, const QList<QRegExp> &globs) +{ + QString rc; + if (globs.empty()) // Binary files + return rc; + { + QTextStream str(&rc); + str << description; + if (!globs.empty()) { + str << " ("; + const int size = globs.size(); + for (int i = 0; i < size; i++) { + if (i) + str << ' '; + str << globs.at(i).pattern(); + } + str << ')'; + } + } + return rc; +} + +QString MimeType::filterString() const +{ + // @todo: Use localeComment() once creator is shipped with translations + return formatFilterString(comment(), m_d->globPatterns); +} + +bool MimeType::matchesType(const QString &type) const +{ + return m_d->type == type || m_d->aliases.contains(type); +} + +unsigned MimeType::matchesFile(const QFileInfo &file) const +{ + Internal::FileMatchContext context(file); + return matchesFile(context); +} + +unsigned MimeType::matchesFile(Internal::FileMatchContext &c) const +{ + // check globs + const QList<QRegExp>::const_iterator cend = m_d->globPatterns.constEnd(); + for (QList<QRegExp>::const_iterator it = m_d->globPatterns.constBegin(); it != cend; ++it) + if (it->exactMatch(c.fileName())) + return GlobMatchPriority; + // Nope, try magic matchers on context data + if (m_d->magicMatchers.empty()) + return 0; + const QByteArray data = c.data(); + if (!data.isEmpty()) { + const MimeTypeData::IMagicMatcherList::const_iterator cend = m_d->magicMatchers.constEnd(); + for (MimeTypeData::IMagicMatcherList::const_iterator it = m_d->magicMatchers.constBegin(); it != cend; ++it) + if ((*it)->matches(data)) + return (*it)->priority(); + } + return 0; +} + +QStringList MimeType::suffixes() const +{ + return m_d->suffixes; +} + +void MimeType::setSuffixes(const QStringList &s) +{ + m_d->suffixes = s; +} + +void MimeType::addMagicMatcher(const QSharedPointer<IMagicMatcher> &matcher) +{ + m_d->magicMatchers.push_back(matcher); +} + +QDebug operator<<(QDebug d, const MimeType &mt) +{ + QString s; + { + QTextStream str(&s); + mt.m_d->debug(str); + } + d << s; + return d; +} + +namespace Internal { + +// MimeDatabase helpers: Generic parser for a sequence of <mime-type>. +// Calls abstract handler function process for MimeType it finds. +class BaseMimeTypeParser { + Q_DISABLE_COPY(BaseMimeTypeParser); +public: + BaseMimeTypeParser(); + virtual ~BaseMimeTypeParser() {} + + bool parse(QIODevice *dev, const QString &fileName, QString *errorMessage); + +private: + // Overwrite to process the sequence of parsed data + virtual bool process(const MimeType &t, QString *errorMessage) = 0; + + void addGlobPattern(const QString &pattern, MimeTypeData *d) const; + + enum ParseStage { ParseBeginning, + ParseMimeInfo, + ParseMimeType, + ParseComment, + ParseGlobPattern, + ParseSubClass, + ParseAlias, + ParseMagic, + ParseMagicMatchRule, + ParseOtherMimeTypeSubTag, + ParseError }; + + static ParseStage nextStage(ParseStage currentStage, const QStringRef &startElement); + + const QRegExp m_suffixPattern; +}; + +BaseMimeTypeParser:: BaseMimeTypeParser() : + // RE to match a suffix glob pattern: "*.ext" (and not sth like "Makefile" or + // "*.log[1-9]" + m_suffixPattern(QLatin1String("^\\*\\.[\\w]+$")) +{ + Q_ASSERT(m_suffixPattern.isValid()); +} + +void BaseMimeTypeParser::addGlobPattern(const QString &pattern, MimeTypeData *d) const +{ + if (pattern.isEmpty()) + return; + // Collect patterns as a QRegExp list and filter out the plain + // suffix ones for our suffix list. Use first one as preferred + const QRegExp wildCard(pattern, Qt::CaseSensitive, QRegExp::Wildcard); + if (!wildCard.isValid()) { + qWarning("%s: Invalid wildcard '%s'.", + Q_FUNC_INFO, pattern.toUtf8().constData()); + return; + } + + d->globPatterns.push_back(wildCard); + if (m_suffixPattern.exactMatch(pattern)) { + const QString suffix = pattern.right(pattern.size() - 2); + d->suffixes.push_back(suffix); + if (d->preferredSuffix.isEmpty()) + d->preferredSuffix = suffix; + } +} + +BaseMimeTypeParser::ParseStage BaseMimeTypeParser::nextStage(ParseStage currentStage, const QStringRef &startElement) +{ + switch (currentStage) { + case ParseBeginning: + if (startElement == QLatin1String(mimeInfoTagC)) + return ParseMimeInfo; + if (startElement == QLatin1String(mimeTypeTagC)) + return ParseMimeType; + return ParseError; + case ParseMimeInfo: + return startElement == QLatin1String(mimeTypeTagC) ? ParseMimeType : ParseError; + case ParseMimeType: + case ParseComment: + case ParseGlobPattern: + case ParseSubClass: + case ParseAlias: + case ParseOtherMimeTypeSubTag: + case ParseMagicMatchRule: + if (startElement == QLatin1String(mimeTypeTagC)) // Sequence of <mime-type> + return ParseMimeType; + if (startElement == QLatin1String(commentTagC )) + return ParseComment; + if (startElement == QLatin1String(globTagC)) + return ParseGlobPattern; + if (startElement == QLatin1String(subClassTagC)) + return ParseSubClass; + if (startElement == QLatin1String(aliasTagC)) + return ParseAlias; + if (startElement == QLatin1String(magicTagC)) + return ParseMagic; + return ParseOtherMimeTypeSubTag; + case ParseMagic: + if (startElement == QLatin1String(matchTagC)) + return ParseMagicMatchRule; + break; + case ParseError: + break; + } + return ParseError; +} + +// Parse int number from an (attribute) string) +static bool parseNumber(const QString &n, int *target, QString *errorMessage) +{ + bool ok; + *target = n.toInt(&ok); + if (!ok) { + *errorMessage = QCoreApplication::translate("MimeDatabase", "Not a number '%1'.").arg(n); + return false; + } + return true; +} + +// Evaluate a magic match rule like +// <match value="must be converted with BinHex" type="string" offset="11"/> +// <match value="0x9501" type="big16" offset="0:64"/> +static bool addMagicMatchRule(const QXmlStreamAttributes &atts, + MagicRuleMatcher *ruleMatcher, + QString *errorMessage) +{ + const QString type = atts.value(QLatin1String(matchTypeAttributeC)).toString(); + if (type != QLatin1String(matchStringTypeValueC)) { + qWarning("%s: match type %s is not supported.", Q_FUNC_INFO, type.toUtf8().constData()); + return true; + } + const QString value = atts.value(QLatin1String(matchValueAttributeC)).toString(); + if (value.isEmpty()) { + *errorMessage = QCoreApplication::translate("MimeDatabase", "Empty match value detected."); + return false; + } + // Parse for offset as "1" or "1:10" + int startPos, endPos; + const QString offsetS = atts.value(QLatin1String(matchOffsetAttributeC)).toString(); + const int colonIndex = offsetS.indexOf(QLatin1Char(':')); + const QString startPosS = colonIndex == -1 ? offsetS : offsetS.mid(0, colonIndex); + const QString endPosS = colonIndex == -1 ? offsetS : offsetS.mid(colonIndex + 1); + if (!parseNumber(startPosS, &startPos, errorMessage) || !parseNumber(endPosS, &endPos, errorMessage)) + return false; + if (debugMimeDB) + qDebug() << Q_FUNC_INFO << value << startPos << endPos; + ruleMatcher->add(QSharedPointer<MagicRule>(MagicRule::createStringRule(value, startPos, endPos))); + return true; +} + +bool BaseMimeTypeParser::parse(QIODevice *dev, const QString &fileName, QString *errorMessage) +{ + MimeTypeData data; + MagicRuleMatcher *ruleMatcher = 0; + + QXmlStreamReader reader(dev); + ParseStage ps = ParseBeginning; + while (!reader.atEnd()) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement: + ps = nextStage(ps, reader.name()); + switch (ps) { + case ParseMimeType: { // start parsing a type + const QString type = reader.attributes().value(QLatin1String(mimeTypeAttributeC)).toString(); + if (type.isEmpty()) { + reader.raiseError(QCoreApplication::translate("MimeDatabase", "Missing 'type'-attribute")); + } else { + data.type = type; + } + } + break; + case ParseGlobPattern: + addGlobPattern(reader.attributes().value(QLatin1String(patternAttributeC)).toString(), &data); + break; + case ParseSubClass: { + const QString inheritsFrom = reader.attributes().value(QLatin1String(mimeTypeAttributeC)).toString(); + if (!inheritsFrom.isEmpty()) + data.subClassesOf.push_back(inheritsFrom); + } + break; + case ParseComment: { + // comments have locale attributes. We want the default, English one + QString locale = reader.attributes().value(QLatin1String(localeAttributeC)).toString(); + const QString comment = reader.readElementText(); + if (locale.isEmpty()) { + data.comment = comment; + } else { + data.localeComments.insert(locale, comment); + } + } + break; + case ParseAlias: { + const QString alias = reader.attributes().value(QLatin1String(mimeTypeAttributeC)).toString(); + if (!alias.isEmpty()) + data.aliases.push_back(alias); + } + break; + case ParseMagic: { + int priority = 0; + const QString priorityS = reader.attributes().value(QLatin1String(priorityAttributeC)).toString(); + if (!priorityS.isEmpty()) { + if (!parseNumber(priorityS, &priority, errorMessage)) + return false; + + } + ruleMatcher = new MagicRuleMatcher; + ruleMatcher->setPriority(priority); + } + break; + case ParseMagicMatchRule: + if (!addMagicMatchRule(reader.attributes(), ruleMatcher, errorMessage)) + return false; + break; + case ParseError: + reader.raiseError(QCoreApplication::translate("MimeDatabase", "Unexpected element <%1>").arg(reader.name().toString())); + break; + default: + break; + } // switch nextStage + break; + // continue switch QXmlStreamReader::Token... + case QXmlStreamReader::EndElement: // Finished element + if (reader.name() == QLatin1String(mimeTypeTagC)) { + if (!process(MimeType(data), errorMessage)) + return false; + data.clear(); + } else { + // Finished a match sequence + if (reader.name() == QLatin1String(QLatin1String(magicTagC))) { + data.magicMatchers.push_back(QSharedPointer<IMagicMatcher>(ruleMatcher)); + ruleMatcher = 0; + } + } + break; + + default: + break; + } // switch reader.readNext() + } + + if (reader.hasError()) { + *errorMessage = QCoreApplication::translate("MimeDatabase", "An error has been encountered at line %1 of %2: %3:").arg(reader.lineNumber()).arg(fileName, reader.errorString()); + return false; + } + return true; +} + +} // namespace Internal + +// MimeMapEntry: Entry of a type map, consisting of type and level. + +enum { Dangling = 32767 }; + +struct MimeMapEntry { + explicit MimeMapEntry(const MimeType &t = MimeType(), int aLevel = Dangling); + MimeType type; + int level; // hierachy level +}; + +MimeMapEntry::MimeMapEntry(const MimeType &t, int aLevel) : + type(t), + level(aLevel) +{ +} + +/* MimeDatabasePrivate: Requirements for storage: + * - Must be robust in case of incomplete hierachies, dangling entries + * - Plugins will not load and register their mime types in order + * of inheritance. + * - Multiple inheritance (several subClassesOf) can occur + * - Provide quick lookup by name + * - Provide quick lookup by file type. + * This basically rules out some pointer-based tree, so the structure choosen + * is: + * - An alias map <QString->QString> for mapping aliases to types + * - A Map <QString-MimeMapEntry> for the types (MimeMapEntry being a pair of + * MimeType and (hierarchy) level. + * - A map <QString->QString> representing parent->child relations (enabling + * recursing over children) + * Using strings avoids dangling pointers. + * The hierarchy level is used for mapping by file types. When findByFile() + * is first called after addMimeType() it recurses over the hierarchy and sets + * the hierarchy level of the entries accordingly (0 toplevel, 1 first + * order...). It then does several passes over the type map, checking the + * globs for maxLevel, maxLevel-1....until it finds a match (idea being to + * to check the most specific types first). Starting a recursion from the + * leaves is not suitable since it will hit parent nodes several times. */ + +class MimeDatabasePrivate { + Q_DISABLE_COPY(MimeDatabasePrivate) +public: + MimeDatabasePrivate(); + + bool addMimeTypes(const QString &fileName, QString *errorMessage); + bool addMimeTypes(QIODevice *device, QString *errorMessage); + bool addMimeType(MimeType mt); + + // Returns a mime type or Null one if none found + MimeType findByType(const QString &type) const; + // Returns a mime type or Null one if none found + MimeType findByFile(const QFileInfo &f) const; + + bool setPreferredSuffix(const QString &typeOrAlias, const QString &suffix); + + // Return all known suffixes + QStringList suffixes() const; + QStringList filterStrings() const; + + void debug(QTextStream &str) const; + +private: + typedef QHash<QString, MimeMapEntry> TypeMimeTypeMap; + typedef QHash<QString, QString> AliasMap; + typedef QMultiHash<QString, QString> ParentChildrenMap; + + bool addMimeTypes(QIODevice *device, const QString &fileName, QString *errorMessage); + inline const QString &resolveAlias(const QString &name) const; + MimeType findByFile(const QFileInfo &f, unsigned *priority) const; + void determineLevels(); + void raiseLevelRecursion(MimeMapEntry &e, int level); + + TypeMimeTypeMap m_typeMimeTypeMap; + AliasMap m_aliasMap; + ParentChildrenMap m_parentChildrenMap; + int m_maxLevel; +}; + +MimeDatabasePrivate::MimeDatabasePrivate() : + m_maxLevel(-1) +{ +} + +namespace Internal { + // Parser that builds MimeDB hierarchy by adding to MimeDatabasePrivate + class MimeTypeParser : public BaseMimeTypeParser { + public: + explicit MimeTypeParser(MimeDatabasePrivate &db) : m_db(db) {} + private: + virtual bool process(const MimeType &t, QString *) { m_db.addMimeType(t); return true; } + + MimeDatabasePrivate &m_db; + }; +} // namespace Internal + +bool MimeDatabasePrivate::addMimeTypes(QIODevice *device, const QString &fileName, QString *errorMessage) +{ + Internal::MimeTypeParser parser(*this); + return parser.parse(device, fileName, errorMessage); +} + +bool MimeDatabasePrivate::addMimeTypes(const QString &fileName, QString *errorMessage) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) { + *errorMessage = QCoreApplication::translate("MimeDatabase", "Cannot open %1: %2").arg(fileName, file.errorString()); + return false; + } + return addMimeTypes(&file, fileName, errorMessage); +} + +bool MimeDatabasePrivate::addMimeTypes(QIODevice *device, QString *errorMessage) +{ + return addMimeTypes(device, QLatin1String("<stream>"), errorMessage); +} + +bool MimeDatabasePrivate::addMimeType(MimeType mt) +{ + if (!mt) + return false; + + const QString type = mt.type(); + // Hack: Add a magic text matcher to "text/plain" and the fallback matcher to + // binary types "application/octet-stream" + if (type == QLatin1String(textTypeC)) { + mt.addMagicMatcher(QSharedPointer<IMagicMatcher>(new Internal::HeuristicTextMagicMatcher)); + } else { + if (type == QLatin1String(binaryTypeC)) + mt.addMagicMatcher(QSharedPointer<IMagicMatcher>(new Internal::BinaryMatcher)); + } + // insert the type. + m_typeMimeTypeMap.insert(type, MimeMapEntry(mt)); + // Register the children, resolved via alias map. Note that it is still + // possible that aliases end up in the map if the parent classes are not inserted + // at this point (thus their aliases not known). + const QStringList subClassesOf = mt.subClassesOf(); + if (!subClassesOf.empty()) { + const QStringList::const_iterator socend = subClassesOf.constEnd(); + for (QStringList::const_iterator soit = subClassesOf.constBegin(); soit != socend; ++soit) + m_parentChildrenMap.insert(resolveAlias(*soit), type); + } + // register aliasses + const QStringList aliases = mt.aliases(); + if (!aliases.empty()) { + const QStringList::const_iterator cend = aliases.constEnd(); + for (QStringList::const_iterator it = aliases.constBegin(); it != cend; ++it) + m_aliasMap.insert(*it, type); + } + m_maxLevel = -1; // Mark as dirty + return true; +} + +const QString &MimeDatabasePrivate::resolveAlias(const QString &name) const +{ + const AliasMap::const_iterator aliasIt = m_aliasMap.constFind(name); + return aliasIt == m_aliasMap.constEnd() ? name : aliasIt.value(); +} + +void MimeDatabasePrivate::raiseLevelRecursion(MimeMapEntry &e, int level) +{ + if (e.level == Dangling || e.level < level) + e.level = level; + if (m_maxLevel < level) + m_maxLevel = level; + // At all events recurse over children since nodes might have been + // added. + const QStringList childTypes = m_parentChildrenMap.values(e.type.type()); + if (childTypes.empty()) + return; + // look them up in the type->mime type map + const int nextLevel = level + 1; + const TypeMimeTypeMap::iterator tm_end = m_typeMimeTypeMap.end(); + const QStringList::const_iterator cend = childTypes.constEnd(); + for (QStringList::const_iterator it = childTypes.constBegin(); it != cend; ++it) { + const TypeMimeTypeMap::iterator tm_it = m_typeMimeTypeMap.find(resolveAlias(*it)); + if (tm_it == tm_end) { + qWarning("%s: Inconsistent mime hierarchy detected, child %s of %s cannot be found.", + Q_FUNC_INFO, it->toUtf8().constData(), e.type.type().toUtf8().constData()); + } else { + raiseLevelRecursion(*tm_it, nextLevel); + } + } +} + +void MimeDatabasePrivate::determineLevels() +{ + // Loop over toplevels and recurse down their hierarchies. + // Determine top levels by subtracting the children from the parent + // set. Note that a toplevel at this point might have 'subclassesOf' + // set to some class that is not in the DB, so, checking for an empty + // 'subclassesOf' set is not sufficient to find the toplevels. + // First, take the parent->child entries whose parent exists and build + // sets of parents/children + QSet<QString> parentSet, childrenSet; + const ParentChildrenMap::const_iterator pcend = m_parentChildrenMap.constEnd(); + for (ParentChildrenMap::const_iterator it = m_parentChildrenMap.constBegin(); it != pcend; ++it) + if (m_typeMimeTypeMap.contains(it.key())) { + parentSet.insert(it.key()); + childrenSet.insert(it.value()); + } + const QSet<QString> topLevels = parentSet.subtract(childrenSet); + if (debugMimeDB) + qDebug() << Q_FUNC_INFO << "top levels" << topLevels; + const TypeMimeTypeMap::iterator tm_end = m_typeMimeTypeMap.end(); + const QSet<QString>::const_iterator tl_cend = topLevels.constEnd(); + for (QSet<QString>::const_iterator tl_it = topLevels.constBegin(); tl_it != tl_cend; ++tl_it) { + const TypeMimeTypeMap::iterator tm_it = m_typeMimeTypeMap.find(resolveAlias(*tl_it)); + if (tm_it == tm_end) { + qWarning("%s: Inconsistent mime hierarchy detected, top level element %s cannot be found.", + Q_FUNC_INFO, tl_it->toUtf8().constData()); + } else { + raiseLevelRecursion(tm_it.value(), 0); + } + } +} + +bool MimeDatabasePrivate::setPreferredSuffix(const QString &typeOrAlias, const QString &suffix) +{ + TypeMimeTypeMap::iterator tit = m_typeMimeTypeMap.find(resolveAlias(typeOrAlias)); + if (tit != m_typeMimeTypeMap.end()) + return tit.value().type.setPreferredSuffix(suffix); + return false; +} + +// Returns a mime type or Null one if none found +MimeType MimeDatabasePrivate::findByType(const QString &typeOrAlias) const +{ + const TypeMimeTypeMap::const_iterator tit = m_typeMimeTypeMap.constFind(resolveAlias(typeOrAlias)); + if (tit != m_typeMimeTypeMap.constEnd()) + return tit.value().type; + return MimeType(); +} + +// Debugging wrapper around findByFile() +MimeType MimeDatabasePrivate::findByFile(const QFileInfo &f) const +{ + unsigned priority = 0; + if (debugMimeDB) + qDebug() << '>' << Q_FUNC_INFO << f.fileName(); + const MimeType rc = findByFile(f, &priority); + if (debugMimeDB) { + if (rc) { + qDebug() << "<MimeDatabase::findByFile: match prio=" << priority << rc.type(); + } else { + qDebug() << "<MimeDatabase::findByFile: no match"; + } + } + return rc; +} + +// Returns a mime type or Null one if none found +MimeType MimeDatabasePrivate::findByFile(const QFileInfo &f, unsigned *priorityPtr) const +{ + typedef QList<MimeMapEntry> MimeMapEntryList; + + // Is the hierarchy set up in case we find several matches? + if (m_maxLevel < 0) { + MimeDatabasePrivate *db = const_cast<MimeDatabasePrivate *>(this); + db->determineLevels(); + } + // Starting from max level (most specific): Try to find a match of + // best (max) priority. Return if a glob match triggers. + *priorityPtr = 0; + unsigned maxPriority = 0; + MimeType rc; + Internal::FileMatchContext context(f); + const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd(); + for (int level = m_maxLevel; level >= 0; level--) + for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it) + if (it.value().level == level) { + const unsigned priority = it.value().type.matchesFile(context); + if (debugMimeDB > 1) + qDebug() << "pass" << level << it.value().type.type() << " matches " << priority; + if (priority) + if (priority > maxPriority) { + rc = it.value().type; + maxPriority = priority; + // Glob (exact) match?! We are done + if (maxPriority == MimeType::GlobMatchPriority) { + *priorityPtr = priority; + return rc; + } + } + } + return rc; +} + +// Return all known suffixes +QStringList MimeDatabasePrivate::suffixes() const +{ + QStringList rc; + const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd(); + for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it) + rc += it.value().type.suffixes(); + return rc; +} + +QStringList MimeDatabasePrivate::filterStrings() const +{ + QStringList rc; + const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd(); + for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it) + rc += it.value().type.filterString(); + return rc; +} + +void MimeDatabasePrivate::debug(QTextStream &str) const +{ + str << ">MimeDatabase\n"; + const TypeMimeTypeMap::const_iterator cend = m_typeMimeTypeMap.constEnd(); + for (TypeMimeTypeMap::const_iterator it = m_typeMimeTypeMap.constBegin(); it != cend; ++it) { + str << "Entry level " << it.value().level << '\n'; + it.value().type.m_d->debug(str); + } + str << "<MimeDatabase\n"; +} + +// --------------- MimeDatabase +MimeDatabase::MimeDatabase() : + m_d(new MimeDatabasePrivate) +{ +} + +MimeDatabase::~MimeDatabase() +{ + delete m_d; +} + +MimeType MimeDatabase::findByType(const QString &typeOrAlias) const +{ + return m_d->findByType(typeOrAlias); +} + +MimeType MimeDatabase::findByFile(const QFileInfo &f) const +{ + return m_d->findByFile(f); +} + +bool MimeDatabase::addMimeType(const MimeType &mt) +{ + return m_d->addMimeType(mt); +} + +bool MimeDatabase::addMimeTypes(const QString &fileName, QString *errorMessage) +{ + return m_d->addMimeTypes(fileName, errorMessage); +} + +bool MimeDatabase::addMimeTypes(QIODevice *device, QString *errorMessage) +{ + return m_d->addMimeTypes(device, errorMessage); +} + +QStringList MimeDatabase::suffixes() const +{ + return m_d->suffixes(); +} + +QStringList MimeDatabase::filterStrings() const +{ + return m_d->filterStrings(); +} + +QString MimeDatabase::preferredSuffixByType(const QString &type) const +{ + if (const MimeType mt = findByType(type)) + return mt.preferredSuffix(); + return QString(); +} + +QString MimeDatabase::preferredSuffixByFile(const QFileInfo &f) const +{ + if (const MimeType mt = findByFile(f)) + return mt.preferredSuffix(); + return QString(); +} + +bool MimeDatabase::setPreferredSuffix(const QString &typeOrAlias, const QString &suffix) +{ + return m_d->setPreferredSuffix(typeOrAlias, suffix); +} + +QDebug operator<<(QDebug d, const MimeDatabase &mt) +{ + QString s; + { + QTextStream str(&s); + mt.m_d->debug(str); + } + d << s; + return d; +} + +} // namespace Core diff --git a/src/plugins/coreplugin/mimedatabase.h b/src/plugins/coreplugin/mimedatabase.h new file mode 100644 index 00000000000..d4f96cf8d90 --- /dev/null +++ b/src/plugins/coreplugin/mimedatabase.h @@ -0,0 +1,226 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef MIMEDATABASE_H +#define MIMEDATABASE_H + +#include <coreplugin/core_global.h> +#include <QtCore/QStringList> +#include <QtCore/QSharedDataPointer> +#include <QtCore/QSharedPointer> +#include <QtCore/QByteArray> + +QT_BEGIN_NAMESPACE +class QIODevice; +class QRegExp; +class QDebug; +class QFileInfo; +QT_END_NAMESPACE + +namespace Core { + +class MimeTypeData; +class MimeDatabasePrivate; + +namespace Internal { + class BaseMimeTypeParser; + class FileMatchContext; +} + +/* Magic (file contents) matcher interface. */ +class CORE_EXPORT IMagicMatcher { + Q_DISABLE_COPY(IMagicMatcher) +protected: + IMagicMatcher() {} +public: + // Check for a match on contents of a file + virtual bool matches(const QByteArray &data) const = 0; + // Return a priority value from 1..100 + virtual int priority() const = 0; + virtual ~IMagicMatcher() {} +}; + +/* Utility class: A standard Magic match rule based on contents. Provides + * static factory methods for creation (currently only for "string". This can + * be extended to handle "little16"/"big16", etc.). */ +class CORE_EXPORT MagicRule { + Q_DISABLE_COPY(MagicRule) +public: + explicit MagicRule(const QByteArray &pattern, int startPos, int endPos); + bool matches(const QByteArray &data) const; + + // Convenience factory methods + static MagicRule *createStringRule(const QString &c, int startPos, int endPos); + +private: + const QByteArray m_pattern; + const int m_startPos; + const int m_endPos; +}; + +/* Utility class: A Magic matcher that checks a number of rules based on + * operator "or". It is used for rules parsed from XML files. */ +class CORE_EXPORT MagicRuleMatcher : public IMagicMatcher { + Q_DISABLE_COPY(MagicRuleMatcher) +public: + typedef QSharedPointer<MagicRule> MagicRuleSharedPointer; + + MagicRuleMatcher(); + void add(const MagicRuleSharedPointer &rule); + virtual bool matches(const QByteArray &data) const; + + virtual int priority() const; + void setPriority(int p); + +private: + typedef QList<MagicRuleSharedPointer> MagicRuleList; + MagicRuleList m_list; + int m_priority; +}; + +/* Mime type data used in Qt Creator. Contains most information from + * standard mime type XML database files. + * Omissions: + * - Only magic of type "string" is supported. In addition, C++ classes + * derived from IMagicMatcher can be added to check on contents + * - acronyms, language-specific comments + * Extensions: + * - List of suffixes and preferred suffix (derived from glob patterns). + */ +class CORE_EXPORT MimeType { +public: + /* Return value of a glob match, which is higher than magic */ + enum { GlobMatchPriority = 101 }; + + MimeType(); + MimeType(const MimeType&); + MimeType &operator=(const MimeType&); + ~MimeType(); + + void clear(); + bool isNull() const; + operator bool() const; + + bool isTopLevel() const; + + QString type() const; + void setType(const QString &type); + + QStringList aliases() const; + void setAliases(const QStringList &); + + QString comment() const; + void setComment(const QString &comment); + + QString localeComment(const QString &locale = QString() /* en, de...*/) const; + void setLocaleComment(const QString &locale, const QString &comment); + + QList<QRegExp> globPatterns() const; + void setGlobPatterns(const QList<QRegExp> &); + + QStringList subClassesOf() const; + void setSubClassesOf(const QStringList &); + + // Extension over standard mime data + QStringList suffixes() const; + void setSuffixes(const QStringList &); + + // Extension over standard mime data + QString preferredSuffix() const; + bool setPreferredSuffix(const QString&); + + // Check for type or one of the aliases + bool matchesType(const QString &type) const; + // Check glob patterns and magic. Returns the match priority (0 no match, + // 1..100 indicating a magic match or GlobMatchPriority indicating an + // exact glob match). + unsigned matchesFile(const QFileInfo &file) const; + + // Return a filter string usable for a file dialog + QString filterString() const; + + // Add magic matcher + void addMagicMatcher(const QSharedPointer<IMagicMatcher> &matcher); + + friend QDebug operator<<(QDebug d, const MimeType &mt); + +private: + explicit MimeType(const MimeTypeData &d); + unsigned matchesFile(Internal::FileMatchContext &c) const; + + friend class Internal::BaseMimeTypeParser; + friend class MimeDatabasePrivate; + QSharedDataPointer<MimeTypeData> m_d; +}; + +/* A Mime data base to which the plugins can add the mime types they handle. + * When adding a "text/plain" to it, the mimetype will receive a magic matcher + * that checks for text files that do not match the globs by heuristics. + * + * A good testcase is to run it over '/usr/share/mime/<*>/<*>.xml' on Linux. */ + +class CORE_EXPORT MimeDatabase +{ + Q_DISABLE_COPY(MimeDatabase) +public: + MimeDatabase(); + + ~MimeDatabase(); + + bool addMimeTypes(const QString &fileName, QString *errorMessage); + bool addMimeTypes(QIODevice *device, QString *errorMessage); + bool addMimeType(const MimeType &mt); + + // Returns a mime type or Null one if none found + MimeType findByType(const QString &type) const; + // Returns a mime type or Null one if none found + MimeType findByFile(const QFileInfo &f) const; + + // Convenience + QString preferredSuffixByType(const QString &type) const; + QString preferredSuffixByFile(const QFileInfo &f) const; + + // Return all known suffixes + QStringList suffixes() const; + bool setPreferredSuffix(const QString &typeOrAlias, const QString &suffix); + + QStringList filterStrings() const; + + friend QDebug operator<<(QDebug d, const MimeDatabase &mt); + +private: + MimeDatabasePrivate *m_d; +}; + +} // namespace Core + +#endif // MIMEDATABASE_H diff --git a/src/plugins/coreplugin/minisplitter.cpp b/src/plugins/coreplugin/minisplitter.cpp new file mode 100644 index 00000000000..dfdad9fb537 --- /dev/null +++ b/src/plugins/coreplugin/minisplitter.cpp @@ -0,0 +1,98 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "minisplitter.h" +#include "stylehelper.h" + +#include <QtGui/QPaintEvent> +#include <QtGui/QPainter> +#include <QtGui/QSplitterHandle> + +namespace Core { +namespace Internal { + +class MiniSplitterHandle : public QSplitterHandle +{ +public: + MiniSplitterHandle(Qt::Orientation orientation, QSplitter *parent) + : QSplitterHandle(orientation, parent) + { + setMask(QRegion(contentsRect())); + setAttribute(Qt::WA_MouseNoMask, true); + } +protected: + void resizeEvent(QResizeEvent *event); + void paintEvent(QPaintEvent *event); +}; + +} // namespace Internal +} // namespace Core + +using namespace Core; +using namespace Core::Internal; + +void MiniSplitterHandle::resizeEvent(QResizeEvent *event) +{ + if (orientation() == Qt::Horizontal) + setContentsMargins(2, 0, 2, 0); + else + setContentsMargins(0, 2, 0, 2); + setMask(QRegion(contentsRect())); + QSplitterHandle::resizeEvent(event); +} + +void MiniSplitterHandle::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + painter.fillRect(event->rect(), StyleHelper::borderColor()); +} + +QSplitterHandle *MiniSplitter::createHandle() +{ + return new MiniSplitterHandle(orientation(), this); +} + +MiniSplitter::MiniSplitter(QWidget *parent) + : QSplitter(parent) +{ + setHandleWidth(1); + setChildrenCollapsible(false); + setProperty("minisplitter", true); +} + +MiniSplitter::MiniSplitter(Qt::Orientation orientation) + : QSplitter(orientation) +{ + setHandleWidth(1); + setChildrenCollapsible(false); + setProperty("minisplitter", true); +} diff --git a/src/plugins/coreplugin/minisplitter.h b/src/plugins/coreplugin/minisplitter.h new file mode 100644 index 00000000000..aedb2b757c7 --- /dev/null +++ b/src/plugins/coreplugin/minisplitter.h @@ -0,0 +1,59 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef MINISPLITTER_H +#define MINISPLITTER_H + +#include "core_global.h" + +#include <QtGui/QSplitter> + +QT_BEGIN_NAMESPACE +class QSplitterHandle; +QT_END_NAMESPACE + +namespace Core { + +/*! This is a simple helper-class to obtain mac-style 1-pixel wide splitters */ +class CORE_EXPORT MiniSplitter : public QSplitter +{ +public: + MiniSplitter(QWidget *parent = 0); + MiniSplitter(Qt::Orientation orientation); + +protected: + QSplitterHandle *createHandle(); +}; + +} // namespace Core + +#endif // MINISPLITTER_H diff --git a/src/plugins/coreplugin/modemanager.cpp b/src/plugins/coreplugin/modemanager.cpp new file mode 100644 index 00000000000..74027ab391a --- /dev/null +++ b/src/plugins/coreplugin/modemanager.cpp @@ -0,0 +1,236 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "modemanager.h" +#include "fancytabwidget.h" +#include "fancyactionbar.h" +#include "mainwindow.h" + +#include <aggregation/aggregate.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/actionmanager/icommand.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/coreimpl.h> +#include <coreplugin/imode.h> +#include <coreplugin/uniqueidmanager.h> + +#include <QtCore/QObject> +#include <QtCore/QDebug> +#include <QtCore/QSignalMapper> +#include <QtGui/QAction> +#include <QtGui/QTabWidget> +#include <QtGui/QVBoxLayout> + +using namespace Core; +using namespace Core::Internal; + +ModeManager *ModeManager::m_instance = 0; + +ModeManager::ModeManager(Internal::MainWindow *mainWindow, FancyTabWidget *modeStack): + m_mainWindow(mainWindow), + m_modeStack(modeStack), + m_signalMapper(new QSignalMapper(this)) +{ + m_instance = this; + + m_actionBar = new FancyActionBar(modeStack); + m_modeStack->addCornerWidget(m_actionBar); + + connect(m_modeStack, SIGNAL(currentAboutToShow(int)), SLOT(currentTabAboutToChange(int))); + connect(m_modeStack, SIGNAL(currentChanged(int)), SLOT(currentTabChanged(int))); + connect(m_signalMapper, SIGNAL(mapped(QString)), this, SLOT(activateMode(QString))); +} + +void ModeManager::init() +{ + QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(objectAdded(QObject*)), + this, SLOT(objectAdded(QObject*))); + QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(aboutToRemoveObject(QObject*)), + this, SLOT(aboutToRemoveObject(QObject*))); +} + +void ModeManager::addWidget(QWidget *widget) +{ + // We want the actionbar to stay on the bottom + // so m_modeStack->cornerWidgetCount() -1 inserts it at the position immediately above + // the actionbar + m_modeStack->insertCornerWidget(m_modeStack->cornerWidgetCount() -1, widget); +} + +IMode *ModeManager::currentMode() const +{ + return m_modes.at(m_modeStack->currentIndex()); +} + +int ModeManager::indexOf(const QString &id) const +{ + for (int i = 0; i < m_modes.count(); ++i) { + if (m_modes.at(i)->uniqueModeName() == id) + return i; + } + qDebug() << "Warning, no such mode:" << id; + return -1; +} + +IMode *ModeManager::mode(const QString &id) const +{ + const int index = indexOf(id); + if (index >= 0) + return m_modes.at(index); + return 0; +} + +void ModeManager::activateMode(const QString &id) +{ + const int index = indexOf(id); + if (index >= 0) + m_modeStack->setCurrentIndex(index); +} + +void ModeManager::objectAdded(QObject *obj) +{ + IMode *mode = Aggregation::query<IMode>(obj); + if (!mode) + return; + + m_mainWindow->addContextObject(mode); + + // Count the number of modes with a higher priority + int index = 0; + foreach (const IMode *m, m_modes) + if (m->priority() > mode->priority()) + ++index; + + m_modes.insert(index, mode); + m_modeStack->insertTab(index, mode->widget(), mode->icon(), mode->name()); + + // Register mode shortcut + ActionManagerInterface *am = m_mainWindow->actionManager(); + const QString shortcutId = QLatin1String("QtCreator.Mode.") + mode->uniqueModeName(); + QShortcut *shortcut = new QShortcut(m_mainWindow); + shortcut->setWhatsThis(tr("Switch to %1 mode").arg(mode->name())); + ICommand *cmd = am->registerShortcut(shortcut, shortcutId, QList<int>() << Constants::C_GLOBAL_ID); + + m_modeShortcuts.insert(index, cmd); + connect(cmd, SIGNAL(keySequenceChanged()), this, SLOT(updateModeToolTip())); + for (int i = 0; i < m_modeShortcuts.size(); ++i) { +#ifdef Q_OS_MAC + m_modeShortcuts.at(i)->setDefaultKeySequence(QKeySequence(QString("Meta+%1").arg(i+1))); +#else + m_modeShortcuts.at(i)->setDefaultKeySequence(QKeySequence(QString("Ctrl+%1").arg(i+1))); +#endif + } + + m_signalMapper->setMapping(shortcut, mode->uniqueModeName()); + connect(shortcut, SIGNAL(activated()), m_signalMapper, SLOT(map())); +} + +void ModeManager::updateModeToolTip() +{ + ICommand *cmd = qobject_cast<ICommand *>(sender()); + if (cmd) { + int index = m_modeShortcuts.indexOf(cmd); + if (index != -1) + m_modeStack->setTabToolTip(index, cmd->stringWithAppendedShortcut(cmd->shortcut()->whatsThis())); + } +} + +void ModeManager::aboutToRemoveObject(QObject *obj) +{ + IMode *mode = Aggregation::query<IMode>(obj); + if (!mode) + return; + + const int index = m_modes.indexOf(mode); + m_modes.remove(index); + m_modeShortcuts.remove(index); + m_modeStack->removeTab(index); + + m_mainWindow->removeContextObject(mode); +} + +void ModeManager::addAction(ICommand *command, int priority, QMenu *menu) +{ + m_actions.insert(command, priority); + + // Count the number of commands with a higher priority + int index = 0; + foreach (int p, m_actions.values()) + if (p > priority) + ++index; + + m_actionBar->insertAction(index, command->action(), menu); +} + +void ModeManager::currentTabAboutToChange(int index) +{ + if (index >= 0) { + IMode *mode = m_modes.at(index); + if (mode) + emit currentModeAboutToChange(mode); + } +} + +void ModeManager::currentTabChanged(int index) +{ + // Tab index changes to -1 when there is no tab left. + if (index >= 0) { + IMode *mode = m_modes.at(index); + + // FIXME: This hardcoded context update is required for the Debug and Edit modes, since + // they use the editor widget, which is already a context widget so the main window won't + // go further up the parent tree to find the mode context. + CoreImpl *core = CoreImpl::instance(); + foreach (const int context, m_addedContexts) + core->removeAdditionalContext(context); + + m_addedContexts = mode->context(); + foreach (const int context, m_addedContexts) + core->addAdditionalContext(context); + emit currentModeChanged(mode); + core->updateContext(); + } +} + +void ModeManager::setFocusToCurrentMode() +{ + IMode *mode = currentMode(); + Q_ASSERT(mode); + QWidget *widget = mode->widget(); + if (widget) { + QWidget *focusWidget = widget->focusWidget(); + if (focusWidget) + focusWidget->setFocus(); + else + widget->setFocus(); + } +} diff --git a/src/plugins/coreplugin/modemanager.h b/src/plugins/coreplugin/modemanager.h new file mode 100644 index 00000000000..775c8d4a611 --- /dev/null +++ b/src/plugins/coreplugin/modemanager.h @@ -0,0 +1,106 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef MODEMANAGER_H +#define MODEMANAGER_H + +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QVector> + +#include <coreplugin/core_global.h> + +QT_BEGIN_NAMESPACE +class QSignalMapper; +class QMenu; +QT_END_NAMESPACE + +namespace Core { + +class ICommand; +class IMode; + +namespace Internal { +class FancyTabWidget; +class FancyActionBar; +class MainWindow; +} // namespace Internal + +class CORE_EXPORT ModeManager : public QObject +{ + Q_OBJECT + +public: + ModeManager(Internal::MainWindow *mainWindow, Internal::FancyTabWidget *modeStack); + + void init(); + static ModeManager *instance() { return m_instance; } + + IMode* currentMode() const; + IMode* mode(const QString &id) const; + + void addAction(ICommand *command, int priority, QMenu *menu = 0); + void addWidget(QWidget *widget); + +signals: + void currentModeAboutToChange(Core::IMode *mode); + void currentModeChanged(Core::IMode *mode); + +public slots: + void activateMode(const QString &id); + void setFocusToCurrentMode(); + +private slots: + void objectAdded(QObject *obj); + void aboutToRemoveObject(QObject *obj); + void currentTabAboutToChange(int index); + void currentTabChanged(int index); + void updateModeToolTip(); + +private: + int indexOf(const QString &id) const; + + static ModeManager *m_instance; + Internal::MainWindow *m_mainWindow; + Internal::FancyTabWidget *m_modeStack; + Internal::FancyActionBar *m_actionBar; + QMap<ICommand*, int> m_actions; + QVector<IMode*> m_modes; + QVector<ICommand*> m_modeShortcuts; + QSignalMapper *m_signalMapper; + QList<int> m_addedContexts; +}; + +} // namespace Core + +#endif // MODEMANAGER_H diff --git a/src/plugins/coreplugin/navigationwidget.cpp b/src/plugins/coreplugin/navigationwidget.cpp new file mode 100644 index 00000000000..0a29c24d7c3 --- /dev/null +++ b/src/plugins/coreplugin/navigationwidget.cpp @@ -0,0 +1,494 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "navigationwidget.h" + +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/inavigationwidgetfactory.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <extensionsystem/ExtensionSystemInterfaces> + +#include <QtGui/QAction> +#include <QtGui/QHBoxLayout> +#include <QtGui/QToolButton> +#include <QtGui/QToolBar> +#include <QtGui/QResizeEvent> +#include <QtCore/QDebug> +#include <QtCore/QSettings> + +Q_DECLARE_METATYPE(Core::INavigationWidgetFactory *) + +using namespace Core; +using namespace Core::Internal; + +NavigationWidgetPlaceHolder *NavigationWidgetPlaceHolder::m_current = 0; + +NavigationWidgetPlaceHolder* NavigationWidgetPlaceHolder::current() +{ + return m_current; +} + +NavigationWidgetPlaceHolder::NavigationWidgetPlaceHolder(Core::IMode *mode, QWidget *parent) + :QWidget(parent), m_mode(mode) +{ + setLayout(new QVBoxLayout); + layout()->setMargin(0); + connect(Core::ModeManager::instance(), SIGNAL(currentModeAboutToChange(Core::IMode *)), + this, SLOT(currentModeAboutToChange(Core::IMode *))); +} + +NavigationWidgetPlaceHolder::~NavigationWidgetPlaceHolder() +{ + if (m_current == this) { + NavigationWidget::instance()->setParent(0); + NavigationWidget::instance()->hide(); + } +} + +void NavigationWidgetPlaceHolder::applyStoredSize(int width) +{ + if (width) { + QSplitter *splitter = qobject_cast<QSplitter *>(parentWidget()); + if (splitter) { + // A splitter we need to resize the splitter sizes + QList<int> sizes = splitter->sizes(); + int index = splitter->indexOf(this); + int diff = width - sizes.at(index); + int adjust = sizes.count() > 1? ( diff / (sizes.count() - 1)) : 0; + for(int i=0; i<sizes.count(); ++i) { + if (i != index) + sizes[i] += adjust; + } + sizes[index]= width; + splitter->setSizes(sizes); + } else { + QSize s = size(); + s.setWidth(width); + resize(s); + } + } +} + +// This function does work even though the order in which +// the placeHolder get the signal is undefined. +// It does ensure that after all PlaceHolders got the signal +// m_current points to the current PlaceHolder, or zero if there +// is no PlaceHolder in this mode +// And that the parent of the NavigationWidget gets the correct parent +void NavigationWidgetPlaceHolder::currentModeAboutToChange(Core::IMode *mode) +{ + NavigationWidget *navigationWidget = NavigationWidget::instance(); + + if (m_current == this) { + m_current = 0; + navigationWidget->setParent(0); + navigationWidget->hide(); + navigationWidget->placeHolderChanged(m_current); + } + if (m_mode == mode) { + m_current = this; + + int width = navigationWidget->storedWidth(); + + layout()->addWidget(navigationWidget); + navigationWidget->show(); + + applyStoredSize(width); + setVisible(navigationWidget->isShown()); + navigationWidget->placeHolderChanged(m_current); + } +} + +NavigationWidget *NavigationWidget::m_instance = 0; + +NavigationWidget::NavigationWidget(QAction *toggleSideBarAction) + : m_shown(true) + , m_suppressed(false) + , m_width(0) + , m_toggleSideBarAction(toggleSideBarAction) +{ + connect(ExtensionSystem::PluginManager::instance(), SIGNAL(objectAdded(QObject*)), + this, SLOT(objectAdded(QObject*))); + + setOrientation(Qt::Vertical); + insertSubItem(0); + m_instance = this; +} + +NavigationWidget::~NavigationWidget() +{ + m_instance = 0; +} + +NavigationWidget *NavigationWidget::instance() +{ + return m_instance; +} + +int NavigationWidget::storedWidth() +{ + return m_width; +} + +void NavigationWidget::placeHolderChanged(NavigationWidgetPlaceHolder *holder) +{ + m_toggleSideBarAction->setEnabled(holder); +} + +void NavigationWidget::resizeEvent(QResizeEvent *re) +{ + if (m_width && re->size().width()) + m_width = re->size().width(); + MiniSplitter::resizeEvent(re); +} + +NavigationSubWidget *NavigationWidget::insertSubItem(int position) +{ + NavigationSubWidget *nsw = new NavigationSubWidget(this); + connect(nsw, SIGNAL(split()), this, SLOT(split())); + connect(nsw, SIGNAL(close()), this, SLOT(close())); + insertWidget(position, nsw); + m_subWidgets.insert(position, nsw); + return nsw; +} + +void NavigationWidget::activateSubWidget() +{ + QShortcut *original = qobject_cast<QShortcut *>(sender()); + QString title = m_shortcutMap[original]; + + foreach (NavigationSubWidget *subWidget, m_subWidgets) + if (subWidget->factory()->displayName() == title) { + subWidget->setFocusWidget(); + return; + } + + m_subWidgets.first()->setFactory(title); + m_subWidgets.first()->setFocusWidget(); +} + +void NavigationWidget::split() +{ + NavigationSubWidget *original = qobject_cast<NavigationSubWidget *>(sender()); + int pos = indexOf(original) + 1; + NavigationSubWidget *newnsw = insertSubItem(pos); + newnsw->setFactory(original->factory()); +} + +void NavigationWidget::close() +{ + if (m_subWidgets.count() != 1) { + NavigationSubWidget *subWidget = qobject_cast<NavigationSubWidget *>(sender()); + m_subWidgets.removeOne(subWidget); + subWidget->hide(); + subWidget->deleteLater(); + } else { + setShown(false); + } +} + +void NavigationWidget::saveSettings(QSettings *settings) +{ + QStringList views; + for (int i=0; i<m_subWidgets.count(); ++i) { + views.append(m_subWidgets.at(i)->factory()->displayName()); + } + settings->setValue("Navigation/Views", views); + settings->setValue("Navigation/Visible", isShown()); + settings->setValue("Navigation/VerticalPosition", saveState()); + settings->setValue("Navigation/Width", m_width); +} + +void NavigationWidget::readSettings(QSettings *settings) +{ + if (settings->contains("Navigation/Views")) { + QStringList views = settings->value("Navigation/Views").toStringList(); + for (int i=0; i<views.count()-1; ++i) { + insertSubItem(0); + } + for (int i=0; i<views.count(); ++i) { + const QString &view = views.at(i); + NavigationSubWidget *nsw = m_subWidgets.at(i); + nsw->setFactory(view); + } + } + + if (settings->contains("Navigation/Visible")) { + setShown(settings->value("Navigation/Visible").toBool()); + } else { + setShown(true); + } + + if (settings->contains("Navigation/VerticalPosition")) + restoreState(settings->value("Navigation/VerticalPosition").toByteArray()); + + if (settings->contains("Navigation/Width")) { + m_width = settings->value("Navigation/Width").toInt(); + if (!m_width) + m_width = 240; + } else { + m_width = 240; //pixel + } + // Apply + if (NavigationWidgetPlaceHolder::m_current) { + NavigationWidgetPlaceHolder::m_current->applyStoredSize(m_width); + } +} + +void NavigationWidget::setShown(bool b) +{ + if (m_shown == b) + return; + m_shown = b; + if (NavigationWidgetPlaceHolder::m_current) + NavigationWidgetPlaceHolder::m_current->setVisible(m_shown && !m_suppressed); +} + +bool NavigationWidget::isShown() const +{ + return m_shown; +} + +bool NavigationWidget::isSuppressed() const +{ + return m_suppressed; +} + +void NavigationWidget::setSuppressed(bool b) +{ + if (m_suppressed == b) + return; + m_suppressed = b; + if (NavigationWidgetPlaceHolder::m_current) + NavigationWidgetPlaceHolder::m_current->setVisible(m_shown && !m_suppressed); +} + +void NavigationWidget::objectAdded(QObject * obj) +{ + INavigationWidgetFactory *factory = Aggregation::query<INavigationWidgetFactory>(obj); + if (!factory) + return; + + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + Core::ActionManagerInterface *am = core->actionManager(); + QList<int> navicontext = QList<int>() << core->uniqueIDManager()-> + uniqueIdentifier(Core::Constants::C_NAVIGATION_PANE); + + QString displayName = factory->displayName(); + QShortcut *shortcut = new QShortcut(this); + shortcut->setWhatsThis(tr("Activate %1 Pane").arg(displayName)); + Core::ICommand *cmd = am->registerShortcut(shortcut, + displayName + QLatin1String(".FocusShortcut"), navicontext); + cmd->setDefaultKeySequence(factory->activationSequence()); + connect(shortcut, SIGNAL(activated()), this, SLOT(activateSubWidget())); + + m_shortcutMap.insert(shortcut, displayName); + m_commandMap.insert(displayName, cmd); +} + +//// +// NavigationSubWidget +//// + + +NavigationSubWidget::NavigationSubWidget(NavigationWidget *parentWidget) + : m_parentWidget(parentWidget) +{ + connect(ExtensionSystem::PluginManager::instance(), SIGNAL(objectAdded(QObject*)), + this, SLOT(objectAdded(QObject*))); + connect(ExtensionSystem::PluginManager::instance(), SIGNAL(aboutToRemoveObject(QObject*)), + this, SLOT(aboutToRemoveObject(QObject*))); + + m_navigationComboBox = new NavComboBox(this); + m_navigationWidget = 0; + + m_toolbar = new QToolBar(this); + m_toolbar->setContentsMargins(0, 0, 0, 0); + m_toolbar->addWidget(m_navigationComboBox); + + QToolButton *split = new QToolButton; + split->setProperty("type", QLatin1String("dockbutton")); + split->setIcon(QIcon(":/qworkbench/images/splitbutton_horizontal.png")); + split->setToolTip(tr("Split")); + connect(split, SIGNAL(clicked(bool)), this, SIGNAL(split())); + + QToolButton *close = new QToolButton; + close->setProperty("type", QLatin1String("dockbutton")); + close->setIcon(QIcon(":/qworkbench/images/closebutton.png")); + close->setToolTip(tr("Close")); + + connect(close, SIGNAL(clicked(bool)), this, SIGNAL(close())); + + QWidget *spacerItem = new QWidget(this); + spacerItem->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + m_toolbar->addWidget(spacerItem); + m_splitAction = m_toolbar->addWidget(split); + m_toolbar->addWidget(close); + + QVBoxLayout *lay = new QVBoxLayout(); + lay->setMargin(0); + lay->setSpacing(0); + setLayout(lay); + lay->addWidget(m_toolbar); + + connect(m_navigationComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(setCurrentIndex(int))); + + foreach (INavigationWidgetFactory *factory, ExtensionSystem::PluginManager::instance()->getObjects<INavigationWidgetFactory>()) { + QVariant v; + v.setValue(factory); + m_navigationComboBox->addItem(factory->displayName(), v); + } +} + +NavigationSubWidget::~NavigationSubWidget() +{ +} + +void NavigationSubWidget::setCurrentIndex(int index) +{ + // Remove toolbutton + foreach (QWidget *w, m_additionalToolBarWidgets) { + delete w; + } + + // Remove old Widget + delete m_navigationWidget; + if (index == -1) + return; + + // Get new stuff + INavigationWidgetFactory *factory = m_navigationComboBox->itemData(index).value<INavigationWidgetFactory *>(); + NavigationView n = factory->createWidget(); + m_navigationWidget = n.widget; + layout()->addWidget(m_navigationWidget); + + // Add Toolbutton + m_additionalToolBarWidgets = n.doockToolBarWidgets; + foreach (QToolButton *w, m_additionalToolBarWidgets) { + m_toolbar->insertWidget(m_splitAction, w); + } +} + +void NavigationSubWidget::setFocusWidget() +{ + if (m_navigationWidget) + m_navigationWidget->setFocus(); +} + +void NavigationSubWidget::objectAdded(QObject * obj) +{ + INavigationWidgetFactory *factory = Aggregation::query<INavigationWidgetFactory>(obj); + if (!factory) + return; + + QVariant v; + v.setValue(factory); + m_navigationComboBox->addItem(factory->displayName(), v); + //qDebug()<<"added factory :"<<factory<<m_navigationComboBox->findData(v); +} + +void NavigationSubWidget::aboutToRemoveObject(QObject *obj) +{ + INavigationWidgetFactory *factory = Aggregation::query<INavigationWidgetFactory>(obj); + if (!factory) + return; + QVariant v; + v.setValue(factory); + int index = m_navigationComboBox->findData(v); + if (index == -1) { + qDebug()<<"factory not found not removing" << factory; + return; + } + m_navigationComboBox->removeItem(index); +} + +void NavigationSubWidget::setFactory(INavigationWidgetFactory *factory) +{ + QVariant v; + v.setValue(factory); + int index = m_navigationComboBox->findData(v); + if (index == -1) + return; + m_navigationComboBox->setCurrentIndex(index); +} + +void NavigationSubWidget::setFactory(const QString &name) +{ + for (int i = 0; i < m_navigationComboBox->count(); ++i) + { + INavigationWidgetFactory *factory = + m_navigationComboBox->itemData(i).value<INavigationWidgetFactory *>(); + if (factory->displayName() == name) + m_navigationComboBox->setCurrentIndex(i); + } +} + +INavigationWidgetFactory *NavigationSubWidget::factory() +{ + int index = m_navigationComboBox->currentIndex(); + if (index == -1) + return 0; + return m_navigationComboBox->itemData(index).value<INavigationWidgetFactory *>(); +} + +Core::ICommand *NavigationSubWidget::command(const QString &title) const +{ + const QHash<QString, Core::ICommand*> commandMap = m_parentWidget->commandMap(); + QHash<QString, Core::ICommand*>::const_iterator r = commandMap.find(title); + if (r != commandMap.end()) + return r.value(); + return 0; +} + +NavComboBox::NavComboBox(NavigationSubWidget *navSubWidget) + : m_navSubWidget(navSubWidget) +{ +} + +bool NavComboBox::event(QEvent *e) +{ + if (e->type() == QEvent::ToolTip) { + QString txt = currentText(); + Core::ICommand *cmd = m_navSubWidget->command(txt); + if (cmd) { + txt = tr("Activate %1").arg(txt); + setToolTip(cmd->stringWithAppendedShortcut(txt)); + } else { + setToolTip(txt); + } + } + return QComboBox::event(e); +} diff --git a/src/plugins/coreplugin/navigationwidget.h b/src/plugins/coreplugin/navigationwidget.h new file mode 100644 index 00000000000..d8244f88682 --- /dev/null +++ b/src/plugins/coreplugin/navigationwidget.h @@ -0,0 +1,172 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef NAVIGATIONWIDGET_H +#define NAVIGATIONWIDGET_H + +#include <QtGui/QWidget> +#include <QtGui/QComboBox> +#include <QtGui/QSplitter> +#include <QtGui/QToolBar> +#include <QtGui/QToolButton> +#include <coreplugin/minisplitter.h> + +QT_BEGIN_NAMESPACE +class QSettings; +class QShortcut; +QT_END_NAMESPACE + +namespace Core { + +class INavigationWidgetFactory; +class IMode; +class ICommand; + +namespace Internal { +class NavigationWidget; +} + +class CORE_EXPORT NavigationWidgetPlaceHolder : public QWidget +{ + friend class Core::Internal::NavigationWidget; + Q_OBJECT +public: + NavigationWidgetPlaceHolder(Core::IMode *mode, QWidget *parent = 0); + ~NavigationWidgetPlaceHolder(); + static NavigationWidgetPlaceHolder* current(); + void applyStoredSize(int width); +private slots: + void currentModeAboutToChange(Core::IMode *); +private: + Core::IMode *m_mode; + static NavigationWidgetPlaceHolder* m_current; +}; + +namespace Internal { + +class NavigationSubWidget; + +class NavigationWidget : public MiniSplitter +{ + Q_OBJECT +public: + NavigationWidget(QAction *toggleSideBarAction); + ~NavigationWidget(); + + void saveSettings(QSettings *settings); + void readSettings(QSettings *settings); + + bool isShown() const; + void setShown(bool b); + + bool isSuppressed() const; + void setSuppressed(bool b); + + static NavigationWidget* instance(); + + int storedWidth(); + + // Called from the place holders + void placeHolderChanged(NavigationWidgetPlaceHolder *holder); + + QHash<QString, Core::ICommand*> commandMap() const { return m_commandMap; } + +protected: + void resizeEvent(QResizeEvent *); +private slots: + void objectAdded(QObject*); + void activateSubWidget(); + void split(); + void close(); + +private: + NavigationSubWidget *insertSubItem(int position); + QList<NavigationSubWidget *> m_subWidgets; + QHash<QShortcut *, QString> m_shortcutMap; + QHash<QString, Core::ICommand*> m_commandMap; + bool m_shown; + bool m_suppressed; + int m_width; + static NavigationWidget* m_instance; + QAction *m_toggleSideBarAction; +}; + +class NavigationSubWidget : public QWidget +{ + Q_OBJECT +public: + NavigationSubWidget(NavigationWidget *parentWidget); + virtual ~NavigationSubWidget(); + + INavigationWidgetFactory *factory(); + void setFactory(INavigationWidgetFactory *factory); + void setFactory(const QString &name); + void setFocusWidget(); + + Core::ICommand *command(const QString &title) const; + +signals: + void split(); + void close(); + +private slots: + void objectAdded(QObject*); + void aboutToRemoveObject(QObject*); + void setCurrentIndex(int); + +private: + NavigationWidget *m_parentWidget; + QComboBox *m_navigationComboBox; + QWidget *m_navigationWidget; + QToolBar *m_toolbar; + QAction *m_splitAction; + QList<QToolButton *> m_additionalToolBarWidgets; +}; + +class NavComboBox : public QComboBox +{ + Q_OBJECT + +public: + NavComboBox(NavigationSubWidget *navSubWidget); + +protected: + bool event(QEvent *event); + +private: + NavigationSubWidget *m_navSubWidget; +}; + +} // namespace Internal +} // namespace Core + +#endif // NAVIGATIONWIDGET_H diff --git a/src/plugins/coreplugin/outputpane.cpp b/src/plugins/coreplugin/outputpane.cpp new file mode 100644 index 00000000000..92378d72e96 --- /dev/null +++ b/src/plugins/coreplugin/outputpane.cpp @@ -0,0 +1,531 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "outputpane.h" +#include "coreconstants.h" +#include "ioutputpane.h" +#include "mainwindow.h" +#include "modemanager.h" + +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/actionmanager/iactioncontainer.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/editorgroup.h> + +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QComboBox> +#include <QtGui/QFocusEvent> +#include <QtGui/QHBoxLayout> +#include <QtGui/QLineEdit> +#include <QtGui/QMenu> +#include <QtGui/QPainter> +#include <QtGui/QPushButton> +#include <QtGui/QToolBar> +#include <QtGui/QToolButton> +#include <QtGui/QStackedWidget> +#include <QDebug> + +using namespace Core; +using namespace Core::Internal; + +namespace Core { +namespace Internal { + +class OutputPaneToggleButton : public QPushButton +{ +public: + OutputPaneToggleButton(int number, const QString &text, QWidget *parent = 0); + QSize sizeHint() const; + void paintEvent(QPaintEvent *event); + +private: + QString m_number; + QString m_text; +}; + +} // namespace Internal +} // namespace Core + +OutputPanePlaceHolder *OutputPanePlaceHolder::m_current = 0; + +OutputPanePlaceHolder::OutputPanePlaceHolder(Core::IMode *mode, QWidget *parent) + :QWidget(parent), m_mode(mode), m_closeable(true) +{ + setVisible(false); + setLayout(new QVBoxLayout); + QSizePolicy sp; + sp.setHorizontalPolicy(QSizePolicy::Preferred); + sp.setVerticalPolicy(QSizePolicy::Preferred); + sp.setHorizontalStretch(0); + setSizePolicy(sp); + layout()->setMargin(0); + connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode *)), + this, SLOT(currentModeChanged(Core::IMode *))); +} + +OutputPanePlaceHolder::~OutputPanePlaceHolder() +{ + if (m_current == this) { + OutputPane::instance()->setParent(0); + OutputPane::instance()->hide(); + } +} + +void OutputPanePlaceHolder::setCloseable(bool b) +{ + m_closeable = b; +} + +bool OutputPanePlaceHolder::closeable() +{ + return m_closeable; +} + +void OutputPanePlaceHolder::currentModeChanged(Core::IMode *mode) +{ + if (m_current == this) { + m_current = 0; + OutputPane::instance()->setParent(0); + OutputPane::instance()->hide(); + OutputPane::instance()->updateStatusButtons(false); + } + if (m_mode == mode) { + m_current = this; + layout()->addWidget(OutputPane::instance()); + OutputPane::instance()->show(); + OutputPane::instance()->updateStatusButtons(isVisible()); + OutputPane::instance()->setCloseable(m_closeable); + } +} + +//// +// OutputPane +//// + +OutputPane *OutputPane::m_instance = 0; + +OutputPane *OutputPane::instance() +{ + return m_instance; +} + +void OutputPane::updateStatusButtons(bool visible) +{ + int idx = m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt(); + if (m_buttons.value(idx)) + m_buttons.value(idx)->setChecked(visible); +} + +OutputPane::OutputPane(const QList<int> &context, QWidget *parent) : + QWidget(parent), + m_context(context), + m_widgetComboBox(new QComboBox), + m_clearButton(new QToolButton), + m_closeButton(new QToolButton), + m_closeAction(0), + m_pluginManager(0), + m_core(0), + m_lastIndex(-1), + m_outputWidgetPane(new QStackedWidget), + m_opToolBarWidgets(new QStackedWidget) +{ + setWindowTitle(tr("Output")); + connect(m_widgetComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changePage())); + + m_clearButton->setIcon(QIcon(Constants::ICON_CLEAN_PANE)); + m_clearButton->setToolTip(tr("Clear")); + connect(m_clearButton, SIGNAL(clicked()), this, SLOT(clearPage())); + + m_closeButton->setIcon(QIcon(":/qworkbench/images/closebutton.png")); + m_closeButton->setProperty("type", QLatin1String("dockbutton")); + connect(m_closeButton, SIGNAL(clicked()), this, SLOT(slotHide())); + + QVBoxLayout *mainlayout = new QVBoxLayout; + mainlayout->setSpacing(0); + mainlayout->setMargin(0); + QToolBar *toolBar = new QToolBar; + toolBar->addWidget(m_widgetComboBox); + toolBar->addWidget(m_clearButton); + toolBar->addWidget(m_opToolBarWidgets); + m_closeAction = toolBar->addWidget(m_closeButton); + mainlayout->addWidget(toolBar); + mainlayout->addWidget(m_outputWidgetPane, 10); + setLayout(mainlayout); + + m_buttonsWidget = new QWidget; + m_buttonsWidget->setLayout(new QHBoxLayout); + m_buttonsWidget->layout()->setContentsMargins(5,0,0,0); +#ifdef Q_OS_MAC + m_buttonsWidget->layout()->setSpacing(16); +#else + m_buttonsWidget->layout()->setSpacing(4); +#endif + + m_instance = this; +} + +OutputPane::~OutputPane() +{ + m_instance = 0; +} + +QWidget *OutputPane::buttonsWidget() +{ + return m_buttonsWidget; +} + +void OutputPane::init(ICore *core, ExtensionSystem::PluginManager *pm) +{ + m_pluginManager = pm; + m_core = core; + + ActionManagerInterface *am = m_core->actionManager(); + IActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW); + + // Window->Output Panes + IActionContainer *mpanes = am->createMenu(Constants::M_WINDOW_PANES); + mwindow->addMenu(mpanes, Constants::G_WINDOW_PANES); + mpanes->menu()->setTitle(tr("Output &Panes")); + + QList<IOutputPane*> panes = m_pluginManager->getObjects<IOutputPane>(); + QMultiMap<int, IOutputPane*> sorted; + foreach (IOutputPane* outPane, panes) + sorted.insertMulti(outPane->priorityInStatusBar(), outPane); + + QMultiMap<int, IOutputPane*>::const_iterator it, begin; + begin = sorted.constBegin(); + it = sorted.constEnd(); + int shortcutNumber = 1; + do { + --it; + IOutputPane* outPane = it.value(); + const int idx = m_outputWidgetPane->addWidget(outPane->outputWidget(this)); + + m_pageMap.insert(idx, outPane); + connect(outPane, SIGNAL(showPage(bool)), this, SLOT(showPage(bool))); + connect(outPane, SIGNAL(hidePage()), this, SLOT(slotHide())); + connect(outPane, SIGNAL(togglePage(bool)), this, SLOT(togglePage(bool))); + + QWidget *toolButtonsContainer = new QWidget(m_opToolBarWidgets); + QHBoxLayout *toolButtonsLayout = new QHBoxLayout; + toolButtonsLayout->setMargin(0); + toolButtonsLayout->setSpacing(0); + foreach (QWidget *toolButton, outPane->toolBarWidgets()) + toolButtonsLayout->addWidget(toolButton); + toolButtonsLayout->addStretch(5); + toolButtonsContainer->setLayout(toolButtonsLayout); + + m_opToolBarWidgets->addWidget(toolButtonsContainer); + + QString actionId = QString("QtCreator.Pane.%1").arg(outPane->name().simplified()); + actionId.remove(QLatin1Char(' ')); + QAction *action = new QAction(outPane->name(), this); + + ICommand *cmd = am->registerAction(action, actionId, m_context); + if (outPane->priorityInStatusBar() != -1) { +#ifdef Q_OS_MAC + cmd->setDefaultKeySequence(QKeySequence("Ctrl+" + QString::number(shortcutNumber))); +#else + cmd->setDefaultKeySequence(QKeySequence("Alt+" + QString::number(shortcutNumber))); +#endif + } + mpanes->addAction(cmd); + m_actions.insert(cmd->action(), idx); + + // TODO priority -1 + if (outPane->priorityInStatusBar() != -1) { + QPushButton *button = new OutputPaneToggleButton(shortcutNumber, outPane->name()); + ++shortcutNumber; + m_buttonsWidget->layout()->addWidget(button); + connect(button, SIGNAL(clicked()), this, SLOT(buttonTriggered())); + m_buttons.insert(idx, button); + } + + // Now add the entry to the combobox, since the first item we add sets the currentIndex, thus we need to be set up for that + m_widgetComboBox->addItem(outPane->name(), idx); + + connect(cmd->action(), SIGNAL(triggered()), this, SLOT(shortcutTriggered())); + connect(cmd->action(), SIGNAL(changed()), this, SLOT(updateToolTip())); + } while(it != begin); + + changePage(); +} + +void OutputPane::shortcutTriggered() +{ + QAction *action = qobject_cast<QAction*>(sender()); + if (action && m_actions.contains(action)) { + int idx = m_actions.value(action); + Core::IOutputPane *outputPane = m_pageMap.value(idx); + // Now check the special case, the output window is already visible, + // we are already on that page + // but the outputpane doesn't have focus + // then just give it focus + // else do the same as clicking on the button does + if(OutputPanePlaceHolder::m_current + && OutputPanePlaceHolder::m_current->isVisible() + && m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt() == idx) { + if (!outputPane->hasFocus() && outputPane->canFocus()) + outputPane->setFocus(); + else + slotHide(); + } else { + outputPane->popup(true); + } + } +} + +void OutputPane::buttonTriggered() +{ + QPushButton *button = qobject_cast<QPushButton *>(sender()); + QMap<int, QPushButton *>::const_iterator it, end; + end = m_buttons.constEnd(); + for (it = m_buttons.begin(); it != end; ++it) { + if (it.value() == button) + break; + } + int idx = it.key(); + + if (m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt() == idx && + OutputPanePlaceHolder::m_current && + OutputPanePlaceHolder::m_current->isVisible() && + OutputPanePlaceHolder::m_current->closeable()) { + // we should toggle and the page is already visible and we are actually closeable + slotHide(); + } else { + showPage(idx, true); + } +} + +void OutputPane::updateToolTip() +{ + QAction *action = qobject_cast<QAction*>(sender()); + if (action) { + QPushButton *button = m_buttons.value(m_actions.value(action)); + if (button) + button->setToolTip(action->toolTip()); + } +} + +void OutputPane::slotHide() +{ + if (OutputPanePlaceHolder::m_current) { + OutputPanePlaceHolder::m_current->setVisible(false); + int idx = m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt(); + if (m_buttons.value(idx)) + m_buttons.value(idx)->setChecked(false); + EditorGroup *group = Core::EditorManager::instance()->currentEditorGroup(); + if (group && group->widget()) + group->widget()->setFocus(); + } +} + +int OutputPane::findIndexForPage(IOutputPane *out) +{ + if (!out) + return -1; + + int stackIndex = -1; + QMap<int, IOutputPane*>::const_iterator it = m_pageMap.constBegin(); + while (it != m_pageMap.constEnd()) { + if (it.value() == out) { + stackIndex = it.key(); + break; + } + ++it; + } + if (stackIndex > -1) + return m_widgetComboBox->findData(stackIndex); + else + return -1; +} + +void OutputPane::ensurePageVisible(int idx) +{ + if (m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt() != idx) { + m_widgetComboBox->setCurrentIndex(m_widgetComboBox->findData(idx)); + } else { + changePage(); + } +} + + +void OutputPane::showPage(bool focus) +{ + int idx = findIndexForPage(qobject_cast<IOutputPane*>(sender())); + showPage(idx, focus); +} + +void OutputPane::showPage(int idx, bool focus) +{ + IOutputPane *out = m_pageMap.value(idx); + if (idx > -1) { + if (!OutputPanePlaceHolder::m_current) { + // In this mode we don't have a placeholder + // switch to the output mode and switch the page + ICore *core = m_pluginManager->getObject<ICore>(); + core->modeManager()->activateMode(Constants::MODE_OUTPUT); + ensurePageVisible(idx); + } else { + // else we make that page visible + OutputPanePlaceHolder::m_current->setVisible(true); + ensurePageVisible(idx); + if (focus && out->canFocus()) + out->setFocus(); + } + } +} + +void OutputPane::togglePage(bool focus) +{ + int idx = findIndexForPage(qobject_cast<IOutputPane*>(sender())); + if(OutputPanePlaceHolder::m_current + && OutputPanePlaceHolder::m_current->isVisible() + && m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt() == idx) { + slotHide(); + } else { + showPage(idx, focus); + } + +} + +void OutputPane::setCloseable(bool b) +{ + m_closeAction->setVisible(b); +} + +bool OutputPane::closeable() +{ + return m_closeButton->isVisibleTo(m_closeButton->parentWidget()); +} + +void OutputPane::focusInEvent(QFocusEvent *e) +{ + if (m_outputWidgetPane->currentWidget()) + m_outputWidgetPane->currentWidget()->setFocus(e->reason()); +} + +void OutputPane::changePage() +{ + if (m_outputWidgetPane->count() <= 0) + return; + + if (!m_pageMap.contains(m_lastIndex)) { + int idx = m_outputWidgetPane->currentIndex(); + m_pageMap.value(idx)->visibilityChanged(true); + if (m_buttons.value(idx)) { + if (OutputPanePlaceHolder::m_current) + m_buttons.value(idx)->setChecked(OutputPanePlaceHolder::m_current->isVisible()); + else + m_buttons.value(idx)->setChecked(false); + } + m_lastIndex = idx; + return; + } + + int idx = m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt(); + m_outputWidgetPane->setCurrentIndex(idx); + m_opToolBarWidgets->setCurrentIndex(idx); + m_pageMap.value(idx)->visibilityChanged(true); + m_pageMap.value(m_lastIndex)->visibilityChanged(false); + + if (m_buttons.value(m_lastIndex)) + m_buttons.value(m_lastIndex)->setChecked(false); + + if (m_buttons.value(idx)) { + if (OutputPanePlaceHolder::m_current) + m_buttons.value(idx)->setChecked(OutputPanePlaceHolder::m_current->isVisible()); + else + m_buttons.value(idx)->setChecked(false); + } + + m_lastIndex = idx; +} + +void OutputPane::clearPage() +{ + if (m_pageMap.contains(m_outputWidgetPane->currentIndex())) + m_pageMap.value(m_outputWidgetPane->currentIndex())->clearContents(); +} + + +OutputPaneToggleButton::OutputPaneToggleButton(int number, const QString &text, QWidget *parent) + : QPushButton(parent) + , m_number(QString::number(number)) + , m_text(text) +{ + setFocusPolicy(Qt::NoFocus); + setCheckable(true); + setStyleSheet( + "QPushButton { border-image: url(:/qworkbench/images/panel_button.png) 2 2 2 19 repeat;" + " border-width: 2px 2px 2px 19px; padding-left: -17; padding-right: 4 } " + "QPushButton:checked { border-image: url(:/qworkbench/images/panel_button_checked.png) 2 2 2 19 repeat } " +#ifndef Q_WS_MAC // Mac UI's dont usually do hover + "QPushButton:checked:hover { border-image: url(:/qworkbench/images/panel_button_checked_hover.png) 2 2 2 19 repeat } " + "QPushButton:pressed:hover { border-image: url(:/qworkbench/images/panel_button_pressed.png) 2 2 2 19 repeat } " + "QPushButton:hover { border-image: url(:/qworkbench/images/panel_button_hover.png) 2 2 2 19 repeat } " +#endif + ); +} + +QSize OutputPaneToggleButton::sizeHint() const +{ + ensurePolished(); + + QSize s = fontMetrics().size(Qt::TextSingleLine, m_text); + + // Expand to account for border image set by stylesheet above + s.rwidth() += 19 + 5 + 2; + s.rheight() += 2 + 2; + + return s.expandedTo(QApplication::globalStrut()); +} + +void OutputPaneToggleButton::paintEvent(QPaintEvent *event) +{ + // For drawing the style sheet stuff + QPushButton::paintEvent(event); + + const QFontMetrics fm = fontMetrics(); + const int baseLine = (height() - fm.height()) / 2 + fm.ascent(); + const int numberWidth = fm.width(m_number); + + QPainter p(this); + p.setFont(font()); + p.setPen(Qt::white); + p.drawText((20 - numberWidth) / 2, baseLine, m_number); + if (!isChecked()) + p.setPen(Qt::black); + int leftPart = 22; + p.drawText(leftPart, baseLine, fm.elidedText(m_text, Qt::ElideRight, width() - leftPart - 1)); +} diff --git a/src/plugins/coreplugin/outputpane.h b/src/plugins/coreplugin/outputpane.h new file mode 100644 index 00000000000..fcc54745b00 --- /dev/null +++ b/src/plugins/coreplugin/outputpane.h @@ -0,0 +1,142 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef OUTPUTPANE_H +#define OUTPUTPANE_H + +#include "core_global.h" + +#include <QtCore/QMap> +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE +class QAction; +class QComboBox; +class QToolButton; +class QStackedWidget; +class QPushButton; +QT_END_NAMESPACE + +namespace ExtensionSystem { class PluginManager; } + +namespace Core { + +class ICore; +class IMode; +class IOutputPane; + +namespace Internal { +class OutputPane; +} + + +class CORE_EXPORT OutputPanePlaceHolder : public QWidget +{ + friend class Core::Internal::OutputPane; // needs to set m_visible and thus access m_current + Q_OBJECT +public: + OutputPanePlaceHolder(Core::IMode *mode, QWidget *parent = 0); + ~OutputPanePlaceHolder(); + void setCloseable(bool b); + bool closeable(); + static OutputPanePlaceHolder *getCurrent() { return m_current; } + +private slots: + void currentModeChanged(Core::IMode *); +private: + Core::IMode *m_mode; + bool m_closeable; + static OutputPanePlaceHolder* m_current; +}; + +namespace Internal { + +class OutputPane + : public QWidget +{ + Q_OBJECT + +public: + OutputPane(const QList<int> &context, QWidget *parent = 0); + ~OutputPane(); + void init(Core::ICore *core, ExtensionSystem::PluginManager *pm); + static OutputPane *instance(); + const QList<int> &context() const { return m_context; } + void setCloseable(bool b); + bool closeable(); + QWidget *buttonsWidget(); + void updateStatusButtons(bool visible); + +public slots: + void slotHide(); + void shortcutTriggered(); + +protected: + void focusInEvent(QFocusEvent *e); + +private slots:; + void changePage(); + void showPage(bool focus); + void togglePage(bool focus); + void clearPage(); + void updateToolTip(); + void buttonTriggered(); + +private: + void showPage(int idx, bool focus); + void ensurePageVisible(int idx); + int findIndexForPage(IOutputPane *out); + const QList<int> m_context; + QComboBox *m_widgetComboBox; + QToolButton *m_clearButton; + QToolButton *m_closeButton; + QAction *m_closeAction; + + ExtensionSystem::PluginManager *m_pluginManager; + Core::ICore *m_core; + + QMap<int, Core::IOutputPane*> m_pageMap; + int m_lastIndex; + + QStackedWidget *m_outputWidgetPane; + QStackedWidget *m_opToolBarWidgets; + QWidget *m_buttonsWidget; + QMap<int, QPushButton *> m_buttons; + QMap<QAction *, int> m_actions; + + static OutputPane *m_instance; +}; + +} // namespace Internal +} // namespace Core + +#endif // OUTPUTPANE_H diff --git a/src/plugins/coreplugin/plugindialog.cpp b/src/plugins/coreplugin/plugindialog.cpp new file mode 100644 index 00000000000..dcf8c9b8268 --- /dev/null +++ b/src/plugins/coreplugin/plugindialog.cpp @@ -0,0 +1,141 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "plugindialog.h" +#include <extensionsystem/pluginmanager.h> +#include <extensionsystem/pluginview.h> +#include <extensionsystem/plugindetailsview.h> +#include <extensionsystem/pluginerrorview.h> +#include <extensionsystem/pluginspec.h> + +#include <QtGui/QVBoxLayout> +#include <QtGui/QHBoxLayout> +#include <QtGui/QDialog> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QPushButton> +#include <QtDebug> + +using namespace Core::Internal; + +PluginDialog::PluginDialog(ExtensionSystem::PluginManager *manager, QWidget *parent) + : QDialog(parent) + , m_view(new ExtensionSystem::PluginView(manager, this)) +{ + QVBoxLayout *vl = new QVBoxLayout(this); + vl->addWidget(m_view); + + m_detailsButton = new QPushButton(tr("Details"), this); + m_errorDetailsButton = new QPushButton(tr("Error Details"), this); + m_closeButton = new QPushButton(tr("Close"), this); + m_detailsButton->setEnabled(false); + m_errorDetailsButton->setEnabled(false); + m_closeButton->setEnabled(true); + m_closeButton->setDefault(true); + + QHBoxLayout *hl = new QHBoxLayout; + hl->addWidget(m_detailsButton); + hl->addWidget(m_errorDetailsButton); + hl->addStretch(5); + hl->addWidget(m_closeButton); + + vl->addLayout(hl); + + resize(650, 400); + setWindowTitle(tr("Installed Plugins")); + + connect(m_view, SIGNAL(currentPluginChanged(ExtensionSystem::PluginSpec*)), + this, SLOT(updateButtons())); + connect(m_view, SIGNAL(pluginActivated(ExtensionSystem::PluginSpec*)), + this, SLOT(openDetails(ExtensionSystem::PluginSpec*))); + connect(m_detailsButton, SIGNAL(clicked()), this, SLOT(openDetails())); + connect(m_errorDetailsButton, SIGNAL(clicked()), this, SLOT(openErrorDetails())); + connect(m_closeButton, SIGNAL(clicked()), this, SLOT(accept())); + updateButtons(); +} + +void PluginDialog::updateButtons() +{ + ExtensionSystem::PluginSpec *selectedSpec = m_view->currentPlugin(); + if (selectedSpec) { + m_detailsButton->setEnabled(true); + m_errorDetailsButton->setEnabled(selectedSpec->hasError()); + } else { + m_detailsButton->setEnabled(false); + m_errorDetailsButton->setEnabled(false); + } +} + + +void PluginDialog::openDetails() +{ + openDetails(m_view->currentPlugin()); +} + +void PluginDialog::openDetails(ExtensionSystem::PluginSpec *spec) +{ + if (!spec) + return; + QDialog dialog(this); + dialog.setWindowTitle(tr("Plugin Details of %1").arg(spec->name())); + QVBoxLayout *layout = new QVBoxLayout; + dialog.setLayout(layout); + ExtensionSystem::PluginDetailsView *details = new ExtensionSystem::PluginDetailsView(&dialog); + layout->addWidget(details); + details->update(spec); + QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, &dialog); + layout->addWidget(buttons); + connect(buttons, SIGNAL(accepted()), &dialog, SLOT(accept())); + connect(buttons, SIGNAL(rejected()), &dialog, SLOT(reject())); + dialog.resize(400, 500); + dialog.exec(); +} + +void PluginDialog::openErrorDetails() +{ + ExtensionSystem::PluginSpec *spec = m_view->currentPlugin(); + if (!spec) + return; + QDialog dialog(this); + dialog.setWindowTitle(tr("Plugin Errors of %1").arg(spec->name())); + QVBoxLayout *layout = new QVBoxLayout; + dialog.setLayout(layout); + ExtensionSystem::PluginErrorView *errors = new ExtensionSystem::PluginErrorView(&dialog); + layout->addWidget(errors); + errors->update(spec); + QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, &dialog); + layout->addWidget(buttons); + connect(buttons, SIGNAL(accepted()), &dialog, SLOT(accept())); + connect(buttons, SIGNAL(rejected()), &dialog, SLOT(reject())); + dialog.resize(500, 300); + dialog.exec(); +} + diff --git a/src/plugins/coreplugin/plugindialog.h b/src/plugins/coreplugin/plugindialog.h new file mode 100644 index 00000000000..d3c0d312a6b --- /dev/null +++ b/src/plugins/coreplugin/plugindialog.h @@ -0,0 +1,75 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGINDIALOG_H +#define PLUGINDIALOG_H + +#include <QtGui/QDialog> + +QT_BEGIN_NAMESPACE +class QPushButton; +QT_END_NAMESPACE + +namespace ExtensionSystem { +class PluginManager; +class PluginSpec; +class PluginView; +} + +namespace Core { +namespace Internal { + +class PluginDialog : public QDialog +{ + Q_OBJECT + +public: + PluginDialog(ExtensionSystem::PluginManager *manager, QWidget *parent); + +private slots: + void updateButtons(); + void openDetails(); + void openDetails(ExtensionSystem::PluginSpec *spec); + void openErrorDetails(); + +private: + ExtensionSystem::PluginView *m_view; + + QPushButton *m_detailsButton; + QPushButton *m_errorDetailsButton; + QPushButton *m_closeButton; +}; + +} // namespace Internal +} // namespace Core + +#endif diff --git a/src/plugins/coreplugin/progressmanager/futureprogress.cpp b/src/plugins/coreplugin/progressmanager/futureprogress.cpp new file mode 100644 index 00000000000..2aeb817bc2f --- /dev/null +++ b/src/plugins/coreplugin/progressmanager/futureprogress.cpp @@ -0,0 +1,167 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "futureprogress.h" +#include "progresspie.h" + +#include <QtGui/QColor> +#include <QtGui/QVBoxLayout> +#include <QtGui/QMenu> + +using namespace Core; + +FutureProgress::FutureProgress(QWidget *parent) + : QWidget(parent), + m_progress(new ProgressBar), + m_widget(0), + m_widgetLayout(new QHBoxLayout) +{ + QVBoxLayout *layout = new QVBoxLayout; + setLayout(layout); + layout->addWidget(m_progress); + layout->setMargin(0); + layout->setSpacing(0); + layout->addLayout(m_widgetLayout); + m_widgetLayout->setContentsMargins(7, 0, 7, 0); + m_widgetLayout->setSpacing(0); + + connect(&m_watcher, SIGNAL(started()), this, SLOT(setStarted())); + connect(&m_watcher, SIGNAL(finished()), this, SLOT(setFinished())); + connect(&m_watcher, SIGNAL(progressRangeChanged(int,int)), this, SLOT(setProgressRange(int,int))); + connect(&m_watcher, SIGNAL(progressValueChanged(int)), this, SLOT(setProgressValue(int))); + connect(&m_watcher, SIGNAL(progressTextChanged(const QString&)), + this, SLOT(setProgressText(const QString&))); + connect(m_progress, SIGNAL(clicked()), this, SLOT(cancel())); +} + +FutureProgress::~FutureProgress() +{ + if (m_widget) + delete m_widget; +} + +void FutureProgress::setWidget(QWidget *widget) +{ + if (m_widget) + delete m_widget; + QSizePolicy sp = widget->sizePolicy(); + sp.setHorizontalPolicy(QSizePolicy::Ignored); + widget->setSizePolicy(sp); + m_widget = widget; + if (m_widget) + m_widgetLayout->addWidget(m_widget); +} + +void FutureProgress::setTitle(const QString &title) +{ + m_progress->setTitle(title); +} + +QString FutureProgress::title() const +{ + return m_progress->title(); +} + +void FutureProgress::cancel() +{ + m_watcher.future().cancel(); +} + +void FutureProgress::setStarted() +{ + m_progress->reset(); + m_progress->setError(false); + m_progress->setRange(m_watcher.progressMinimum(), m_watcher.progressMaximum()); + m_progress->setValue(m_watcher.progressValue()); +// if (m_watcher.progressMinimum() == 0 && m_watcher.progressMaximum() == 0) +// m_progress->startAnimation(); +} + +void FutureProgress::setFinished() +{ +// m_progress->stopAnimation(); + setToolTip(m_watcher.future().progressText()); + if (m_watcher.future().isCanceled()) { + m_progress->setError(true); +// m_progress->execGlowOut(true); + } else { + m_progress->setError(false); +// m_progress->execGlowOut(false); + } +// m_progress->showToolTip(); + emit finished(); +} + +void FutureProgress::setProgressRange(int min, int max) +{ + m_progress->setRange(min, max); + if (min != 0 || max != 0) { +// m_progress->setUsingAnimation(false); + } else { +// m_progress->setUsingAnimation(true); + if (m_watcher.future().isRunning()) { + //m_progress->startAnimation(); + } + } +} + +void FutureProgress::setProgressValue(int val) +{ + m_progress->setValue(val); +} + +void FutureProgress::setProgressText(const QString &text) +{ + setToolTip(text); +} + +void FutureProgress::setFuture(const QFuture<void> &future) +{ + m_watcher.setFuture(future); +} + +QFuture<void> FutureProgress::future() const +{ + return m_watcher.future(); +} + +void FutureProgress::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + emit clicked(); + QWidget::mousePressEvent(event); +} + +bool FutureProgress::hasError() const +{ + return m_progress->hasError(); +} diff --git a/src/plugins/coreplugin/progressmanager/futureprogress.h b/src/plugins/coreplugin/progressmanager/futureprogress.h new file mode 100644 index 00000000000..4f46a4a1fb2 --- /dev/null +++ b/src/plugins/coreplugin/progressmanager/futureprogress.h @@ -0,0 +1,94 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FUTUREPROGRESS_H +#define FUTUREPROGRESS_H + +#include <coreplugin/core_global.h> + +#include <QtCore/QString> +#include <QtCore/QFuture> +#include <QtCore/QFutureWatcher> +#include <QtGui/QWidget> +#include <QtGui/QIcon> +#include <QtGui/QAction> +#include <QtGui/QProgressBar> +#include <QtGui/QMouseEvent> +#include <QtGui/QHBoxLayout> + +class ProgressBar; + +namespace Core { + +class CORE_EXPORT FutureProgress : public QWidget +{ + Q_OBJECT + +public: + FutureProgress(QWidget *parent = 0); + ~FutureProgress(); + + void setFuture(const QFuture<void> &future); + QFuture<void> future() const; + + void setTitle(const QString &title); + QString title() const; + + bool hasError() const; + + void setWidget(QWidget *widget); + QWidget *widget() const { return m_widget; } + +signals: + void clicked(); + void finished(); + +protected: + void mousePressEvent(QMouseEvent *event); + +private slots: + void cancel(); + void setStarted(); + void setFinished(); + void setProgressRange(int min, int max); + void setProgressValue(int val); + void setProgressText(const QString &text); + +private: + QFutureWatcher<void> m_watcher; + ProgressBar *m_progress; + QWidget *m_widget; + QHBoxLayout *m_widgetLayout; +}; + +} // namespace Core +#endif // FUTUREPROGRESS_H diff --git a/src/plugins/coreplugin/progressmanager/progressmanager.cpp b/src/plugins/coreplugin/progressmanager/progressmanager.cpp new file mode 100644 index 00000000000..6e4e15644e9 --- /dev/null +++ b/src/plugins/coreplugin/progressmanager/progressmanager.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "progressmanager.h" +#include "progressview.h" +#include "coreimpl.h" +#include "baseview.h" + +#include "coreconstants.h" +#include "uniqueidmanager.h" +#include "viewmanagerinterface.h" + +using namespace Core; +using namespace Core::Internal; + +ProgressManager::ProgressManager(QObject *parent) : + ProgressManagerInterface(parent) +{ + m_progressView = new ProgressView; + ICore *core = CoreImpl::instance(); + connect(core, SIGNAL(coreAboutToClose()), this, SLOT(cancelAllRunningTasks())); +} + +ProgressManager::~ProgressManager() +{ +} + +void ProgressManager::init() +{ + +} + +void ProgressManager::cancelTasks(const QString &type) +{ + QMap<QFutureWatcher<void> *, QString>::iterator task = m_runningTasks.begin(); + while (task != m_runningTasks.end()) { + if (task.value() != type) { + ++task; + continue; + } + disconnect(task.key(), SIGNAL(finished()), this, SLOT(taskFinished())); + task.key()->cancel(); + delete task.key(); + task = m_runningTasks.erase(task); + } +} + +void ProgressManager::cancelAllRunningTasks() +{ + QMap<QFutureWatcher<void> *, QString>::const_iterator task = m_runningTasks.constBegin(); + while (task != m_runningTasks.constEnd()) { + disconnect(task.key(), SIGNAL(finished()), this, SLOT(taskFinished())); + task.key()->cancel(); + delete task.key(); + ++task; + } + m_runningTasks.clear(); +} + +FutureProgress *ProgressManager::addTask(const QFuture<void> &future, const QString &title, const QString &type, PersistentType persistency) +{ + QFutureWatcher<void> *watcher = new QFutureWatcher<void>(); + m_runningTasks.insert(watcher, type); + connect(watcher, SIGNAL(finished()), this, SLOT(taskFinished())); + watcher->setFuture(future); + return m_progressView->addTask(future, title, type, persistency); +} + +QWidget *ProgressManager::progressView() +{ + return m_progressView; +} + +void ProgressManager::taskFinished() +{ + QObject *taskObject = sender(); + Q_ASSERT(taskObject); + QFutureWatcher<void> *task = static_cast<QFutureWatcher<void> *>(taskObject); + m_runningTasks.remove(task); + delete task; +} diff --git a/src/plugins/coreplugin/progressmanager/progressmanager.h b/src/plugins/coreplugin/progressmanager/progressmanager.h new file mode 100644 index 00000000000..0c40dcc0faa --- /dev/null +++ b/src/plugins/coreplugin/progressmanager/progressmanager.h @@ -0,0 +1,74 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROGRESSMANAGER_H +#define PROGRESSMANAGER_H + +#include "progressmanagerinterface.h" + +#include <QtCore/QPointer> +#include <QtCore/QList> +#include <QtCore/QFutureWatcher> + +namespace Core { + +namespace Internal { + +class ProgressView; + +class ProgressManager : public Core::ProgressManagerInterface +{ + Q_OBJECT +public: + ProgressManager(QObject *parent = 0); + ~ProgressManager(); + void init(); + + FutureProgress *addTask(const QFuture<void> &future, const QString &title, const QString &type, PersistentType persistency); + + QWidget *progressView(); + +public slots: + void cancelTasks(const QString &type); + +private slots: + void taskFinished(); + void cancelAllRunningTasks(); +private: + QPointer<ProgressView> m_progressView; + QMap<QFutureWatcher<void> *, QString> m_runningTasks; +}; + +} // namespace Internal +} // namespace Core + +#endif //PROGRESSMANAGER_H diff --git a/src/plugins/coreplugin/progressmanager/progressmanagerinterface.h b/src/plugins/coreplugin/progressmanager/progressmanagerinterface.h new file mode 100644 index 00000000000..6a89293467f --- /dev/null +++ b/src/plugins/coreplugin/progressmanager/progressmanagerinterface.h @@ -0,0 +1,62 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROGRESSMANAGERINTERFACE_H +#define PROGRESSMANAGERINTERFACE_H + +#include <coreplugin/core_global.h> +#include <coreplugin/progressmanager/futureprogress.h> + +#include <QtCore/QObject> +#include <QtCore/QFuture> +#include <QtGui/QIcon> + +namespace Core { + +class CORE_EXPORT ProgressManagerInterface : public QObject +{ + Q_OBJECT +public: + enum PersistentType { CloseOnSuccess, KeepOnFinish }; + + ProgressManagerInterface(QObject *parent = 0) : QObject(parent) {} + virtual ~ProgressManagerInterface() {} + + virtual FutureProgress *addTask(const QFuture<void> &future, const QString &title, const QString &type, PersistentType persistency = KeepOnFinish) = 0; + +public slots: + virtual void cancelTasks(const QString &type) = 0; +}; + +} //namespace + +#endif //PROGRESSMANAGERINTERFACE_H diff --git a/src/plugins/coreplugin/progressmanager/progresspie.cpp b/src/plugins/coreplugin/progressmanager/progresspie.cpp new file mode 100644 index 00000000000..7a237fe5d9f --- /dev/null +++ b/src/plugins/coreplugin/progressmanager/progresspie.cpp @@ -0,0 +1,166 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "progresspie.h" +#include "stylehelper.h" + +#include <QtGui/QPainter> +#include <QtGui/QFont> +#include <QtGui/QBrush> +#include <QtGui/QColor> +#include <QtDebug> + +ProgressBar::ProgressBar(QWidget *parent) + : QProgressBar(parent), m_error(false) +{ + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); +} + +ProgressBar::~ProgressBar() +{ + +} + +QString ProgressBar::title() const +{ + return m_title; +} + +bool ProgressBar::hasError() const +{ + return m_error; +} + + +void ProgressBar::setTitle(const QString &title) +{ + m_title = title; +} + +void ProgressBar::setError(bool on) +{ + m_error = on; + update(); +} + +QSize ProgressBar::sizeHint() const +{ + QSize s; + s.setWidth(50); + s.setHeight(fontMetrics().height() * 2); + return s; +} + +namespace { const int INDENT = 7; } + +void ProgressBar::mousePressEvent(QMouseEvent *event) +{ + if (event->modifiers() == Qt::NoModifier + && event->x() >= size().width()-INDENT-m_progressHeight) { + event->accept(); + emit clicked(); + return; + } + QProgressBar::mousePressEvent(event); +} + +void ProgressBar::paintEvent(QPaintEvent *) +{ + // TODO move font into stylehelper + // TODO use stylehelper white + + double range = maximum() - minimum(); + double percent = 0.50; + if (range != 0) + percent = (value() - minimum()) / range; + if(percent > 1) + percent = 1; + else if(percent < 0) + percent = 0; + + QPainter p(this); + QFont boldFont(p.font()); + boldFont.setPointSizeF(StyleHelper::sidebarFontSize()); + boldFont.setBold(true); + p.setFont(boldFont); + QFontMetrics fm(boldFont); + + // Draw separator + int h = fm.height(); + p.setPen(QColor(0, 0, 0, 70)); + p.drawLine(0,0, size().width(), 0); + + p.setPen(QColor(255, 255, 255, 70)); + p.drawLine(0, 1, size().width(), 1); + + p.setPen(StyleHelper::panelTextColor()); + p.drawText(QPoint(7, h+1), m_title); + + m_progressHeight = h-4; + m_progressHeight += ((m_progressHeight % 2) + 1) % 2; // make odd + // draw outer rect + QRect rect(INDENT, h+6, size().width()-2*INDENT-m_progressHeight+1, m_progressHeight-1); + p.setPen(StyleHelper::panelTextColor()); + p.drawRect(rect); + + // draw inner rect + QColor c = StyleHelper::panelTextColor(); + c.setAlpha(180); + p.setPen(Qt::NoPen); + p.setBrush(c); + QRect inner = rect.adjusted(2, 2, -1, -1); + inner.adjust(0, 0, qRound((percent - 1) * inner.width()), 0); + if (m_error) { + // TODO this is not fancy enough + QColor red(255, 0, 0, 180); + p.setBrush(red); + // avoid too small red bar + if (inner.width() < 10) + inner.adjust(0, 0, 10 - inner.width(), 0); + } else if (value() == maximum()) { + QColor green(140, 255, 140, 180); + p.setBrush(green); + } + p.drawRect(inner); + + if (value() < maximum() && !m_error) { + // draw cancel thingy + // TODO this is quite ugly at the moment + p.setPen(StyleHelper::panelTextColor()); + p.setBrush(QBrush(Qt::NoBrush)); + QRect cancelRect(size().width()-INDENT-m_progressHeight+3, h+6+1, m_progressHeight-3, m_progressHeight-3); + p.drawRect(cancelRect); + p.setPen(c); + p.drawLine(cancelRect.center()+QPoint(-1,-1), cancelRect.center()+QPoint(+3,+3)); + p.drawLine(cancelRect.center()+QPoint(+3,-1), cancelRect.center()+QPoint(-1,+3)); + } +} diff --git a/src/plugins/coreplugin/progressmanager/progresspie.h b/src/plugins/coreplugin/progressmanager/progresspie.h new file mode 100644 index 00000000000..dfeb33e5581 --- /dev/null +++ b/src/plugins/coreplugin/progressmanager/progresspie.h @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROGRESSPIE_H +#define PROGRESSPIE_H + +#include <QtCore/QString> +#include <QtGui/QWidget> +#include <QtGui/QProgressBar> +#include <QtGui/QMouseEvent> + +class ProgressBar : public QProgressBar +{ + Q_OBJECT +public: + ProgressBar(QWidget *parent = 0); + ~ProgressBar(); + + QString title() const; + void setTitle(const QString &title); + // TODO rename setError + void setError(bool on); + bool hasError() const; + QSize sizeHint() const; + void paintEvent(QPaintEvent *); + +signals: + void clicked(); + +protected: + void mousePressEvent(QMouseEvent *event); + +private: + QString m_text; + QString m_title; + bool m_error; + int m_progressHeight; +}; + +#endif // PROGRESSPIE_H diff --git a/src/plugins/coreplugin/progressmanager/progressview.cpp b/src/plugins/coreplugin/progressmanager/progressview.cpp new file mode 100644 index 00000000000..bb0008056bd --- /dev/null +++ b/src/plugins/coreplugin/progressmanager/progressview.cpp @@ -0,0 +1,142 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "progressview.h" +#include "futureprogress.h" + +#include <QtGui/QHBoxLayout> + +using namespace Core; +using namespace Core::Internal; + +ProgressView::ProgressView(QWidget *parent) +: QWidget(parent) +{ + m_layout = new QVBoxLayout; + setLayout(m_layout); + m_layout->setMargin(0); + m_layout->setSpacing(0); + setWindowTitle(tr("Processes")); +} + +ProgressView::~ProgressView() +{ + qDeleteAll(m_taskList); + m_taskList.clear(); + m_type.clear(); + m_keep.clear(); +} + +FutureProgress *ProgressView::addTask(const QFuture<void> &future, + const QString &title, + const QString &type, + ProgressManagerInterface::PersistentType persistency) +{ + removeOldTasks(type); + if (m_taskList.size() == 3) + removeOneOldTask(); + FutureProgress *progress = new FutureProgress(this); + progress->setTitle(title); + progress->setFuture(future); + m_layout->insertWidget(0, progress); + m_taskList.append(progress); + m_type.insert(progress, type); + m_keep.insert(progress, (persistency == ProgressManagerInterface::KeepOnFinish)); + connect(progress, SIGNAL(finished()), this, SLOT(slotFinished())); + return progress; +} + +void ProgressView::removeOldTasks(const QString &type, bool keepOne) +{ + bool firstFound = !keepOne; // start with false if we want to keep one + QList<FutureProgress *>::iterator i = m_taskList.end(); + while (i != m_taskList.begin()) { + --i; + if (m_type.value(*i) == type) { + if (firstFound && (*i)->future().isFinished()) { + deleteTask(*i); + i = m_taskList.erase(i); + } + firstFound = true; + } + } +} + +void ProgressView::deleteTask(FutureProgress *progress) +{ + m_type.remove(progress); + m_keep.remove(progress); + layout()->removeWidget(progress); + progress->deleteLater(); +} + +void ProgressView::removeOneOldTask() +{ + if (m_taskList.isEmpty()) + return; + // look for oldest ended process + for (QList<FutureProgress *>::iterator i = m_taskList.begin(); i != m_taskList.end(); ++i) { + if ((*i)->future().isFinished()) { + deleteTask(*i); + i = m_taskList.erase(i); + return; + } + } + // no ended process, look for a task type with multiple running tasks and remove the oldest one + for (QList<FutureProgress *>::iterator i = m_taskList.begin(); i != m_taskList.end(); ++i) { + QString type = m_type.value(*i); + if (m_type.keys(type).size() > 1) { // don't care for optimizations it's only a handful of entries + deleteTask(*i); + i = m_taskList.erase(i); + return; + } + } + + // no ended process, no type with multiple processes, just remove the oldest task + FutureProgress *task = m_taskList.takeFirst(); + deleteTask(task); +} + +void ProgressView::removeTask(FutureProgress *task) +{ + m_taskList.removeAll(task); + deleteTask(task); +} + +void ProgressView::slotFinished() +{ + FutureProgress *progress = qobject_cast<FutureProgress *>(sender()); + Q_ASSERT(progress); + if (m_keep.contains(progress) && !m_keep.value(progress) && !progress->hasError()) + removeTask(progress); + removeOldTasks(m_type.value(progress), true); +} diff --git a/src/plugins/coreplugin/progressmanager/progressview.h b/src/plugins/coreplugin/progressmanager/progressview.h new file mode 100644 index 00000000000..7e832f92f24 --- /dev/null +++ b/src/plugins/coreplugin/progressmanager/progressview.h @@ -0,0 +1,81 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROGRESSVIEW_H +#define PROGRESSVIEW_H + +#include "progressmanagerinterface.h" + +#include <QtCore/QFuture> +#include <QtGui/QWidget> +#include <QtGui/QIcon> +#include <QtGui/QVBoxLayout> + +namespace Core { + +class FutureProgress; + +namespace Internal { + +class ProgressView : public QWidget +{ + Q_OBJECT + +public: + ProgressView(QWidget *parent = 0); + ~ProgressView(); + + /** The returned FutureProgress instance is guaranteed to live till next main loop event processing (deleteLater). */ + FutureProgress *addTask(const QFuture<void> &future, + const QString &title, + const QString &type, + ProgressManagerInterface::PersistentType persistency); + +private slots: + void slotFinished(); + +private: + void removeOldTasks(const QString &type, bool keepOne = false); + void removeOneOldTask(); + void removeTask(FutureProgress *task); + void deleteTask(FutureProgress *task); + + QVBoxLayout *m_layout; + QList<FutureProgress *> m_taskList; + QHash<FutureProgress *, QString> m_type; + QHash<FutureProgress *, bool> m_keep; +}; + +} // namespace Internal +} // namespace Core + +#endif //PROGRESSVIEW_H diff --git a/src/plugins/coreplugin/rightpane.cpp b/src/plugins/coreplugin/rightpane.cpp new file mode 100644 index 00000000000..90db8df7c23 --- /dev/null +++ b/src/plugins/coreplugin/rightpane.cpp @@ -0,0 +1,242 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "rightpane.h" + +#include <QtGui/QVBoxLayout> +#include <QtGui/QSplitter> +#include <QtGui/QResizeEvent> +#include <QtGui/QTextEdit> +#include <coreplugin/modemanager.h> +#include <extensionsystem/pluginmanager.h> + +using namespace Core; +using namespace Core::Internal; + +RightPanePlaceHolder *RightPanePlaceHolder::m_current = 0; + +RightPanePlaceHolder* RightPanePlaceHolder::current() +{ + return m_current; +} + +RightPanePlaceHolder::RightPanePlaceHolder(Core::IMode *mode, QWidget *parent) + :QWidget(parent), m_mode(mode) +{ + setLayout(new QVBoxLayout); + layout()->setMargin(0); + connect(Core::ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode *)), + this, SLOT(currentModeChanged(Core::IMode *))); +} + +RightPanePlaceHolder::~RightPanePlaceHolder() +{ + if (m_current == this) { + RightPaneWidget::instance()->setParent(0); + RightPaneWidget::instance()->hide(); + } +} + +void RightPanePlaceHolder::applyStoredSize(int width) +{ + if (width) { + QSplitter *splitter = qobject_cast<QSplitter *>(parentWidget()); + if (splitter) { + // A splitter we need to resize the splitter sizes + QList<int> sizes = splitter->sizes(); + int index = splitter->indexOf(this); + int diff = width - sizes.at(index); + int adjust = sizes.count() > 1? ( diff / (sizes.count() - 1)) : 0; + for(int i=0; i<sizes.count(); ++i) { + if (i != index) + sizes[i] -= adjust; + } + sizes[index]= width; + splitter->setSizes(sizes); + } else { + QSize s = size(); + s.setWidth(width); + resize(s); + } + } +} + +// This function does work even though the order in which +// the placeHolder get the signal is undefined. +// It does ensure that after all PlaceHolders got the signal +// m_current points to the current PlaceHolder, or zero if there +// is no PlaceHolder in this mode +// And that the parent of the RightPaneWidget gets the correct parent +void RightPanePlaceHolder::currentModeChanged(Core::IMode *mode) +{ + if (m_current == this) { + m_current = 0; + RightPaneWidget::instance()->setParent(0); + RightPaneWidget::instance()->hide(); + } + if (m_mode == mode) { + m_current = this; + + int width = RightPaneWidget::instance()->storedWidth(); + + layout()->addWidget(RightPaneWidget::instance()); + RightPaneWidget::instance()->show(); + + applyStoredSize(width); + setVisible(RightPaneWidget::instance()->isShown()); + } +} + +///// +// RightPaneWidget +///// + + +RightPaneWidget *RightPaneWidget::m_instance = 0; + +RightPaneWidget::RightPaneWidget() + :m_shown(true), m_width(0) +{ + m_instance = this; + + QVBoxLayout *layout = new QVBoxLayout; + layout->setMargin(0); + setLayout(layout); + + ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); + + BaseRightPaneWidget *rpw = pm->getObject<BaseRightPaneWidget>(); + if (rpw) { + layout->addWidget(rpw->widget()); + } + connect(pm, SIGNAL(objectAdded(QObject *)), + this, SLOT(objectAdded(QObject *))); + connect(pm, SIGNAL(aboutToRemoveObject(QObject *)), + this, SLOT(aboutToRemoveObject(QObject *))); +} + +RightPaneWidget::~RightPaneWidget() +{ + m_instance = 0; +} + +void RightPaneWidget::objectAdded(QObject *obj) +{ + BaseRightPaneWidget *rpw = qobject_cast<BaseRightPaneWidget *>(obj); + if (rpw) { + layout()->addWidget(rpw->widget()); + setFocusProxy(rpw->widget()); + } +} + +void RightPaneWidget::aboutToRemoveObject(QObject *obj) +{ + BaseRightPaneWidget *rpw = qobject_cast<BaseRightPaneWidget *>(obj); + if (rpw) { + delete rpw->widget(); + } +} + +RightPaneWidget *RightPaneWidget::instance() +{ + return m_instance; +} + +int RightPaneWidget::storedWidth() +{ + return m_width; +} + +void RightPaneWidget::resizeEvent(QResizeEvent *re) +{ + if (m_width && re->size().width()) + m_width = re->size().width(); + QWidget::resizeEvent(re); +} + +void RightPaneWidget::saveSettings(QSettings *settings) +{ + settings->setValue("RightPane/Visible", isShown()); + settings->setValue("RightPane/Width", m_width); +} + +void RightPaneWidget::readSettings(QSettings *settings) +{ + if (settings->contains("RightPane/Visible")) { + setShown(settings->value("RightPane/Visible").toBool()); + } else { + setShown(false); //TODO set to false + } + + if (settings->contains("RightPane/Width")) { + m_width = settings->value("RightPane/Width").toInt(); + if (!m_width) + m_width = 500; + } else { + m_width = 500; //pixel + } + // Apply + if (RightPanePlaceHolder::m_current) { + RightPanePlaceHolder::m_current->applyStoredSize(m_width); + } +} + +void RightPaneWidget::setShown(bool b) +{ + if (RightPanePlaceHolder::m_current) + RightPanePlaceHolder::m_current->setVisible(b); + m_shown = b; +} + +bool RightPaneWidget::isShown() +{ + return m_shown; +} + +///// +// BaseRightPaneWidget +///// + +BaseRightPaneWidget::BaseRightPaneWidget(QWidget *widget) +{ + m_widget = widget; +} + +BaseRightPaneWidget::~BaseRightPaneWidget() +{ + +} + +QWidget *BaseRightPaneWidget::widget() const +{ + return m_widget; +} diff --git a/src/plugins/coreplugin/rightpane.h b/src/plugins/coreplugin/rightpane.h new file mode 100644 index 00000000000..7b44b6fe010 --- /dev/null +++ b/src/plugins/coreplugin/rightpane.h @@ -0,0 +1,108 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef RIGHTPANE_H +#define RIGHTPANE_H + +#include "core_global.h" +#include <QtGui/QWidget> +#include <QtCore/QSettings> + +namespace Core { +class IMode; + +class RightPaneWidget; + +// TODO: The right pane works only for the help plugin atm. +// It can't cope with more than one plugin publishing objects they want in the right pane +// For that the API would need to be different. (Might be that instead of adding objects +// to the pool, there should be a method RightPaneWidget::setWidget(QWidget *w) +// Anyway if a second plugin wants to show something there, redesign this API +class CORE_EXPORT RightPanePlaceHolder : public QWidget +{ + friend class Core::RightPaneWidget; + Q_OBJECT +public: + RightPanePlaceHolder(Core::IMode *mode, QWidget *parent = 0); + ~RightPanePlaceHolder(); + static RightPanePlaceHolder *current(); +private slots: + void currentModeChanged(Core::IMode *); +private: + void applyStoredSize(int width); + Core::IMode *m_mode; + static RightPanePlaceHolder* m_current; +}; + +class CORE_EXPORT BaseRightPaneWidget : public QObject +{ + Q_OBJECT +public: + BaseRightPaneWidget(QWidget *widget); + ~BaseRightPaneWidget(); + QWidget *widget() const; +private: + QWidget *m_widget; +}; + +class CORE_EXPORT RightPaneWidget : public QWidget +{ + Q_OBJECT +public: + RightPaneWidget(); + ~RightPaneWidget(); + + void saveSettings(QSettings *settings); + void readSettings(QSettings *settings); + + bool isShown(); + void setShown(bool b); + + static RightPaneWidget* instance(); + + int storedWidth(); +protected: + void resizeEvent(QResizeEvent *); +private slots: + void objectAdded(QObject *obj); + void aboutToRemoveObject(QObject *obj); + +private: + bool m_shown; + int m_width; + static RightPaneWidget *m_instance; +}; + +} // namespace Core + + +#endif // RIGHTPANE_H diff --git a/src/plugins/coreplugin/scriptmanager/metatypedeclarations.h b/src/plugins/coreplugin/scriptmanager/metatypedeclarations.h new file mode 100644 index 00000000000..221dc7975d8 --- /dev/null +++ b/src/plugins/coreplugin/scriptmanager/metatypedeclarations.h @@ -0,0 +1,70 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef METATYPEDECLARATIONS_H +#define METATYPEDECLARATIONS_H + +#include <coreplugin/messagemanager.h> +#include <coreplugin/filemanager.h> +#include <coreplugin/ifile.h> +#include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/editormanager/editorgroup.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/icore.h> + +#include <QtCore/QList> +#include <QtCore/QMetaType> + +QT_BEGIN_NAMESPACE +class QMainWindow; +class QStatusBar; +class QToolBar; +class QSettings; +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(Core::MessageManager*) +Q_DECLARE_METATYPE(Core::FileManager*) +Q_DECLARE_METATYPE(Core::IFile*) +Q_DECLARE_METATYPE(QList<Core::IFile*>) +Q_DECLARE_METATYPE(Core::IEditor*) +Q_DECLARE_METATYPE(QList<Core::IEditor*>) +Q_DECLARE_METATYPE(Core::EditorGroup*) +Q_DECLARE_METATYPE(QList<Core::EditorGroup*>) +Q_DECLARE_METATYPE(Core::EditorManager*) +Q_DECLARE_METATYPE(Core::ICore*) + +Q_DECLARE_METATYPE(QMainWindow*) +Q_DECLARE_METATYPE(QStatusBar*) +Q_DECLARE_METATYPE(QToolBar*) +Q_DECLARE_METATYPE(QSettings*) + +#endif // METATYPEDECLARATIONS_H diff --git a/src/plugins/coreplugin/scriptmanager/qworkbench_wrapper.cpp b/src/plugins/coreplugin/scriptmanager/qworkbench_wrapper.cpp new file mode 100644 index 00000000000..acb84ff2ce1 --- /dev/null +++ b/src/plugins/coreplugin/scriptmanager/qworkbench_wrapper.cpp @@ -0,0 +1,381 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qworkbench_wrapper.h" +#include <wrap_helpers.h> + +#include <QtCore/QDebug> +#include <QtCore/QSettings> + +#include <QtGui/QMainWindow> +#include <QtGui/QStatusBar> +#include <QtGui/QToolBar> +#include <QtScript/QScriptEngine> + +#include <coreplugin/messagemanager.h> +#include <coreplugin/editormanager/editorgroup.h> + +namespace { + enum { debugQWorkbenchWrappers = 0 }; +} + +namespace Core { +namespace Internal { + +// -- CorePrototype +CorePrototype::CorePrototype(QObject *parent) : + QObject(parent) +{ +} + +Core::MessageManager *CorePrototype::messageManager() const +{ + return callee()->messageManager(); +} + +Core::FileManager *CorePrototype::fileManager() const +{ + return callee()->fileManager(); +} + +Core::EditorManager *CorePrototype::editorManager() const +{ + return callee()->editorManager(); +} + +QMainWindow *CorePrototype::mainWindow() const +{ + return callee()->mainWindow(); +} + +QStatusBar *CorePrototype::statusBar() const +{ + return callee()->statusBar(); +} + +QSettings *CorePrototype::settings() const +{ + return callee()->settings(); +} + +void CorePrototype::addAdditionalContext(int context) +{ + callee()->addAdditionalContext(context); +} + +void CorePrototype::removeAdditionalContext(int context) +{ + callee()->removeAdditionalContext(context); +} + +QString CorePrototype::toString() const +{ + return QLatin1String("Core"); +} + +CorePrototype::ICore *CorePrototype::callee() const +{ + ICore *rc = qscriptvalue_cast<ICore *>(thisObject()); + Q_ASSERT(rc); + return rc; +} + +// -- MessageManager + +MessageManagerPrototype::MessageManagerPrototype(QObject *parent) : + QObject(parent) +{ +} + +void MessageManagerPrototype::displayStatusBarMessage(const QString &text, int ms) +{ + MessageManager *mm = qscriptvalue_cast<MessageManager *>(thisObject()); + Q_ASSERT(mm); + mm->displayStatusBarMessage(text, ms); +} + +void MessageManagerPrototype::printToOutputPane(const QString &text, bool bringToForeground) +{ + MessageManager *mm = qscriptvalue_cast<MessageManager *>(thisObject()); + Q_ASSERT(mm); + mm->printToOutputPane(text, bringToForeground); +} + +QString MessageManagerPrototype::toString() const +{ + return QLatin1String("MessageManager"); +} + +// -- FileManagerPrototype + +FileManagerPrototype::FileManagerPrototype(QObject *parent) : + QObject(parent) +{ +} + +FileManager *FileManagerPrototype::callee() const +{ + FileManager *rc = qscriptvalue_cast<FileManager *>(thisObject()); + Q_ASSERT(rc); + return rc; +} + +bool FileManagerPrototype::addFiles(const QList<Core::IFile *> &files) { return callee()->addFiles(files); } +bool FileManagerPrototype::addFile(Core::IFile *file) { return callee()->addFile(file); } +bool FileManagerPrototype::removeFile(Core::IFile *file) { return callee()->removeFile(file); } + +QList<Core::IFile*> + FileManagerPrototype::saveModifiedFilesSilently(const QList<Core::IFile*> &files) { return callee()->saveModifiedFilesSilently(files); } + +QString FileManagerPrototype::getSaveAsFileName(Core::IFile *file) { return callee()->getSaveAsFileName(file); } + +bool FileManagerPrototype::isFileManaged(const QString &fileName) const { return callee()->isFileManaged(fileName); } +QList<Core::IFile *> + FileManagerPrototype::managedFiles(const QString &fileName) const { return callee()->managedFiles(fileName); } + +void FileManagerPrototype::blockFileChange(Core::IFile *file) { callee()->blockFileChange(file); } +void FileManagerPrototype::unblockFileChange(Core::IFile *file) { return callee()->unblockFileChange(file); } + +void FileManagerPrototype::addToRecentFiles(const QString &fileName) { return callee()->addToRecentFiles(fileName); } +QStringList FileManagerPrototype::recentFiles() const { return callee()->recentFiles(); } + +QString FileManagerPrototype::toString() const +{ + return QLatin1String("FileManager"); +} + +// ------- FilePrototype + +FilePrototype::FilePrototype(QObject *parent) : + QObject(parent) +{ +} + +IFile *FilePrototype::callee() const +{ + IFile *rc = qscriptvalue_cast<IFile *>(thisObject()); + Q_ASSERT(rc); + return rc; +} + +QString FilePrototype::fileName () const { return callee()->fileName(); } +QString FilePrototype::defaultPath () const { return callee()->defaultPath(); } +QString FilePrototype::suggestedFileName () const { return callee()->suggestedFileName(); } + +bool FilePrototype::isModified () const { return callee()->isModified(); } +bool FilePrototype::isReadOnly () const { return callee()->isReadOnly(); } +bool FilePrototype::isSaveAsAllowed () const { return callee()->isSaveAsAllowed(); } + +QString FilePrototype::toString() const +{ + QString rc = QLatin1String("File("); + rc += fileName(); + rc += QLatin1Char(')'); + return rc; +} + +// ------------- EditorManagerPrototype + +EditorManagerPrototype::EditorManagerPrototype(QObject *parent) : + QObject(parent) +{ +} + +Core::IEditor *EditorManagerPrototype::currentEditor() const +{ + return callee()->currentEditor(); +} + +void EditorManagerPrototype::setCurrentEditor(Core::IEditor *editor) +{ + callee()->setCurrentEditor(editor); +} + +QList<Core::IEditor*> EditorManagerPrototype::openedEditors() const +{ + return callee()->openedEditors(); +} + +QList<Core::IEditor*> EditorManagerPrototype::editorHistory() const +{ + return callee()->editorHistory(); +} + +QList<Core::EditorGroup *> EditorManagerPrototype::editorGroups() const +{ + return callee()->editorGroups(); +} + +QList<Core::IEditor*> EditorManagerPrototype::editorsForFiles(QList<Core::IFile*> files) const +{ + return callee()->editorsForFiles(files); +} + +bool EditorManagerPrototype::closeEditors(const QList<Core::IEditor*> editorsToClose, bool askAboutModifiedEditors) +{ + return callee()->closeEditors(editorsToClose, askAboutModifiedEditors); +} + +Core::IEditor *EditorManagerPrototype::openEditor(const QString &fileName, const QString &editorKind) +{ + return callee()->openEditor(fileName, editorKind); +} + +Core::IEditor *EditorManagerPrototype::newFile(const QString &editorKind, QString titlePattern, const QString &contents) +{ + return callee()->newFile(editorKind, &titlePattern, contents); +} + +int EditorManagerPrototype::makeEditorWritable(Core::IEditor *editor) +{ + return callee()->makeEditorWritable(editor); +} + +QString EditorManagerPrototype::toString() const +{ + return QLatin1String("EditorManager"); +} + +EditorManagerPrototype::EditorManager *EditorManagerPrototype::callee() const +{ + EditorManager *rc = qscriptvalue_cast<EditorManager *>(thisObject()); + Q_ASSERT(rc); + return rc; + +} + +// ------------- EditorPrototype + +EditorPrototype::EditorPrototype(QObject *parent) : + QObject(parent) +{ +} + +QString EditorPrototype::displayName() const { return callee()->displayName(); } +void EditorPrototype::setDisplayName(const QString &title) { callee()->setDisplayName(title); } + +QString EditorPrototype::kind() const { return QLatin1String(callee()->kind()); } +bool EditorPrototype::duplicateSupported() const { return callee()->duplicateSupported(); } + +bool EditorPrototype::createNew(const QString &contents) { return callee()->createNew(contents); } +bool EditorPrototype::open(const QString &fileName) { return callee()->open(fileName); } + +Core::IEditor *EditorPrototype::duplicate(QWidget *parent) +{ + return callee()->duplicate(parent); +} + +Core::IFile *EditorPrototype::file() const { return callee()->file(); } +QToolBar* EditorPrototype::toolBar() const { return callee()->toolBar();} + +Core::IEditor *EditorPrototype::callee() const +{ + IEditor *rc = qscriptvalue_cast<IEditor *>(thisObject()); + Q_ASSERT(rc); + return rc; +} + +QString EditorPrototype::toString() const +{ + QString rc = QLatin1String("Editor("); + rc += displayName(); + rc += QLatin1Char(')'); + return rc; +} + +// ----------- EditorGroupPrototype + +EditorGroupPrototype::EditorGroupPrototype(QObject *parent) : + QObject(parent) +{ +} + +int EditorGroupPrototype::editorCount() const +{ + return callee()->editorCount(); +} + +Core::IEditor *EditorGroupPrototype::currentEditor() const +{ + return callee()->currentEditor(); +} + +void EditorGroupPrototype::setCurrentEditor(Core::IEditor *editor) +{ + callee()->setCurrentEditor(editor); +} + +QList<Core::IEditor*> EditorGroupPrototype::editors() const +{ + return callee()->editors(); +} + +void EditorGroupPrototype::addEditor(Core::IEditor *editor) +{ + callee()->addEditor(editor); +} + +void EditorGroupPrototype::insertEditor(int i, Core::IEditor *editor) +{ + callee()->insertEditor(i, editor); +} + +void EditorGroupPrototype::removeEditor(Core::IEditor *editor) +{ + callee()->removeEditor(editor); +} + + +void EditorGroupPrototype::moveEditorsFromGroup(Core::EditorGroup *group) +{ + callee()->moveEditorsFromGroup(group); +} + +void EditorGroupPrototype::moveEditorFromGroup(Core::EditorGroup *group, Core::IEditor *editor) +{ + callee()->moveEditorFromGroup(group, editor); +} + +QString EditorGroupPrototype::toString() const +{ + return QLatin1String("EditorGroup"); +} + +Core::EditorGroup *EditorGroupPrototype::callee() const +{ + EditorGroup *rc = qscriptvalue_cast<EditorGroup *>(thisObject()); + Q_ASSERT(rc); + return rc; +} + +} +} diff --git a/src/plugins/coreplugin/scriptmanager/qworkbench_wrapper.h b/src/plugins/coreplugin/scriptmanager/qworkbench_wrapper.h new file mode 100644 index 00000000000..0561ac42214 --- /dev/null +++ b/src/plugins/coreplugin/scriptmanager/qworkbench_wrapper.h @@ -0,0 +1,262 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QWORKBENCH_WRAPPER_H +#define QWORKBENCH_WRAPPER_H + +#include "metatypedeclarations.h" // required for property declarations + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtScript/QScriptable> +#include <QtScript/QScriptValue> + +namespace Core { +namespace Internal { + +// Script prototype for the core interface. + +class CorePrototype : public QObject, public QScriptable { + Q_OBJECT + + Q_PROPERTY(Core::MessageManager* messageManager READ messageManager DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(Core::FileManager* fileManager READ fileManager DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(Core::EditorManager* editorManager READ editorManager DESIGNABLE false SCRIPTABLE true STORED false) + + Q_PROPERTY(QMainWindow* mainWindow READ mainWindow DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(QStatusBar* statusBar READ statusBar DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(QSettings* settings READ settings DESIGNABLE false SCRIPTABLE true STORED false) + +public: + typedef Core::ICore ICore; + + CorePrototype(QObject *parent); + + Core::MessageManager *messageManager() const; + Core::FileManager *fileManager() const; + Core::EditorManager *editorManager() const; + + QMainWindow *mainWindow() const; + QStatusBar *statusBar() const; + QSettings *settings() const; + +public slots: + void addAdditionalContext(int context); + void removeAdditionalContext(int context); + QString toString() const; + +private: + ICore *callee() const; +}; + +// Script prototype for the message manager. + +class MessageManagerPrototype : public QObject, public QScriptable +{ + Q_OBJECT + +public: + typedef Core::MessageManager MessageManager; + + MessageManagerPrototype(QObject *parent = 0); + +public slots: + void displayStatusBarMessage(const QString &text, int ms = 0); + void printToOutputPane(const QString &text, bool bringToForeground = true); + QString toString() const; +}; + +// Script prototype for the file manager interface. + +class FileManagerPrototype : public QObject, public QScriptable { + Q_OBJECT + + Q_PROPERTY(QStringList recentFiles READ recentFiles DESIGNABLE false SCRIPTABLE true STORED false) + +public: + typedef Core::FileManager FileManager; + + FileManagerPrototype(QObject *parent = 0); + QStringList recentFiles() const; + +public slots: + bool addFiles(const QList<Core::IFile *> &files); + bool addFile(Core::IFile *file); + bool removeFile(Core::IFile *file); + + QList<Core::IFile*> saveModifiedFilesSilently(const QList<Core::IFile*> &files); + QString getSaveAsFileName(Core::IFile *file); + + bool isFileManaged(const QString &fileName) const; + QList<Core::IFile *> managedFiles(const QString &fileName) const; + + void blockFileChange(Core::IFile *file); + void unblockFileChange(Core::IFile *file); + + void addToRecentFiles(const QString &fileName); + QString toString() const; + +private: + FileManager *callee() const; +}; + +// Script prototype for the file interface. + +class FilePrototype : public QObject, public QScriptable +{ + Q_OBJECT + + Q_PROPERTY(QString fileName READ fileName DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(QString defaultPath READ defaultPath DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(QString suggestedFileName READ suggestedFileName DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(bool isModified READ isModified DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(bool isReadOnly READ isReadOnly DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(bool isSaveAsAllowed READ isSaveAsAllowed DESIGNABLE false SCRIPTABLE true STORED false) +public: + typedef Core::IFile IFile; + + FilePrototype(QObject *parent = 0); + + QString fileName() const; + QString defaultPath() const; + QString suggestedFileName() const; + + bool isModified() const; + bool isReadOnly() const; + bool isSaveAsAllowed() const; + +public slots: + QString toString() const; + +private: + IFile *callee() const; +}; + +// Script prototype for the editor manager interface. + +class EditorManagerPrototype : public QObject, public QScriptable { + Q_OBJECT + Q_PROPERTY(Core::IEditor* currentEditor READ currentEditor WRITE setCurrentEditor DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(QList<Core::IEditor*> openedEditors READ openedEditors DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(QList<Core::IEditor*> editorHistory READ editorHistory DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(QList<Core::EditorGroup *> editorGroups READ editorGroups DESIGNABLE false SCRIPTABLE true STORED false) +public: + typedef Core::EditorManager EditorManager; + + EditorManagerPrototype(QObject *parent = 0); + + Core::IEditor *currentEditor() const; + void setCurrentEditor(Core::IEditor *editor); + QList<Core::IEditor*> openedEditors() const; + QList<Core::IEditor*> editorHistory() const; + QList<Core::EditorGroup *> editorGroups() const; + +public slots: + QList<Core::IEditor*> editorsForFiles(QList<Core::IFile*> files) const; + bool closeEditors(const QList<Core::IEditor*> editorsToClose, bool askAboutModifiedEditors); + Core::IEditor *openEditor(const QString &fileName, const QString &editorKind); + Core::IEditor *newFile(const QString &editorKind, QString titlePattern, const QString &contents); + int makeEditorWritable(Core::IEditor *editor); + + QString toString() const; + +private: + EditorManager *callee() const; +}; + +// Script prototype for the editor interface. + +class EditorPrototype : public QObject, public QScriptable { + Q_OBJECT + Q_PROPERTY(QString displayName READ displayName WRITE setDisplayName DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(QString kind READ kind DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(bool duplicateSupported READ duplicateSupported DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(Core::IFile* file READ file DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(QToolBar* toolBar READ toolBar DESIGNABLE false SCRIPTABLE true STORED false) + +public: + EditorPrototype(QObject *parent = 0); + + QString displayName() const; + void setDisplayName(const QString &title); + + QString kind() const; + bool duplicateSupported() const; + + Core::IFile *file() const; + QToolBar* toolBar() const; + +public slots: + bool createNew(const QString &contents); + bool open(const QString &fileName); + Core::IEditor *duplicate(QWidget *parent); + + QString toString() const; + +private: + Core::IEditor *callee() const; +}; + +// Script prototype for the editor group interface with Script-managed life cycle. + +class EditorGroupPrototype : public QObject, public QScriptable { + Q_OBJECT + Q_PROPERTY(int editorCount READ editorCount DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(Core::IEditor* currentEditor READ currentEditor WRITE setCurrentEditor DESIGNABLE false SCRIPTABLE true STORED false) + Q_PROPERTY(QList<Core::IEditor*> editors READ editors DESIGNABLE false SCRIPTABLE true STORED false) + +public: + EditorGroupPrototype(QObject *parent = 0); + + int editorCount() const; + Core::IEditor *currentEditor() const; + void setCurrentEditor(Core::IEditor *editor); + QList<Core::IEditor*> editors() const; + +public slots: + void addEditor(Core::IEditor *editor); + void insertEditor(int i, Core::IEditor *editor); + void removeEditor(Core::IEditor *editor); + + void moveEditorsFromGroup(Core::EditorGroup *group); + void moveEditorFromGroup(Core::EditorGroup *group, Core::IEditor *editor); + + QString toString() const; + +private: + Core::EditorGroup *callee() const; +}; + +} // namespace Internal +} // namespace Core + +#endif //QWORKBENCH_WRAPPER_H diff --git a/src/plugins/coreplugin/scriptmanager/scriptmanager.cpp b/src/plugins/coreplugin/scriptmanager/scriptmanager.cpp new file mode 100644 index 00000000000..b4bcae7adab --- /dev/null +++ b/src/plugins/coreplugin/scriptmanager/scriptmanager.cpp @@ -0,0 +1,313 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "scriptmanager.h" +#include "qworkbench_wrapper.h" +#include "metatypedeclarations.h" + +#include <extensionsystem/ExtensionSystemInterfaces> +#include <interface_wrap_helpers.h> +#include <wrap_helpers.h> +#include <limits.h> + +#include <QtCore/QDebug> +#include <QtCore/QSettings> +#include <QtGui/QMessageBox> +#include <QtGui/QInputDialog> +#include <QtGui/QFileDialog> +#include <QtGui/QMainWindow> +#include <QtGui/QToolBar> +#include <QtGui/QStatusBar> + +namespace { + enum { debugQWorkbenchWrappers = 0 }; +} + +// Script function template to pop up a message box +// with a certain icon and buttons. +template <int MsgBoxIcon, int MsgBoxButtons> + static QScriptValue messageBox(QScriptContext *context, QScriptEngine *engine) +{ + if (context->argumentCount() < 3) + return QScriptValue(engine, -1); + + QWidget *parent = qscriptvalue_cast<QWidget *>(context->argument(0)); + const QString title = context->argument(1).toString(); + const QString msg = context->argument(2).toString(); + + QMessageBox msgBox(static_cast<QMessageBox::Icon>(MsgBoxIcon), title, msg, + static_cast<QMessageBox::StandardButtons>(MsgBoxButtons), parent); + + return QScriptValue(engine, msgBox.exec()); +} + +static QScriptValue inputDialogGetText(QScriptContext *context, QScriptEngine *engine) +{ + const int argumentCount = context->argumentCount(); + if (argumentCount < 3) + return QScriptValue(engine, QScriptValue::NullValue); + + QWidget *parent = qscriptvalue_cast<QWidget *>(context->argument(0)); + const QString title = context->argument(1).toString(); + const QString label = context->argument(2).toString(); + const QString defaultValue = argumentCount > 3 ? context->argument(3).toString() : QString(); + + bool ok; + const QString rc = QInputDialog::getText(parent, title, label, QLineEdit::Normal, defaultValue, &ok); + if (!ok) + return QScriptValue(engine, QScriptValue::NullValue); + return QScriptValue(engine, rc); +} + +static QScriptValue inputDialogGetInteger(QScriptContext *context, QScriptEngine *engine) +{ + const int argumentCount = context->argumentCount(); + if (argumentCount < 3) + return QScriptValue(engine, QScriptValue::NullValue); + + QWidget *parent = qscriptvalue_cast<QWidget *>(context->argument(0)); + const QString title = context->argument(1).toString(); + const QString label = context->argument(2).toString(); + const int defaultValue = argumentCount > 3 ? context->argument(3).toInt32() : 0; + const int minValue = argumentCount > 4 ? context->argument(4).toInt32() : INT_MIN; + const int maxValue = argumentCount > 5 ? context->argument(5).toInt32() : INT_MAX; + + bool ok; + const int rc = QInputDialog::getInteger(parent, title, label, defaultValue, minValue, maxValue, 1, &ok); + if (!ok) + return QScriptValue(engine, QScriptValue::NullValue); + return QScriptValue(engine, rc); +} + +static QScriptValue inputDialogGetDouble(QScriptContext *context, QScriptEngine *engine) +{ + const int argumentCount = context->argumentCount(); + if (argumentCount < 3) + return QScriptValue(engine, QScriptValue::NullValue); + + QWidget *parent = qscriptvalue_cast<QWidget *>(context->argument(0)); + const QString title = context->argument(1).toString(); + const QString label = context->argument(2).toString(); + const double defaultValue = argumentCount > 3 ? context->argument(3).toNumber() : 0; + // Use QInputDialog defaults + const double minValue = argumentCount > 4 ? context->argument(4).toNumber() : INT_MIN; + const double maxValue = argumentCount > 5 ? context->argument(5).toNumber() : INT_MAX; + + bool ok; + const double rc = QInputDialog::getDouble(parent, title, label, defaultValue, minValue, maxValue, 1, &ok); + if (!ok) + return QScriptValue(engine, QScriptValue::NullValue); + return QScriptValue(engine, rc); +} + +static QScriptValue inputDialogGetItem(QScriptContext *context, QScriptEngine *engine) +{ + const int argumentCount = context->argumentCount(); + if (argumentCount < 4) + return QScriptValue(engine, QScriptValue::NullValue); + + QWidget *parent = qscriptvalue_cast<QWidget *>(context->argument(0)); + const QString title = context->argument(1).toString(); + const QString label = context->argument(2).toString(); + const QStringList items = qscriptvalue_cast<QStringList>(context->argument(3)); + const int defaultItem = argumentCount > 4 ? context->argument(4).toInt32() : 0; + const bool editable = argumentCount > 5 ? context->argument(5).toInt32() : 0; + + bool ok; + const QString rc = QInputDialog::getItem (parent, title, label, items, defaultItem, editable, &ok); + if (!ok) + return QScriptValue(engine, QScriptValue::NullValue); + + return QScriptValue(engine, rc); +} + +// Script function template to pop up a file box +// with a certain icon and buttons. +template <int TAcceptMode, int TFileMode> + static QScriptValue fileBox(QScriptContext *context, QScriptEngine *engine) +{ + const int argumentCount = context->argumentCount(); + if (argumentCount < 2) + return QScriptValue(engine, QScriptValue::NullValue); + + QWidget *parent = qscriptvalue_cast<QWidget *>(context->argument(0)); + const QString title = context->argument(1).toString(); + const QString directory = argumentCount > 2 ? context->argument(2).toString() : QString(); + const QString filter = argumentCount > 3 ? context->argument(3).toString() : QString(); + QFileDialog fileDialog(parent, title, directory, filter); + fileDialog.setAcceptMode(static_cast<QFileDialog::AcceptMode>(TAcceptMode)); + fileDialog.setFileMode (static_cast<QFileDialog::FileMode>(TFileMode)); + if (fileDialog.exec() == QDialog::Rejected) + return QScriptValue(engine, QScriptValue::NullValue); + const QStringList rc = fileDialog.selectedFiles(); + Q_ASSERT(!rc.empty()); + return TFileMode == QFileDialog::ExistingFiles ? + engine->toScriptValue(rc) : engine->toScriptValue(rc.front()); +} + +// ------ ScriptManager + +namespace Core { +namespace Internal { + +ScriptManager::ScriptManager(QObject *parent, ICore *core) : + ScriptManagerInterface(parent), + m_core(core), + m_initialized(false) +{ +} + +QScriptEngine &ScriptManager::scriptEngine() +{ + ensureEngineInitialized(); + return m_engine; +} + +// Split a backtrace of the form: +// "<anonymous>(BuildManagerCommand(ls))@:0 +// demoProjectExplorer()@:237 +// <anonymous>()@:276 +// <global>()@:0" +static void parseBackTrace(const QStringList &backTrace, ScriptManager::Stack &stack) +{ + const QChar at = QLatin1Char('@'); + const QChar colon = QLatin1Char(':'); + stack.clear(); + foreach (const QString &line, backTrace) { + const int atPos = line.lastIndexOf(at); + if (atPos == -1) + continue; + const int colonPos = line.indexOf(colon, atPos + 1); + if (colonPos == -1) + continue; + + ScriptManager::StackFrame frame; + frame.function = line.left(atPos); + frame.fileName = line.mid(atPos + 1, colonPos - atPos - 1); + frame.lineNumber = line.right(line.size() - colonPos - 1).toInt(); + stack.push_back(frame); + } +} + +bool ScriptManager::runScript(const QString &script, QString *errorMessage) +{ + Stack stack; + return runScript(script, errorMessage, &stack); +} + +bool ScriptManager::runScript(const QString &script, QString *errorMessage, Stack *stack) +{ + ensureEngineInitialized(); + stack->clear(); + + m_engine.pushContext(); + m_engine.evaluate(script); + + const bool failed = m_engine.hasUncaughtException (); + if (failed) { + const int errorLineNumber = m_engine.uncaughtExceptionLineNumber(); + const QStringList backTrace = m_engine.uncaughtExceptionBacktrace(); + parseBackTrace(backTrace, *stack); + const QString backtrace = backTrace.join(QString(QLatin1Char('\n'))); + *errorMessage = QObject::tr("Exception at line %1: %2\n%3").arg(errorLineNumber).arg(engineError(m_engine)).arg(backtrace); + } + m_engine.popContext(); + return !failed; +} + +void ScriptManager::ensureEngineInitialized() +{ + if (m_initialized) + return; + Q_ASSERT(m_core); + // register QObjects that occur as properties + SharedTools::registerQObject<QMainWindow>(m_engine); + SharedTools::registerQObject<QStatusBar>(m_engine); + SharedTools::registerQObject<QToolBar>(m_engine); + SharedTools::registerQObject<QSettings>(m_engine); + // WB interfaces +// SharedTools::registerQObjectInterface<Core::MessageManager, MessageManagerPrototype>(m_engine); + +// SharedTools::registerQObjectInterface<Core::IFile, FilePrototype>(m_engine); +// qScriptRegisterSequenceMetaType<QList<Core::IFile *> >(&m_engine); +// SharedTools::registerQObjectInterface<Core::FileManager, FileManagerPrototype>(m_engine); + +// SharedTools::registerQObjectInterface<Core::IEditor, EditorPrototype>(m_engine); + qScriptRegisterSequenceMetaType<QList<Core::IEditor *> >(&m_engine); + +// SharedTools::registerQObjectInterface<Core::EditorGroup, EditorGroupPrototype>(m_engine); + qScriptRegisterSequenceMetaType<QList<Core::EditorGroup *> >(&m_engine); + + SharedTools::registerQObjectInterface<Core::EditorManager, EditorManagerPrototype>(m_engine); + +// SharedTools::registerQObjectInterface<Core::ICore, CorePrototype>(m_engine); + + // Make "core" available + m_engine.globalObject().setProperty(QLatin1String("core"), qScriptValueFromValue(&m_engine, m_core)); + + // CLASSIC: registerInterfaceWithDefaultPrototype<Core::MessageManager, MessageManagerPrototype>(m_engine); + + // Message box conveniences + m_engine.globalObject().setProperty(QLatin1String("critical"), + m_engine.newFunction(messageBox<QMessageBox::Critical, QMessageBox::Ok>, 3)); + m_engine.globalObject().setProperty(QLatin1String("warning"), + m_engine.newFunction(messageBox<QMessageBox::Warning, QMessageBox::Ok>, 3)); + m_engine.globalObject().setProperty(QLatin1String("information"), + m_engine.newFunction(messageBox<QMessageBox::Information, QMessageBox::Ok>, 3)); + // StandardButtons has overloaded operator '|' - grrr. + enum { MsgBoxYesNo = 0x00014000 }; + m_engine.globalObject().setProperty(QLatin1String("yesNoQuestion"), + m_engine.newFunction(messageBox<QMessageBox::Question, MsgBoxYesNo>, 3)); + + m_engine.globalObject().setProperty(QLatin1String("getText"), m_engine.newFunction(inputDialogGetText, 3)); + m_engine.globalObject().setProperty(QLatin1String("getInteger"), m_engine.newFunction(inputDialogGetInteger, 3)); + m_engine.globalObject().setProperty(QLatin1String("getDouble"), m_engine.newFunction(inputDialogGetDouble, 3)); + m_engine.globalObject().setProperty(QLatin1String("getItem"), m_engine.newFunction(inputDialogGetItem, 3)); + + // file box + m_engine.globalObject().setProperty(QLatin1String("getOpenFileNames"), m_engine.newFunction(fileBox<QFileDialog::AcceptOpen, QFileDialog::ExistingFiles> , 2)); + m_engine.globalObject().setProperty(QLatin1String("getOpenFileName"), m_engine.newFunction(fileBox<QFileDialog::AcceptOpen, QFileDialog::ExistingFile> , 2)); + m_engine.globalObject().setProperty(QLatin1String("getSaveFileName"), m_engine.newFunction(fileBox<QFileDialog::AcceptSave, QFileDialog::AnyFile> , 2)); + m_engine.globalObject().setProperty(QLatin1String("getExistingDirectory"), m_engine.newFunction(fileBox<QFileDialog::AcceptSave, QFileDialog::DirectoryOnly> , 2)); + m_initialized = true; +} + +QString ScriptManager::engineError(QScriptEngine &scriptEngine) +{ + QScriptValue error = scriptEngine.evaluate(QLatin1String("Error")); + if (error.isValid()) + return error.toString(); + return QObject::tr("Unknown error"); +} + +} +} diff --git a/src/plugins/coreplugin/scriptmanager/scriptmanager.h b/src/plugins/coreplugin/scriptmanager/scriptmanager.h new file mode 100644 index 00000000000..51359707105 --- /dev/null +++ b/src/plugins/coreplugin/scriptmanager/scriptmanager.h @@ -0,0 +1,71 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SCRIPTMANAGER_H +#define SCRIPTMANAGER_H + +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QtScript/QScriptEngine> + +#include <coreplugin/scriptmanager/scriptmanagerinterface.h> +#include <coreplugin/icore.h> + +namespace Core { +namespace Internal { + +class ScriptManager : public Core::ScriptManagerInterface +{ + Q_OBJECT + +public: + ScriptManager(QObject *parent, ICore *core); + + virtual QScriptEngine &scriptEngine(); + + virtual bool runScript(const QString &script, QString *errorMessage, Stack *stack); + virtual bool runScript(const QString &script, QString *errorMessage); + + static QString engineError(QScriptEngine &scriptEngine); + +private: + void ensureEngineInitialized(); + + QScriptEngine m_engine; + ICore *m_core; + bool m_initialized; +}; + +} // namespace Internal +} // namespace Core + +#endif //SCRIPTMANAGER_H diff --git a/src/plugins/coreplugin/scriptmanager/scriptmanagerinterface.h b/src/plugins/coreplugin/scriptmanager/scriptmanagerinterface.h new file mode 100644 index 00000000000..5d3a907e756 --- /dev/null +++ b/src/plugins/coreplugin/scriptmanager/scriptmanagerinterface.h @@ -0,0 +1,75 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SCRIPTMANAGERINTERFACE_H +#define SCRIPTMANAGERINTERFACE_H + +#include <coreplugin/core_global.h> + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtScript/QScriptEngine> + +namespace Core { + +/* Script Manager. + * Provides a script engine that is initialized with + * QWorkBenchs interfaces and allows for running scripts. + * @{todo} Should it actually manage script files, too? */ + +class CORE_EXPORT ScriptManagerInterface : public QObject +{ + Q_OBJECT +public: + // A stack frame as returned by a failed invocation (exception) + // fileName may be empty. lineNumber can be 0 for the top frame (goof-up?). + struct StackFrame { + QString function; + QString fileName; + int lineNumber; + }; + typedef QList<StackFrame> Stack; + + ScriptManagerInterface(QObject *parent = 0) : QObject(parent) {} + virtual ~ScriptManagerInterface() { } + + // Access the engine (for plugins to wrap additional interfaces). + virtual QScriptEngine &scriptEngine() = 0; + + // Run a script + virtual bool runScript(const QString &script, QString *errorMessage, Stack *errorStack) = 0; + virtual bool runScript(const QString &script, QString *errorMessage) = 0; +}; + +} // namespace Core + +#endif // SCRIPTMANAGERINTERFACE_H diff --git a/src/plugins/coreplugin/sidebar.cpp b/src/plugins/coreplugin/sidebar.cpp new file mode 100644 index 00000000000..6ea56b917ad --- /dev/null +++ b/src/plugins/coreplugin/sidebar.cpp @@ -0,0 +1,371 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "sidebar.h" +#include "imode.h" +#include "modemanager.h" +#include "actionmanager/actionmanagerinterface.h" + +#include <QtCore/QDebug> +#include <QtCore/QEvent> +#include <QtCore/QSettings> +#include <QtGui/QLayout> +#include <QtGui/QToolBar> +#include <QtGui/QAction> +#include <QtGui/QToolButton> + +using namespace Core; +using namespace Core::Internal; + +SideBarItem::~SideBarItem() +{ + delete m_widget; +} + +SideBar::SideBar(QList<SideBarItem*> itemList, + QList<SideBarItem*> defaultVisible) +{ + setOrientation(Qt::Vertical); + foreach (SideBarItem *item, itemList) { + const QString title = item->widget()->windowTitle(); + m_itemMap.insert(title, item); + if (defaultVisible.contains(item)) + m_defaultVisible.append(title); + } + + m_availableItems = m_itemMap.keys(); +} + +SideBar::~SideBar() +{ + qDeleteAll(m_itemMap.values()); +} + +QStringList SideBar::availableItems() const +{ + return m_availableItems; +} + +void SideBar::makeItemAvailable(SideBarItem *item) +{ + QMap<QString, SideBarItem*>::const_iterator it = m_itemMap.constBegin(); + while (it != m_itemMap.constEnd()) { + if (it.value() == item) { + m_availableItems.append(it.key()); + qSort(m_availableItems); + break; + } + ++it; + } +} + +SideBarItem *SideBar::item(const QString &title) +{ + if (m_itemMap.contains(title)) { + m_availableItems.removeAll(title); + return m_itemMap.value(title); + } + return 0; +} + +SideBarWidget *SideBar::insertSideBarWidget(int position, const QString &title) +{ + SideBarWidget *item = new SideBarWidget(this, title); + connect(item, SIGNAL(split()), this, SLOT(split())); + connect(item, SIGNAL(close()), this, SLOT(close())); + connect(item, SIGNAL(currentWidgetChanged()), this, SLOT(updateWidgets())); + insertWidget(position, item); + m_widgets.insert(position, item); + updateWidgets(); + return item; +} + +void SideBar::split() +{ + SideBarWidget *original = qobject_cast<SideBarWidget*>(sender()); + int pos = indexOf(original) + 1; + insertSideBarWidget(pos); + updateWidgets(); +} + +void SideBar::close() +{ + if (m_widgets.count() != 1) { + SideBarWidget *widget = qobject_cast<SideBarWidget*>(sender()); + if (!widget) + return; + widget->removeCurrentItem(); + m_widgets.removeOne(widget); + widget->hide(); + widget->deleteLater(); + updateWidgets(); + } +} + +void SideBar::updateWidgets() +{ + foreach (SideBarWidget *i, m_widgets) + i->updateAvailableItems(); +} + +void SideBar::saveSettings(QSettings *settings) +{ + QStringList views; + for (int i = 0; i < m_widgets.count(); ++i) + views.append(m_widgets.at(i)->currentItemTitle()); + settings->setValue("HelpSideBar/Views", views); + settings->setValue("HelpSideBar/Visible", true);//isVisible()); + settings->setValue("HelpSideBar/VerticalPosition", saveState()); + settings->setValue("HelpSideBar/Width", width()); +} + +void SideBar::readSettings(QSettings *settings) +{ + if (settings->contains("HelpSideBar/Views")) { + QStringList views = settings->value("HelpSideBar/Views").toStringList(); + if (views.count()) { + foreach (const QString &title, views) + insertSideBarWidget(m_widgets.count(), title); + } else { + insertSideBarWidget(0); + } + } else { + foreach (const QString &title, m_defaultVisible) + insertSideBarWidget(0, title); + } + + if (settings->contains("HelpSideBar/Visible")) + setVisible(settings->value("HelpSideBar/Visible").toBool()); + + if (settings->contains("HelpSideBar/VerticalPosition")) + restoreState(settings->value("HelpSideBar/VerticalPosition").toByteArray()); + + if (settings->contains("HelpSideBar/Width")) { + QSize s = size(); + s.setWidth(settings->value("HelpSideBar/Width").toInt()); + resize(s); + } +} + +void SideBar::activateItem(SideBarItem *item) +{ + QMap<QString, SideBarItem*>::const_iterator it = m_itemMap.constBegin(); + QString title; + while (it != m_itemMap.constEnd()) { + if (it.value() == item) { + title = it.key(); + break; + } + ++it; + } + + if (title.isEmpty()) + return; + + for (int i = 0; i < m_widgets.count(); ++i) { + if (m_widgets.at(i)->currentItemTitle() == title) { + item->widget()->setFocus(); + return; + } + } + + SideBarWidget *widget = m_widgets.first(); + widget->setCurrentItem(title); + updateWidgets(); + item->widget()->setFocus(); +} + +void SideBar::setShortcutMap(const QMap<QString, Core::ICommand*> &shortcutMap) +{ + m_shortcutMap = shortcutMap; +} + +QMap<QString, Core::ICommand*> SideBar::shortcutMap() const +{ + return m_shortcutMap; +} + + + +SideBarWidget::SideBarWidget(SideBar *sideBar, const QString &title) + : m_currentItem(0) + , m_sideBar(sideBar) +{ + m_comboBox = new ComboBox(this); + m_comboBox->setMinimumContentsLength(15); + + m_toolbar = new QToolBar(this); + m_toolbar->setContentsMargins(0, 0, 0, 0); + m_toolbar->addWidget(m_comboBox); + + m_splitButton = new QToolButton; + m_splitButton->setProperty("type", QLatin1String("dockbutton")); + m_splitButton->setIcon(QIcon(":/qworkbench/images/splitbutton_horizontal.png")); + m_splitButton->setToolTip(tr("Split")); + connect(m_splitButton, SIGNAL(clicked(bool)), this, SIGNAL(split())); + + m_closeButton = new QToolButton; + m_closeButton->setProperty("type", QLatin1String("dockbutton")); + m_closeButton->setIcon(QIcon(":/qworkbench/images/closebutton.png")); + m_closeButton->setToolTip(tr("Close")); + + connect(m_closeButton, SIGNAL(clicked(bool)), this, SIGNAL(close())); + + QWidget *spacerItem = new QWidget(this); + spacerItem->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + m_toolbar->addWidget(spacerItem); + m_splitAction = m_toolbar->addWidget(m_splitButton); + m_toolbar->addWidget(m_closeButton); + + QVBoxLayout *lay = new QVBoxLayout(); + lay->setMargin(0); + lay->setSpacing(0); + setLayout(lay); + lay->addWidget(m_toolbar); + + const QStringList lst = m_sideBar->availableItems(); + QString t = title; + if (lst.count()) { + m_comboBox->addItems(lst); + m_comboBox->setCurrentIndex(0); + if (t.isEmpty()) + t = m_comboBox->currentText(); + } + setCurrentItem(t); + + connect(m_comboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(setCurrentIndex(int))); +} + +SideBarWidget::~SideBarWidget() +{ +} + +QString SideBarWidget::currentItemTitle() const +{ + return m_comboBox->currentText(); +} + +void SideBarWidget::setCurrentItem(const QString &title) +{ + if (!title.isEmpty()) { + int idx = m_comboBox->findText(title); + if (idx < 0) + idx = 0; + m_comboBox->blockSignals(true); + m_comboBox->setCurrentIndex(idx); + m_comboBox->blockSignals(false); + } + + SideBarItem *item = m_sideBar->item(title); + if (!item) + return; + removeCurrentItem(); + m_currentItem = item; + layout()->addWidget(m_currentItem->widget()); + + // Add buttons and remember their actions for later removal + foreach (QToolButton *b, m_currentItem->createToolBarWidgets()) + m_addedToolBarActions.append(m_toolbar->insertWidget(m_splitAction, b)); +} + +void SideBarWidget::updateAvailableItems() +{ + m_comboBox->blockSignals(true); + QString current = m_comboBox->currentText(); + m_comboBox->clear(); + QStringList itms = m_sideBar->availableItems(); + if (!current.isEmpty() && !itms.contains(current)) + itms.append(current); + qSort(itms); + m_comboBox->addItems(itms); + int idx = m_comboBox->findText(current); + if (idx < 0) + idx = 0; + m_comboBox->setCurrentIndex(idx); + m_splitButton->setEnabled(itms.count() > 1); + m_comboBox->blockSignals(false); +} + +void SideBarWidget::removeCurrentItem() +{ + if (!m_currentItem) + return; + + QWidget *w = m_currentItem->widget(); + layout()->removeWidget(w); + w->setParent(0); + m_sideBar->makeItemAvailable(m_currentItem); + + // Delete custom toolbar widgets + qDeleteAll(m_addedToolBarActions); + m_addedToolBarActions.clear(); + + m_currentItem = 0; +} + +void SideBarWidget::setCurrentIndex(int) +{ + setCurrentItem(m_comboBox->currentText()); + emit currentWidgetChanged(); +} + +Core::ICommand *SideBarWidget::command(const QString &title) const +{ + const QMap<QString, Core::ICommand*> shortcutMap = m_sideBar->shortcutMap(); + QMap<QString, Core::ICommand*>::const_iterator r = shortcutMap.find(title); + if (r != shortcutMap.end()) + return r.value(); + return 0; +} + + + +ComboBox::ComboBox(SideBarWidget *sideBarWidget) + : m_sideBarWidget(sideBarWidget) +{ +} + +bool ComboBox::event(QEvent *e) +{ + if (e->type() == QEvent::ToolTip) { + QString txt = currentText(); + Core::ICommand *cmd = m_sideBarWidget->command(txt); + if (cmd) { + txt = tr("Activate %1").arg(txt); + setToolTip(cmd->stringWithAppendedShortcut(txt)); + } else { + setToolTip(txt); + } + } + return QComboBox::event(e); +} diff --git a/src/plugins/coreplugin/sidebar.h b/src/plugins/coreplugin/sidebar.h new file mode 100644 index 00000000000..98ed1070c16 --- /dev/null +++ b/src/plugins/coreplugin/sidebar.h @@ -0,0 +1,184 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SIDEBAR_H +#define SIDEBAR_H + +#include <QtCore/QMap> +#include <QtGui/QWidget> +#include <QtGui/QComboBox> + +#include <coreplugin/minisplitter.h> + +QT_BEGIN_NAMESPACE +class QSettings; +class QComboBox; +class QToolBar; +class QAction; +class QToolButton; +QT_END_NAMESPACE + +namespace Core { + +class ICommand; + +namespace Internal { +class SideBarWidget; +class ComboBox; +} // namespace Internal + +/* + * An item in the sidebar. Has a widget that is displayed in the sidebar and + * optionally a list of tool buttons that are added to the toolbar above it. + * The window title of the widget is displayed in the combo box. + * + * The SideBarItem takes ownership over the widget. + */ +class CORE_EXPORT SideBarItem +{ +public: + SideBarItem(QWidget *widget) + : m_widget(widget) + {} + + virtual ~SideBarItem(); + + QWidget *widget() { return m_widget; } + + /* Should always return a new set of tool buttons. + * + * Workaround since there doesn't seem to be a nice way to remove widgets + * that have been added to a QToolBar without either not deleting the + * associated QAction or causing the QToolButton to be deleted. + */ + virtual QList<QToolButton *> createToolBarWidgets() + { + return QList<QToolButton *>(); + } + +private: + QWidget *m_widget; +}; + +class CORE_EXPORT SideBar : public MiniSplitter +{ + Q_OBJECT +public: + /* + * The SideBar takes ownership of the SideBarItems. + */ + SideBar(QList<SideBarItem*> widgetList, + QList<SideBarItem*> defaultVisible); + ~SideBar(); + + QStringList availableItems() const; + void makeItemAvailable(SideBarItem *item); + SideBarItem *item(const QString &title); + + void saveSettings(QSettings *settings); + void readSettings(QSettings *settings); + + void activateItem(SideBarItem *item); + + void setShortcutMap(const QMap<QString, Core::ICommand*> &shortcutMap); + QMap<QString, Core::ICommand*> shortcutMap() const; + +private slots: + void split(); + void close(); + void updateWidgets(); + +private: + Internal::SideBarWidget *insertSideBarWidget(int position, + const QString &title = QString()); + QList<Internal::SideBarWidget*> m_widgets; + + QMap<QString, SideBarItem*> m_itemMap; + QStringList m_availableItems; + QStringList m_defaultVisible; + QMap<QString, Core::ICommand*> m_shortcutMap; +}; + +namespace Internal { + +class SideBarWidget : public QWidget +{ + Q_OBJECT +public: + SideBarWidget(SideBar *sideBar, const QString &title); + ~SideBarWidget(); + + QString currentItemTitle() const; + void setCurrentItem(const QString &title); + + void updateAvailableItems(); + void removeCurrentItem(); + + Core::ICommand *command(const QString &title) const; + +signals: + void split(); + void close(); + void currentWidgetChanged(); + +private slots: + void setCurrentIndex(int); + +private: + ComboBox *m_comboBox; + SideBarItem *m_currentItem; + QToolBar *m_toolbar; + QAction *m_splitAction; + QList<QAction *> m_addedToolBarActions; + SideBar *m_sideBar; + QToolButton *m_splitButton; + QToolButton *m_closeButton; +}; + +class ComboBox : public QComboBox +{ + Q_OBJECT + +public: + ComboBox(SideBarWidget *sideBarWidget); + +protected: + bool event(QEvent *event); + +private: + SideBarWidget *m_sideBarWidget; +}; + +} // namespace Internal +} // namespace Core + +#endif // SIDEBAR_H diff --git a/src/plugins/coreplugin/styleanimator.cpp b/src/plugins/coreplugin/styleanimator.cpp new file mode 100644 index 00000000000..5133876b6c9 --- /dev/null +++ b/src/plugins/coreplugin/styleanimator.cpp @@ -0,0 +1,156 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "styleanimator.h" + +#include <QtGui/QStyleOption> + + +Animation * StyleAnimator::widgetAnimation(const QWidget *widget) const +{ + if (!widget) + return 0; + foreach (Animation *a, animations) { + if (a->widget() == widget) + return a; + } + return 0; +} + +void Animation::paint(QPainter *painter, const QStyleOption *option) +{ + Q_UNUSED(option); + Q_UNUSED(painter); +} + +void Animation::drawBlendedImage(QPainter *painter, QRect rect, float alpha) { + if (_secondaryImage.isNull() || _primaryImage.isNull()) + return; + + if (_tempImage.isNull()) + _tempImage = _secondaryImage; + + const int a = qRound(alpha*256); + const int ia = 256 - a; + const int sw = _primaryImage.width(); + const int sh = _primaryImage.height(); + const int bpl = _primaryImage.bytesPerLine(); + switch(_primaryImage.depth()) { + case 32: + { + uchar *mixed_data = _tempImage.bits(); + const uchar *back_data = _primaryImage.bits(); + const uchar *front_data = _secondaryImage.bits(); + for (int sy = 0; sy < sh; sy++) { + quint32* mixed = (quint32*)mixed_data; + const quint32* back = (const quint32*)back_data; + const quint32* front = (const quint32*)front_data; + for (int sx = 0; sx < sw; sx++) { + quint32 bp = back[sx]; + quint32 fp = front[sx]; + mixed[sx] = qRgba ((qRed(bp)*ia + qRed(fp)*a)>>8, + (qGreen(bp)*ia + qGreen(fp)*a)>>8, + (qBlue(bp)*ia + qBlue(fp)*a)>>8, + (qAlpha(bp)*ia + qAlpha(fp)*a)>>8); + } + mixed_data += bpl; + back_data += bpl; + front_data += bpl; + } + } + default: + break; + } + painter->drawImage(rect, _tempImage); +} + +void Transition::paint(QPainter *painter, const QStyleOption *option) +{ + float alpha = 1.0; + if (_duration > 0) { + QTime current = QTime::currentTime(); + + if (_startTime > current) + _startTime = current; + + int timeDiff = _startTime.msecsTo(current); + alpha = timeDiff/(float)_duration; + if (timeDiff > _duration) { + _running = false; + alpha = 1.0; + } + } + else { + _running = false; } + drawBlendedImage(painter, option->rect, alpha); +} + +void StyleAnimator::timerEvent(QTimerEvent *) +{ + for (int i = animations.size() - 1 ; i >= 0 ; --i) { + if (animations[i]->widget()) + animations[i]->widget()->update(); + + if (!animations[i]->widget() || + !animations[i]->widget()->isEnabled() || + !animations[i]->widget()->isVisible() || + animations[i]->widget()->window()->isMinimized() || + !animations[i]->running()) + { + Animation *a = animations.takeAt(i); + delete a; + } + } + if (animations.size() == 0 && animationTimer.isActive()) { + animationTimer.stop(); + } +} + +void StyleAnimator::stopAnimation(const QWidget *w) +{ + for (int i = animations.size() - 1 ; i >= 0 ; --i) { + if (animations[i]->widget() == w) { + Animation *a = animations.takeAt(i); + delete a; + break; + } + } +} + +void StyleAnimator::startAnimation(Animation *t) +{ + stopAnimation(t->widget()); + animations.append(t); + if (animations.size() > 0 && !animationTimer.isActive()) { + animationTimer.start(35, this); + } +} diff --git a/src/plugins/coreplugin/styleanimator.h b/src/plugins/coreplugin/styleanimator.h new file mode 100644 index 00000000000..6e006ab25a3 --- /dev/null +++ b/src/plugins/coreplugin/styleanimator.h @@ -0,0 +1,104 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ANIMATION_H +#define ANIMATION_H + +#include <QtCore/QPointer> +#include <QtCore/QTime> +#include <QtCore/QBasicTimer> +#include <QtGui/QStyle> +#include <QtGui/QPainter> +#include <QtGui/QWidget> + +/* + * This is a set of helper classes to allow for widget animations in + * the style. Its mostly taken from Vista style so it should be fully documented + * there. + * + */ + +class Animation +{ +public : + Animation() : _running(true) { } + virtual ~Animation() { } + QWidget * widget() const { return _widget; } + bool running() const { return _running; } + const QTime &startTime() const { return _startTime; } + void setRunning(bool val) { _running = val; } + void setWidget(QWidget *widget) { _widget = widget; } + void setStartTime(const QTime &startTime) { _startTime = startTime; } + virtual void paint(QPainter *painter, const QStyleOption *option); + +protected: + void drawBlendedImage(QPainter *painter, QRect rect, float value); + QTime _startTime; + QPointer<QWidget> _widget; + QImage _primaryImage; + QImage _secondaryImage; + QImage _tempImage; + bool _running; +}; + +// Handles state transition animations +class Transition : public Animation +{ +public : + Transition() : Animation() {} + virtual ~Transition() { } + void setDuration(int duration) { _duration = duration; } + void setStartImage(const QImage &image) { _primaryImage = image; } + void setEndImage(const QImage &image) { _secondaryImage = image; } + virtual void paint(QPainter *painter, const QStyleOption *option); + int duration() const { return _duration; } + int _duration; //set time in ms to complete a state transition +}; + +class StyleAnimator : public QObject +{ + Q_OBJECT; + +public: + StyleAnimator(QObject *parent = 0) : QObject(parent) {} + + void timerEvent(QTimerEvent *); + void startAnimation(Animation *); + void stopAnimation(const QWidget *); + Animation* widgetAnimation(const QWidget *) const; + +private: + QBasicTimer animationTimer; + QList <Animation*> animations; +}; + +#endif // ANIMATION_H diff --git a/src/plugins/coreplugin/stylehelper.cpp b/src/plugins/coreplugin/stylehelper.cpp new file mode 100644 index 00000000000..5630b464df7 --- /dev/null +++ b/src/plugins/coreplugin/stylehelper.cpp @@ -0,0 +1,229 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "stylehelper.h" + +#include <QtGui/QPixmapCache> +#include <QtGui/QWidget> + +// Clamps float color values within (0, 255) +static int clamp(float x) { + const int val = x > 255 ? 255 : static_cast<int>(x); + return val < 0 ? 0 : val; +} + +// Clamps float color values within (0, 255) +/* +static int range(float x, int min, int max) { + int val = x > max ? max : x; + return val < min ? min : val; +} +*/ + +qreal StyleHelper::sidebarFontSize() +{ +#if defined(Q_OS_MAC) + return 9; +#else + return 7.5; +#endif +} + +QPalette StyleHelper::sidebarFontPalette(const QPalette &original) +{ + QPalette palette = original; + palette.setColor(QPalette::Active, QPalette::Text, panelTextColor()); + palette.setColor(QPalette::Active, QPalette::WindowText, panelTextColor()); + palette.setColor(QPalette::Inactive, QPalette::Text, panelTextColor().darker()); + palette.setColor(QPalette::Inactive, QPalette::WindowText, panelTextColor().darker()); + return palette; +} + +QColor StyleHelper::panelTextColor() +{ + //qApp->palette().highlightedText().color(); + return Qt::white; +} + +QColor StyleHelper::m_baseColor(0x666666); + +QColor StyleHelper::baseColor() +{ + return m_baseColor; +} + +QColor StyleHelper::highlightColor() +{ + QColor result = baseColor(); + result.setHsv(result.hue(), + clamp(result.saturation()), + clamp(result.value() * 1.16)); + return result; +} + +QColor StyleHelper::shadowColor() +{ + QColor result = baseColor(); + result.setHsv(result.hue(), + clamp(result.saturation() * 1.1), + clamp(result.value() * 0.70)); + return result; +} + +QColor StyleHelper::borderColor() +{ + QColor result = baseColor(); + result.setHsv(result.hue(), + result.saturation(), + result.value() / 2); + return result; +} + +void StyleHelper::setBaseColor(const QColor &color) +{ + if (color.isValid() && color != m_baseColor) { + m_baseColor = color; + foreach (QWidget *w, QApplication::topLevelWidgets()) + w->update(); + } +} + +static QColor mergedColors(const QColor &colorA, const QColor &colorB, int factor = 50) +{ + const int maxFactor = 100; + QColor tmp = colorA; + tmp.setRed((tmp.red() * factor) / maxFactor + (colorB.red() * (maxFactor - factor)) / maxFactor); + tmp.setGreen((tmp.green() * factor) / maxFactor + (colorB.green() * (maxFactor - factor)) / maxFactor); + tmp.setBlue((tmp.blue() * factor) / maxFactor + (colorB.blue() * (maxFactor - factor)) / maxFactor); + return tmp; +} + +void StyleHelper::verticalGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect) +{ + QString key; + key.sprintf("mh_toolbar %d %d %d %d %d", spanRect.width(), spanRect.height(), clipRect.width(), + clipRect.height(), StyleHelper::baseColor().rgb());; + QPixmap pixmap; + QPainter *p = painter; + QRect rect = clipRect; + if (StyleHelper::usePixmapCache() && !QPixmapCache::find(key, pixmap)) { + pixmap = QPixmap(clipRect.size()); + p = new QPainter(&pixmap); + rect = QRect(0, 0, clipRect.width(), clipRect.height()); + } + + QColor base = StyleHelper::baseColor(); + QLinearGradient grad(spanRect.topRight(), spanRect.topLeft()); + grad.setColorAt(0, highlightColor()); + grad.setColorAt(0.301, base); + grad.setColorAt(1, shadowColor()); + p->fillRect(rect, grad); + + QColor light(255, 255, 255, 80); + p->setPen(light); + p->drawLine(rect.topRight() - QPoint(1, 0), rect.bottomRight() - QPoint(1, 0)); + + if (StyleHelper::usePixmapCache() && !QPixmapCache::find(key, pixmap)) { + painter->drawPixmap(clipRect.topLeft(), pixmap); + p->end(); + delete p; + QPixmapCache::insert(key, pixmap); + } + +} + +void StyleHelper::horizontalGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect) +{ + QString key; + key.sprintf("mh_toolbar %d %d %d %d %d", spanRect.width(), spanRect.height(), + clipRect.width(), clipRect.height(), StyleHelper::baseColor().rgb()); + QPixmap pixmap; + QPainter *p = painter; + QRect rect = clipRect; + if (StyleHelper::usePixmapCache() && !QPixmapCache::find(key, pixmap)) { + pixmap = QPixmap(clipRect.size()); + p = new QPainter(&pixmap); + rect = QRect(0, 0, clipRect.width(), clipRect.height()); + } + + QColor base = StyleHelper::baseColor(); + QLinearGradient grad(rect.topLeft(), rect.bottomLeft()); + + grad.setColorAt(0, highlightColor().lighter(120)); + grad.setColorAt(0.4, highlightColor()); + grad.setColorAt(0.401, base); + grad.setColorAt(1, shadowColor()); + p->fillRect(rect, grad); + + QLinearGradient shadowGradient(spanRect.topLeft(), spanRect.topRight()); + shadowGradient.setColorAt(0, QColor(0, 0, 0, 30)); + QColor highlight = highlightColor().lighter(130); + highlight.setAlpha(100); + shadowGradient.setColorAt(0.7, highlight); + shadowGradient.setColorAt(1, QColor(0, 0, 0, 40)); + p->fillRect(rect, shadowGradient); + + if (StyleHelper::usePixmapCache() && !QPixmapCache::find(key, pixmap)) { + painter->drawPixmap(clipRect.topLeft(), pixmap); + p->end(); + delete p; + QPixmapCache::insert(key, pixmap); + } +} + +void StyleHelper::menuGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect) +{ + QString key; + key.sprintf("mh_toolbar %d %d %d %d %d", spanRect.width(), spanRect.height(), clipRect.width(), + clipRect.height(), StyleHelper::baseColor().rgb());; + QPixmap pixmap; + QPainter *p = painter; + QRect rect = clipRect; + if (StyleHelper::usePixmapCache() && !QPixmapCache::find(key, pixmap)) { + pixmap = QPixmap(clipRect.size()); + p = new QPainter(&pixmap); + rect = QRect(0, 0, clipRect.width(), clipRect.height()); + } + + QLinearGradient grad(spanRect.topLeft(), spanRect.bottomLeft()); + QColor menuColor = mergedColors(StyleHelper::baseColor(), QColor(240, 240, 240), 25); + grad.setColorAt(0, menuColor.lighter(112)); + grad.setColorAt(1, menuColor); + p->fillRect(rect, grad); + + if (StyleHelper::usePixmapCache() && !QPixmapCache::find(key, pixmap)) { + painter->drawPixmap(clipRect.topLeft(), pixmap); + p->end(); + delete p; + QPixmapCache::insert(key, pixmap); + } +} diff --git a/src/plugins/coreplugin/stylehelper.h b/src/plugins/coreplugin/stylehelper.h new file mode 100644 index 00000000000..cceb23a0123 --- /dev/null +++ b/src/plugins/coreplugin/stylehelper.h @@ -0,0 +1,78 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef STYLEHELPER_H +#define STYLEHELPER_H + +#include "core_global.h" + +#include <QtCore/QRect> +#include <QtGui/QPainter> +#include <QtGui/QApplication> +#include <QtGui/QPalette> +#include <QtGui/QColor> + +// Helper class holding all custom color values + +class CORE_EXPORT StyleHelper { +public: + // Height of the project explorer navigation bar + static int navigationWidgetHeight() { return 24; } + static qreal sidebarFontSize(); + static QPalette sidebarFontPalette(const QPalette &original); + + // This is our color table, all colors derive from baseColor + static QColor baseColor(); + static QColor panelTextColor(); + static QColor highlightColor(); + static QColor shadowColor(); + static QColor borderColor(); + static QColor buttonTextColor() { return QColor(0x4c4c4c); } + + // Sets the base color and makes sure all top level widgets are updated + static void setBaseColor(const QColor &color); + + // Gradients used for panels + static void horizontalGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect); + static void verticalGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect); + static void menuGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect); + + // Pixmap cache should only be enabled for X11 due to slow gradients + static bool usePixmapCache() { + return true; + } + +private: + static QColor m_baseColor; +}; + +#endif // STYLEHELPER_H diff --git a/src/plugins/coreplugin/tabpositionindicator.cpp b/src/plugins/coreplugin/tabpositionindicator.cpp new file mode 100644 index 00000000000..497b351ad99 --- /dev/null +++ b/src/plugins/coreplugin/tabpositionindicator.cpp @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtGui/QPainter> +#include <QtGui/QPaintEvent> +#include <QtGui/QBrush> +#include <QtGui/QPalette> + +#include "tabpositionindicator.h" + +using namespace Core::Internal; + +TabPositionIndicator::TabPositionIndicator() + : QWidget(0, Qt::ToolTip) +{ + +} + +void TabPositionIndicator::paintEvent(QPaintEvent *event) +{ + QPainter p(this); + QPen pen = p.pen(); + pen.setWidth(2); + pen.setColor(palette().color(QPalette::Active, QPalette::LinkVisited)); + pen.setStyle(Qt::DotLine); + p.setPen(pen); + p.drawLine(event->rect().topLeft(), event->rect().bottomLeft()); +} diff --git a/src/plugins/coreplugin/tabpositionindicator.h b/src/plugins/coreplugin/tabpositionindicator.h new file mode 100644 index 00000000000..db159a58d0d --- /dev/null +++ b/src/plugins/coreplugin/tabpositionindicator.h @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef TABPOSITIONINDICATOR_H +#define TABPOSITIONINDICATOR_H + +#include <QtGui/QWidget> + +#define TABPOSITIONINDICATOR_WIDTH 2 + +namespace Core { +namespace Internal { + +class TabPositionIndicator : public QWidget +{ + Q_OBJECT +public: + TabPositionIndicator(); + inline int indicatorWidth() { return TABPOSITIONINDICATOR_WIDTH; } + +private: + void paintEvent(QPaintEvent *event); +}; + +} // namespace Internal +} // namespace Core + +#endif //TABPOSITIONINDICATOR_H diff --git a/src/plugins/coreplugin/uniqueidmanager.cpp b/src/plugins/coreplugin/uniqueidmanager.cpp new file mode 100644 index 00000000000..802bd685e38 --- /dev/null +++ b/src/plugins/coreplugin/uniqueidmanager.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "uniqueidmanager.h" +#include "coreconstants.h" + +using namespace Core; + +UniqueIDManager* UniqueIDManager::m_instance = 0; + +UniqueIDManager::UniqueIDManager() +{ + m_instance = this; + m_uniqueIdentifiers.insert(Constants::C_GLOBAL, Constants::C_GLOBAL_ID); +} + +UniqueIDManager::~UniqueIDManager() +{ + m_instance = 0; +} + +bool UniqueIDManager::hasUniqueIdentifier(const QString &id) const +{ + return m_uniqueIdentifiers.contains(id); +} + +int UniqueIDManager::uniqueIdentifier(const QString &id) +{ + if (hasUniqueIdentifier(id)) + return m_uniqueIdentifiers.value(id); + + int uid = m_uniqueIdentifiers.count() + 1; + m_uniqueIdentifiers.insert(id, uid); + return uid; +} + +QString UniqueIDManager::stringForUniqueIdentifier(int uid) +{ + return m_uniqueIdentifiers.key(uid); +} diff --git a/src/plugins/coreplugin/uniqueidmanager.h b/src/plugins/coreplugin/uniqueidmanager.h new file mode 100644 index 00000000000..36b5df034d1 --- /dev/null +++ b/src/plugins/coreplugin/uniqueidmanager.h @@ -0,0 +1,62 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef UNIQUEIDMANAGER_H +#define UNIQUEIDMANAGER_H + +#include "core_global.h" + +#include <QtCore/QString> +#include <QtCore/QHash> + +namespace Core { + +class CORE_EXPORT UniqueIDManager +{ +public: + UniqueIDManager(); + ~UniqueIDManager(); + + static UniqueIDManager* instance() { return m_instance; } + + bool hasUniqueIdentifier(const QString &id) const; + int uniqueIdentifier(const QString &id); + QString stringForUniqueIdentifier(int uid); + +private: + QHash<QString, int> m_uniqueIdentifiers; + static UniqueIDManager *m_instance; +}; + +} // namespace Core + +#endif // UNIQUEIDMANAGER_H diff --git a/src/plugins/coreplugin/variablemanager.cpp b/src/plugins/coreplugin/variablemanager.cpp new file mode 100644 index 00000000000..adf65b64b41 --- /dev/null +++ b/src/plugins/coreplugin/variablemanager.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "variablemanager.h" + +using namespace Core; + +VariableManager *VariableManager::m_instance = 0; + +VariableManager::VariableManager(QObject *parent) : QObject(parent) +{ + m_instance = this; +} + +VariableManager::~VariableManager() +{ + m_instance = 0; +} + +void VariableManager::insert(const QString &variable, const QString &value) +{ + m_map.insert(variable, value); +} + +QString VariableManager::value(const QString &variable) +{ + return m_map.value(variable); +} + +QString VariableManager::value(const QString &variable, const QString &defaultValue) +{ + return m_map.value(variable, defaultValue); +} + +void VariableManager::remove(const QString &variable) +{ + m_map.remove(variable); +} + +QString VariableManager::resolve(const QString &stringWithVariables) +{ + QString result = stringWithVariables; + QMapIterator<QString, QString> i(m_map); + while (i.hasNext()) { + i.next(); + result.replace(QString("${%1}").arg(i.key()), i.value()); + } + return result; +} diff --git a/src/plugins/coreplugin/variablemanager.h b/src/plugins/coreplugin/variablemanager.h new file mode 100644 index 00000000000..a5e39522ab9 --- /dev/null +++ b/src/plugins/coreplugin/variablemanager.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef VARIABLEMANAGER_H +#define VARIABLEMANAGER_H + +#include "core_global.h" + +#include <QtCore/QObject> +#include <QtCore/QMap> +#include <QtCore/QString> + +namespace Core { + +class CORE_EXPORT VariableManager : public QObject +{ + Q_OBJECT + +public: + VariableManager(QObject *parent); + ~VariableManager(); + + static VariableManager* instance() { return m_instance; } + + void insert(const QString &variable, const QString &value); + QString value(const QString &variable); + QString value(const QString &variable, const QString &defaultValue); + void remove(const QString &variable); + QString resolve(const QString &stringWithVariables); + +private: + QMap<QString, QString> m_map; + static VariableManager *m_instance; +}; + +} // namespace Core + +#endif // VARIABLEMANAGER_H diff --git a/src/plugins/coreplugin/vcsmanager.cpp b/src/plugins/coreplugin/vcsmanager.cpp new file mode 100644 index 00000000000..36ba1ecd280 --- /dev/null +++ b/src/plugins/coreplugin/vcsmanager.cpp @@ -0,0 +1,113 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "vcsmanager.h" +#include "iversioncontrol.h" + +#include <extensionsystem/pluginmanager.h> + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QCoreApplication> + +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtGui/QMessageBox> + +namespace Core { + +struct VCSManagerPrivate { + QMap<QString, IVersionControl *> m_cachedMatches; +}; + +VCSManager::VCSManager() : + m_d(new VCSManagerPrivate) +{ +} + +VCSManager::~VCSManager() +{ + delete m_d; +} + +IVersionControl* VCSManager::findVersionControlForDirectory(const QString &directory) +{ + // first look into the cache + int pos = 0; + { // First try the whole name + QMap<QString, IVersionControl *>::const_iterator it = m_d->m_cachedMatches.constFind(directory); + if (it != m_d->m_cachedMatches.constEnd()) { + return it.value(); + } + } + + while(true) { + int index = directory.indexOf('/', pos); + if (index == -1) + break; + QString directoryPart = directory.left(index); + QMap<QString, IVersionControl *>::const_iterator it = m_d->m_cachedMatches.constFind(directoryPart); + if (it != m_d->m_cachedMatches.constEnd()) { + return it.value(); + } + pos = index+1; + } + + // ah nothing so ask the IVersionControls directly + QList<IVersionControl *> versionControls = ExtensionSystem::PluginManager::instance()->getObjects<IVersionControl>(); + foreach(IVersionControl * versionControl, versionControls) { + if (versionControl->managesDirectory(directory)) { + m_d->m_cachedMatches.insert(versionControl->findTopLevelForDirectory(directory), versionControl); + return versionControl; + } + } + return 0; +} + +void VCSManager::showDeleteDialog(const QString &fileName) +{ + IVersionControl *vc = findVersionControlForDirectory(QFileInfo(fileName).absolutePath()); + if (!vc) + return; + const QString title = QCoreApplication::translate("VCSManager", "Version Control"); + const QString msg = QCoreApplication::translate("VCSManager", + "Would you like to remove this file from the version control system?\n" + "Note: This might remove the local file."); + const QMessageBox::StandardButton button = + QMessageBox::question(0, title, msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (button == QMessageBox::Yes) { + vc->vcsDelete(fileName); + } +} + +} diff --git a/src/plugins/coreplugin/vcsmanager.h b/src/plugins/coreplugin/vcsmanager.h new file mode 100644 index 00000000000..3fad411ec49 --- /dev/null +++ b/src/plugins/coreplugin/vcsmanager.h @@ -0,0 +1,75 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef VCSMANAGER_H +#define VCSMANAGER_H + + +#include "core_global.h" +#include <QtCore/QString> + +namespace Core { + +struct VCSManagerPrivate; +class IVersionControl; + +// The VCSManager has only one notable function: +// findVersionControlFor(), which returns the IVersionControl * for a given +// filename. Note that the VCSManager assumes that if a IVersionControl * +// manages a directory, then it also manages all the files and all the +// subdirectories. +// +// It works by asking all IVersionControl * if they manage the file, and ask +// for the topmost directory it manages. This information is cached and +// VCSManager thus knows pretty fast which IVersionControl * is responsible. + +class CORE_EXPORT VCSManager +{ + Q_DISABLE_COPY(VCSManager) +public: + VCSManager(); + virtual ~VCSManager(); + + IVersionControl *findVersionControlForDirectory(const QString &directory); + + // Shows a confirmation dialog, + // wheter the file should also be deleted from revision control + // Calls sccDelete on the file + void showDeleteDialog(const QString &fileName); + +private: + VCSManagerPrivate *m_d; +}; + +} // namespace Core + +#endif // VCSMANAGER_H diff --git a/src/plugins/coreplugin/versiondialog.cpp b/src/plugins/coreplugin/versiondialog.cpp new file mode 100644 index 00000000000..45b8d6a8006 --- /dev/null +++ b/src/plugins/coreplugin/versiondialog.cpp @@ -0,0 +1,135 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "versiondialog.h" +#include "coreconstants.h" +#include "coreimpl.h" + +using namespace Core; +using namespace Core::Internal; +using namespace Core::Constants; + +#include <QtCore/QDate> +#include <QtCore/QFile> +#include <QtGui/QGridLayout> +#include <QtGui/QLabel> +#include <QtGui/QPushButton> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QTextBrowser> + +VersionDialog::VersionDialog(QWidget *parent): + QDialog(parent) +{ + // We need to set the window icon explicitly here since for some reason the + // application icon isn't used when the size of the dialog is fixed (at least not on X11/GNOME) + setWindowIcon(QIcon(":/qworkbench/images/qtcreator_logo_128.png")); + + setWindowTitle(tr("About Qt Creator")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + QGridLayout *layout = new QGridLayout(this); + layout->setSizeConstraint(QLayout::SetFixedSize); + + QString version = QLatin1String(IDE_VERSION_LONG); + version += QDate(2007, 25, 10).toString(Qt::SystemLocaleDate); + + const QString description = tr( + "<h3>Qt Creator %1</h3>" + "Based on Qt %2<br/>" + "<br/>" + "Built on " __DATE__ " at " __TIME__ "<br />" +#ifdef IDE_REVISION + "Using revision %5<br/>" +#endif + "<br/>" + "<br/>" + "Copyright 2006-%3 %4. All rights reserved.<br/>" + "<br/>" + "The program is provided AS IS with NO WARRANTY OF ANY KIND, " + "INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A " + "PARTICULAR PURPOSE.<br/>") + .arg(version, QLatin1String(QT_VERSION_STR), QLatin1String(IDE_YEAR), (QLatin1String(IDE_AUTHOR)) +#ifdef IDE_REVISION + , QString(IDE_REVISION_STR).left(10) +#endif + ); + + QLabel *copyRightLabel = new QLabel(description); + copyRightLabel->setWordWrap(true); + copyRightLabel->setOpenExternalLinks(true); + copyRightLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); + QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close); + Q_ASSERT(closeButton); + buttonBox->addButton(closeButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::RejectRole | QDialogButtonBox::AcceptRole)); + connect(buttonBox , SIGNAL(rejected()), this, SLOT(reject())); + + buttonBox->addButton(tr("Show License"), QDialogButtonBox::HelpRole); + connect(buttonBox , SIGNAL(helpRequested()), this, SLOT(popupLicense())); + + QLabel *logoLabel = new QLabel; + logoLabel->setPixmap(QPixmap(QLatin1String(":/qworkbench/images/qtcreator_logo_128.png"))); + layout->addWidget(logoLabel , 0, 0, 1, 1); + layout->addWidget(copyRightLabel, 0, 1, 4, 4); + layout->addWidget(buttonBox, 4, 0, 1, 5); +} + +void VersionDialog::popupLicense() +{ + QDialog *dialog = new QDialog(this); + dialog->setWindowTitle("License"); + QVBoxLayout *layout = new QVBoxLayout(dialog); + QTextBrowser *licenseBrowser = new QTextBrowser(dialog); + layout->addWidget(licenseBrowser); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); + connect(buttonBox , SIGNAL(rejected()), dialog, SLOT(reject())); + layout->addWidget(buttonBox); + + // Read file into string + ICore * core = CoreImpl::instance(); + Q_ASSERT(core != NULL); + QString fileName = core->resourcePath() + "/license.txt"; + QFile file(fileName); + + QString licenseText; + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + licenseText = "File '" + fileName + "' could not be read."; + else + licenseText = file.readAll(); + + licenseBrowser->setPlainText(licenseText); + + dialog->setMinimumSize(QSize(550, 690)); + dialog->exec(); + delete dialog; +} diff --git a/src/plugins/coreplugin/versiondialog.h b/src/plugins/coreplugin/versiondialog.h new file mode 100644 index 00000000000..aa17bb8d3ec --- /dev/null +++ b/src/plugins/coreplugin/versiondialog.h @@ -0,0 +1,53 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef VERSIONDIALOG_H +#define VERSIONDIALOG_H + +#include <QtGui/QDialog> + +namespace Core { +namespace Internal { + +class VersionDialog : public QDialog +{ + Q_OBJECT +public: + explicit VersionDialog(QWidget *parent); +private slots: + void popupLicense(); +}; + +} // namespace Internal +} // namespace Core + +#endif // VERSIONDIALOG_H diff --git a/src/plugins/coreplugin/viewmanager.cpp b/src/plugins/coreplugin/viewmanager.cpp new file mode 100644 index 00000000000..ea520e0b253 --- /dev/null +++ b/src/plugins/coreplugin/viewmanager.cpp @@ -0,0 +1,132 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "viewmanager.h" + +#include "coreconstants.h" +#include "mainwindow.h" +#include "uniqueidmanager.h" +#include "iview.h" + +#include <QtCore/QSettings> +#include <QtGui/QAction> +#include <QtGui/QActionGroup> +#include <QtGui/QDockWidget> +#include <QtGui/QVBoxLayout> +#include <QtGui/QMenu> +#include <QtGui/QStatusBar> +#include <QtGui/QLabel> +#include <QtGui/QComboBox> +#include <QtGui/QStackedWidget> +#include <QtGui/QToolButton> + +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/actionmanager/icommand.h> +#include <extensionsystem/ExtensionSystemInterfaces> +#include <aggregation/aggregate.h> + +#include <QtGui/QHBoxLayout> + +using namespace Core; +using namespace Core::Internal; + +ViewManager::ViewManager(MainWindow *mainWnd) : + ViewManagerInterface(mainWnd), + m_mainWnd(mainWnd) +{ + for(int i = 0; i< 3; ++i) { + QWidget *w = new QWidget(); + m_mainWnd->statusBar()->insertPermanentWidget(i, w); + w->setLayout(new QHBoxLayout); + w->setVisible(true); + w->layout()->setMargin(0); + m_statusBarWidgets.append(w); + } + QLabel *l = new QLabel(); + m_mainWnd->statusBar()->insertPermanentWidget(3, l, 1); +} + +ViewManager::~ViewManager() +{ +} + +void ViewManager::init() +{ + connect(ExtensionSystem::PluginManager::instance(), SIGNAL(objectAdded(QObject*)), + this, SLOT(objectAdded(QObject*))); + connect(ExtensionSystem::PluginManager::instance(), SIGNAL(aboutToRemoveObject(QObject*)), + this, SLOT(aboutToRemoveObject(QObject*))); +} + +void ViewManager::objectAdded(QObject *obj) +{ + IView * view = Aggregation::query<IView>(obj); + if (!view) + return; + + QWidget *viewWidget = 0; + viewWidget = view->widget(); + m_statusBarWidgets.at(view->defaultPosition())->layout()->addWidget(viewWidget); + + m_viewMap.insert(view, viewWidget); + viewWidget->setObjectName(view->uniqueViewName()); + m_mainWnd->addContextObject(view); +} + +void ViewManager::aboutToRemoveObject(QObject *obj) +{ + IView * view = Aggregation::query<IView>(obj); + if(!view) + return; + m_mainWnd->removeContextObject(view); +} + +void ViewManager::extensionsInitalized() +{ + QSettings *settings = m_mainWnd->settings(); + m_mainWnd->restoreState(settings->value(QLatin1String("ViewGroup_Default"), QByteArray()).toByteArray()); +} + +void ViewManager::saveSettings(QSettings *settings) +{ + settings->setValue(QLatin1String("ViewGroup_Default"), m_mainWnd->saveState()); +} + +IView * ViewManager::view(const QString & id) +{ + QList<IView *> list = m_mainWnd->pluginManager()->getObjects<IView>(); + foreach (IView * view, list) { + if (view->uniqueViewName() == id) + return view; + } + return 0; +} diff --git a/src/plugins/coreplugin/viewmanager.h b/src/plugins/coreplugin/viewmanager.h new file mode 100644 index 00000000000..615a867beed --- /dev/null +++ b/src/plugins/coreplugin/viewmanager.h @@ -0,0 +1,86 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef VIEWMANAGER_H +#define VIEWMANAGER_H + +#include "viewmanagerinterface.h" + +#include <QtCore/QMap> +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE +class QAction; +class QSettings; +class QMainWindow; +class QComboBox; +class QStackedWidget; +QT_END_NAMESPACE + +namespace Core { + +class UniqueIDManager; + +namespace Internal { + +class MainWindow; +class NavigationWidget; + +class ViewManager + : public Core::ViewManagerInterface +{ + Q_OBJECT + +public: + ViewManager(MainWindow *mainWnd); + ~ViewManager(); + + void init(); + void extensionsInitalized(); + void saveSettings(QSettings *settings); + + IView * view(const QString & id); +private slots: + void objectAdded(QObject *obj); + void aboutToRemoveObject(QObject *obj); + +private: + QMap<Core::IView *, QWidget *> m_viewMap; + + MainWindow *m_mainWnd; + QList<QWidget *> m_statusBarWidgets; +}; + +} // namespace Internal +} // namespace Core + +#endif //VIEWMANAGER_H diff --git a/src/plugins/coreplugin/viewmanagerinterface.h b/src/plugins/coreplugin/viewmanagerinterface.h new file mode 100644 index 00000000000..0b53e02908d --- /dev/null +++ b/src/plugins/coreplugin/viewmanagerinterface.h @@ -0,0 +1,64 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef VIEWMANAGERINTERFACE_H +#define VIEWMANAGERINTERFACE_H + +#include <coreplugin/core_global.h> +#include <QtCore/QObject> +#include <QtGui/QKeySequence> + +QT_BEGIN_NAMESPACE +class QIcon; +class QString; +QT_END_NAMESPACE + +namespace Core { + +class IView; + +class CORE_EXPORT ViewManagerInterface + : public QObject +{ + Q_OBJECT + +public: + ViewManagerInterface(QObject *parent = 0) : QObject(parent) { } + virtual ~ViewManagerInterface() { } + + // returns the view @p id + virtual IView * view(const QString & id) = 0; +}; + +} //namespace Core + +#endif //VIEWMANAGERINTERFACE_H diff --git a/src/plugins/coreplugin/welcomemode.cpp b/src/plugins/coreplugin/welcomemode.cpp new file mode 100644 index 00000000000..e6c69c0001c --- /dev/null +++ b/src/plugins/coreplugin/welcomemode.cpp @@ -0,0 +1,271 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "welcomemode.h" +#include "coreconstants.h" +#include "uniqueidmanager.h" +#include "coreimpl.h" +#include "modemanager.h" + +#ifdef QT_WEBKIT +#include <QtWebKit/QWebView> +#include <QtGui/QApplication> +#include <QtCore/QFileInfo> +#else +#include <QtGui/QLabel> +#endif +#include <QtGui/QToolBar> +#include <QtGui/QDesktopServices> + +#include <QtCore/QFile> +#include <QtCore/QTextStream> +#include <QtCore/QDebug> +#include <QtCore/QUrl> + +namespace Core { +namespace Internal { + +static QString readFile(const QString &name) +{ + QFile f(name); + if (!f.open(QIODevice::ReadOnly)) { + qWarning("Unable to open %s: %s", name.toUtf8().constData(), f.errorString().toUtf8().constData()); + return QString(); + } + QTextStream ts(&f); + return ts.readAll(); +} + +struct WelcomeModePrivate { + WelcomeModePrivate(); + + QWidget *m_widget; +#ifdef QT_WEBKIT + QWebView *m_webview; +#else + QLabel *m_label; +#endif + + WelcomeMode::WelcomePageData lastData; + + const QString m_htmlTemplate; + const QString m_sessionHtmlTemplate; + const QString m_projectHtmlTemplate; + const QUrl m_baseUrl; +}; + +WelcomeModePrivate::WelcomeModePrivate() : + m_widget(new QWidget), +#ifdef QT_WEBKIT + m_webview(new QWebView), +#else + m_label(new QLabel), +#endif + m_htmlTemplate(readFile(QLatin1String(":/qworkbench/html/welcome.html"))), + m_sessionHtmlTemplate(readFile(QLatin1String(":/qworkbench/html/recent_sessions.html"))), + m_projectHtmlTemplate(readFile(QLatin1String(":/qworkbench/html/recent_projects.html"))), + m_baseUrl(QUrl(QLatin1String("qrc:/qworkbench/html/welcome.html"))) +{ +} + +#ifndef QT_WEBKIT + +const char *LABEL = "<center><table><tr><td><img src=\":/qworkbench/html/images/product_logo.png\"/></td><td width=300>" + "<h2><br/><br/>Welcome</h2><p> Qt Creator is an intuitive, modern cross platform IDE that enables " + "developers to create graphically appealing applications for desktop, " + "embedded, and mobile devices. " + "<p><font color=\"red\">(This startup page lacks features due to disabled webkit support)</font>" + "</td></tr></table>"; + +#endif +// --- WelcomePageData + +bool WelcomeMode::WelcomePageData::operator==(const WelcomePageData &rhs) const +{ + return previousSession == rhs.previousSession + && activeSession == rhs.activeSession + && sessionList == rhs.sessionList + && projectList == rhs.projectList; +} + +bool WelcomeMode::WelcomePageData::operator!=(const WelcomePageData &rhs) const +{ + return previousSession != rhs.previousSession + || activeSession != rhs.activeSession + || sessionList != rhs.sessionList + || projectList != rhs.projectList; +} + +QDebug operator<<(QDebug dgb, const WelcomeMode::WelcomePageData &d) +{ + dgb.nospace() << "PreviousSession=" << d.previousSession + << " activeSession=" << d.activeSession + << " sessionList=" << d.sessionList + << " projectList=" << d.projectList; + return dgb; +} + +// --- WelcomeMode +WelcomeMode::WelcomeMode() : + m_d(new WelcomeModePrivate) +{ + QVBoxLayout *l = new QVBoxLayout(m_d->m_widget); + l->setMargin(0); + l->setSpacing(0); + l->addWidget(new QToolBar(m_d->m_widget)); +#ifdef QT_WEBKIT + connect(m_d->m_webview, SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); + + WelcomePageData welcomePageData; + updateWelcomePage(welcomePageData); + + l->addWidget(m_d->m_webview); + +#else + m_d->m_label->setWordWrap(true); + m_d->m_label->setAlignment(Qt::AlignCenter); + m_d->m_label->setText(LABEL); + l->addWidget(m_d->m_label); +#endif +} + +WelcomeMode::~WelcomeMode() +{ + delete m_d; +} + +QString WelcomeMode::name() const +{ + return QLatin1String("Welcome"); +} + +QIcon WelcomeMode::icon() const +{ + return QIcon(QLatin1String(":/qworkbench/images/qtcreator_logo_32.png")); +} + +int WelcomeMode::priority() const +{ + return Constants::P_MODE_WELCOME; +} + +QWidget* WelcomeMode::widget() +{ + return m_d->m_widget; +} + +const char* WelcomeMode::uniqueModeName() const +{ + return Constants::MODE_WELCOME; +} + +QList<int> WelcomeMode::context() const +{ + static QList<int> contexts = QList<int>() + << CoreImpl::instance()->uniqueIDManager()->uniqueIdentifier(Constants::C_WELCOME_MODE); + return contexts; +} + +void WelcomeMode::updateWelcomePage(const WelcomePageData &welcomePageData) +{ +// should really only modify the DOM tree + +#ifndef QT_WEBKIT + Q_UNUSED(welcomePageData); +#else + + // Update only if data are modified + if (welcomePageData == m_d->lastData) + return; + m_d->lastData = welcomePageData; + + QString html = m_d->m_htmlTemplate; + + if (!welcomePageData.previousSession.isEmpty() || !welcomePageData.projectList.isEmpty()) { + QString sessionHtml = m_d->m_sessionHtmlTemplate; + sessionHtml.replace(QLatin1String("LAST_SESSION"), welcomePageData.previousSession); + + if (welcomePageData.sessionList.count() > 1) { + QString sessions; + foreach (QString s, welcomePageData.sessionList) { + QString last; + if (s == welcomePageData.previousSession) + last = tr(" (last session)"); + sessions += QString::fromLatin1("<li><p><a href=\"gh-session:%1\">%2%3</a></p></li>").arg(s, s, last); + } + sessionHtml.replace(QLatin1String("<!-- RECENT SESSIONS LIST -->"), sessions); + } + html.replace(QLatin1String("<!-- RECENT SESSIONS -->"), sessionHtml); + + QString projectHtml = m_d->m_projectHtmlTemplate; + { + QString projects; + QTextStream str(&projects); + foreach (const QString &s, welcomePageData.projectList) { + const QFileInfo fi(s); + str << "<li><p><a href=\"gh-project:" << s << "\" title=\"" + << fi.absolutePath() << "\">" << fi.fileName() << "</a></p></li>\n"; + } + projectHtml.replace(QLatin1String("<!-- RECENT PROJECTS LIST -->"), projects); + } + html.replace(QLatin1String("<!-- RECENT PROJECTS -->"), projectHtml); + } + + m_d->m_webview->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); + m_d->m_webview->setHtml(html, m_d->m_baseUrl); +#endif +} + +void WelcomeMode::linkClicked(const QUrl &url) +{ + QString scheme = url.scheme(); + Core::ModeManager *modeManager = CoreImpl::instance()->modeManager(); + if (scheme.startsWith(QLatin1String("gh"))) { + QString s = url.toString(QUrl::RemoveScheme); + if (scheme == QLatin1String("gh")) { + emit requestHelp(s); + } else if (scheme == QLatin1String("gh-project")) { + emit requestProject(s); + if (modeManager->currentMode() == this) + modeManager->activateMode(Core::Constants::MODE_EDIT); + } else if (scheme == QLatin1String("gh-session")) { + emit requestSession(s); + if (modeManager->currentMode() == this) + modeManager->activateMode(Core::Constants::MODE_EDIT); + } + } else { + QDesktopServices::openUrl(url); + } +} + +} // namespace Internal +} // namespace Core diff --git a/src/plugins/coreplugin/welcomemode.h b/src/plugins/coreplugin/welcomemode.h new file mode 100644 index 00000000000..416e54c2dc7 --- /dev/null +++ b/src/plugins/coreplugin/welcomemode.h @@ -0,0 +1,98 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef WELCOMEMODE_H +#define WELCOMEMODE_H + +#include <coreplugin/imode.h> + +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE +class QWidget; +class QUrl; +class QLabel; +QT_END_NAMESPACE + +namespace Core { + +namespace Internal { + +struct WelcomeModePrivate; + +class CORE_EXPORT WelcomeMode : public Core::IMode +{ + Q_OBJECT + +public: + WelcomeMode(); + ~WelcomeMode(); + + struct WelcomePageData{ + bool operator==(const WelcomePageData &rhs) const; + bool operator!=(const WelcomePageData &rhs) const; + + QString previousSession; + QString activeSession; + QStringList sessionList; + QStringList projectList; + }; + + void updateWelcomePage(const WelcomePageData &welcomePageData); + + // IMode + QString name() const; + QIcon icon() const; + int priority() const; + QWidget* widget(); + const char* uniqueModeName() const; + QList<int> context() const; + void activated(); + QString contextHelpId() const + { return QLatin1String("Qt Creator"); } + +signals: + void requestProject(const QString &project); + void requestSession(const QString &session); + void requestHelp(const QString &help); + +private slots: + void linkClicked(const QUrl &url); + +private: + WelcomeModePrivate *m_d; +}; + +} // namespace Internal +} // namespace Core + +#endif // WELCOMEMODE_H diff --git a/src/plugins/cpaster/CodePaster.pluginspec b/src/plugins/cpaster/CodePaster.pluginspec new file mode 100644 index 00000000000..2ddd73902e5 --- /dev/null +++ b/src/plugins/cpaster/CodePaster.pluginspec @@ -0,0 +1,12 @@ +<plugin name="CodePaster" version="0.1" compatVersion="0.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Codepaster plugin for pushing/fetching diff from server</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="TextEditor" version="0.9.1"/> + <dependency name="ProjectExplorer" version="0.9.1"/> + <dependency name="Core" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/cpaster/cpaster.pro b/src/plugins/cpaster/cpaster.pro new file mode 100644 index 00000000000..4ecdc545371 --- /dev/null +++ b/src/plugins/cpaster/cpaster.pro @@ -0,0 +1,15 @@ +QT += network +TEMPLATE = lib +TARGET = CodePaster + +include(../../qworkbenchplugin.pri) +include(cpaster_dependencies.pri) + +HEADERS += cpasterplugin.h \ + settingspage.h +SOURCES += cpasterplugin.cpp \ + settingspage.cpp +FORMS += settingspage.ui \ + pasteselect.ui + +include(../../../shared/cpaster/cpaster.pri) diff --git a/src/plugins/cpaster/cpaster_dependencies.pri b/src/plugins/cpaster/cpaster_dependencies.pri new file mode 100644 index 00000000000..5c489ae1c49 --- /dev/null +++ b/src/plugins/cpaster/cpaster_dependencies.pri @@ -0,0 +1,3 @@ +include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/texteditor/texteditor.pri) +include(../../plugins/coreplugin/coreplugin.pri) diff --git a/src/plugins/cpaster/cpasterplugin.cpp b/src/plugins/cpaster/cpasterplugin.cpp new file mode 100644 index 00000000000..ee62688f3d4 --- /dev/null +++ b/src/plugins/cpaster/cpasterplugin.cpp @@ -0,0 +1,304 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cpasterplugin.h" + +#include "ui_pasteselect.h" + +#include "splitter.h" +#include "view.h" + +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/filemanager.h> +#include <coreplugin/messagemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/itexteditor.h> +#include <coreplugin/messageoutputwindow.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QDebug> +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QClipboard> +#include <QtGui/QMenu> +#include <QtGui/QMessageBox> +#include <QtGui/QMainWindow> +#include <QtGui/QListWidget> + +using namespace CodePaster; +using namespace Core; +using namespace TextEditor; + +Core::ICore *gCoreInstance = NULL; + +CodepasterPlugin::CodepasterPlugin() + : m_settingsPage(0) + , m_fetcher(0) + , m_poster(0) +{ +} + +CodepasterPlugin::~CodepasterPlugin() +{ + if (m_settingsPage) { + removeObject(m_settingsPage); + delete m_settingsPage; + m_settingsPage = 0; + } +} + +bool CodepasterPlugin::initialize(const QStringList &arguments, QString *error_message) +{ + Q_UNUSED(arguments); + Q_UNUSED(error_message); + + gCoreInstance = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + + // Create the globalcontext list to register actions accordingly + QList<int> globalcontext; + globalcontext << gCoreInstance->uniqueIDManager()-> + uniqueIdentifier(Core::Constants::C_GLOBAL); + + // Create the settings Page + m_settingsPage = new SettingsPage(); + addObject(m_settingsPage); + + //register actions + Core::ActionManagerInterface *actionManager = gCoreInstance->actionManager(); + + Core::IActionContainer *toolsContainer = + actionManager->actionContainer(Core::Constants::M_TOOLS); + + Core::IActionContainer *cpContainer = + actionManager->createMenu(QLatin1String("CodePaster")); + cpContainer->menu()->setTitle(tr("&CodePaster")); + toolsContainer->addMenu(cpContainer); + + Core::ICommand *command; + + m_postAction = new QAction(tr("Paste snippet..."), this); + command = actionManager->registerAction(m_postAction, "CodePaster.post", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+C,Alt+P"))); + connect(m_postAction, SIGNAL(triggered()), this, SLOT(post())); + cpContainer->addAction(command); + + m_fetchAction = new QAction(tr("Fetch snippet..."), this); + command = actionManager->registerAction(m_fetchAction, "CodePaster.fetch", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+C,Alt+F"))); + connect(m_fetchAction, SIGNAL(triggered()), this, SLOT(fetch())); + cpContainer->addAction(command); + + return true; +} + +void CodepasterPlugin::extensionsInitialized() +{ + m_projectExplorer = ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::ProjectExplorerPlugin>(); +} + +void CodepasterPlugin::post() +{ + if (m_poster) + delete m_poster; + IEditor* editor = gCoreInstance->editorManager()->currentEditor(); + ITextEditor* textEditor = qobject_cast<ITextEditor*>(editor); + if (!textEditor) + return; + + QString data = textEditor->selectedText(); + if (!data.isEmpty()) { + QChar *uc = data.data(); + QChar *e = uc + data.size(); + + for (; uc != e; ++uc) { + switch (uc->unicode()) { + case 0xfdd0: // QTextBeginningOfFrame + case 0xfdd1: // QTextEndOfFrame + case QChar::ParagraphSeparator: + case QChar::LineSeparator: + *uc = QLatin1Char('\n'); + break; + case QChar::Nbsp: + *uc = QLatin1Char(' '); + break; + default: + ; + } + } + } else + data = textEditor->contents(); + + FileDataList lst = splitDiffToFiles(data.toLatin1()); + QString username = m_settingsPage->username(); + QString description; + QString comment; + + View view(0); + if (!view.show(username, description, comment, lst)) + return; // User canceled post + + username = view.getUser(); + description = view.getDescription(); + comment = view.getComment(); + data = view.getContent(); + + // Submit to codepaster + m_poster = new CustomPoster(m_settingsPage->serverUrl().toString()); + + // Copied from cpaster. Otherwise lineendings will screw up + if (!data.contains("\r\n")) { + if (data.contains('\n')) + data.replace('\n', "\r\n"); + else if (data.contains('\r')) + data.replace('\r', "\r\n"); + } + m_poster->post(description, comment, data, username); +} + +void CodepasterPlugin::fetch() +{ + if (m_fetcher) + delete m_fetcher; + m_fetcher = new CustomFetcher(m_settingsPage->serverUrl().toString()); + + QDialog dialog; + Ui_PasteSelectDialog ui; + ui.setupUi(&dialog); + + ui.listWidget->addItems(QStringList() << "Waiting for items"); + ui.listWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); + ui.listWidget->setFrameStyle(QFrame::NoFrame); + m_fetcher->list(ui.listWidget); + + int result = dialog.exec(); + if (!result) + return; + bool ok; + QStringList list = ui.pasteEdit->text().split(QLatin1Char(' ')); + int pasteID = !list.isEmpty() ? list.first().toInt(&ok) : -1; + if (!ok || pasteID <= 0) + return; + + delete m_fetcher; + m_fetcher = new CustomFetcher(m_settingsPage->serverUrl().toString()); + m_fetcher->fetch(pasteID); +} + +CustomFetcher::CustomFetcher(const QString &host) + : Fetcher(host) + , m_host(host) + , m_listWidget(0) + , m_id(-1) + , m_customError(false) +{ + // cpaster calls QCoreApplication::exit which we want to avoid here + disconnect(this, SIGNAL(requestFinished(int,bool)) + ,this, SLOT(gotRequestFinished(int,bool))); + connect(this, SIGNAL(requestFinished(int,bool)) + , SLOT(customRequestFinished(int,bool))); +} + +void CustomFetcher::customRequestFinished(int, bool error) +{ + m_customError = error; + if (m_customError || hadError()) { + QMessageBox::warning(0, QLatin1String("CodePaster Error") + , QLatin1String("Could not fetch code") + , QMessageBox::Ok); + return; + } + + QByteArray data = body(); + if (!m_listWidget) { + QString title = QString::fromLatin1("Code Paster: %1").arg(m_id); + gCoreInstance->editorManager()->newFile(Core::Constants::K_DEFAULT_TEXT_EDITOR + , &title, data); + } else { + m_listWidget->clear(); + QStringList lines = QString(data).split(QLatin1Char('\n')); + m_listWidget->addItems(lines); + m_listWidget = 0; + } +} + +int CustomFetcher::fetch(int pasteID) +{ + m_id = pasteID; + return Fetcher::fetch(pasteID); +} + +void CustomFetcher::list(QListWidget* list) +{ + m_listWidget = list; + QString url = QLatin1String("https://"); + url += m_host; + url += QLatin1String("/?command=browse&format=raw"); + Fetcher::fetch(url); +} + +CustomPoster::CustomPoster(const QString &host + , bool copyToClipboard + , bool displayOutput) + : Poster(host) + , m_copy(copyToClipboard) + , m_output(displayOutput) +{ + // cpaster calls QCoreApplication::exit which we want to avoid here + disconnect(this, SIGNAL(requestFinished(int,bool)) + ,this, SLOT(gotRequestFinished(int,bool))); + connect(this, SIGNAL(requestFinished(int,bool)) + , SLOT(customRequestFinished(int,bool))); +} + +void CustomPoster::customRequestFinished(int, bool error) +{ + if (!error) { + if (m_copy) + QApplication::clipboard()->setText(pastedUrl()); + gCoreInstance->messageManager()->printToOutputPane(pastedUrl(), m_output); + } else + QMessageBox::warning(0, "Code Paster Error", "Some error occured while posting", QMessageBox::Ok); +#if 0 // Figure out how to access + Core::Internal::MessageOutputWindow* messageWindow = + ExtensionSystem::PluginManager::instance()->getObject<Core::Internal::MessageOutputWindow>(); + if (!messageWindow) + qDebug() << "Pasted at:" << pastedUrl(); + + messageWindow->append(pastedUrl()); + messageWindow->setFocus(); +#endif +} + +Q_EXPORT_PLUGIN(CodepasterPlugin) diff --git a/src/plugins/cpaster/cpasterplugin.h b/src/plugins/cpaster/cpasterplugin.h new file mode 100644 index 00000000000..394dff4226a --- /dev/null +++ b/src/plugins/cpaster/cpasterplugin.h @@ -0,0 +1,119 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CodepasterPlugin_H +#define CodepasterPlugin_H + +#include "settingspage.h" +#include "fetcher.h" +#include "poster.h" + +#include <coreplugin/editormanager/ieditorfactory.h> +#include <coreplugin/icorelistener.h> +#include <projectexplorer/ProjectExplorerInterfaces> +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE +class QListWidget; +QT_END_NAMESPACE + +namespace CodePaster { + +class CustomFetcher; +class CustomPoster; + +class CodepasterPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + CodepasterPlugin(); + ~CodepasterPlugin(); + + bool initialize(const QStringList &arguments + , QString *error_message); + void extensionsInitialized(); + +public slots: + void post(); + void fetch(); + +private: + + QAction *m_postAction; + QAction *m_fetchAction; + ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer; + SettingsPage *m_settingsPage; + CustomFetcher *m_fetcher; + CustomPoster *m_poster; +}; + +class CustomFetcher : public Fetcher +{ + Q_OBJECT +public: + CustomFetcher(const QString &host); + + int fetch(int pasteID); + inline bool hadCustomError() { return m_customError; } + + void list(QListWidget*); +private slots: + void customRequestFinished(int id, bool error); + +private: + QString m_host; + QListWidget *m_listWidget; + int m_id; + bool m_customError; +}; + +class CustomPoster : public Poster +{ + Q_OBJECT +public: + CustomPoster(const QString &host + , bool copyToClipboard = true + , bool displayOutput = true); + +private slots: + void customRequestFinished(int id, bool error); +private: + bool m_copy; + bool m_output; +}; + +} // namespace CodePaster + +#endif diff --git a/src/plugins/cpaster/pasteselect.ui b/src/plugins/cpaster/pasteselect.ui new file mode 100644 index 00000000000..6906f135b77 --- /dev/null +++ b/src/plugins/cpaster/pasteselect.ui @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>CodePaster::PasteSelectDialog</class> + <widget class="QDialog" name="CodePaster::PasteSelectDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>451</width> + <height>215</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <item> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Paste:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="pasteEdit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QListWidget" name="listWidget"/> + </item> + <item> + <widget class="QDialogButtonBox" name="buttons"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttons</sender> + <signal>accepted()</signal> + <receiver>CodePaster::PasteSelectDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>266</x> + <y>205</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttons</sender> + <signal>rejected()</signal> + <receiver>CodePaster::PasteSelectDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>334</x> + <y>205</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>listWidget</sender> + <signal>currentTextChanged(QString)</signal> + <receiver>pasteEdit</receiver> + <slot>setText(QString)</slot> + <hints> + <hint type="sourcelabel"> + <x>247</x> + <y>123</y> + </hint> + <hint type="destinationlabel"> + <x>308</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>listWidget</sender> + <signal>doubleClicked(QModelIndex)</signal> + <receiver>CodePaster::PasteSelectDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>90</x> + <y>86</y> + </hint> + <hint type="destinationlabel"> + <x>3</x> + <y>81</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/cpaster/settingspage.cpp b/src/plugins/cpaster/settingspage.cpp new file mode 100644 index 00000000000..95458eb41de --- /dev/null +++ b/src/plugins/cpaster/settingspage.cpp @@ -0,0 +1,117 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "settingspage.h" + +#include <coreplugin/icore.h> +#include <extensionsystem/pluginmanager.h> + +#include <QtCore/QSettings> +#include <QtGui/QLineEdit> +#include <QtGui/QFileDialog> +#include <QtCore/QDebug> +#include <QtCore/QVariant> + +using namespace CodePaster; + +SettingsPage::SettingsPage() +{ + Core::ICore *coreIFace = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + if (coreIFace) + m_settings = coreIFace->settings(); + + if (m_settings) { + m_settings->beginGroup("CodePaster"); + m_username = m_settings->value("UserName", qgetenv("USER")).toString(); + m_server = m_settings->value("Server", "<no url>").toUrl(); + m_copy = m_settings->value("CopyToClipboard", true).toBool(); + m_output = m_settings->value("DisplayOutput", true).toBool(); + m_settings->endGroup(); + } +} + +QString SettingsPage::name() const +{ + return "General"; +} + +QString SettingsPage::category() const +{ + return "CodePaster"; +} + +QString SettingsPage::trCategory() const +{ + return tr("CodePaster"); +} + +QWidget *SettingsPage::createPage(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + m_ui.setupUi(w); + m_ui.userEdit->setText(m_username); + m_ui.serverEdit->setText(m_server.toString()); + m_ui.clipboardBox->setChecked(m_copy); + m_ui.displayBox->setChecked(m_output); + return w; +} + +void SettingsPage::finished(bool accepted) +{ + if (!accepted) + return; + + m_username = m_ui.userEdit->text(); + m_server = QUrl(m_ui.serverEdit->text()); + m_copy = m_ui.clipboardBox->isChecked(); + m_output = m_ui.displayBox->isChecked(); + + if (!m_settings) + return; + + m_settings->beginGroup("CodePaster"); + m_settings->setValue("UserName", m_username); + m_settings->setValue("Server", m_server); + m_settings->setValue("CopyToClipboard", m_copy); + m_settings->setValue("DisplayOutput", m_output); + m_settings->endGroup(); +} + +QString SettingsPage::username() const +{ + return m_username; +} + +QUrl SettingsPage::serverUrl() const +{ + return m_server; +} diff --git a/src/plugins/cpaster/settingspage.h b/src/plugins/cpaster/settingspage.h new file mode 100644 index 00000000000..f80b2904619 --- /dev/null +++ b/src/plugins/cpaster/settingspage.h @@ -0,0 +1,81 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SETTINGSPAGE_H +#define SETTINGSPAGE_H + +#include <QtCore/QUrl> +#include <QtGui/QWidget> + +#include <coreplugin/dialogs/ioptionspage.h> + +#include "ui_settingspage.h" + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace CodePaster { + +class SettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + SettingsPage(); + + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget* createPage(QWidget *parent); + void finished(bool accepted); + + QString username() const; + QUrl serverUrl() const; + + bool copyToClipBoard() const; + bool displayOutput() const; + +private: + Ui_SettingsPage m_ui; + QSettings *m_settings; + + QString m_username; + QUrl m_server; + bool m_copy; + bool m_output; +}; + +} //namespace CodePaster + +#endif diff --git a/src/plugins/cpaster/settingspage.ui b/src/plugins/cpaster/settingspage.ui new file mode 100644 index 00000000000..0a085c75bcd --- /dev/null +++ b/src/plugins/cpaster/settingspage.ui @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>CodePaster::SettingsPage</class> + <widget class="QWidget" name="CodePaster::SettingsPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>341</width> + <height>258</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>CodePaster Server:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="serverEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Username:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="userEdit"/> + </item> + <item row="2" column="0"> + <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> + <item row="2" column="1"> + <widget class="QCheckBox" name="clipboardBox"> + <property name="text"> + <string>Copy Paste URL to clipboard</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <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> + <item row="3" column="1"> + <widget class="QCheckBox" name="displayBox"> + <property name="text"> + <string>Display Output Pane after sending a post</string> + </property> + </widget> + </item> + <item row="4" column="0" colspan="2"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>223</width> + <height>100</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/cppeditor/CppEditor.mimetypes.xml b/src/plugins/cppeditor/CppEditor.mimetypes.xml new file mode 100644 index 00000000000..cd79d5f441e --- /dev/null +++ b/src/plugins/cppeditor/CppEditor.mimetypes.xml @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="text/x-csrc"> + <sub-class-of type="text/plain"/> + <comment>C Source file</comment> + <comment xml:lang="bg">Изходен код на C</comment> + <comment xml:lang="ca">codi font en C</comment> + <comment xml:lang="cs">Zdrojový kód v C</comment> + <comment xml:lang="cy">Ffynhonnell Rhaglen C</comment> + <comment xml:lang="da">C-kildekode</comment> + <comment xml:lang="de">C-Quelltext</comment> + <comment xml:lang="el">πηγαίος κώδικας C</comment> + <comment xml:lang="eo">C-fontkodo</comment> + <comment xml:lang="es">código fuente en C</comment> + <comment xml:lang="eu">C iturburu-kodea</comment> + <comment xml:lang="fi">C-lähdekoodi</comment> + <comment xml:lang="fr">code source C</comment> + <comment xml:lang="hu">C-forráskód</comment> + <comment xml:lang="it">Codice sorgente C</comment> + <comment xml:lang="ja">C ソースコード</comment> + <comment xml:lang="ko">C 소스 코드</comment> + <comment xml:lang="lt">C pradinis kodas</comment> + <comment xml:lang="ms">Kod sumber C</comment> + <comment xml:lang="nb">C-kildekode</comment> + <comment xml:lang="nl">C-broncode</comment> + <comment xml:lang="nn">C-kjeldekode</comment> + <comment xml:lang="pl">Kod źródłowy w C</comment> + <comment xml:lang="pt">código fonte C</comment> + <comment xml:lang="pt_BR">Código fonte C</comment> + <comment xml:lang="ru">программа на языке C</comment> + <comment xml:lang="sq">Kod burues C</comment> + <comment xml:lang="sr">C изворни ко̂д</comment> + <comment xml:lang="sv">C-källkod</comment> + <comment xml:lang="uk">Вихідний код на мові C</comment> + <comment xml:lang="zh_CN">C 源代码</comment> + <comment xml:lang="zh_TW">C 源代碼</comment> + <glob pattern="*.c"/> + </mime-type> + + <!-- A C Header file is virtually undistinguishable from the C++ header --> + <mime-type type="text/x-chdr"> + <sub-class-of type="text/x-csrc"/> + <comment>C Header file</comment> + <comment xml:lang="bg">Заглавен файл на C</comment> + <comment xml:lang="cs">Hlavička v C</comment> + <comment xml:lang="de">C-Header</comment> + <comment xml:lang="es">cabecera de código fuente en C</comment> + <comment xml:lang="eu">C goiburua</comment> + <comment xml:lang="fi">C-otsake</comment> + <comment xml:lang="fr">en-tête C</comment> + <comment xml:lang="hu">C fejléc</comment> + <comment xml:lang="it">Header C</comment> + <comment xml:lang="ko">C 헤더</comment> + <comment xml:lang="nb">C-kildekodeheader</comment> + <comment xml:lang="nn">C-hovud</comment> + <comment xml:lang="pl">Plik nagłówkowy w C</comment> + <comment xml:lang="sv">C-huvud</comment> + <comment xml:lang="uk">Файл заголовків на C</comment> + <glob pattern="*.h"/> + </mime-type> + + <!-- Those are used to find matching headers by the CppTools plugin, + so, they should match --> + <mime-type type="text/x-c++hdr"> + <sub-class-of type="text/x-chdr"/> + <comment>C++ Header file</comment> + <comment>C++ header</comment> + <comment xml:lang="bg">Заглавен файл на C++</comment> + <comment xml:lang="cs">Hlavička v C++</comment> + <comment xml:lang="de">C++-Header</comment> + <comment xml:lang="es">cabecera de código fuente en C++</comment> + <comment xml:lang="eu">C++ goiburua</comment> + <comment xml:lang="fi">C++-otsake</comment> + <comment xml:lang="fr">en-tête C++</comment> + <comment xml:lang="hu">C++ fejléc</comment> + <comment xml:lang="it">Header C++</comment> + <comment xml:lang="nb">C++-kildekodeheader</comment> + <comment xml:lang="nn">C++-hovud</comment> + <comment xml:lang="pl">Plik nagłówkowy w C++</comment> + <comment xml:lang="sv">C++-huvud</comment> + <comment xml:lang="uk">Файл заголовків на C++</comment> + <glob pattern="*.h"/> + <glob pattern="*.hh"/> + <glob pattern="*.hxx"/> + <glob pattern="*.h++"/> + <glob pattern="*.H"/> + <glob pattern="*.hpp"/> + <glob pattern="*.hp"/> + <!-- Find include guards of header files without extension, for + example, STL ones like <string> --> + <magic priority="50"> + <match value="#ifndef" type="string" offset="0:1000"/> + </magic> + </mime-type> + + <mime-type type="text/x-c++src"> + <comment>C++ Source file</comment> + <sub-class-of type="text/x-csrc"/> + <comment>C++ source code</comment> + <comment xml:lang="bg">Изходен код на C++</comment> + <comment xml:lang="ca">codi font en C++</comment> + <comment xml:lang="cs">Zdrojový kód v C++</comment> + <comment xml:lang="cy">Ffynhonnell Rhaglen C++</comment> + <comment xml:lang="da">C++-kildekode</comment> + <comment xml:lang="de">C++-Quelltext</comment> + <comment xml:lang="el">πηγαίος κώδικας C++</comment> + <comment xml:lang="eo">C++-fontkodo</comment> + <comment xml:lang="es">código fuente en C++</comment> + <comment xml:lang="eu">C++ iturburu-kodea</comment> + <comment xml:lang="fi">C++-lähdekoodi</comment> + <comment xml:lang="fr">code source C++</comment> + <comment xml:lang="hu">C++-forráskód</comment> + <comment xml:lang="it">Codice sorgente C++</comment> + <comment xml:lang="ja">C++ ソースコード</comment> + <comment xml:lang="ko">C++ 소스 코드</comment> + <comment xml:lang="lt">C++ pradinis kodas</comment> + <comment xml:lang="ms">Kod sumber C++</comment> + <comment xml:lang="nb">C++-kildekode</comment> + <comment xml:lang="nl">C++-broncode</comment> + <comment xml:lang="nn">C++-kjeldekode</comment> + <comment xml:lang="pl">Kod źródłowy w C++</comment> + <comment xml:lang="pt">código fonte C++</comment> + <comment xml:lang="pt_BR">Código fonte C++</comment> + <comment xml:lang="ru">программа на языке C++</comment> + <comment xml:lang="sq">Kod burues C++</comment> + <comment xml:lang="sr">C++ изворни ко̂д</comment> + <comment xml:lang="sv">C++-källkod</comment> + <comment xml:lang="uk">Вихідний код на мові C++</comment> + <comment xml:lang="zh_CN">C++ 源代码</comment> + <comment xml:lang="zh_TW">C++ 源代碼</comment> + <glob pattern="*.cpp"/> + <glob pattern="*.cc"/> + <glob pattern="*.cxx"/> + <glob pattern="*.c++"/> + <glob pattern="*.C"/> + </mime-type> +</mime-info> diff --git a/src/plugins/cppeditor/CppEditor.pluginspec b/src/plugins/cppeditor/CppEditor.pluginspec new file mode 100644 index 00000000000..119f0461a67 --- /dev/null +++ b/src/plugins/cppeditor/CppEditor.pluginspec @@ -0,0 +1,12 @@ +<plugin name="CppEditor" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>C/C++ editor component.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> + <dependency name="TextEditor" version="0.9.1"/> + <dependency name="CppTools" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/cppeditor/CppEditor.qwp b/src/plugins/cppeditor/CppEditor.qwp new file mode 100644 index 00000000000..6b921e9d07b --- /dev/null +++ b/src/plugins/cppeditor/CppEditor.qwp @@ -0,0 +1,23 @@ +<!DOCTYPE QtWorkbenchManifest> +<qwp> + <pluginName>CppEditor</pluginName> + <author>Trolltech</author> + <requiredPluginList> + <requiredPlugin> + <name>TextEditor</name> + <provider></provider> + </requiredPlugin> + <requiredPlugin> + <name>Find</name> + <provider></provider> + </requiredPlugin> + </requiredPluginList> + <extendsInterfaceList> + <extendsInterface> + <name>Core::OptionsPageInterface</name> + </extendsInterface> + <extendsInterface> + <name>Core::WizardInterface</name> + </extendsInterface> + </extendsInterfaceList> +</qwp> diff --git a/src/plugins/cppeditor/CppEditor.tpa b/src/plugins/cppeditor/CppEditor.tpa new file mode 100644 index 00000000000..17e530152af --- /dev/null +++ b/src/plugins/cppeditor/CppEditor.tpa @@ -0,0 +1,16 @@ +<tpa> + <pluginName>CppEditor</pluginName> + <author>Trolltech AS</author> + <version>1.0</version> + <extendsInterfaceList> + <extendsInterface> + <name>EditorFactoryInterface</name> + </extendsInterface> + <extendsInterface> + <name>WizardInterface</name> + </extendsInterface> + <extendsInterface> + <name>OptionsPageInterface</name> + </extendsInterface> + </extendsInterfaceList> +</tpa> diff --git a/src/plugins/cppeditor/cppclasswizard.cpp b/src/plugins/cppeditor/cppclasswizard.cpp new file mode 100644 index 00000000000..90fa126727f --- /dev/null +++ b/src/plugins/cppeditor/cppclasswizard.cpp @@ -0,0 +1,232 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cppclasswizard.h" +#include "cppeditorconstants.h" + +#include <utils/codegeneration.h> +#include <utils/newclasswidget.h> + +#include <QtCore/QTextStream> +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtGui/QComboBox> +#include <QtGui/QCheckBox> +#include <QtGui/QLabel> +#include <QtGui/QVBoxLayout> +#include <QtGui/QWizard> + +using namespace CppEditor; +using namespace CppEditor::Internal; + + +// ========= ClassNamePage ========= + +ClassNamePage::ClassNamePage(const QString &sourceSuffix, + const QString &headerSuffix, + QWidget *parent) : + QWizardPage(parent), + m_isValid(false) +{ + setTitle(tr("Enter class name")); + setSubTitle(tr("The header and source file names will be derived from the class name")); + + m_newClassWidget = new Core::Utils::NewClassWidget; + // Order, set extensions first before suggested name is derived + m_newClassWidget->setHeaderExtension(headerSuffix); + m_newClassWidget->setSourceExtension(sourceSuffix); + m_newClassWidget->setBaseClassInputVisible(true); + m_newClassWidget->setBaseClassChoices(QStringList() << QString() + << QLatin1String("QObject") + << QLatin1String("QWidget") + << QLatin1String("QMainWindow")); + m_newClassWidget->setBaseClassEditable(true); + m_newClassWidget->setFormInputVisible(false); + m_newClassWidget->setNamespacesEnabled(true); + + connect(m_newClassWidget, SIGNAL(validChanged()), + this, SLOT(slotValidChanged())); + + QVBoxLayout *pageLayout = new QVBoxLayout(this); + pageLayout->addWidget(m_newClassWidget); +} + +void ClassNamePage::slotValidChanged() +{ + const bool validNow = m_newClassWidget->isValid(); + if (m_isValid != validNow) { + m_isValid = validNow; + emit completeChanged(); + } +} + +CppClassWizardDialog::CppClassWizardDialog(const QString &sourceSuffix, + const QString &headerSuffix, + QWidget *parent) : + QWizard(parent), + m_classNamePage(new ClassNamePage(sourceSuffix, headerSuffix, this)) +{ + Core::BaseFileWizard::setupWizard(this); + setWindowTitle(tr("C++ Class Wizard")); + addPage(m_classNamePage); +} + +void CppClassWizardDialog::setPath(const QString &path) +{ + m_classNamePage->newClassWidget()->setPath(path); +} + +CppClassWizardParameters CppClassWizardDialog::parameters() const +{ + CppClassWizardParameters rc; + const Core::Utils::NewClassWidget *ncw = m_classNamePage->newClassWidget(); + rc.className = ncw->className(); + rc.headerFile = ncw->headerFileName(); + rc.sourceFile = ncw->sourceFileName(); + rc.baseClass = ncw->baseClassName(); + rc.path = ncw->path(); + return rc; +} + +// ========= CppClassWizard ========= + +CppClassWizard::CppClassWizard(const Core::BaseFileWizardParameters ¶meters, + Core::ICore *core, QObject *parent) : + Core::BaseFileWizard(parameters, core, parent) +{ +} + +QString CppClassWizard::sourceSuffix() const +{ + return preferredSuffix(QLatin1String(Constants::CPP_SOURCE_MIMETYPE)); +} + +QString CppClassWizard::headerSuffix() const +{ + return preferredSuffix(QLatin1String(Constants::CPP_HEADER_MIMETYPE)); +} + +QWizard *CppClassWizard::createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const +{ + CppClassWizardDialog *wizard = new CppClassWizardDialog(sourceSuffix(), headerSuffix(), parent); + foreach (QWizardPage *p, extensionPages) + wizard->addPage(p); + wizard->setPath(defaultPath); + return wizard; +} + +Core::GeneratedFiles CppClassWizard::generateFiles(const QWizard *w, QString *errorMessage) const +{ + const CppClassWizardDialog *wizard = qobject_cast<const CppClassWizardDialog *>(w); + const CppClassWizardParameters params = wizard->parameters(); + + const QString sourceFileName = Core::BaseFileWizard::buildFileName(params.path, params.sourceFile, sourceSuffix()); + const QString headerFileName = Core::BaseFileWizard::buildFileName(params.path, params.headerFile, headerSuffix()); + + Core::GeneratedFile sourceFile(sourceFileName); + sourceFile.setEditorKind(QLatin1String(Constants::CPPEDITOR_KIND)); + + Core::GeneratedFile headerFile(headerFileName); + headerFile.setEditorKind(QLatin1String(Constants::CPPEDITOR_KIND)); + + QString header, source; + if (!generateHeaderAndSource(params, &header, &source)) { + *errorMessage = tr("Error while generating file contents."); + return Core::GeneratedFiles(); + } + headerFile.setContents(header); + sourceFile.setContents(source); + return Core::GeneratedFiles() << headerFile << sourceFile; +} + + +bool CppClassWizard::generateHeaderAndSource(const CppClassWizardParameters ¶ms, + QString *header, QString *source) +{ + // TODO: + // Quite a bit of this code has been copied from FormClassWizardParameters::generateCpp. + // Maybe more of it could be merged into Core::Utils. + + const QString indent = QString(4, QLatin1Char(' ')); + + // Do we have namespaces? + QStringList namespaceList = params.className.split(QLatin1String("::")); + if (namespaceList.empty()) // Paranoia! + return false; + + const QString unqualifiedClassName = namespaceList.takeLast(); + const QString guard = Core::Utils::headerGuard(unqualifiedClassName); + + // == Header file == + QTextStream headerStr(header); + headerStr << "#ifndef " << guard + << "\n#define " << guard << '\n' << '\n'; + + const QRegExp qtClassExpr(QLatin1String("^Q[A-Z3].+")); + Q_ASSERT(qtClassExpr.isValid()); + const bool superIsQtClass = qtClassExpr.exactMatch(params.baseClass); + if (superIsQtClass) { + Core::Utils::writeIncludeFileDirective(params.baseClass, true, headerStr); + headerStr << '\n'; + } + + const QString namespaceIndent = Core::Utils::writeOpeningNameSpaces(namespaceList, 0, headerStr); + + // Class declaration + headerStr << namespaceIndent << "class " << unqualifiedClassName; + if (!params.baseClass.isEmpty()) + headerStr << " : public " << params.baseClass << "\n"; + else + headerStr << "\n"; + headerStr << namespaceIndent << "{\n"; + headerStr << namespaceIndent << "public:\n" + << namespaceIndent << indent << unqualifiedClassName << "();\n"; + headerStr << namespaceIndent << "};\n\n"; + + Core::Utils::writeClosingNameSpaces(namespaceList, 0, headerStr); + headerStr << "#endif // "<< guard << '\n'; + + + // == Source file == + QTextStream sourceStr(source); + Core::Utils::writeIncludeFileDirective(params.headerFile, false, sourceStr); + Core::Utils::writeOpeningNameSpaces(namespaceList, 0, sourceStr); + + // Constructor + sourceStr << '\n' << namespaceIndent << unqualifiedClassName << "::" << unqualifiedClassName << "()\n"; + sourceStr << namespaceIndent << "{\n" << namespaceIndent << "}\n"; + + Core::Utils::writeClosingNameSpaces(namespaceList, indent, sourceStr); + return true; +} diff --git a/src/plugins/cppeditor/cppclasswizard.h b/src/plugins/cppeditor/cppclasswizard.h new file mode 100644 index 00000000000..6f2055efd71 --- /dev/null +++ b/src/plugins/cppeditor/cppclasswizard.h @@ -0,0 +1,128 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPCLASSWIZARD_H +#define CPPCLASSWIZARD_H + +#include <coreplugin/basefilewizard.h> + +#include <QtCore/QStringList> +#include <QtGui/QWizardPage> +#include <QtGui/QWizard> + +QT_BEGIN_NAMESPACE +class QCheckBox; +class QComboBox; +QT_END_NAMESPACE + +namespace Core { + namespace Utils { + class NewClassWidget; + } +} + +namespace CppEditor { +namespace Internal { + +class ClassNamePage : public QWizardPage +{ + Q_OBJECT + +public: + ClassNamePage(const QString &sourceSuffix, + const QString &headerSuffix, + QWidget *parent = 0); + + bool isComplete() const { return m_isValid; } + Core::Utils::NewClassWidget *newClassWidget() const { return m_newClassWidget; } + +private slots: + void slotValidChanged(); + +private: + Core::Utils::NewClassWidget *m_newClassWidget; + bool m_isValid; +}; + + +struct CppClassWizardParameters { + QString className; + QString headerFile; + QString sourceFile; + QString baseClass; + QString path; +}; + +class CppClassWizardDialog : public QWizard { + Q_DISABLE_COPY(CppClassWizardDialog) + Q_OBJECT +public: + explicit CppClassWizardDialog(const QString &sourceSuffix, + const QString &headerSuffix, + QWidget *parent = 0); + + void setPath(const QString &path); + CppClassWizardParameters parameters() const; + +private: + ClassNamePage *m_classNamePage; +}; + + +class CppClassWizard : public Core::BaseFileWizard +{ + Q_OBJECT +public: + explicit CppClassWizard(const Core::BaseFileWizardParameters ¶meters, + Core::ICore *core, QObject *parent = 0); + +protected: + virtual QWizard *createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const; + + + virtual Core::GeneratedFiles generateFiles(const QWizard *w, + QString *errorMessage) const; + QString sourceSuffix() const; + QString headerSuffix() const; + +private: + static bool generateHeaderAndSource(const CppClassWizardParameters ¶ms, + QString *header, QString *source); + +}; + +} // namespace Internal +} // namespace CppEditor + +#endif // CPPCLASSWIZARD_H diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp new file mode 100644 index 00000000000..708d825426a --- /dev/null +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -0,0 +1,815 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cppeditor.h" +#include "cppeditorconstants.h" +#include "cppplugin.h" +#include "cpphighlighter.h" + +#include <AST.h> +#include <Token.h> +#include <Scope.h> +#include <Symbols.h> +#include <Names.h> +#include <Control.h> +#include <CoreTypes.h> +#include <Literals.h> +#include <Semantic.h> +#include <cplusplus/ExpressionUnderCursor.h> +#include <cplusplus/LookupContext.h> +#include <cplusplus/Overview.h> +#include <cplusplus/OverviewModel.h> +#include <cplusplus/SimpleLexer.h> +#include <cplusplus/TypeOfExpression.h> +#include <cpptools/cppmodelmanagerinterface.h> + +#include <coreplugin/icore.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/editormanager/editormanager.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <texteditor/basetextdocument.h> +#include <texteditor/fontsettings.h> +#include <texteditor/texteditorconstants.h> +#include <texteditor/textblockiterator.h> +#include <indenter.h> + +#include <QtCore/QFileInfo> +#include <QtCore/QTextStream> +#include <QtCore/QDebug> +#include <QtCore/QTime> +#include <QtCore/QTimer> +#include <QtGui/QAction> +#include <QtGui/QKeyEvent> +#include <QtGui/QLayout> +#include <QtGui/QMenu> +#include <QtGui/QShortcut> +#include <QtGui/QTextEdit> +#include <QtGui/QComboBox> +#include <QtGui/QTreeView> +#include <QtGui/QHeaderView> + +using namespace CPlusPlus; +using namespace CppEditor::Internal; + +namespace { + +class OverviewTreeView : public QTreeView +{ +public: + OverviewTreeView(QWidget *parent = 0) + : QTreeView(parent) + { + // TODO: Disable the root for all items (with a custom delegate?) + setRootIsDecorated(false); + } + + void sync() + { + expandAll(); + setMinimumWidth(qMax(sizeHintForColumn(0), minimumSizeHint().width())); + } +}; + +} // end of anonymous namespace + +QualifiedNameId *qualifiedNameIdForSymbol(Symbol *s, const LookupContext &context) +{ + Name *symbolName = s->name(); + if (! symbolName) + return 0; // nothing to do. + + QVector<Name *> names; + + for (Scope *scope = s->scope(); scope; scope = scope->enclosingScope()) { + if (scope->isClassScope() || scope->isNamespaceScope()) { + if (scope->owner() && scope->owner()->name()) { + Name *ownerName = scope->owner()->name(); + if (QualifiedNameId *q = ownerName->asQualifiedNameId()) { + for (unsigned i = 0; i < q->nameCount(); ++i) { + names.prepend(q->nameAt(i)); + } + } else { + names.prepend(ownerName); + } + } + } + } + + if (QualifiedNameId *q = symbolName->asQualifiedNameId()) { + for (unsigned i = 0; i < q->nameCount(); ++i) { + names.append(q->nameAt(i)); + } + } else { + names.append(symbolName); + } + + return context.control()->qualifiedNameId(names.constData(), names.size()); +} + +CPPEditorEditable::CPPEditorEditable(CPPEditor *editor) + :BaseTextEditorEditable(editor) +{ + Core::ICore *core = CppPlugin::core(); + m_context << core->uniqueIDManager()->uniqueIdentifier(CppEditor::Constants::C_CPPEDITOR); + m_context << core->uniqueIDManager()->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX); + m_context << core->uniqueIDManager()->uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR); +} + +CPPEditor::CPPEditor(QWidget *parent) : + TextEditor::BaseTextEditor(parent), + m_core(CppPlugin::core()) +{ + setParenthesesMatchingEnabled(true); + setMarksVisible(true); + setCodeFoldingVisible(true); + baseTextDocument()->setSyntaxHighlighter(new CPPHighlighter); +// new QShortcut(QKeySequence("Ctrl+Alt+M"), this, SLOT(foo()), 0, Qt::WidgetShortcut); + +#ifdef WITH_TOKEN_MOVE_POSITION + new QShortcut(QKeySequence::MoveToPreviousWord, this, SLOT(moveToPreviousToken()), + /*ambiguousMember=*/ 0, Qt::WidgetShortcut); + + new QShortcut(QKeySequence::MoveToNextWord, this, SLOT(moveToNextToken()), + /*ambiguousMember=*/ 0, Qt::WidgetShortcut); + + new QShortcut(QKeySequence::DeleteStartOfWord, this, SLOT(deleteStartOfToken()), + /*ambiguousMember=*/ 0, Qt::WidgetShortcut); + + new QShortcut(QKeySequence::DeleteEndOfWord, this, SLOT(deleteEndOfToken()), + /*ambiguousMember=*/ 0, Qt::WidgetShortcut); +#endif + + m_modelManager = m_core->pluginManager()->getObject<CppTools::CppModelManagerInterface>(); + + if (m_modelManager) { + connect(m_modelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)), + this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr))); + } +} + +CPPEditor::~CPPEditor() +{ +} + +TextEditor::BaseTextEditorEditable *CPPEditor::createEditableInterface() +{ + CPPEditorEditable *editable = new CPPEditorEditable(this); + createToolBar(editable); + return editable; +} + +void CPPEditor::createToolBar(CPPEditorEditable *editable) +{ + m_methodCombo = new QComboBox; + m_methodCombo->setMinimumContentsLength(22); + m_methodCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); + QTreeView *methodView = new OverviewTreeView(); + methodView->header()->hide(); + methodView->setItemsExpandable(false); + m_methodCombo->setView(methodView); + m_methodCombo->setMaxVisibleItems(20); + + m_overviewModel = new OverviewModel(this); + m_methodCombo->setModel(m_overviewModel); + + connect(m_methodCombo, SIGNAL(activated(int)), this, SLOT(jumpToMethod(int))); + connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateMethodBoxIndex())); + + connect(file(), SIGNAL(changed()), this, SLOT(updateFileName())); + + QToolBar *toolBar = editable->toolBar(); + QList<QAction*> actions = toolBar->actions(); + toolBar->insertWidget(actions.first(), m_methodCombo); +} + +int CPPEditor::previousBlockState(QTextBlock block) const +{ + block = block.previous(); + if (block.isValid()) { + int state = block.userState(); + + if (state != -1) + return state; + } + return 0; +} + +QTextCursor CPPEditor::moveToPreviousToken(QTextCursor::MoveMode mode) const +{ + SimpleLexer tokenize; + QTextCursor c(textCursor()); + QTextBlock block = c.block(); + int column = c.columnNumber(); + + for (; block.isValid(); block = block.previous()) { + const QString textBlock = block.text(); + QList<SimpleToken> tokens = tokenize(textBlock, previousBlockState(block)); + + if (! tokens.isEmpty()) { + tokens.prepend(SimpleToken()); + + for (int index = tokens.size() - 1; index != -1; --index) { + const SimpleToken &tk = tokens.at(index); + if (tk.position() < column) { + c.setPosition(block.position() + tk.position(), mode); + return c; + } + } + } + + column = INT_MAX; + } + + c.movePosition(QTextCursor::Start, mode); + return c; +} + +QTextCursor CPPEditor::moveToNextToken(QTextCursor::MoveMode mode) const +{ + SimpleLexer tokenize; + QTextCursor c(textCursor()); + QTextBlock block = c.block(); + int column = c.columnNumber(); + + for (; block.isValid(); block = block.next()) { + const QString textBlock = block.text(); + QList<SimpleToken> tokens = tokenize(textBlock, previousBlockState(block)); + + if (! tokens.isEmpty()) { + for (int index = 0; index < tokens.size(); ++index) { + const SimpleToken &tk = tokens.at(index); + if (tk.position() > column) { + c.setPosition(block.position() + tk.position(), mode); + return c; + } + } + } + + column = -1; + } + + c.movePosition(QTextCursor::End, mode); + return c; +} + +void CPPEditor::moveToPreviousToken() +{ + setTextCursor(moveToPreviousToken(QTextCursor::MoveAnchor)); +} + +void CPPEditor::moveToNextToken() +{ + setTextCursor(moveToNextToken(QTextCursor::MoveAnchor)); +} + +void CPPEditor::deleteStartOfToken() +{ + QTextCursor c = moveToPreviousToken(QTextCursor::KeepAnchor); + c.removeSelectedText(); + setTextCursor(c); +} + +void CPPEditor::deleteEndOfToken() +{ + QTextCursor c = moveToNextToken(QTextCursor::KeepAnchor); + c.removeSelectedText(); + setTextCursor(c); +} + +void CPPEditor::onDocumentUpdated(Document::Ptr doc) +{ + if (doc->fileName() != file()->fileName()) + return; + + m_overviewModel->rebuild(doc); + OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_methodCombo->view()); + treeView->sync(); + updateMethodBoxIndex(); +} + +void CPPEditor::updateFileName() +{ } + +void CPPEditor::jumpToMethod(int) +{ + QModelIndex index = m_methodCombo->view()->currentIndex(); + Symbol *symbol = m_overviewModel->symbolFromIndex(index); + if (! symbol) + return; + + m_core->editorManager()->addCurrentPositionToNavigationHistory(true); + int line = symbol->line(); + gotoLine(line); + m_core->editorManager()->addCurrentPositionToNavigationHistory(); + setFocus(); +} + +void CPPEditor::updateMethodBoxIndex() +{ + int line = 0, column = 0; + convertPosition(position(), &line, &column); + + QModelIndex lastIndex; + + const int rc = m_overviewModel->rowCount(QModelIndex()); + for (int row = 0; row < rc; ++row) { + const QModelIndex index = m_overviewModel->index(row, 0, QModelIndex()); + Symbol *symbol = m_overviewModel->symbolFromIndex(index); + if (symbol->line() > unsigned(line)) + break; + lastIndex = index; + } + + if (lastIndex.isValid()) { + bool blocked = m_methodCombo->blockSignals(true); + m_methodCombo->setCurrentIndex(lastIndex.row()); + (void) m_methodCombo->blockSignals(blocked); + } +} + +static bool isCompatible(Name *name, Name *otherName) +{ + if (NameId *nameId = name->asNameId()) { + if (TemplateNameId *otherTemplId = otherName->asTemplateNameId()) + return nameId->identifier()->isEqualTo(otherTemplId->identifier()); + } else if (TemplateNameId *templId = name->asTemplateNameId()) { + if (NameId *otherNameId = otherName->asNameId()) + return templId->identifier()->isEqualTo(otherNameId->identifier()); + } + + return name->isEqualTo(otherName); +} + +static bool isCompatible(Function *definition, Symbol *declaration, QualifiedNameId *declarationName) +{ + Function *declTy = declaration->type()->asFunction(); + if (! declTy) + return false; + + Name *definitionName = definition->name(); + if (QualifiedNameId *q = definitionName->asQualifiedNameId()) { + if (! isCompatible(q->unqualifiedNameId(), declaration->name())) + return false; + else if (q->nameCount() > declarationName->nameCount()) + return false; + else if (declTy->argumentCount() != definition->argumentCount()) + return false; + else if (declTy->isConst() != definition->isConst()) + return false; + else if (declTy->isVolatile() != definition->isVolatile()) + return false; + + for (unsigned i = 0; i < definition->argumentCount(); ++i) { + Symbol *arg = definition->argumentAt(i); + Symbol *otherArg = declTy->argumentAt(i); + if (! arg->type().isEqualTo(otherArg->type())) + return false; + } + + for (unsigned i = 0; i != q->nameCount(); ++i) { + Name *n = q->nameAt(q->nameCount() - i - 1); + Name *m = declarationName->nameAt(declarationName->nameCount() - i - 1); + if (! isCompatible(n, m)) + return false; + } + return true; + } else { + // ### TODO: implement isCompatible for unqualified name ids. + } + return false; +} + +void CPPEditor::switchDeclarationDefinition() +{ + int line = 0, column = 0; + convertPosition(position(), &line, &column); + + if (!m_modelManager) + return; + + Document::Ptr doc = m_modelManager->document(file()->fileName()); + if (!doc) + return; + Symbol *lastSymbol = doc->findSymbolAt(line, column); + if (!lastSymbol || !lastSymbol->scope()) + return; + + Function *f = lastSymbol->asFunction(); + if (! f) { + Scope *fs = lastSymbol->scope(); + if (! fs->isFunctionScope()) + fs = fs->enclosingFunctionScope(); + if (fs) + f = fs->owner()->asFunction(); + } + + if (f) { + TypeOfExpression typeOfExpression; + typeOfExpression.setDocuments(m_modelManager->documents()); + QList<TypeOfExpression::Result> resolvedSymbols = typeOfExpression(QString(), doc, lastSymbol); + const LookupContext &context = typeOfExpression.lookupContext(); + + QualifiedNameId *q = qualifiedNameIdForSymbol(f, context); + QList<Symbol *> symbols = context.resolve(q); + + Symbol *declaration = 0; + foreach (declaration, symbols) { + if (isCompatible(f, declaration, q)) + break; + } + + if (! declaration && ! symbols.isEmpty()) + declaration = symbols.first(); + + if (declaration) + openEditorAt(declaration); + } else if (lastSymbol->type()->isFunction()) { + if (Symbol *def = findDefinition(lastSymbol)) + openEditorAt(def); + } +} + +void CPPEditor::jumpToDefinition() +{ + if (!m_modelManager) + return; + + // Find the last symbol up to the cursor position + int line = 0, column = 0; + convertPosition(position(), &line, &column); + Document::Ptr doc = m_modelManager->document(file()->fileName()); + if (!doc) + return; + Symbol *lastSymbol = doc->findSymbolAt(line, column); + if (!lastSymbol) + return; + + // Get the expression under the cursor + const int endOfName = endOfNameUnderCursor(); + QTextCursor tc = textCursor(); + tc.setPosition(endOfName); + ExpressionUnderCursor expressionUnderCursor; + const QString expression = expressionUnderCursor(tc); + + // Evaluate the type of the expression + TypeOfExpression typeOfExpression; + typeOfExpression.setDocuments(m_modelManager->documents()); + QList<TypeOfExpression::Result> resolvedSymbols = + typeOfExpression(expression, doc, lastSymbol); + + if (!resolvedSymbols.isEmpty()) { + Symbol *symbol = resolvedSymbols.first().second; + if (symbol) { + Symbol *def = 0; + if (!lastSymbol->isFunction()) + def = findDefinition(symbol); + + if (def) + openEditorAt(def); + else + openEditorAt(symbol); + + // This would jump to the type of a name +#if 0 + } else if (NamedType *namedType = firstType->asNamedType()) { + QList<Symbol *> candidates = context.resolve(namedType->name()); + if (!candidates.isEmpty()) { + Symbol *s = candidates.takeFirst(); + openEditorAt(s->fileName(), s->line(), s->column()); + } +#endif + } + } else { + qDebug() << "No results for expression:" << expression; + } +} + +Symbol *CPPEditor::findDefinition(Symbol *lastSymbol) +{ + // Currently only functions are supported + if (!lastSymbol->type()->isFunction()) + return 0; + + QVector<Name *> qualifiedName; + Scope *scope = lastSymbol->scope(); + for (; scope; scope = scope->enclosingScope()) { + if (scope->isClassScope() || scope->isNamespaceScope()) { + if (scope->owner() && scope->owner()->name()) { + Name *scopeOwnerName = scope->owner()->name(); + if (QualifiedNameId *q = scopeOwnerName->asQualifiedNameId()) { + for (unsigned i = 0; i < q->nameCount(); ++i) { + qualifiedName.prepend(q->nameAt(i)); + } + } else { + qualifiedName.prepend(scopeOwnerName); + } + } + } + } + + qualifiedName.append(lastSymbol->name()); + + Control control; + QualifiedNameId *q = control.qualifiedNameId(&qualifiedName[0], qualifiedName.size()); + LookupContext context(&control); + + const QMap<QString, Document::Ptr> documents = m_modelManager->documents(); + foreach (Document::Ptr doc, documents) { + QList<Scope *> visibleScopes; + visibleScopes.append(doc->globalSymbols()); + visibleScopes = context.expand(visibleScopes); + //qDebug() << "** doc:" << doc->fileName() << "visible scopes:" << visibleScopes.count(); + foreach (Scope *visibleScope, visibleScopes) { + Symbol *symbol = 0; + if (NameId *nameId = q->unqualifiedNameId()->asNameId()) + symbol = visibleScope->lookat(nameId->identifier()); + else if (DestructorNameId *dtorId = q->unqualifiedNameId()->asDestructorNameId()) + symbol = visibleScope->lookat(dtorId->identifier()); + else if (TemplateNameId *templNameId = q->unqualifiedNameId()->asTemplateNameId()) + symbol = visibleScope->lookat(templNameId->identifier()); + else if (OperatorNameId *opId = q->unqualifiedNameId()->asOperatorNameId()) + symbol = visibleScope->lookat(opId->kind()); + // ### cast operators + for (; symbol; symbol = symbol->next()) { + if (! symbol->isFunction()) + continue; + else if (! isCompatible(symbol->asFunction(), lastSymbol, q)) + continue; + return symbol; + } + } + } + + return 0; +} + +bool CPPEditor::isElectricCharacter(const QChar &ch) const +{ + if (ch == QLatin1Char('{') || + ch == QLatin1Char('}') || + ch == QLatin1Char('#')) { + return true; + } + return false; +} + +// Indent a code line based on previous +template <class Iterator> +static void indentCPPBlock(const CPPEditor::TabSettings &ts, + const QTextBlock &block, + const Iterator &programBegin, + const Iterator &programEnd, + QChar typedChar) +{ + typedef typename SharedTools::Indenter<Iterator> Indenter; + Indenter &indenter = Indenter::instance(); + indenter.setIndentSize(ts.m_indentSize); + indenter.setTabSize(ts.m_tabSize); + + const TextEditor::TextBlockIterator current(block); + const int indent = indenter.indentForBottomLine(current, programBegin, programEnd, typedChar); + ts.indentLine(block, indent); +} + +void CPPEditor::indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar) +{ + const TextEditor::TextBlockIterator begin(doc->begin()); + const TextEditor::TextBlockIterator end(block.next()); + + indentCPPBlock(tabSettings(), block, begin, end, typedChar); +} + +void CPPEditor::contextMenuEvent(QContextMenuEvent *e) +{ + QMenu *menu = createStandardContextMenu(); + + // Remove insert unicode control character + QAction *lastAction = menu->actions().last(); + if (lastAction->menu() && QLatin1String(lastAction->menu()->metaObject()->className()) == QLatin1String("QUnicodeControlCharacterMenu")) + menu->removeAction(lastAction); + + Core::IActionContainer *mcontext = + m_core->actionManager()->actionContainer(CppEditor::Constants::M_CONTEXT); + QMenu *contextMenu = mcontext->menu(); + + foreach (QAction *action, contextMenu->actions()) + menu->addAction(action); + + menu->exec(e->globalPos()); + delete menu; +} + +QList<int> CPPEditorEditable::context() const +{ + return m_context; +} + +Core::IEditor *CPPEditorEditable::duplicate(QWidget *parent) +{ + CPPEditor *newEditor = new CPPEditor(parent); + newEditor->duplicateFrom(editor()); + CppPlugin::instance()->initializeEditor(newEditor); + return newEditor->editableInterface(); +} + +const char *CPPEditorEditable::kind() const +{ + return CppEditor::Constants::CPPEDITOR_KIND; +} + +void CPPEditor::setFontSettings(const TextEditor::FontSettings &fs) +{ + TextEditor::BaseTextEditor::setFontSettings(fs); + CPPHighlighter *highlighter = qobject_cast<CPPHighlighter*>(baseTextDocument()->syntaxHighlighter()); + if (!highlighter) + return; + + static QVector<QString> categories; + if (categories.isEmpty()) { + categories << QLatin1String(TextEditor::Constants::C_NUMBER) + << QLatin1String(TextEditor::Constants::C_STRING) + << QLatin1String(TextEditor::Constants::C_TYPE) + << QLatin1String(TextEditor::Constants::C_KEYWORD) + << QLatin1String(TextEditor::Constants::C_OPERATOR) + << QLatin1String(TextEditor::Constants::C_PREPROCESSOR) + << QLatin1String(TextEditor::Constants::C_LABEL) + << QLatin1String(TextEditor::Constants::C_COMMENT); + } + + const QVector<QTextCharFormat> formats = fs.toTextCharFormats(categories); + highlighter->setFormats(formats.constBegin(), formats.constEnd()); + highlighter->rehighlight(); +} + + +void CPPEditor::unCommentSelection() +{ + QTextCursor cursor = textCursor(); + cursor.beginEditBlock(); + + int pos = cursor.position(); + int anchor = cursor.anchor(); + int start = qMin(anchor, pos); + int end = qMax(anchor, pos); + bool anchorIsStart = (anchor == start); + + QTextBlock startBlock = document()->findBlock(start); + QTextBlock endBlock = document()->findBlock(end); + + if (end > start && endBlock.position() == end) { + --end; + endBlock = endBlock.previous(); + } + + bool doCStyleUncomment = false; + bool doCStyleComment = false; + bool doCppStyleUncomment = false; + + bool hasSelection = cursor.hasSelection(); + + if (hasSelection) { + QString startText = startBlock.text(); + int startPos = start - startBlock.position(); + bool hasLeadingCharacters = !startText.left(startPos).trimmed().isEmpty(); + if ((startPos >= 2 + && startText.at(startPos-2) == QLatin1Char('/') + && startText.at(startPos-1) == QLatin1Char('*'))) { + startPos -= 2; + start -= 2; + } + + bool hasSelStart = (startPos < startText.length() - 2 + && startText.at(startPos) == QLatin1Char('/') + && startText.at(startPos+1) == QLatin1Char('*')); + + + QString endText = endBlock.text(); + int endPos = end - endBlock.position(); + bool hasTrailingCharacters = !endText.mid(endPos).trimmed().isEmpty(); + if ((endPos <= endText.length() - 2 + && endText.at(endPos) == QLatin1Char('*') + && endText.at(endPos+1) == QLatin1Char('/'))) { + endPos += 2; + end += 2; + } + + bool hasSelEnd = (endPos >= 2 + && endText.at(endPos-2) == QLatin1Char('*') + && endText.at(endPos-1) == QLatin1Char('/')); + + doCStyleUncomment = hasSelStart && hasSelEnd; + doCStyleComment = !doCStyleUncomment && (hasLeadingCharacters || hasTrailingCharacters); + } + + if (doCStyleUncomment) { + cursor.setPosition(end); + cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, 2); + cursor.removeSelectedText(); + cursor.setPosition(start); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2); + cursor.removeSelectedText(); + } else if (doCStyleComment) { + cursor.setPosition(end); + cursor.insertText(QLatin1String("*/")); + cursor.setPosition(start); + cursor.insertText(QLatin1String("/*")); + } else { + endBlock = endBlock.next(); + doCppStyleUncomment = true; + for (QTextBlock block = startBlock; block != endBlock; block = block.next()) { + QString text = block.text(); + if (!text.trimmed().startsWith(QLatin1String("//"))) { + doCppStyleUncomment = false; + break; + } + } + for (QTextBlock block = startBlock; block != endBlock; block = block.next()) { + if (doCppStyleUncomment) { + QString text = block.text(); + int i = 0; + while (i < text.size() - 1) { + if (text.at(i) == QLatin1Char('/') + && text.at(i + 1) == QLatin1Char('/')) { + cursor.setPosition(block.position() + i); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2); + cursor.removeSelectedText(); + break; + } + if (!text.at(i).isSpace()) + break; + ++i; + } + } else { + cursor.setPosition(block.position()); + cursor.insertText(QLatin1String("//")); + } + } + } + + // adjust selection when commenting out + if (hasSelection && !doCStyleUncomment && !doCppStyleUncomment) { + cursor = textCursor(); + if (!doCStyleComment) + start = startBlock.position(); // move the double slashes into the selection + int lastSelPos = anchorIsStart ? cursor.position() : cursor.anchor(); + if (anchorIsStart) { + cursor.setPosition(start); + cursor.setPosition(lastSelPos, QTextCursor::KeepAnchor); + } else { + cursor.setPosition(lastSelPos); + cursor.setPosition(start, QTextCursor::KeepAnchor); + } + setTextCursor(cursor); + } + + cursor.endEditBlock(); +} + +int CPPEditor::endOfNameUnderCursor() +{ + int pos = position(); + QChar chr = characterAt(pos); + + // Skip to the start of a name + while (chr.isLetterOrNumber() || chr == QLatin1Char('_')) + chr = characterAt(++pos); + + return pos; +} + +bool CPPEditor::openEditorAt(Symbol *s) +{ + const QString fileName = QString::fromUtf8(s->fileName(), s->fileNameLength()); + return TextEditor::BaseTextEditor::openEditorAt(fileName, s->line(), s->column()); +} diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h new file mode 100644 index 00000000000..404d8047ada --- /dev/null +++ b/src/plugins/cppeditor/cppeditor.h @@ -0,0 +1,145 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPEDITOR_H +#define CPPEDITOR_H + +#include "cppeditorenums.h" +#include <cplusplus/CppDocument.h> +#include <texteditor/basetexteditor.h> +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE +class QAction; +class QComboBox; +QT_END_NAMESPACE + +namespace Core { +class ICore; +} + +namespace CPlusPlus { +class OverviewModel; +class Symbol; +} + +namespace CppTools { +class CppModelManagerInterface; +} + +namespace TextEditor { +class FontSettings; +} + +namespace CppEditor { +namespace Internal { + +class CPPEditor; + +class CPPEditorEditable : public TextEditor::BaseTextEditorEditable +{ + Q_OBJECT +public: + CPPEditorEditable(CPPEditor *); + QList<int> context() const; + + bool duplicateSupported() const { return true; } + Core::IEditor *duplicate(QWidget *parent); + const char *kind() const; + +private: + QList<int> m_context; +}; + +class CPPEditor : public TextEditor::BaseTextEditor +{ + Q_OBJECT + +public: + typedef TextEditor::TabSettings TabSettings; + + CPPEditor(QWidget *parent); + ~CPPEditor(); + + void unCommentSelection(); + +public slots: + virtual void setFontSettings(const TextEditor::FontSettings &); + void switchDeclarationDefinition(); + void jumpToDefinition(); + + void moveToPreviousToken(); + void moveToNextToken(); + + void deleteStartOfToken(); + void deleteEndOfToken(); + +protected: + void contextMenuEvent(QContextMenuEvent *e); + TextEditor::BaseTextEditorEditable *createEditableInterface(); + + // Rertuns true if key triggers anindent. + virtual bool isElectricCharacter(const QChar &ch) const; + +private slots: + void updateFileName(); + void jumpToMethod(int index); + void updateMethodBoxIndex(); + + void onDocumentUpdated(CPlusPlus::Document::Ptr doc); + +private: + CPlusPlus::Symbol *findDefinition(CPlusPlus::Symbol *symbol); + virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar); + + int previousBlockState(QTextBlock block) const; + QTextCursor moveToPreviousToken(QTextCursor::MoveMode mode) const; + QTextCursor moveToNextToken(QTextCursor::MoveMode mode) const; + + void createToolBar(CPPEditorEditable *editable); + + int endOfNameUnderCursor(); + + bool openEditorAt(CPlusPlus::Symbol *symbol); + + Core::ICore *m_core; + CppTools::CppModelManagerInterface *m_modelManager; + + QList<int> m_contexts; + QComboBox *m_methodCombo; + CPlusPlus::OverviewModel *m_overviewModel; +}; + +} // namespace Internal +} // namespace CppEditor + +#endif // CPPEDITOR_H diff --git a/src/plugins/cppeditor/cppeditor.pri b/src/plugins/cppeditor/cppeditor.pri new file mode 100644 index 00000000000..c4d6fdf753e --- /dev/null +++ b/src/plugins/cppeditor/cppeditor.pri @@ -0,0 +1,3 @@ +include(cppeditor_dependencies.pri) + +LIBS *= -l$$qtLibraryTarget(CppEditor) diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro new file mode 100644 index 00000000000..cb71e9a8380 --- /dev/null +++ b/src/plugins/cppeditor/cppeditor.pro @@ -0,0 +1,22 @@ +TEMPLATE = lib +TARGET = CppEditor +DEFINES += CPPEDITOR_LIBRARY +include(../../libs/utils/utils.pri) +include(../../qworkbenchplugin.pri) +include(cppeditor_dependencies.pri) +HEADERS += cppplugin.h \ + cppeditor.h \ + cppeditoractionhandler.h \ + cpphighlighter.h \ + cppfilewizard.h \ + cppeditorconstants.h \ + cppeditorenums.h \ + cppeditor_global.h \ + cppclasswizard.h +SOURCES += cppplugin.cpp \ + cppeditoractionhandler.cpp \ + cppeditor.cpp \ + cpphighlighter.cpp \ + cppfilewizard.cpp \ + cppclasswizard.cpp +RESOURCES += cppeditor.qrc diff --git a/src/plugins/cppeditor/cppeditor.qrc b/src/plugins/cppeditor/cppeditor.qrc new file mode 100644 index 00000000000..6ddcbac8bbb --- /dev/null +++ b/src/plugins/cppeditor/cppeditor.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/cppeditor" > + <file>images/qt_cpp.png</file> + <file>images/qt_h.png</file> + <file>CppEditor.mimetypes.xml</file> + </qresource> +</RCC> diff --git a/src/plugins/cppeditor/cppeditor_dependencies.pri b/src/plugins/cppeditor/cppeditor_dependencies.pri new file mode 100644 index 00000000000..0d56376efe8 --- /dev/null +++ b/src/plugins/cppeditor/cppeditor_dependencies.pri @@ -0,0 +1,6 @@ +include(../../libs/utils/utils.pri) +include(../../libs/cplusplus/cplusplus.pri) +include(../../plugins/texteditor/texteditor.pri) +include(../../../shared/indenter/indenter.pri) +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/cpptools/cpptools.pri) diff --git a/src/plugins/cppeditor/cppeditor_global.h b/src/plugins/cppeditor/cppeditor_global.h new file mode 100644 index 00000000000..61063831227 --- /dev/null +++ b/src/plugins/cppeditor/cppeditor_global.h @@ -0,0 +1,44 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPEDITOR_GLOBAL_H +#define CPPEDITOR_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(CPPEDITOR_LIBRARY) +# define CPPEDITOR_EXPORT Q_DECL_EXPORT +#else +# define CPPEDITOR_EXPORT Q_DECL_IMPORT +#endif + +#endif // CPPEDITOR_GLOBAL_H diff --git a/src/plugins/cppeditor/cppeditoractionhandler.cpp b/src/plugins/cppeditor/cppeditoractionhandler.cpp new file mode 100644 index 00000000000..1ea013fb2af --- /dev/null +++ b/src/plugins/cppeditor/cppeditoractionhandler.cpp @@ -0,0 +1,54 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "cppeditoractionhandler.h" +#include "cppeditorconstants.h" +#include "cppeditor.h" +#include <QAction> + +using namespace CppEditor::Internal; + +CPPEditorActionHandler::CPPEditorActionHandler(Core::ICore *core, + const QString &context, + uint optionalActions) + : TextEditor::TextEditorActionHandler(core, context, optionalActions) +{ } + +CPPEditorActionHandler::~CPPEditorActionHandler() +{ } + +void CPPEditorActionHandler::createActions() +{ TextEditor::TextEditorActionHandler::createActions(); } + +void CPPEditorActionHandler::updateActions(UpdateMode um) +{ TextEditor::TextEditorActionHandler::updateActions(um); } diff --git a/src/plugins/cppeditor/cppeditoractionhandler.h b/src/plugins/cppeditor/cppeditoractionhandler.h new file mode 100644 index 00000000000..75c1102d225 --- /dev/null +++ b/src/plugins/cppeditor/cppeditoractionhandler.h @@ -0,0 +1,61 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPEDITORACTIONHANDLER_H +#define CPPEDITORACTIONHANDLER_H + +#include <texteditor/texteditoractionhandler.h> + +namespace CppEditor { +namespace Internal { + +class CPPEditorActionHandler : public TextEditor::TextEditorActionHandler +{ + Q_OBJECT + +public: + CPPEditorActionHandler(Core::ICore *core, + const QString &context, + uint optionalActions = None); + virtual ~CPPEditorActionHandler(); + + using TextEditor::TextEditorActionHandler::updateActions; + +protected: + virtual void createActions(); + virtual void updateActions(UpdateMode um); +}; + +} // namespace Internal +} // namespace CppEditor + +#endif // CPPEDITORACTIONHANDLER_H diff --git a/src/plugins/cppeditor/cppeditorconstants.h b/src/plugins/cppeditor/cppeditorconstants.h new file mode 100644 index 00000000000..0c29ec96303 --- /dev/null +++ b/src/plugins/cppeditor/cppeditorconstants.h @@ -0,0 +1,59 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPPLUGIN_GLOBAL_H +#define CPPPLUGIN_GLOBAL_H + +namespace CppEditor { +namespace Constants { + +const char * const FORMATCODE = "CppEditor.FormatCode"; +const char * const M_CONTEXT = "CppEditor.ContextMenu"; +const char * const C_CPPEDITOR = "C++ Editor"; +const char * const CPPEDITOR_KIND = "C++ Editor"; +const char * const SWITCH_DECLARATION_DEFINITION = "CppEditor.SwitchDeclarationDefinition"; +const char * const JUMP_TO_DEFINITION = "CppEditor.JumpToDefinition"; + +const char * const HEADER_FILE_TYPE = "CppHeaderFiles"; +const char * const SOURCE_FILE_TYPE = "CppSourceFiles"; + +const char * const MOVE_TO_PREVIOUS_TOKEN = "CppEditor.MoveToPreviousToken"; +const char * const MOVE_TO_NEXT_TOKEN = "CppEditor.MoveToNextToken"; + +const char * const C_SOURCE_MIMETYPE = "text/x-csrc"; +const char * const C_HEADER_MIMETYPE = "text/x-chdr"; +const char * const CPP_SOURCE_MIMETYPE = "text/x-c++src"; +const char * const CPP_HEADER_MIMETYPE = "text/x-c++hdr"; +} // namespace Constants +} // namespace CppEditor + +#endif // CPPPLUGIN_GLOBAL_H diff --git a/src/plugins/cppeditor/cppeditorenums.h b/src/plugins/cppeditor/cppeditorenums.h new file mode 100644 index 00000000000..85e64972155 --- /dev/null +++ b/src/plugins/cppeditor/cppeditorenums.h @@ -0,0 +1,59 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPPLUGIN_ENUMS_H +#define CPPPLUGIN_ENUMS_H + +namespace CppEditor { +namespace Internal { + +enum FileType { + Header, + Source +}; + +enum CppFormats { + CppNumberFormat, + CppStringFormat, + CppTypeFormat, + CppKeywordFormat, + CppOperatorFormat, + CppPreprocessorFormat, + CppLabelFormat, + CppCommentFormat, + NumCppFormats +}; + +} // namespace Internal +} // namespace CppEditor + +#endif // CPPPLUGIN_ENUMS_H diff --git a/src/plugins/cppeditor/cppfilewizard.cpp b/src/plugins/cppeditor/cppfilewizard.cpp new file mode 100644 index 00000000000..a8e477f219c --- /dev/null +++ b/src/plugins/cppeditor/cppfilewizard.cpp @@ -0,0 +1,103 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cppfilewizard.h" +#include "cppeditor.h" +#include "cppeditorconstants.h" + +#include <QtCore/QTextStream> +#include <QtCore/QFileInfo> +#include <QtCore/QDebug> + +using namespace CppEditor; +using namespace CppEditor::Internal; + +enum { debugWizard = 0 }; + +CppFileWizard::CppFileWizard(const BaseFileWizardParameters ¶meters, + FileType type, + Core::ICore *core, + QObject *parent) : + Core::StandardFileWizard(parameters, core, parent), + m_type(type) +{ +} + +QString CppFileWizard::toAlphaNum(const QString &s) +{ + QString rc; + const int len = s.size(); + const QChar underscore = QLatin1Char('_'); + + for (int i = 0; i < len; i++) { + const QChar c = s.at(i); + if (c == underscore || c.isLetterOrNumber()) + rc += c; + } + return rc; +} + +Core::GeneratedFiles CppFileWizard::generateFilesFromPath(const QString &path, + const QString &name, + QString * /*errorMessage*/) const + +{ + const QString mimeType = m_type == Source ? QLatin1String(Constants::CPP_SOURCE_MIMETYPE) : QLatin1String(Constants::CPP_HEADER_MIMETYPE); + const QString fileName = Core::BaseFileWizard::buildFileName(path, name, preferredSuffix(mimeType)); + Core::GeneratedFile file(fileName); + file.setEditorKind(QLatin1String(Constants::C_CPPEDITOR)); + const QString cleanName = toAlphaNum(QFileInfo(name).baseName()); + + file.setContents(fileContents(m_type, fileName)); + return Core::GeneratedFiles() << file; +} + +QString CppFileWizard::fileContents(FileType type, const QString &fileName) const +{ + const QString baseName = QFileInfo(fileName).baseName(); + QString contents; + QTextStream str(&contents); + switch (type) { + case Header: { + QString guard = toAlphaNum(baseName).toUpper(); + guard += QLatin1String("_H"); + str << QLatin1String("#ifndef ") << guard + << QLatin1String("\n#define ") << guard << QLatin1String("\n\n#endif // ") + << guard << QLatin1String("\n"); + } + break; + case Source: + str << QLatin1String("#include \"") << baseName << '.' << preferredSuffix(QLatin1String(Constants::CPP_HEADER_MIMETYPE)) << QLatin1String("\"\n\n"); + break; + } + return contents; +} diff --git a/src/plugins/cppeditor/cppfilewizard.h b/src/plugins/cppeditor/cppfilewizard.h new file mode 100644 index 00000000000..551aac86884 --- /dev/null +++ b/src/plugins/cppeditor/cppfilewizard.h @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPFILEWIZARD_H +#define CPPFILEWIZARD_H + +#include "cppeditorenums.h" + +#include <coreplugin/basefilewizard.h> + +namespace CppEditor { +namespace Internal { + +class CppFileWizard : public Core::StandardFileWizard +{ + Q_OBJECT + +public: + typedef Core::BaseFileWizardParameters BaseFileWizardParameters; + + explicit CppFileWizard(const BaseFileWizardParameters ¶meters, + FileType type, + Core::ICore *core, QObject *parent = 0); + +protected: + static QString toAlphaNum(const QString &s); + QString fileContents(FileType type, const QString &baseName) const; + +protected: + Core::GeneratedFiles generateFilesFromPath(const QString &path, + const QString &fileName, + QString *errorMessage) const; +private: + const FileType m_type; +}; + +} // namespace Internal +} // namespace CppEditor + +#endif // CPPFILEWIZARD_H diff --git a/src/plugins/cppeditor/cpphighlighter.cpp b/src/plugins/cppeditor/cpphighlighter.cpp new file mode 100644 index 00000000000..4bd823c0c8d --- /dev/null +++ b/src/plugins/cppeditor/cpphighlighter.cpp @@ -0,0 +1,303 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cpphighlighter.h" + +#include <Token.h> +#include <cplusplus/SimpleLexer.h> +#include <texteditor/basetexteditor.h> + +#include <QtGui/QTextDocument> +#include <QtCore/QDebug> + +using namespace CppEditor::Internal; +using namespace TextEditor; +using namespace CPlusPlus; + +CPPHighlighter::CPPHighlighter(QTextDocument *document) : + QSyntaxHighlighter(document) +{ + visualSpaceFormat.setForeground(Qt::lightGray); +} + +void CPPHighlighter::highlightBlock(const QString &text) +{ + QTextCharFormat emptyFormat; + + const int previousState = previousBlockState(); + int state = 0, braceDepth = 0; + if (previousState != -1) { + state = previousState & 0xff; + braceDepth = previousState >> 8; + } + + SimpleLexer tokenize; + tokenize.setQtMocRunEnabled(false); + + int initialState = state; + const QList<SimpleToken> tokens = tokenize(text, initialState); + state = tokenize.state(); // refresh the state + + if (tokens.isEmpty()) { + setCurrentBlockState(previousState); + if (TextBlockUserData *userData = TextEditDocumentLayout::testUserData(currentBlock())) { + userData->setClosingCollapseMode(TextBlockUserData::NoClosingCollapse); + userData->setCollapseMode(TextBlockUserData::NoCollapse); + } + TextEditDocumentLayout::clearParentheses(currentBlock()); + return; + } + + const int firstNonSpace = tokens.first().position(); + + Parentheses parentheses; + parentheses.reserve(20); // assume wizard level ;-) + + bool highlightAsPreprocessor = false; + + for (int i = 0; i < tokens.size(); ++i) { + const SimpleToken &tk = tokens.at(i); + + int previousTokenEnd = 0; + if (i != 0) { + // mark the whitespaces + previousTokenEnd = tokens.at(i - 1).position() + + tokens.at(i - 1).length(); + } + + if (previousTokenEnd != tk.position()) { + setFormat(previousTokenEnd, tk.position() - previousTokenEnd, + visualSpaceFormat); + } + + if (tk.is(T_LPAREN) || tk.is(T_LBRACE) || tk.is(T_LBRACKET)) { + const QChar c(tk.text().at(0)); + parentheses.append(Parenthesis(Parenthesis::Opened, c, tk.position())); + if (tk.is(T_LBRACE)) + ++braceDepth; + } else if (tk.is(T_RPAREN) || tk.is(T_RBRACE) || tk.is(T_RBRACKET)) { + const QChar c(tk.text().at(0)); + parentheses.append(Parenthesis(Parenthesis::Closed, c, tk.position())); + if (tk.is(T_RBRACE)) { + if (--braceDepth < 0) + braceDepth = 0; + } + } + + bool highlightCurrentWordAsPreprocessor = highlightAsPreprocessor; + if (highlightAsPreprocessor) + highlightAsPreprocessor = false; + + if (i == 0 && tk.is(T_POUND)) { + setFormat(tk.position(), tk.length(), m_formats[CppPreprocessorFormat]); + highlightAsPreprocessor = true; + } else if (highlightCurrentWordAsPreprocessor && + (tk.isKeyword() || tk.is(T_IDENTIFIER)) && isPPKeyword(tk.text())) + setFormat(tk.position(), tk.length(), m_formats[CppPreprocessorFormat]); + else if (tk.is(T_INT_LITERAL) || tk.is(T_FLOAT_LITERAL)) + setFormat(tk.position(), tk.length(), m_formats[CppNumberFormat]); + else if (tk.is(T_STRING_LITERAL) || tk.is(T_CHAR_LITERAL) || tk.is(T_ANGLE_STRING_LITERAL)) + setFormat(tk.position(), tk.length(), m_formats[CppStringFormat]); + else if (tk.is(T_WIDE_STRING_LITERAL) || tk.is(T_WIDE_CHAR_LITERAL)) + setFormat(tk.position(), tk.length(), m_formats[CppStringFormat]); + else if (tk.is(T_COMMENT)) { + setFormat(tk.position(), tk.length(), m_formats[CppCommentFormat]); + // we need to insert a close comment parenthesis, if + // - the line starts in a C Comment (initalState != 0) + // - the first token of the line is a T_COMMENT (i == 0 && tk.is(T_COMMENT)) + // - is not a continuation line (tokens.size() > 1 || ! state) + if (initialState && i == 0 && (tokens.size() > 1 || ! state)) { + --braceDepth; + + const int tokenEnd = tk.position() + tk.length() - 1; + parentheses.append(Parenthesis(Parenthesis::Closed, QLatin1Char('-'), tokenEnd)); + + // clear the initial state. + initialState = 0; + } + } else if (tk.isKeyword() || isQtKeyword(tk.text())) + setFormat(tk.position(), tk.length(), m_formats[CppKeywordFormat]); + else if (tk.isOperator()) + setFormat(tk.position(), tk.length(), m_formats[CppOperatorFormat]); + else if (i == 0 && tokens.size() > 1 && tk.is(T_IDENTIFIER) && tokens.at(1).is(T_COLON)) + setFormat(tk.position(), tk.length(), m_formats[CppLabelFormat]); + else if (tk.is(T_IDENTIFIER)) + highlightWord(tk.text(), tk.position(), tk.length()); + } + + // mark the trailing white spaces + if (! tokens.isEmpty()) { + const SimpleToken tk = tokens.last(); + const int lastTokenEnd = tk.position() + tk.length(); + if (text.length() > lastTokenEnd) + setFormat(lastTokenEnd, text.length() - lastTokenEnd, visualSpaceFormat); + } + + if (TextBlockUserData *userData = TextEditDocumentLayout::testUserData(currentBlock())) { + userData->setClosingCollapseMode(TextBlockUserData::NoClosingCollapse); + userData->setCollapseMode(TextBlockUserData::NoCollapse); + } + + if (! initialState && state && ! tokens.isEmpty()) { + parentheses.append(Parenthesis(Parenthesis::Opened, QLatin1Char('+'), + tokens.last().position())); + ++braceDepth; + } + + QChar c; + int collapse = Parenthesis::collapseAtPos(parentheses, &c); + if (collapse >= 0) { + TextBlockUserData::CollapseMode collapseMode = TextBlockUserData::CollapseAfter; + if (collapse == firstNonSpace && c != QLatin1Char('+')) + collapseMode = TextBlockUserData::CollapseThis; + TextEditDocumentLayout::userData(currentBlock())->setCollapseMode(collapseMode); + } + + + int cc = Parenthesis::closeCollapseAtPos(parentheses); + if (cc >= 0) { + TextBlockUserData *userData = TextEditDocumentLayout::userData(currentBlock()); + userData->setClosingCollapseMode(TextBlockUserData::ClosingCollapse); + QString trailingText = text.mid(cc+1).simplified(); + if (trailingText.isEmpty() || trailingText == QLatin1String(";")) { + userData->setClosingCollapseMode(TextBlockUserData::ClosingCollapseAtEnd); + } + } + + TextEditDocumentLayout::setParentheses(currentBlock(), parentheses); + + setCurrentBlockState((braceDepth << 8) | tokenize.state()); +} + + +bool CPPHighlighter::isPPKeyword(const QStringRef &text) const +{ + switch (text.length()) + { + case 2: + if (text.at(0) == 'i' && text.at(1) == 'f') + return true; + break; + + case 4: + if (text.at(0) == 'e' && text == QLatin1String("elif")) + return true; + else if (text.at(0) == 'e' && text == QLatin1String("else")) + return true; + break; + + case 5: + if (text.at(0) == 'i' && text == QLatin1String("ifdef")) + return true; + else if (text.at(0) == 'u' && text == QLatin1String("undef")) + return true; + else if (text.at(0) == 'e' && text == QLatin1String("endif")) + return true; + else if (text.at(0) == 'e' && text == QLatin1String("error")) + return true; + break; + + case 6: + if (text.at(0) == 'i' && text == QLatin1String("ifndef")) + return true; + else if (text.at(0) == 'd' && text == QLatin1String("define")) + return true; + else if (text.at(0) == 'p' && text == QLatin1String("pragma")) + return true; + break; + + case 7: + if (text.at(0) == 'i' && text == QLatin1String("include")) + return true; + else if (text.at(0) == 'w' && text == QLatin1String("warning")) + return true; + break; + + case 12: + if (text.at(0) == 'i' && text == QLatin1String("include_next")) + return true; + break; + + default: + break; + } + + return false; +} + +bool CPPHighlighter::isQtKeyword(const QStringRef &text) const +{ + switch (text.length()) { + case 4: + if (text.at(0) == 'e' && text == QLatin1String("emit")) + return true; + else if (text.at(0) == 'S' && text == QLatin1String("SLOT")) + return true; + break; + + case 5: + if (text.at(0) == 's' && text == QLatin1String("slots")) + return true; + break; + + case 6: + if (text.at(0) == 'S' && text == QLatin1String("SIGNAL")) + return true; + break; + + case 7: + if (text.at(0) == 's' && text == QLatin1String("signals")) + return true; + else if (text.at(0) == 'f' && text == QLatin1String("foreach")) + return true; + else if (text.at(0) == 'f' && text == QLatin1String("forever")) + return true; + break; + + default: + break; + } + return false; +} + +void CPPHighlighter::highlightWord(QStringRef word, int position, int length) +{ + // try to highlight Qt 'identifiers' like QObject and Q_PROPERTY + // but don't highlight words like 'Query' + if (word.length() > 1 + && word.at(0) == QLatin1Char('Q') + && (word.at(1).isUpper() + || word.at(1) == QLatin1Char('_') + || word.at(1) == QLatin1Char('t'))) { + setFormat(position, length, m_formats[CppTypeFormat]); + } +} diff --git a/src/plugins/cppeditor/cpphighlighter.h b/src/plugins/cppeditor/cpphighlighter.h new file mode 100644 index 00000000000..cdfc2d8360c --- /dev/null +++ b/src/plugins/cppeditor/cpphighlighter.h @@ -0,0 +1,73 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPHIGHLIGHTER_H +#define CPPHIGHLIGHTER_H + +#include "cppeditorenums.h" +#include <QtGui/QSyntaxHighlighter> +#include <QtGui/QTextCharFormat> +#include <QtCore/QtAlgorithms> + +namespace CppEditor { +namespace Internal { + +class CPPEditor; + +class CPPHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT + +public: + CPPHighlighter(QTextDocument *document = 0); + + virtual void highlightBlock(const QString &text); + + // Set formats from a sequence of type QTextCharFormat + template <class InputIterator> + void setFormats(InputIterator begin, InputIterator end) { + qCopy(begin, end, m_formats); + } + +private: + void highlightWord(QStringRef word, int position, int length); + bool isPPKeyword(const QStringRef &text) const; + bool isQtKeyword(const QStringRef &text) const; + + QTextCharFormat m_formats[NumCppFormats]; + QTextCharFormat visualSpaceFormat; +}; + +} // namespace Internal +} // namespace CppEditor + +#endif // CPPHIGHLIGHTER_H diff --git a/src/plugins/cppeditor/cppplugin.cpp b/src/plugins/cppeditor/cppplugin.cpp new file mode 100644 index 00000000000..576624f5b42 --- /dev/null +++ b/src/plugins/cppeditor/cppplugin.cpp @@ -0,0 +1,258 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cppplugin.h" +#include "cppeditor.h" +#include "cppeditorconstants.h" +#include "cppeditorenums.h" +#include "cppfilewizard.h" +#include "cppclasswizard.h" +#include "cppeditoractionhandler.h" + +#include <coreplugin/coreconstants.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/fileiconprovider.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/actionmanager/icommand.h> +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/completionsupport.h> +#include <texteditor/fontsettings.h> +#include <texteditor/storagesettings.h> +#include <texteditor/texteditorsettings.h> +#include <cpptools/cpptoolsconstants.h> + +#include <QtCore/QFileInfo> +#include <QtCore/QSettings> +#include <QtGui/QMenu> +#include <QtGui/QAction> + +static const char *headerSuffixKeyC = "CppEditor/HeaderSuffix"; +static const char *sourceSuffixKeyC = "CppEditor/SourceSuffix"; + +using namespace CppEditor::Internal; + +///////////////////////////////// CppPluginEditorFactory ////////////////////////////////// + +CppPluginEditorFactory::CppPluginEditorFactory(CppPlugin *owner) : + m_kind(QLatin1String(CppEditor::Constants::CPPEDITOR_KIND)), + m_owner(owner) +{ + m_mimeTypes << QLatin1String(CppEditor::Constants::C_SOURCE_MIMETYPE) + << QLatin1String(CppEditor::Constants::C_HEADER_MIMETYPE) + << QLatin1String(CppEditor::Constants::CPP_SOURCE_MIMETYPE) + << QLatin1String(CppEditor::Constants::CPP_HEADER_MIMETYPE); + Core::FileIconProvider *iconProvider = Core::FileIconProvider::instance(); + iconProvider->registerIconForSuffix(QIcon(":/cppeditor/images/qt_cpp.png"), + QLatin1String("cpp")); + iconProvider->registerIconForSuffix(QIcon(":/cppeditor/images/qt_h.png"), + QLatin1String("h")); +} + +QString CppPluginEditorFactory::kind() const +{ + return m_kind; +} + +Core::IFile *CppPluginEditorFactory::open(const QString &fileName) +{ + Core::IEditor *iface = m_owner->m_core->editorManager()->openEditor(fileName, kind()); + return iface ? iface->file() : 0; +} + +Core::IEditor *CppPluginEditorFactory::createEditor(QWidget *parent) +{ + CPPEditor *editor = new CPPEditor(parent); + editor->setRevisionsVisible(true); + editor->setMimeType(CppEditor::Constants::CPP_SOURCE_MIMETYPE); + m_owner->initializeEditor(editor); + return editor->editableInterface(); +} + +QStringList CppPluginEditorFactory::mimeTypes() const +{ + return m_mimeTypes; +} + +///////////////////////////////// CppPlugin ////////////////////////////////// + +CppPlugin *CppPlugin::m_instance = 0; + +CppPlugin::CppPlugin() : + m_core(0), + m_actionHandler(0), + m_factory(0) +{ + m_instance = this; +} + +CppPlugin::~CppPlugin() +{ + removeObject(m_factory); + delete m_factory; + delete m_actionHandler; + m_instance = 0; +} + +CppPlugin *CppPlugin::instance() +{ + return m_instance; +} + +Core::ICore *CppPlugin::core() +{ + return m_instance->m_core; +} + +void CppPlugin::initializeEditor(CPPEditor *editor) +{ + // common actions + m_actionHandler->setupActions(editor); + + // settings + TextEditor::TextEditorSettings *settings = TextEditor::TextEditorSettings::instance(); + connect(settings, SIGNAL(fontSettingsChanged(TextEditor::FontSettings)), + editor, SLOT(setFontSettings(TextEditor::FontSettings))); + connect(settings, SIGNAL(tabSettingsChanged(TextEditor::TabSettings)), + editor, SLOT(setTabSettings(TextEditor::TabSettings))); + connect(settings, SIGNAL(storageSettingsChanged(TextEditor::StorageSettings)), + editor, SLOT(setStorageSettings(TextEditor::StorageSettings))); + connect(settings, SIGNAL(displaySettingsChanged(TextEditor::DisplaySettings)), + editor, SLOT(setDisplaySettings(TextEditor::DisplaySettings))); + + // tab settings rely on font settings + editor->setFontSettings(settings->fontSettings()); + editor->setTabSettings(settings->tabSettings()); + editor->setStorageSettings(settings->storageSettings()); + editor->setDisplaySettings(settings->displaySettings()); + + // auto completion + connect(editor, SIGNAL(requestAutoCompletion(ITextEditable*, bool)), + TextEditor::Internal::CompletionSupport::instance(core()), SLOT(autoComplete(ITextEditable*, bool))); +} + +bool CppPlugin::initialize(const QStringList & /*arguments*/, QString *errorMessage) +{ + typedef TextEditor::TextEditorActionHandler TextEditorActionHandler; + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + if (!m_core->mimeDatabase()->addMimeTypes(QLatin1String(":/cppeditor/CppEditor.mimetypes.xml"), errorMessage)) + return false; + + m_factory = new CppPluginEditorFactory(this); + addObject(m_factory); + + CppFileWizard::BaseFileWizardParameters wizardParameters(Core::IWizard::FileWizard); + + wizardParameters.setCategory(QLatin1String("C++")); + wizardParameters.setTrCategory(tr("C++")); + wizardParameters.setDescription(tr("Creates a new C++ header file.")); + wizardParameters.setName(tr("C++ Header File")); + addAutoReleasedObject(new CppFileWizard(wizardParameters, Header, m_core)); + + wizardParameters.setDescription(tr("Creates a new C++ source file.")); + wizardParameters.setName(tr("C++ Source File")); + addAutoReleasedObject(new CppFileWizard(wizardParameters, Source, m_core)); + + wizardParameters.setKind(Core::IWizard::ClassWizard); + wizardParameters.setName(tr("C++ Class")); + wizardParameters.setDescription(tr("Creates a header and a source file for a new class.")); + addAutoReleasedObject(new CppClassWizard(wizardParameters, m_core)); + + QList<int> context; + context << m_core->uniqueIDManager()->uniqueIdentifier(CppEditor::Constants::C_CPPEDITOR); + + Core::ActionManagerInterface *am = m_core->actionManager(); + am->createMenu(CppEditor::Constants::M_CONTEXT); + + Core::ICommand *cmd; + + QAction *jumpToDefinition = new QAction(tr("Follow Symbol under Cursor"), this); + cmd = am->registerAction(jumpToDefinition, + Constants::JUMP_TO_DEFINITION, context); + cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F2)); + connect(jumpToDefinition, SIGNAL(triggered()), + this, SLOT(jumpToDefinition())); + am->actionContainer(CppEditor::Constants::M_CONTEXT)->addAction(cmd); + am->actionContainer(CppTools::Constants::M_TOOLS_CPP)->addAction(cmd); + + QAction *switchDeclarationDefinition = new QAction(tr("Switch between Method Declaration/Definition"), this); + cmd = am->registerAction(switchDeclarationDefinition, + Constants::SWITCH_DECLARATION_DEFINITION, context); + cmd->setDefaultKeySequence(QKeySequence("Shift+F2")); + connect(switchDeclarationDefinition, SIGNAL(triggered()), + this, SLOT(switchDeclarationDefinition())); + am->actionContainer(CppEditor::Constants::M_CONTEXT)->addAction(cmd); + am->actionContainer(CppTools::Constants::M_TOOLS_CPP)->addAction(cmd); + + m_actionHandler = new CPPEditorActionHandler(m_core, + CppEditor::Constants::C_CPPEDITOR, + TextEditor::TextEditorActionHandler::Format + | TextEditor::TextEditorActionHandler::UnCommentSelection + | TextEditor::TextEditorActionHandler::UnCollapseAll); + + // Check Suffixes + if (const QSettings *settings = m_core->settings()) { + const QString headerSuffixKey = QLatin1String(headerSuffixKeyC); + if (settings->contains(headerSuffixKey)) { + const QString headerSuffix = settings->value(headerSuffixKey, QString()).toString(); + if (!headerSuffix.isEmpty()) + m_core->mimeDatabase()->setPreferredSuffix(QLatin1String(Constants::CPP_HEADER_MIMETYPE), headerSuffix); + const QString sourceSuffix = settings->value(QLatin1String(sourceSuffixKeyC), QString()).toString(); + if (!sourceSuffix.isEmpty()) + m_core->mimeDatabase()->setPreferredSuffix(QLatin1String(Constants::CPP_SOURCE_MIMETYPE), sourceSuffix); + } + } + return true; +} + +void CppPlugin::extensionsInitialized() +{ + m_actionHandler->initializeActions(); +} + +void CppPlugin::switchDeclarationDefinition() +{ + CPPEditor *editor = qobject_cast<CPPEditor*>(m_core->editorManager()->currentEditor()->widget()); + if (editor) { + editor->switchDeclarationDefinition(); + } +} + +void CppPlugin::jumpToDefinition() +{ + CPPEditor *editor = qobject_cast<CPPEditor*>(m_core->editorManager()->currentEditor()->widget()); + if (editor) { + editor->jumpToDefinition(); + } +} + +Q_EXPORT_PLUGIN(CppPlugin) diff --git a/src/plugins/cppeditor/cppplugin.h b/src/plugins/cppeditor/cppplugin.h new file mode 100644 index 00000000000..8bc4e8d61e1 --- /dev/null +++ b/src/plugins/cppeditor/cppplugin.h @@ -0,0 +1,113 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPPLUGIN_H +#define CPPPLUGIN_H + +#include <QtCore/qplugin.h> +#include <QtCore/QStringList> + +#include <extensionsystem/iplugin.h> +#include <coreplugin/editormanager/ieditorfactory.h> + +namespace Core { +class ICore; +class IWizard; +} + +namespace TextEditor { +class TextEditorActionHandler; +} // namespace TextEditor + +namespace CppEditor { +namespace Internal { + +class CPPEditor; +class CPPEditorActionHandler; +class CppPluginEditorFactory; + +class CppPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + CppPlugin(); + ~CppPlugin(); + + static CppPlugin *instance(); + static Core::ICore *core(); + + bool initialize(const QStringList &arguments, QString *error_message = 0); + void extensionsInitialized(); + + // Connect editor to settings changed signals. + void initializeEditor(CPPEditor *editor); + +private slots: + void switchDeclarationDefinition(); + void jumpToDefinition(); + +private: + friend class CppPluginEditorFactory; + Core::IEditor *createEditor(QWidget *parent); + + static CppPlugin *m_instance; + + Core::ICore *m_core; + CPPEditorActionHandler *m_actionHandler; + CppPluginEditorFactory *m_factory; +}; + +class CppPluginEditorFactory : public Core::IEditorFactory +{ + Q_OBJECT + +public: + CppPluginEditorFactory(CppPlugin *owner); + + virtual QStringList mimeTypes() const; + + Core::IEditor *createEditor(QWidget *parent); + + virtual QString kind() const; + Core::IFile *open(const QString &fileName); + +private: + const QString m_kind; + CppPlugin *m_owner; + QStringList m_mimeTypes; +}; + +} // namespace Internal +} // namespace CppEditor + +#endif // CPPPLUGIN_H diff --git a/src/plugins/cppeditor/images/qt_cpp.png b/src/plugins/cppeditor/images/qt_cpp.png Binary files differnew file mode 100644 index 00000000000..73fd1642b3c --- /dev/null +++ b/src/plugins/cppeditor/images/qt_cpp.png diff --git a/src/plugins/cppeditor/images/qt_h.png b/src/plugins/cppeditor/images/qt_h.png Binary files differnew file mode 100644 index 00000000000..dec0475219f --- /dev/null +++ b/src/plugins/cppeditor/images/qt_h.png diff --git a/src/plugins/cpptools/CppTools.pluginspec b/src/plugins/cpptools/CppTools.pluginspec new file mode 100644 index 00000000000..ca4a8a0e523 --- /dev/null +++ b/src/plugins/cpptools/CppTools.pluginspec @@ -0,0 +1,12 @@ +<plugin name="CppTools" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Tools for analyzing C/C++ code.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="TextEditor" version="0.9.1"/> + <dependency name="ProjectExplorer" version="0.9.1"/> + <dependency name="QuickOpen" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/cpptools/cppcodecompletion.cpp b/src/plugins/cpptools/cppcodecompletion.cpp new file mode 100644 index 00000000000..59504771840 --- /dev/null +++ b/src/plugins/cpptools/cppcodecompletion.cpp @@ -0,0 +1,1040 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cppcodecompletion.h" +#include "cppmodelmanager.h" + +#include <Control.h> +#include <AST.h> +#include <ASTVisitor.h> +#include <CoreTypes.h> +#include <Literals.h> +#include <Names.h> +#include <NameVisitor.h> +#include <Symbols.h> +#include <SymbolVisitor.h> +#include <Scope.h> +#include <TranslationUnit.h> +#include <cplusplus/ResolveExpression.h> +#include <cplusplus/LookupContext.h> +#include <cplusplus/Overview.h> +#include <cplusplus/ExpressionUnderCursor.h> +#include <cplusplus/TokenUnderCursor.h> + +#include <coreplugin/icore.h> +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/itexteditor.h> +#include <texteditor/itexteditable.h> +#include <texteditor/basetexteditor.h> + +#include <QtCore/QDebug> +#include <QtCore/QMap> +#include <QtCore/QFile> +#include <QtGui/QAction> +#include <QtGui/QKeyEvent> +#include <QtGui/QLabel> +#include <QtGui/QVBoxLayout> +#include <QtGui/QApplication> + +using namespace CPlusPlus; + +namespace CppTools { +namespace Internal { + +class FunctionArgumentWidget : public QLabel { +public: + FunctionArgumentWidget(Core::ICore *core); + void showFunctionHint(Function *functionSymbol); + +protected: + bool eventFilter(QObject *obj, QEvent *e); + +private: + void update(); + void close(); + void updateHintText(); + + int m_startpos; + int m_currentarg; + + TextEditor::ITextEditor *m_editor; + + QFrame *m_popupFrame; + Function *m_item; +}; + +class ConvertToCompletionItem: protected NameVisitor +{ + // The completion collector. + CppCodeCompletion *_collector; + + // The completion item. + TextEditor::CompletionItem _item; + + // The current symbol. + Symbol *_symbol; + + // The pretty printer. + Overview overview; + +public: + ConvertToCompletionItem(CppCodeCompletion *collector) + : _collector(collector), + _item(0), + _symbol(0) + { } + + TextEditor::CompletionItem operator()(Symbol *symbol) + { + if (! symbol || ! symbol->name() || symbol->name()->isQualifiedNameId()) + return 0; + + TextEditor::CompletionItem previousItem = switchCompletionItem(0); + Symbol *previousSymbol = switchSymbol(symbol); + accept(symbol->identity()); + if (_item) + _item.m_data = QVariant::fromValue(symbol); + (void) switchSymbol(previousSymbol); + return switchCompletionItem(previousItem); + } + +protected: + Symbol *switchSymbol(Symbol *symbol) + { + Symbol *previousSymbol = _symbol; + _symbol = symbol; + return previousSymbol; + } + + TextEditor::CompletionItem switchCompletionItem(TextEditor::CompletionItem item) + { + TextEditor::CompletionItem previousItem = _item; + _item = item; + return previousItem; + } + + TextEditor::CompletionItem newCompletionItem(Name *name) + { + TextEditor::CompletionItem item(_collector); + item.m_text = overview.prettyName(name); + item.m_icon = _collector->iconForSymbol(_symbol); + return item; + } + + virtual void visit(NameId *name) + { _item = newCompletionItem(name); } + + virtual void visit(TemplateNameId *name) + { + _item = newCompletionItem(name); + _item.m_text = QLatin1String(name->identifier()->chars()); + } + + virtual void visit(DestructorNameId *name) + { _item = newCompletionItem(name); } + + virtual void visit(OperatorNameId *name) + { _item = newCompletionItem(name); } + + virtual void visit(ConversionNameId *name) + { _item = newCompletionItem(name); } + + virtual void visit(QualifiedNameId *name) + { _item = newCompletionItem(name->unqualifiedNameId()); } +}; + + +} // namespace Internal +} // namespace CppTools + + + +using namespace CppTools::Internal; + +FunctionArgumentWidget::FunctionArgumentWidget(Core::ICore *core) + : m_item(0) +{ + QObject *editorObject = core->editorManager()->currentEditor(); + m_editor = qobject_cast<TextEditor::ITextEditor *>(editorObject); + + m_popupFrame = new QFrame(0, Qt::ToolTip|Qt::WindowStaysOnTopHint); + m_popupFrame->setFocusPolicy(Qt::NoFocus); + m_popupFrame->setAttribute(Qt::WA_DeleteOnClose); + + setFrameStyle(QFrame::Box); + setFrameShadow(QFrame::Plain); + + setParent(m_popupFrame); + setFocusPolicy(Qt::NoFocus); + + QVBoxLayout *layout = new QVBoxLayout(); + layout->addWidget(this); + layout->setMargin(0); + m_popupFrame->setLayout(layout); + + QPalette pal = palette(); + setAutoFillBackground(true); + pal.setColor(QPalette::Background, QColor(255, 255, 220)); + setPalette(pal); + + setTextFormat(Qt::RichText); + setMargin(1); +} + +void FunctionArgumentWidget::showFunctionHint(Function *functionSymbol) +{ + m_item = functionSymbol; + m_startpos = m_editor->position(); + + // update the text + m_currentarg = -1; + update(); + + QPoint pos = m_editor->cursorRect().topLeft(); + pos.setY(pos.y() - sizeHint().height()); + m_popupFrame->move(pos); + m_popupFrame->show(); + + QCoreApplication::instance()->installEventFilter(this); +} + +void FunctionArgumentWidget::update() +{ + int curpos = m_editor->position(); + if (curpos < m_startpos) { + close(); + return; + } + + QString str = m_editor->textAt(m_startpos, curpos - m_startpos); + int argnr = 0; + int parcount = 0; + SimpleLexer tokenize; + QList<SimpleToken> tokens = tokenize(str); + for (int i = 0; i < tokens.count(); ++i) { + const SimpleToken &tk = tokens.at(i); + if (tk.is(T_LPAREN)) + ++parcount; + else if (tk.is(T_RPAREN)) + --parcount; + else if (! parcount && tk.is(T_COMMA)) + ++argnr; + } + + if (m_currentarg != argnr) { + m_currentarg = argnr; + updateHintText(); + } + + if (parcount < 0) + close(); +} + +bool FunctionArgumentWidget::eventFilter(QObject *obj, QEvent *e) +{ + switch (e->type()) { + case QEvent::KeyRelease: + { + if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) { + close(); + return false; + } + update(); + break; + } + case QEvent::WindowDeactivate: + case QEvent::Leave: + case QEvent::FocusOut: + { + if (obj != m_editor->widget()) + break; + } + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::Wheel: + close(); + break; + default: + break; + } + + return false; +} + +void FunctionArgumentWidget::close() +{ + m_popupFrame->close(); +} + +void FunctionArgumentWidget::updateHintText() +{ + Overview overview; + overview.setShowReturnTypes(true); + overview.setShowArgumentNames(true); + overview.setMarkArgument(m_currentarg + 1); + QString text = overview(m_item->type(), m_item->name()); + setText(text); +} + +CppCodeCompletion::CppCodeCompletion(CppModelManager *manager, Core::ICore *core) + : ICompletionCollector(manager), + m_core(core), + m_manager(manager), + m_forcedCompletion(false), + m_completionOperator(T_EOF_SYMBOL) +{ } + +QIcon CppCodeCompletion::iconForSymbol(Symbol *symbol) const +{ return m_icons.iconForSymbol(symbol); } + +/* + Searches beckward for an access operator. +*/ +static int startOfOperator(TextEditor::ITextEditable *editor, + int pos, unsigned *kind, + bool wantFunctionCall) +{ + const QChar ch = pos > -1 ? editor->characterAt(pos - 1) : QChar(); + const QChar ch2 = pos > 0 ? editor->characterAt(pos - 2) : QChar(); + const QChar ch3 = pos > 1 ? editor->characterAt(pos - 3) : QChar(); + + int start = pos; + + if (ch2 != QLatin1Char('.') && ch == QLatin1Char('.')) { + if (kind) + *kind = T_DOT; + --start; + } else if (wantFunctionCall && ch == QLatin1Char('(')) { + if (kind) + *kind = T_LPAREN; + --start; + } else if (ch2 == QLatin1Char(':') && ch == QLatin1Char(':')) { + if (kind) + *kind = T_COLON_COLON; + start -= 2; + } else if (ch2 == QLatin1Char('-') && ch == QLatin1Char('>')) { + if (kind) + *kind = T_ARROW; + start -= 2; + } else if (ch2 == QLatin1Char('.') && ch == QLatin1Char('*')) { + if (kind) + *kind = T_DOT_STAR; + start -= 2; + } else if (ch3 == QLatin1Char('-') && ch2 == QLatin1Char('>') && ch == QLatin1Char('*')) { + if (kind) + *kind = T_ARROW_STAR; + start -= 3; + } + + if (start != pos) { + TextEditor::BaseTextEditor *edit = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget()); + QTextCursor tc(edit->textCursor()); + tc.setPosition(pos); + static CPlusPlus::TokenUnderCursor tokenUnderCursor; + const SimpleToken tk = tokenUnderCursor(tc); + if (tk.is(T_COMMENT) || tk.isLiteral()) { + if (kind) + *kind = T_EOF_SYMBOL; + return pos; + } + } + + return start; +} + +bool CppCodeCompletion::triggersCompletion(TextEditor::ITextEditable *editor) +{ + if (! m_manager->isCppEditor(editor)) // ### remove me + return false; + + const int pos = editor->position(); + if (startOfOperator(editor, pos, /*token =*/ 0, + /*want function call=*/ true) != pos) + return true; + + return false; +} + +int CppCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) +{ + TextEditor::BaseTextEditor *edit = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget()); + if (! edit) + return -1; + + m_editor = editor; + m_startPosition = findStartOfName(editor); + m_completionOperator = T_EOF_SYMBOL; + + int endOfExpression = m_startPosition; + + // Skip whitespace preceding this position + while (editor->characterAt(endOfExpression - 1).isSpace()) + --endOfExpression; + + endOfExpression = startOfOperator(editor, endOfExpression, + &m_completionOperator, + /*want function call =*/ editor->position() == endOfExpression); + + Core::IFile *file = editor->file(); + QString fileName = file->fileName(); + + int line = 0, column = 0; + edit->convertPosition(editor->position(), &line, &column); + // qDebug() << "line:" << line << "column:" << column; + + ExpressionUnderCursor expressionUnderCursor; + QString expression; + + if (m_completionOperator) { + QTextCursor tc(edit->document()); + tc.setPosition(endOfExpression); + expression = expressionUnderCursor(tc); + if (m_completionOperator == T_LPAREN) { + if (expression.endsWith(QLatin1String("SIGNAL"))) + m_completionOperator = T_SIGNAL; + else if (expression.endsWith(QLatin1String("SLOT"))) + m_completionOperator = T_SLOT; + } + } + + //if (! expression.isEmpty()) + //qDebug() << "***** expression:" << expression; + + if (Document::Ptr thisDocument = m_manager->document(fileName)) { + Symbol *symbol = thisDocument->findSymbolAt(line, column); + + typeOfExpression.setDocuments(m_manager->documents()); + + QList<TypeOfExpression::Result> resolvedTypes = typeOfExpression(expression, thisDocument, symbol); + LookupContext context = typeOfExpression.lookupContext(); + + if (!typeOfExpression.expressionAST() && (! m_completionOperator || + m_completionOperator == T_COLON_COLON)) { + if (!m_completionOperator) { + addKeywords(); + addMacros(context); + } + + const QList<Scope *> scopes = context.expand(context.visibleScopes()); + foreach (Scope *scope, scopes) { + for (unsigned i = 0; i < scope->symbolCount(); ++i) { + addCompletionItem(scope->symbolAt(i)); + } + } + return m_startPosition; + } + + // qDebug() << "found" << resolvedTypes.count() << "symbols for expression:" << expression; + + if (resolvedTypes.isEmpty() && (m_completionOperator == T_SIGNAL || + m_completionOperator == T_SLOT)) { + // Apply signal/slot completion on 'this' + expression = QLatin1String("this"); + resolvedTypes = typeOfExpression(expression, thisDocument, symbol); + context = typeOfExpression.lookupContext(); + } + + if (! resolvedTypes.isEmpty()) { + FullySpecifiedType exprTy = resolvedTypes.first().first; + + if (exprTy->isReferenceType()) + exprTy = exprTy->asReferenceType()->elementType(); + + if (m_completionOperator == T_LPAREN && completeFunction(exprTy, resolvedTypes, context)) { + return m_startPosition; + } if ((m_completionOperator == T_DOT || m_completionOperator == T_ARROW) && + completeMember(exprTy, resolvedTypes, context)) { + return m_startPosition; + } else if (m_completionOperator == T_COLON_COLON && completeScope(exprTy, resolvedTypes, context)) { + return m_startPosition; + } else if (m_completionOperator == T_SIGNAL && completeSignal(exprTy, resolvedTypes, context)) { + return m_startPosition; + } else if (m_completionOperator == T_SLOT && completeSlot(exprTy, resolvedTypes, context)) { + return m_startPosition; + } + } + } + + // nothing to do. + return -1; +} + +bool CppCodeCompletion::completeFunction(FullySpecifiedType exprTy, + const QList<TypeOfExpression::Result> &resolvedTypes, + const LookupContext &) +{ + ConvertToCompletionItem toCompletionItem(this); + Overview o; + o.setShowReturnTypes(true); + o.setShowArgumentNames(true); + + if (Class *klass = exprTy->asClass()) { + for (unsigned i = 0; i < klass->memberCount(); ++i) { + Symbol *member = klass->memberAt(i); + if (! member->type()->isFunction()) + continue; + else if (! member->identity()) + continue; + else if (! member->identity()->isEqualTo(klass->identity())) + continue; + if (TextEditor::CompletionItem item = toCompletionItem(member)) { + item.m_text = o(member->type(), member->name()); + m_completions.append(item); + } + } + } else { + QSet<QString> signatures; + foreach (TypeOfExpression::Result p, resolvedTypes) { + FullySpecifiedType ty = p.first; + if (Function *fun = ty->asFunction()) { + if (TextEditor::CompletionItem item = toCompletionItem(fun)) { + QString signature; + signature += overview.prettyName(fun->name()); + signature += overview.prettyType(fun->type()); + if (signatures.contains(signature)) + continue; + signatures.insert(signature); + + item.m_text = o(ty, fun->name()); + m_completions.append(item); + } + } + } + } + + return ! m_completions.isEmpty(); +} + +bool CppCodeCompletion::completeMember(FullySpecifiedType, + const QList<TypeOfExpression::Result> &results, + const LookupContext &context) +{ + Q_ASSERT(! results.isEmpty()); + + QList<Symbol *> classObjectCandidates; + + TypeOfExpression::Result p = results.first(); + + if (m_completionOperator == T_ARROW) { + FullySpecifiedType ty = p.first; + + if (ReferenceType *refTy = ty->asReferenceType()) + ty = refTy->elementType(); + + if (NamedType *namedTy = ty->asNamedType()) { + ResolveExpression resolveExpression(context); + + Name *className = namedTy->name(); + const QList<Symbol *> candidates = + context.resolveClass(className, context.visibleScopes(p)); + + foreach (Symbol *classObject, candidates) { + const QList<TypeOfExpression::Result> overloads = + resolveExpression.resolveArrowOperator(p, namedTy, + classObject->asClass()); + + foreach (TypeOfExpression::Result r, overloads) { + FullySpecifiedType ty = r.first; + Function *funTy = ty->asFunction(); + if (! funTy) + continue; + + ty = funTy->returnType(); + + if (ReferenceType *refTy = ty->asReferenceType()) + ty = refTy->elementType(); + + if (PointerType *ptrTy = ty->asPointerType()) { + if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) { + const QList<Symbol *> classes = + context.resolveClass(namedTy->name(), + context.visibleScopes(p)); + + foreach (Symbol *c, classes) { + if (! classObjectCandidates.contains(c)) + classObjectCandidates.append(c); + } + } + } + } + } + } else if (PointerType *ptrTy = ty->asPointerType()) { + if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) { + const QList<Symbol *> classes = + context.resolveClass(namedTy->name(), + context.visibleScopes(p)); + + foreach (Symbol *c, classes) { + if (! classObjectCandidates.contains(c)) + classObjectCandidates.append(c); + } + } + } + } else if (m_completionOperator == T_DOT) { + FullySpecifiedType ty = p.first; + + if (ReferenceType *refTy = ty->asReferenceType()) + ty = refTy->elementType(); + + NamedType *namedTy = 0; + if (PointerType *ptrTy = ty->asPointerType()) { + // Replace . with -> + int length = m_editor->position() - m_startPosition + 1; + m_editor->setCurPos(m_startPosition - 1); + m_editor->replace(length, QLatin1String("->")); + m_startPosition++; + namedTy = ptrTy->elementType()->asNamedType(); + } else { + namedTy = ty->asNamedType(); + if (! namedTy) { + Function *fun = ty->asFunction(); + if (fun && (fun->scope()->isBlockScope() || fun->scope()->isNamespaceScope())) + namedTy = fun->returnType()->asNamedType(); + } + } + + if (namedTy) { + const QList<Symbol *> classes = + context.resolveClass(namedTy->name(), + context.visibleScopes(p)); + + foreach (Symbol *c, classes) { + if (! classObjectCandidates.contains(c)) + classObjectCandidates.append(c); + } + } + } + + completeClass(classObjectCandidates, context, /*static lookup = */ false); + if (! m_completions.isEmpty()) + return true; + + return false; +} + +bool CppCodeCompletion::completeScope(FullySpecifiedType exprTy, + const QList<TypeOfExpression::Result> &resolvedTypes, + const LookupContext &context) +{ + // Search for a class or a namespace. + foreach (TypeOfExpression::Result p, resolvedTypes) { + if (p.first->isClass() || p.first->isNamespace()) { + exprTy = p.first; + break; + } + } + + if (exprTy->asNamespace()) { + QList<Symbol *> candidates; + foreach (TypeOfExpression::Result p, resolvedTypes) { + if (Namespace *ns = p.first->asNamespace()) + candidates.append(ns); + } + completeNamespace(candidates, context); + } else if (exprTy->isClass()) { + QList<Symbol *> candidates; + foreach (TypeOfExpression::Result p, resolvedTypes) { + if (Class *k = p.first->asClass()) + candidates.append(k); + } + completeClass(candidates, context); + } + + return ! m_completions.isEmpty(); +} + +void CppCodeCompletion::addKeywords() +{ + // keyword completion items. + for (int i = T_FIRST_KEYWORD; i < T_FIRST_QT_KEYWORD; ++i) { + TextEditor::CompletionItem item(this); + item.m_text = QLatin1String(Token::name(i)); + item.m_icon = m_icons.keywordIcon(); + m_completions.append(item); + } +} + +void CppCodeCompletion::addMacros(const LookupContext &context) +{ + // macro completion items. + QSet<QByteArray> macroNames; + QSet<QString> processed; + QList<QString> todo; + todo.append(context.thisDocument()->fileName()); + while (! todo.isEmpty()) { + QString fn = todo.last(); + todo.removeLast(); + if (processed.contains(fn)) + continue; + processed.insert(fn); + if (Document::Ptr doc = context.document(fn)) { + macroNames += doc->macroNames(); + todo += doc->includedFiles(); + } + } + + foreach (const QByteArray macroName, macroNames) { + TextEditor::CompletionItem item(this); + item.m_text = QString::fromLatin1(macroName.constData(), macroName.length()); + item.m_icon = m_icons.macroIcon(); + m_completions.append(item); + } +} + +void CppCodeCompletion::addCompletionItem(Symbol *symbol) +{ + ConvertToCompletionItem toCompletionItem(this); + if (TextEditor::CompletionItem item = toCompletionItem(symbol)) + m_completions.append(item); +} + +void CppCodeCompletion::completeNamespace(const QList<Symbol *> &candidates, + const LookupContext &context) +{ + QList<Scope *> todo; + QList<Scope *> visibleScopes = context.visibleScopes(); + foreach (Symbol *candidate, candidates) { + if (Namespace *ns = candidate->asNamespace()) + context.expand(ns->members(), visibleScopes, &todo); + } + + foreach (Scope *scope, todo) { + addCompletionItem(scope->owner()); + + for (unsigned i = 0; i < scope->symbolCount(); ++i) { + addCompletionItem(scope->symbolAt(i)); + } + } +} + +void CppCodeCompletion::completeClass(const QList<Symbol *> &candidates, + const LookupContext &context, + bool staticLookup) +{ + if (candidates.isEmpty()) + return; + + Class *klass = candidates.first()->asClass(); + + QList<Scope *> todo; + context.expand(klass->members(), context.visibleScopes(), &todo); + + foreach (Scope *scope, todo) { + addCompletionItem(scope->owner()); + + for (unsigned i = 0; i < scope->symbolCount(); ++i) { + Symbol *symbol = scope->symbolAt(i); + + if (symbol->type().isFriend()) + continue; + else if (! staticLookup && (symbol->isTypedef() || + symbol->isEnum() || + symbol->isClass())) + continue; + + addCompletionItem(symbol); + } + } +} + +bool CppCodeCompletion::completeQtMethod(CPlusPlus::FullySpecifiedType, + const QList<TypeOfExpression::Result> &results, + const LookupContext &context, + bool wantSignals) +{ + if (results.isEmpty()) + return false; + + ConvertToCompletionItem toCompletionItem(this); + Overview o; + o.setShowReturnTypes(false); + o.setShowArgumentNames(false); + o.setShowFunctionSignatures(true); + + QSet<QString> signatures; + foreach (TypeOfExpression::Result p, results) { + FullySpecifiedType ty = p.first; + if (ReferenceType *refTy = ty->asReferenceType()) + ty = refTy->elementType(); + if (PointerType *ptrTy = ty->asPointerType()) + ty = ptrTy->elementType(); + else + continue; // not a pointer or a reference to a pointer. + + NamedType *namedTy = ty->asNamedType(); + if (! namedTy) // not a class name. + continue; + + const QList<Scope *> visibleScopes = context.visibleScopes(p); + + const QList<Symbol *> classObjects = + context.resolveClass(namedTy->name(), visibleScopes); + + if (classObjects.isEmpty()) + continue; + + Class *klass = classObjects.first()->asClass(); + + QList<Scope *> todo; + context.expand(klass->members(), visibleScopes, &todo); + + foreach (Scope *scope, todo) { + if (! scope->isClassScope()) + continue; + + for (unsigned i = 0; i < scope->symbolCount(); ++i) { + Symbol *member = scope->symbolAt(i); + Function *fun = member->type()->asFunction(); + if (! fun) + continue; + if (wantSignals && ! fun->isSignal()) + continue; + else if (! wantSignals && ! fun->isSlot()) + continue; + if (TextEditor::CompletionItem item = toCompletionItem(fun)) { + unsigned count = fun->argumentCount(); + while (true) { + TextEditor::CompletionItem i = item; + + QString signature; + signature += overview.prettyName(fun->name()); + signature += QLatin1Char('('); + for (unsigned i = 0; i < count; ++i) { + Symbol *arg = fun->argumentAt(i); + if (i != 0) + signature += QLatin1Char(','); + signature += o.prettyType(arg->type()); + } + signature += QLatin1Char(')'); + + const QByteArray normalized = + QMetaObject::normalizedSignature(signature.toLatin1()); + + signature = QString::fromLatin1(normalized, normalized.size()); + + if (! signatures.contains(signature)) { + signatures.insert(signature); + + i.m_text = signature; // fix the completion item. + m_completions.append(i); + } + + if (count && fun->argumentAt(count - 1)->asArgument()->hasInitializer()) + --count; + else + break; + } + } + } + } + } + + return ! m_completions.isEmpty(); +} + +void CppCodeCompletion::completions(QList<TextEditor::CompletionItem> *completions) +{ + const int length = m_editor->position() - m_startPosition; + + if (length == 0) + *completions = m_completions; + else if (length > 0) { + const QString key = m_editor->textAt(m_startPosition, length); + + if (m_completionOperator != T_LPAREN) { + /* + * This code builds a regular expression in order to more intelligently match + * camel-case style. This means upper-case characters will be rewritten as follows: + * + * A => [a-z0-9_]*A (for any but the first capital letter) + * + * Meaning it allows any sequence of lower-case characters to preceed an + * upper-case character. So for example gAC matches getActionController. + * + * The match is case-sensitive as soon as at least one upper-case character is + * present. + */ + QString keyRegExp; + keyRegExp += QLatin1Char('^'); + bool first = true; + Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive; + foreach (const QChar &c, key) { + if (c.isLower()) { + keyRegExp.append(c); + } else if (c.isUpper()) { + sensitivity = Qt::CaseSensitive; + if (!first) { + keyRegExp.append("[a-z0-9_]*"); + } + keyRegExp.append(c); + } else { + keyRegExp.append(QRegExp::escape(c)); + } + first = false; + } + const QRegExp regExp(keyRegExp, sensitivity); + + foreach (TextEditor::CompletionItem item, m_completions) { + if (regExp.indexIn(item.m_text) == 0) { + item.m_relevance = (key.length() > 0 && + item.m_text.startsWith(key, Qt::CaseInsensitive)) ? 1 : 0; + (*completions) << item; + } + } + } else if (m_completionOperator == T_LPAREN || + m_completionOperator == T_SIGNAL || + m_completionOperator == T_SLOT) { + foreach (TextEditor::CompletionItem item, m_completions) { + if (item.m_text.startsWith(key, Qt::CaseInsensitive)) { + (*completions) << item; + } + } + } + } +} + +void CppCodeCompletion::complete(const TextEditor::CompletionItem &item) +{ + Symbol *symbol = 0; + + if (item.m_data.isValid()) + symbol = item.m_data.value<Symbol *>(); + + // qDebug() << "*** complete symbol:" << symbol->fileName() << symbol->line(); + + if (m_completionOperator == T_LPAREN) { + if (symbol) { + Function *function = symbol->type()->asFunction(); + Q_ASSERT(function != 0); + + m_functionArgumentWidget = new FunctionArgumentWidget(m_core); + m_functionArgumentWidget->showFunctionHint(function); + } + } else if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) { + QString toInsert = item.m_text; + toInsert += QLatin1Char(')'); + // Insert the remainder of the name + int length = m_editor->position() - m_startPosition; + m_editor->setCurPos(m_startPosition); + m_editor->replace(length, toInsert); + } else { + QString toInsert = item.m_text; + + //qDebug() << "current symbol:" << overview.prettyName(symbol->name()) + //<< overview.prettyType(symbol->type()); + + if (symbol) { + if (Function *function = symbol->type()->asFunction()) { + // If the member is a function, automatically place the opening parenthesis, + // except when it might take template parameters. + if (!function->returnType().isValid() + && (function->identity() && !function->identity()->isDestructorNameId())) { + // Don't insert any magic, since the user might have just wanted to select the class + + } else if (function->templateParameterCount() != 0) { + // If there are no arguments, then we need the template specification + if (function->argumentCount() == 0) { + toInsert.append(QLatin1Char('<')); + } + } else { + toInsert.append(QLatin1Char('(')); + + // If the function takes no arguments, automatically place the closing parenthesis + if (function->argumentCount() == 0 || (function->argumentCount() == 1 && + function->argumentAt(0)->type()->isVoidType())) { + toInsert.append(QLatin1Char(')')); + + // If the function doesn't return anything, automatically place the semicolon, + // unless we're doing a scope completion (then it might be function definition). + if (function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON) { + toInsert.append(QLatin1Char(';')); + } + } + } + } + } + + // Insert the remainder of the name + int length = m_editor->position() - m_startPosition; + m_editor->setCurPos(m_startPosition); + m_editor->replace(length, toInsert); + } +} + +bool CppCodeCompletion::partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems) +{ + if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) { + return false; + } else if (completionItems.count() == 1) { + complete(completionItems.first()); + return true; + } else if (m_completionOperator != T_LPAREN) { + // Compute common prefix + QString firstKey = completionItems.first().m_text; + QString lastKey = completionItems.last().m_text; + const int length = qMin(firstKey.length(), lastKey.length()); + firstKey.truncate(length); + lastKey.truncate(length); + + while (firstKey != lastKey) { + firstKey.chop(1); + lastKey.chop(1); + } + + int typedLength = m_editor->position() - m_startPosition; + if (!firstKey.isEmpty() && firstKey.length() > typedLength) { + m_editor->setCurPos(m_startPosition); + m_editor->replace(typedLength, firstKey); + } + } + + return false; +} + +void CppCodeCompletion::cleanup() +{ + m_completions.clear(); +} + +int CppCodeCompletion::findStartOfName(const TextEditor::ITextEditor *editor) +{ + int pos = editor->position(); + QChar chr; + + // Skip to the start of a name + do { + chr = editor->characterAt(--pos); + } while (chr.isLetterOrNumber() || chr == QLatin1Char('_')); + + return pos + 1; +} diff --git a/src/plugins/cpptools/cppcodecompletion.h b/src/plugins/cpptools/cppcodecompletion.h new file mode 100644 index 00000000000..49773935212 --- /dev/null +++ b/src/plugins/cpptools/cppcodecompletion.h @@ -0,0 +1,145 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPCODECOMPLETION_H +#define CPPCODECOMPLETION_H + +// Qt +#include <QtCore/QObject> +#include <QtCore/QPointer> + +// C++ front-end +#include <ASTfwd.h> +#include <FullySpecifiedType.h> +#include <cplusplus/Icons.h> +#include <cplusplus/Overview.h> +#include <cplusplus/TypeOfExpression.h> + +// Qt Creator +#include <texteditor/icompletioncollector.h> + +namespace Core { +class ICore; +} + +namespace TextEditor { +class ITextEditor; +} + +namespace CppTools { +namespace Internal { + +class CppModelManager; +class FunctionArgumentWidget; + +class CppCodeCompletion : public TextEditor::ICompletionCollector +{ + Q_OBJECT +public: + CppCodeCompletion(CppModelManager *manager, Core::ICore *core); + + bool triggersCompletion(TextEditor::ITextEditable *editor); + int startCompletion(TextEditor::ITextEditable *editor); + void completions(QList<TextEditor::CompletionItem> *completions); + + void complete(const TextEditor::CompletionItem &item); + bool partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems); + void cleanup(); + + QIcon iconForSymbol(CPlusPlus::Symbol *symbol) const; + +private: + void addKeywords(); + void addMacros(const CPlusPlus::LookupContext &context); + void addCompletionItem(CPlusPlus::Symbol *symbol); + + bool completeFunction(CPlusPlus::FullySpecifiedType exprTy, + const QList<CPlusPlus::TypeOfExpression::Result> &, + const CPlusPlus::LookupContext &context); + + bool completeMember(CPlusPlus::FullySpecifiedType exprTy, + const QList<CPlusPlus::TypeOfExpression::Result> &, + const CPlusPlus::LookupContext &context); + + bool completeScope(CPlusPlus::FullySpecifiedType exprTy, + const QList<CPlusPlus::TypeOfExpression::Result> &, + const CPlusPlus::LookupContext &context); + + void completeNamespace(const QList<CPlusPlus::Symbol *> &candidates, + const CPlusPlus::LookupContext &context); + + void completeClass(const QList<CPlusPlus::Symbol *> &candidates, + const CPlusPlus::LookupContext &context, + bool staticLookup = true); + + bool completeQtMethod(CPlusPlus::FullySpecifiedType exprTy, + const QList<CPlusPlus::TypeOfExpression::Result> &, + const CPlusPlus::LookupContext &context, + bool wantSignals); + + bool completeSignal(CPlusPlus::FullySpecifiedType exprTy, + const QList<CPlusPlus::TypeOfExpression::Result> &results, + const CPlusPlus::LookupContext &context) + { return completeQtMethod(exprTy, results, context, true); } + + bool completeSlot(CPlusPlus::FullySpecifiedType exprTy, + const QList<CPlusPlus::TypeOfExpression::Result> &results, + const CPlusPlus::LookupContext &context) + { return completeQtMethod(exprTy, results, context, false); } + + static int findStartOfName(const TextEditor::ITextEditor *editor); + + QList<TextEditor::CompletionItem> m_completions; + + TextEditor::ITextEditable *m_editor; + int m_startPosition; // Position of the cursor from which completion started + + Core::ICore *m_core; + CppModelManager *m_manager; + + bool m_forcedCompletion; + + CPlusPlus::Icons m_icons; + CPlusPlus::Overview overview; + CPlusPlus::TypeOfExpression typeOfExpression; + + unsigned m_completionOperator; + + QPointer<FunctionArgumentWidget> m_functionArgumentWidget; +}; + +} // namespace Internal +} // namespace CppTools + +Q_DECLARE_METATYPE(CPlusPlus::Symbol *) + +#endif // CPPCODECOMPLETION_H diff --git a/src/plugins/cpptools/cpphoverhandler.cpp b/src/plugins/cpptools/cpphoverhandler.cpp new file mode 100644 index 00000000000..dc9bb966616 --- /dev/null +++ b/src/plugins/cpptools/cpphoverhandler.cpp @@ -0,0 +1,243 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cpphoverhandler.h" +#include "cppmodelmanager.h" + +#include <coreplugin/icore.h> +#include <coreplugin/uniqueidmanager.h> +#include <texteditor/itexteditor.h> +#include <debugger/debuggerconstants.h> + +#include <CoreTypes.h> +#include <FullySpecifiedType.h> +#include <Literals.h> +#include <Names.h> +#include <Scope.h> +#include <Symbol.h> +#include <Symbols.h> +#include <cplusplus/ExpressionUnderCursor.h> +#include <cplusplus/Overview.h> +#include <cplusplus/TypeOfExpression.h> + +#include <QtGui/QToolTip> +#include <QtGui/QPlainTextEdit> +#include <QtGui/QTextCursor> +#include <QtGui/QTextBlock> +#include <QtHelp/QHelpEngineCore> +#include <QtCore/QtCore> + +using namespace CppTools::Internal; + +CppHoverHandler::CppHoverHandler(CppModelManager *manager, QObject *parent) + : QObject(parent), m_manager(manager) +{ + QFileInfo fi(ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()->settings()->fileName()); + m_helpEngine = new QHelpEngineCore(fi.absolutePath() + + QLatin1String("/helpcollection.qhc"), this); + //m_helpEngine->setAutoSaveFilter(false); + m_helpEngine->setupData(); + m_helpEngine->setCurrentFilter(tr("Unfiltered")); +} + +void CppHoverHandler::updateContextHelpId(TextEditor::ITextEditor *editor, int pos) +{ + updateHelpIdAndTooltip(editor, pos); +} + +void CppHoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint &point, int pos) +{ + const int dbgcontext = m_manager->core()-> + uniqueIDManager()->uniqueIdentifier(Debugger::Constants::C_GDBDEBUGGER); + + if (m_manager->core()->hasContext(dbgcontext)) + return; + + if (! editor) + return; + + updateHelpIdAndTooltip(editor, pos); + + if (m_toolTip.isEmpty()) + QToolTip::hideText(); + else { + const QPoint pnt = point - QPoint(0, +#ifdef Q_WS_WIN + 24 +#else + 16 +#endif + ); + + QToolTip::showText(pnt, m_toolTip); + } +} + +static QString buildHelpId(const CPlusPlus::FullySpecifiedType &type, + const CPlusPlus::Symbol *symbol) +{ + using namespace CPlusPlus; + + Name *name = 0; + Scope *scope = 0; + + if (const Function *f = type->asFunction()) { + name = f->name(); + scope = f->scope(); + } else if (const Class *c = type->asClass()) { + name = c->name(); + scope = c->scope(); + } else if (const Enum *e = type->asEnum()) { + name = e->name(); + scope = e->scope(); + } else if (const NamedType *t = type->asNamedType()) { + name = t->name(); + } else if (const Declaration *d = symbol->asDeclaration()) { + if (d->scope() && d->scope()->owner()->isEnum()) { + name = d->name(); + scope = d->scope(); + } + } + + Overview overview; + overview.setShowArgumentNames(false); + overview.setShowReturnTypes(false); + + QStringList qualifiedNames; + qualifiedNames.prepend(overview.prettyName(name)); + + for (; scope; scope = scope->enclosingScope()) { + if (scope->owner() && scope->owner()->name() && !scope->isEnumScope()) { + Name *name = scope->owner()->name(); + Identifier *id = 0; + if (NameId *nameId = name->asNameId()) { + id = nameId->identifier(); + } else if (TemplateNameId *nameId = name->asTemplateNameId()) { + id = nameId->identifier(); + } + if (id) + qualifiedNames.prepend(QString::fromLatin1(id->chars(), id->size())); + } + } + + return qualifiedNames.join(QLatin1String("::")); +} + +void CppHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos) +{ + using namespace CPlusPlus; + + m_helpId.clear(); + m_toolTip.clear(); + + QPlainTextEdit *edit = qobject_cast<QPlainTextEdit *>(editor->widget()); + if (!edit) + return; + + QTextCursor tc(edit->document()); + tc.setPosition(pos); + + const int lineNumber = tc.block().blockNumber() + 1; + + QString fileName = editor->file()->fileName(); + Document::Ptr doc = m_manager->document(fileName); + if (doc) { + foreach (Document::DiagnosticMessage m, doc->diagnosticMessages()) { + if (m.line() == lineNumber) { + m_toolTip = m.text(); + break; + } + } + } + + if (m_toolTip.isEmpty()) { + // Move to the end of a qualified name + bool stop = false; + while (!stop) { + const QChar ch = editor->characterAt(tc.position()); + if (ch.isLetterOrNumber() || ch == QLatin1Char('_')) + tc.setPosition(tc.position() + 1); + else if (ch == QLatin1Char(':') && editor->characterAt(tc.position() + 1) == QLatin1Char(':')) { + tc.setPosition(tc.position() + 2); + } else { + stop = true; + } + } + + // Fetch the expression's code. + ExpressionUnderCursor expressionUnderCursor; + const QString expression = expressionUnderCursor(tc); + + if (doc) { + // Find the last symbol up to the cursor position + int line = 0, column = 0; + editor->convertPosition(tc.position(), &line, &column); + Symbol *lastSymbol = doc->findSymbolAt(line, column); + + TypeOfExpression typeOfExpression; + typeOfExpression.setDocuments(m_manager->documents()); + QList<TypeOfExpression::Result> types = typeOfExpression(expression, doc, lastSymbol); + + if (!types.isEmpty()) { + FullySpecifiedType firstType = types.first().first; + FullySpecifiedType docType = firstType; + + if (const PointerType *pt = firstType->asPointerType()) { + docType = pt->elementType(); + } else if (const ReferenceType *rt = firstType->asReferenceType()) { + docType = rt->elementType(); + } + + + m_helpId = buildHelpId(docType, types.first().second); + QString displayName = buildHelpId(firstType, types.first().second); + + if (!firstType->isClass() && !firstType->isNamedType()) { + Overview overview; + overview.setShowArgumentNames(true); + overview.setShowReturnTypes(true); + m_toolTip = overview.prettyType(firstType, displayName); + } else { + m_toolTip = m_helpId; + } + } + } + } + + if (!m_helpId.isEmpty() && !m_helpEngine->linksForIdentifier(m_helpId).isEmpty()) { + m_toolTip = QString(QLatin1String("<table><tr><td valign=middle><nobr>%1</td>" + "<td><img src=\":/cpptools/images/f1.svg\"></td></tr></table>")).arg(Qt::escape(m_toolTip)); + editor->setContextHelpId(m_helpId); + } else if (!m_toolTip.isEmpty()) { + m_toolTip = QString(QLatin1String("<nobr>%1")).arg(Qt::escape(m_toolTip)); + } +} diff --git a/src/plugins/cpptools/cpphoverhandler.h b/src/plugins/cpptools/cpphoverhandler.h new file mode 100644 index 00000000000..ca828c35dbc --- /dev/null +++ b/src/plugins/cpptools/cpphoverhandler.h @@ -0,0 +1,75 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPHOVERHANDLER_H +#define CPPHOVERHANDLER_H + +#include <QtCore/QObject> +#include <QtCore/QPoint> + +QT_BEGIN_NAMESPACE +class QHelpEngineCore; +QT_END_NAMESPACE + +namespace TextEditor { +class ITextEditor; +} + +namespace CppTools { +namespace Internal { + +class CppModelManager; + +class CppHoverHandler : public QObject +{ + Q_OBJECT + +public: + CppHoverHandler(CppModelManager *manager, QObject *parent); + +public slots: + void showToolTip(TextEditor::ITextEditor *editor, const QPoint &point, int pos); + void updateContextHelpId(TextEditor::ITextEditor *editor, int pos); + +private: + void updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos); + + CppModelManager *m_manager; + QHelpEngineCore *m_helpEngine; + QString m_helpId; + QString m_toolTip; +}; + +} // namespace Internal +} // namespace CppTools + +#endif // CPPHOVERHANDLER_H diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp new file mode 100644 index 00000000000..6ec5391267e --- /dev/null +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -0,0 +1,740 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#define _SCL_SECURE_NO_WARNINGS 1 +#include "pp.h" + +#include "cppmodelmanager.h" +#include "cpphoverhandler.h" +#include "cpptoolsconstants.h" +#include "cpptoolseditorsupport.h" + +#include <qtconcurrent/runextensions.h> +#include <texteditor/itexteditor.h> +#include <texteditor/basetexteditor.h> + +#include <projectexplorer/project.h> +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/session.h> + +#include <coreplugin/icore.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/progressmanager/progressmanager.h> + +#include <TranslationUnit.h> +#include <Semantic.h> +#include <AST.h> +#include <Scope.h> +#include <Literals.h> +#include <Symbols.h> +#include <Names.h> +#include <NameVisitor.h> +#include <TypeVisitor.h> +#include <Lexer.h> +#include <Token.h> + +#include <QPlainTextEdit> +#include <QTime> +#include <QDebug> + +using namespace CPlusPlus; + +namespace CppTools { +namespace Internal { + +static const char pp_configuration_file[] = "<configuration>"; + +static const char pp_configuration[] = + "# 1 \"<configuration>\"\n" + "#define __GNUC_MINOR__ 0\n" + "#define __GNUC__ 4\n" + "#define __GNUG__ 4\n" + "#define __STDC_HOSTED__ 1\n" + "#define __VERSION__ \"4.0.1 (fake)\"\n" + "#define __cplusplus 1\n" + + "#define __extension__\n" + "#define __context__\n" + "#define __range__\n" + "#define __asm(a...)\n" + "#define __asm__(a...)\n" + "#define restrict\n" + "#define __restrict\n" + + // ### add macros for win32 + "#define __cdecl\n" + "#define QT_WA(x) x\n" + "#define API\n" + "#define WINAPI\n" + "#define CALLBACK\n" + "#define STDMETHODCALLTYPE\n" + "#define __RPC_FAR\n" + "#define APIENTRY\n" + "#define __declspec(a)\n" + "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n"; + +class CppPreprocessor: public rpp::Client +{ +public: + CppPreprocessor(QPointer<CppModelManager> modelManager) + : m_modelManager(modelManager), + m_documents(modelManager->documents()), + m_proc(this, env) + { } + + void setWorkingCopy(const QMap<QString, QByteArray> &workingCopy) + { m_workingCopy = workingCopy; } + + void setIncludePaths(const QStringList &includePaths) + { m_includePaths = includePaths; } + + void setFrameworkPaths(const QStringList &frameworkPaths) + { m_frameworkPaths = frameworkPaths; } + + void addIncludePath(const QString &path) + { m_includePaths.append(path); } + + void setProjectFiles(const QStringList &files) + { m_projectFiles = files; } + + void operator()(QString &fileName) + { sourceNeeded(fileName, IncludeGlobal); } + +protected: + bool includeFile(const QString &absoluteFilePath, QByteArray *result) + { + if (absoluteFilePath.isEmpty() || m_included.contains(absoluteFilePath)) { + return true; + } + + if (m_workingCopy.contains(absoluteFilePath)) { + m_included.insert(absoluteFilePath); + *result = m_workingCopy.value(absoluteFilePath); + return true; + } + + QFileInfo fileInfo(absoluteFilePath); + if (! fileInfo.isFile()) + return false; + + QFile file(absoluteFilePath); + if (file.open(QFile::ReadOnly)) { + m_included.insert(absoluteFilePath); + QTextStream stream(&file); + const QString contents = stream.readAll(); + *result = contents.toUtf8(); + file.close(); + return true; + } + + return false; + } + + QByteArray tryIncludeFile(QString &fileName, IncludeType type) + { + QFileInfo fileInfo(fileName); + if (fileName == QLatin1String(pp_configuration_file) || fileInfo.isAbsolute()) { + QByteArray contents; + includeFile(fileName, &contents); + return contents; + } + + if (type == IncludeLocal && m_currentDoc) { + QFileInfo currentFileInfo(m_currentDoc->fileName()); + QString path = currentFileInfo.absolutePath(); + path += QLatin1Char('/'); + path += fileName; + path = QDir::cleanPath(path); + QByteArray contents; + if (includeFile(path, &contents)) { + fileName = path; + return contents; + } + } + + foreach (const QString &includePath, m_includePaths) { + QString path = includePath; + path += QLatin1Char('/'); + path += fileName; + path = QDir::cleanPath(path); + QByteArray contents; + if (includeFile(path, &contents)) { + fileName = path; + return contents; + } + } + + // look in the system include paths + foreach (const QString &includePath, m_systemIncludePaths) { + QString path = includePath; + path += QLatin1Char('/'); + path += fileName; + path = QDir::cleanPath(path); + QByteArray contents; + if (includeFile(path, &contents)) { + fileName = path; + return contents; + } + } + + int index = fileName.indexOf(QLatin1Char('/')); + if (index != -1) { + QString frameworkName = fileName.left(index); + QString name = fileName.mid(index + 1); + + foreach (const QString &frameworkPath, m_frameworkPaths) { + QString path = frameworkPath; + path += QLatin1Char('/'); + path += frameworkName; + path += QLatin1String(".framework/Headers/"); + path += name; + QByteArray contents; + if (includeFile(path, &contents)) { + fileName = path; + return contents; + } + } + } + + QString path = fileName; + if (path.at(0) != QLatin1Char('/')) + path.prepend(QLatin1Char('/')); + + foreach (const QString &projectFile, m_projectFiles) { + if (projectFile.endsWith(path)) { + fileName = projectFile; + QByteArray contents; + includeFile(fileName, &contents); + return contents; + } + } + + //qDebug() << "**** file" << fileName << "not found!"; + return QByteArray(); + } + + virtual void macroAdded(const QByteArray ¯oName, const QByteArray ¯oText) + { + if (! m_currentDoc) + return; + + m_currentDoc->appendMacro(macroName, macroText); + } + + void mergeEnvironment(Document::Ptr doc) + { + QSet<QString> processed; + mergeEnvironment(doc, &processed); + } + + void mergeEnvironment(Document::Ptr doc, QSet<QString> *processed) + { + if (! doc) + return; + + const QString fn = doc->fileName(); + + if (processed->contains(fn)) + return; + + processed->insert(fn); + + foreach (QString includedFile, doc->includedFiles()) + mergeEnvironment(m_documents.value(includedFile), processed); + + const QByteArray macros = doc->definedMacros(); + QByteArray localFileName = doc->fileName().toUtf8(); + + QByteArray dummy; + m_proc(localFileName, macros, &dummy); + } + + virtual void startSkippingBlocks(unsigned offset) + { + //qDebug() << "start skipping blocks:" << offset; + if (m_currentDoc) + m_currentDoc->startSkippingBlocks(offset); + } + + virtual void stopSkippingBlocks(unsigned offset) + { + //qDebug() << "stop skipping blocks:" << offset; + if (m_currentDoc) + m_currentDoc->stopSkippingBlocks(offset); + } + + virtual void sourceNeeded(QString &fileName, IncludeType type) + { + if (fileName.isEmpty()) + return; + + QByteArray contents = tryIncludeFile(fileName, type); + + if (m_currentDoc) { + m_currentDoc->addIncludeFile(fileName); + if (contents.isEmpty() && ! QFileInfo(fileName).isAbsolute()) { + QString msg; + msg += fileName; + msg += QLatin1String(": No such file or directory"); + Document::DiagnosticMessage d(Document::DiagnosticMessage::Warning, + m_currentDoc->fileName(), + env.currentLine, /*column = */ 0, + msg); + m_currentDoc->addDiagnosticMessage(d); + //qWarning() << "file not found:" << fileName << m_currentDoc->fileName() << env.current_line; + } + } + + if (! contents.isEmpty()) { + Document::Ptr cachedDoc = m_documents.value(fileName); + if (cachedDoc && m_currentDoc) { + mergeEnvironment(cachedDoc); + } else { + Document::Ptr previousDoc = switchDocument(Document::create(fileName)); + + const QByteArray previousFile = env.current_file; + const unsigned previousLine = env.currentLine; + + env.current_file = QByteArray(m_currentDoc->translationUnit()->fileName(), + m_currentDoc->translationUnit()->fileNameLength()); + + QByteArray preprocessedCode; + m_proc(contents, &preprocessedCode); + //qDebug() << preprocessedCode; + + env.current_file = previousFile; + env.currentLine = previousLine; + + m_currentDoc->setSource(preprocessedCode); + m_currentDoc->parse(); + m_currentDoc->check(); + m_currentDoc->releaseTranslationUnit(); // release the AST and the token stream. + + if (m_modelManager) + m_modelManager->emitDocumentUpdated(m_currentDoc); + (void) switchDocument(previousDoc); + } + } + } + + Document::Ptr switchDocument(Document::Ptr doc) + { + Document::Ptr previousDoc = m_currentDoc; + m_currentDoc = doc; + return previousDoc; + } + +private: + QPointer<CppModelManager> m_modelManager; + CppModelManager::DocumentTable m_documents; + rpp::Environment env; + rpp::pp m_proc; + QStringList m_includePaths; + QStringList m_systemIncludePaths; + QMap<QString, QByteArray> m_workingCopy; + QStringList m_projectFiles; + QStringList m_frameworkPaths; + QSet<QString> m_included; + Document::Ptr m_currentDoc; +}; + +} // namespace Internal +} // namespace CppTools + + +using namespace CppTools; +using namespace CppTools::Internal; + +/*! + \class CppTools::CppModelManager + \brief The CppModelManager keeps track of one CppCodeModel instance + for each project and all related CppCodeModelPart instances. + + It also takes care of updating the code models when C++ files are + modified within Workbench. +*/ + +CppModelManager::CppModelManager(QObject *parent) : + CppModelManagerInterface(parent), + m_core(ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()) +{ + m_projectExplorer = ExtensionSystem::PluginManager::instance() + ->getObject<ProjectExplorer::ProjectExplorerPlugin>(); + + Q_ASSERT(m_projectExplorer); + + ProjectExplorer::SessionManager *session = m_projectExplorer->session(); + Q_ASSERT(session != 0); + + connect(session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project *)), + this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project *))); + + connect(session, SIGNAL(sessionUnloaded()), + this, SLOT(onSessionUnloaded())); + + qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr"); + + // thread connections + connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)), + this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr))); + + m_hoverHandler = new CppHoverHandler(this, this); + + // Listen for editor closed and opened events so that we can keep track of changing files + connect(m_core->editorManager(), SIGNAL(editorOpened(Core::IEditor *)), + this, SLOT(editorOpened(Core::IEditor *))); + + connect(m_core->editorManager(), SIGNAL(editorAboutToClose(Core::IEditor *)), + this, SLOT(editorAboutToClose(Core::IEditor *))); +} + +CppModelManager::~CppModelManager() +{ } + +Document::Ptr CppModelManager::document(const QString &fileName) +{ return m_documents.value(fileName); } + +CppModelManager::DocumentTable CppModelManager::documents() +{ return m_documents; } + +QStringList CppModelManager::projectFiles() const +{ + QStringList files; + QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects); + while (it.hasNext()) { + it.next(); + ProjectInfo pinfo = it.value(); + files += pinfo.sourceFiles; + } + return files; +} + +QStringList CppModelManager::includePaths() const +{ + QStringList includePaths; + QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects); + while (it.hasNext()) { + it.next(); + ProjectInfo pinfo = it.value(); + includePaths += pinfo.includePaths; + } + return includePaths; +} + +QStringList CppModelManager::frameworkPaths() const +{ + QStringList frameworkPaths; + QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects); + while (it.hasNext()) { + it.next(); + ProjectInfo pinfo = it.value(); + frameworkPaths += pinfo.frameworkPaths; + } + return frameworkPaths; +} + +QByteArray CppModelManager::definedMacros() const +{ + QByteArray macros; + QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects); + while (it.hasNext()) { + it.next(); + ProjectInfo pinfo = it.value(); + macros += pinfo.defines; + } + return macros; +} + +QMap<QString, QByteArray> CppModelManager::buildWorkingCopyList() const +{ + QMap<QString, QByteArray> workingCopy; + QMapIterator<TextEditor::ITextEditor *, CppEditorSupport *> it(m_editorSupport); + while (it.hasNext()) { + it.next(); + TextEditor::ITextEditor *textEditor = it.key(); + CppEditorSupport *editorSupport = it.value(); + QString fileName = textEditor->file()->fileName(); + workingCopy[fileName] = editorSupport->contents().toUtf8(); + } + + // add the project configuration file + QByteArray conf(pp_configuration); + conf += definedMacros(); + workingCopy[pp_configuration_file] = conf; + + return workingCopy; +} + +void CppModelManager::updateSourceFiles(const QStringList &sourceFiles) +{ (void) refreshSourceFiles(sourceFiles); } + +CppModelManager::ProjectInfo *CppModelManager::projectInfo(ProjectExplorer::Project *project) +{ return &m_projects[project]; } + +QFuture<void> CppModelManager::refreshSourceFiles(const QStringList &sourceFiles) +{ + if (qgetenv("QTCREATOR_NO_CODE_INDEXER").isNull()) { + const QMap<QString, QByteArray> workingCopy = buildWorkingCopyList(); + + QFuture<void> result = QtConcurrent::run(&CppModelManager::parse, this, + sourceFiles, workingCopy); + + if (sourceFiles.count() > 1) { + m_core->progressManager()->addTask(result, tr("Indexing"), + CppTools::Constants::TASK_INDEX, + Core::ProgressManagerInterface::CloseOnSuccess); + } + return result; + } + return QFuture<void>(); +} + +/*! + \fn void CppModelManager::editorOpened(Core::IEditor *editor) + \brief If a C++ editor is opened, the model manager listens to content changes + in order to update the CppCodeModel accordingly. It also updates the + CppCodeModel for the first time with this editor. + + \sa void CppModelManager::editorContentsChanged() + */ +void CppModelManager::editorOpened(Core::IEditor *editor) +{ + if (isCppEditor(editor)) { + TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor); + Q_ASSERT(textEditor != 0); + + CppEditorSupport *editorSupport = new CppEditorSupport(this); + editorSupport->setTextEditor(textEditor); + m_editorSupport[textEditor] = editorSupport; + + // ### move in CppEditorSupport + connect(editor, SIGNAL(tooltipRequested(TextEditor::ITextEditor*, QPoint, int)), + m_hoverHandler, SLOT(showToolTip(TextEditor::ITextEditor*, QPoint, int))); + + // ### move in CppEditorSupport + connect(editor, SIGNAL(contextHelpIdRequested(TextEditor::ITextEditor*, int)), + m_hoverHandler, SLOT(updateContextHelpId(TextEditor::ITextEditor*, int))); + } +} + +void CppModelManager::editorAboutToClose(Core::IEditor *editor) +{ + if (isCppEditor(editor)) { + TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor); + Q_ASSERT(textEditor != 0); + + CppEditorSupport *editorSupport = m_editorSupport.value(textEditor); + m_editorSupport.remove(textEditor); + delete editorSupport; + } +} + +bool CppModelManager::isCppEditor(Core::IEditor *editor) const +{ + Core::UniqueIDManager *uidm = m_core->uniqueIDManager(); + const int uid = uidm->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX); + return editor->context().contains(uid); +} + +void CppModelManager::emitDocumentUpdated(Document::Ptr doc) +{ emit documentUpdated(doc); } + +void CppModelManager::onDocumentUpdated(Document::Ptr doc) +{ + const QString fileName = doc->fileName(); + m_documents[fileName] = doc; + QList<Core::IEditor *> openedEditors = m_core->editorManager()->openedEditors(); + foreach (Core::IEditor *editor, openedEditors) { + if (editor->file()->fileName() == fileName) { + TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor); + if (! textEditor) + continue; + + TextEditor::BaseTextEditor *ed = qobject_cast<TextEditor::BaseTextEditor *>(textEditor->widget()); + if (! ed) + continue; + + QList<TextEditor::BaseTextEditor::BlockRange> blockRanges; + + foreach (const Document::Block block, doc->skippedBlocks()) { + blockRanges.append(TextEditor::BaseTextEditor::BlockRange(block.begin(), block.end())); + } + ed->setIfdefedOutBlocks(blockRanges); + + QList<QTextEdit::ExtraSelection> selections; + + // set up the format for the errors + QTextCharFormat errorFormat; + errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); + errorFormat.setUnderlineColor(Qt::red); + + // set up the format for the warnings. + QTextCharFormat warningFormat; + warningFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); + warningFormat.setUnderlineColor(Qt::darkYellow); + + QSet<int> lines; + foreach (const Document::DiagnosticMessage m, doc->diagnosticMessages()) { + if (m.fileName() != fileName) + continue; + else if (lines.contains(m.line())) + continue; + else if (lines.size() == MAX_SELECTION_COUNT) + break; // we're done. + + lines.insert(m.line()); + + QTextEdit::ExtraSelection sel; + if (m.isWarning()) + sel.format = warningFormat; + else + sel.format = errorFormat; + + QTextCursor c(ed->document()->findBlockByNumber(m.line() - 1)); + const QString text = c.block().text(); + for (int i = 0; i < text.size(); ++i) { + if (! text.at(i).isSpace()) { + c.setPosition(c.position() + i); + break; + } + } + c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + sel.cursor = c; + selections.append(sel); + } + ed->setExtraExtraSelections(selections); + break; + } + } +} + +void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project) +{ + m_projects.remove(project); + GC(); +} + +void CppModelManager::onSessionUnloaded() +{ + if (m_core->progressManager()) + m_core->progressManager()->cancelTasks(CppTools::Constants::TASK_INDEX); +} + +void CppModelManager::parse(QFutureInterface<void> &future, + CppModelManager *model, + QStringList files, + QMap<QString, QByteArray> workingCopy) +{ + // Change the priority of the background parser thread to idle. + QThread::currentThread()->setPriority(QThread::IdlePriority); + + future.setProgressRange(0, files.size()); + + CppPreprocessor preproc(model); + preproc.setWorkingCopy(workingCopy); + preproc.setProjectFiles(model->projectFiles()); + preproc.setIncludePaths(model->includePaths()); + preproc.setFrameworkPaths(model->frameworkPaths()); + + QString conf = QLatin1String(pp_configuration_file); + (void) preproc(conf); + + const int STEP = 10; + + for (int i = 0; i < files.size(); ++i) { + if (future.isPaused()) + future.waitForResume(); + + if (future.isCanceled()) + break; + + future.setProgressValue(i); + +#ifdef CPPTOOLS_DEBUG_PARSING_TIME + QTime tm; + tm.start(); +#endif + + QString fileName = files.at(i); + preproc(fileName); + + if (! (i % STEP)) // Yields execution of the current thread. + QThread::yieldCurrentThread(); + +#ifdef CPPTOOLS_DEBUG_PARSING_TIME + qDebug() << fileName << "parsed in:" << tm.elapsed(); +#endif + } + + // Restore the previous thread priority. + QThread::currentThread()->setPriority(QThread::NormalPriority); +} + +void CppModelManager::GC() +{ + DocumentTable documents = m_documents; + + QSet<QString> processed; + QStringList todo = m_projectFiles; + + while (! todo.isEmpty()) { + QString fn = todo.last(); + todo.removeLast(); + + if (processed.contains(fn)) + continue; + + processed.insert(fn); + + if (Document::Ptr doc = documents.value(fn)) { + todo += doc->includedFiles(); + } + } + + QStringList removedFiles; + QMutableMapIterator<QString, Document::Ptr> it(documents); + while (it.hasNext()) { + it.next(); + const QString fn = it.key(); + if (! processed.contains(fn)) { + removedFiles.append(fn); + it.remove(); + } + } + + emit aboutToRemoveFiles(removedFiles); + m_documents = documents; +} + + diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h new file mode 100644 index 00000000000..bed882f9cd9 --- /dev/null +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -0,0 +1,135 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPMODELMANAGER_H +#define CPPMODELMANAGER_H + +#include <cpptools/cppmodelmanagerinterface.h> +#include <projectexplorer/project.h> +#include <cplusplus/CppDocument.h> + +#include <QMap> +#include <QFutureInterface> + +namespace Core { +class ICore; +class IEditor; +} + +namespace TextEditor { +class ITextEditor; +} + +namespace ProjectExplorer { +class ProjectExplorerPlugin; +} + +namespace CppTools { +namespace Internal { + +class CppEditorSupport; +class CppHoverHandler; + +class CppModelManager : public CppModelManagerInterface +{ + Q_OBJECT + +public: + CppModelManager(QObject *parent); + virtual ~CppModelManager(); + + virtual void updateSourceFiles(const QStringList &sourceFiles); + virtual ProjectInfo *projectInfo(ProjectExplorer::Project *project); + virtual CPlusPlus::Document::Ptr document(const QString &fileName); + virtual DocumentTable documents(); + virtual void GC(); + + QFuture<void> refreshSourceFiles(const QStringList &sourceFiles); + + inline Core::ICore *core() const { return m_core; } + + bool isCppEditor(Core::IEditor *editor) const; // ### private + + void emitDocumentUpdated(CPlusPlus::Document::Ptr doc); + +Q_SIGNALS: + void projectPathChanged(const QString &projectPath); + + void documentUpdated(CPlusPlus::Document::Ptr doc); + void aboutToRemoveFiles(const QStringList &files); + +public Q_SLOTS: + void editorOpened(Core::IEditor *editor); + void editorAboutToClose(Core::IEditor *editor); + +private Q_SLOTS: + // this should be executed in the GUI thread. + void onDocumentUpdated(CPlusPlus::Document::Ptr doc); + void onAboutToRemoveProject(ProjectExplorer::Project *project); + void onSessionUnloaded(); + +private: + QMap<QString, QByteArray> buildWorkingCopyList() const; + QStringList projectFiles() const; + QStringList includePaths() const; + QStringList frameworkPaths() const; + QByteArray definedMacros() const; + + static void parse(QFutureInterface<void> &future, + CppModelManager *model, + QStringList files, + QMap<QString, QByteArray> workingCopy); + +private: + Core::ICore *m_core; + ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer; + CppHoverHandler *m_hoverHandler; + DocumentTable m_documents; + + // List of available source files + QStringList m_projectFiles; + + // editor integration + QMap<TextEditor::ITextEditor *, CppEditorSupport *> m_editorSupport; + + // project integration + QMap<ProjectExplorer::Project *, ProjectInfo> m_projects; + + enum { + MAX_SELECTION_COUNT = 5 + }; +}; + +} // namespace Internal +} // namespace CppTools + +#endif // CPPMODELMANAGER_H diff --git a/src/plugins/cpptools/cppmodelmanagerinterface.h b/src/plugins/cpptools/cppmodelmanagerinterface.h new file mode 100644 index 00000000000..3b93c346c55 --- /dev/null +++ b/src/plugins/cpptools/cppmodelmanagerinterface.h @@ -0,0 +1,79 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPMODELMANAGERINTERFACE_H +#define CPPMODELMANAGERINTERFACE_H + +#include <cpptools/cpptools_global.h> +#include <cplusplus/CppDocument.h> +#include <QtCore/QObject> +#include <QtCore/QMap> + +namespace ProjectExplorer { + class Project; +} + +namespace CppTools { + +class CPPTOOLS_EXPORT CppModelManagerInterface + : public QObject +{ + Q_OBJECT + +public: + typedef QMap<QString, CPlusPlus::Document::Ptr> DocumentTable; + + struct ProjectInfo + { + QString projectPath; + QByteArray defines; + QStringList sourceFiles; + QStringList includePaths; + QStringList frameworkPaths; + }; + +public: + CppModelManagerInterface(QObject *parent = 0) : QObject(parent) {} + virtual ~CppModelManagerInterface() {} + + virtual void GC() = 0; + virtual void updateSourceFiles(const QStringList &sourceFiles) = 0; + + virtual CPlusPlus::Document::Ptr document(const QString &fileName) = 0; + virtual DocumentTable documents() = 0; + + virtual ProjectInfo *projectInfo(ProjectExplorer::Project *project) = 0; +}; + +} // namespace CppTools + +#endif // CPPMODELMANAGERINTERFACE_H diff --git a/src/plugins/cpptools/cppquickopenfilter.cpp b/src/plugins/cpptools/cppquickopenfilter.cpp new file mode 100644 index 00000000000..98086d3f81c --- /dev/null +++ b/src/plugins/cpptools/cppquickopenfilter.cpp @@ -0,0 +1,280 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cppquickopenfilter.h" + +#include <Literals.h> +#include <Symbols.h> +#include <SymbolVisitor.h> +#include <Scope.h> +#include <cplusplus/Overview.h> +#include <cplusplus/Icons.h> + +#include <coreplugin/editormanager/ieditor.h> +#include <texteditor/itexteditor.h> +#include <texteditor/basetexteditor.h> + +#include <QtCore/QMultiMap> + +#include <functional> + +using namespace CPlusPlus; + +namespace CppTools { +namespace Internal { + +class SearchSymbols: public std::unary_function<Document::Ptr, QList<ModelItemInfo> >, + protected SymbolVisitor +{ + Overview overview; + Icons icons; + QList<ModelItemInfo> items; + +public: + QList<ModelItemInfo> operator()(Document::Ptr doc) + { return operator()(doc, QString()); } + + QList<ModelItemInfo> operator()(Document::Ptr doc, const QString &scope) + { + QString previousScope = switchScope(scope); + items.clear(); + for (unsigned i = 0; i < doc->globalSymbolCount(); ++i) { + accept(doc->globalSymbolAt(i)); + } + (void) switchScope(previousScope); + return items; + } + +protected: + using SymbolVisitor::visit; + + void accept(Symbol *symbol) + { Symbol::visitSymbol(symbol, this); } + + QString switchScope(const QString &scope) + { + QString previousScope = _scope; + _scope = scope; + return previousScope; + } + + virtual bool visit(Enum *symbol) + { + QString name = symbolName(symbol); + QString previousScope = switchScope(name); + QIcon icon = icons.iconForSymbol(symbol); + Scope *members = symbol->members(); + items.append(ModelItemInfo(name, QString(), ModelItemInfo::Enum, + QString::fromUtf8(symbol->fileName(), symbol->fileNameLength()), + symbol->line(), + icon)); + for (unsigned i = 0; i < members->symbolCount(); ++i) { + accept(members->symbolAt(i)); + } + (void) switchScope(previousScope); + return false; + } + + virtual bool visit(Function *symbol) + { + QString name = symbolName(symbol); + QString type = overview.prettyType(symbol->type()); + QIcon icon = icons.iconForSymbol(symbol); + items.append(ModelItemInfo(name, type, ModelItemInfo::Method, + QString::fromUtf8(symbol->fileName(), symbol->fileNameLength()), + symbol->line(), + icon)); + return false; + } + + virtual bool visit(Namespace *symbol) + { + QString name = symbolName(symbol); + QString previousScope = switchScope(name); + Scope *members = symbol->members(); + for (unsigned i = 0; i < members->symbolCount(); ++i) { + accept(members->symbolAt(i)); + } + (void) switchScope(previousScope); + return false; + } +#if 0 + // This visit method would make function declaration be included in QuickOpen + virtual bool visit(Declaration *symbol) + { + if (symbol->type()->isFunction()) { + QString name = symbolName(symbol); + QString type = overview.prettyType(symbol->type()); + //QIcon icon = ...; + items.append(ModelItemInfo(name, type, ModelItemInfo::Method, + QString::fromUtf8(symbol->fileName(), symbol->line()), + symbol->line())); + } + return false; + } +#endif + virtual bool visit(Class *symbol) + { + QString name = symbolName(symbol); + QString previousScope = switchScope(name); + QIcon icon = icons.iconForSymbol(symbol); + items.append(ModelItemInfo(name, QString(), ModelItemInfo::Class, + QString::fromUtf8(symbol->fileName(), symbol->fileNameLength()), + symbol->line(), + icon)); + Scope *members = symbol->members(); + for (unsigned i = 0; i < members->symbolCount(); ++i) { + accept(members->symbolAt(i)); + } + (void) switchScope(previousScope); + return false; + } + + QString symbolName(Symbol *symbol) const + { + QString name = _scope; + if (! name.isEmpty()) + name += QLatin1String("::"); + QString symbolName = overview.prettyName(symbol->name()); + if (symbolName.isEmpty()) { + QString type; + if (symbol->isNamespace()) { + type = QLatin1String("namespace"); + } else if (symbol->isEnum()) { + type = QLatin1String("enum"); + } else if (Class *c = symbol->asClass()) { + if (c->isUnion()) { + type = QLatin1String("union"); + } else if (c->isStruct()) { + type = QLatin1String("struct"); + } else { + type = QLatin1String("class"); + } + } else { + type = QLatin1String("symbol"); + } + symbolName = QLatin1String("<anonymous "); + symbolName += type; + symbolName += QLatin1String(">"); + } + name += symbolName; + return name; + } + +private: + QString _scope; +}; + +} // namespace Internal +} // namespace CppTools + +using namespace CppTools::Internal; + +CppQuickOpenFilter::CppQuickOpenFilter(CppModelManager *manager, Core::EditorManager *editorManager) + : m_manager(manager), + m_editorManager(editorManager), + m_forceNewSearchList(true) +{ + setShortcutString(":"); + setIncludedByDefault(false); + + connect(manager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)), + this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr))); + + connect(manager, SIGNAL(aboutToRemoveFiles(QStringList)), + this, SLOT(onAboutToRemoveFiles(QStringList))); +} + +CppQuickOpenFilter::~CppQuickOpenFilter() +{ } + +void CppQuickOpenFilter::onDocumentUpdated(CPlusPlus::Document::Ptr doc) +{ + m_searchList[doc->fileName()] = Info(doc); +} + +void CppQuickOpenFilter::onAboutToRemoveFiles(const QStringList &files) +{ + foreach (QString file, files) { + m_searchList.remove(file); + } +} + +void CppQuickOpenFilter::refresh(QFutureInterface<void> &future) +{ + Q_UNUSED(future); +} + +QList<QuickOpen::FilterEntry> CppQuickOpenFilter::matchesFor(const QString &origEntry) +{ + QString entry = trimWildcards(origEntry); + QList<QuickOpen::FilterEntry> entries; + QStringMatcher matcher(entry, Qt::CaseInsensitive); + const QRegExp regexp("*"+entry+"*", Qt::CaseInsensitive, QRegExp::Wildcard); + if (!regexp.isValid()) + return entries; + bool hasWildcard = (entry.contains('*') || entry.contains('?')); + + SearchSymbols search; + QMutableMapIterator<QString, Info> it(m_searchList); + while (it.hasNext()) { + it.next(); + + Info info = it.value(); + if (info.dirty) { + info.dirty = false; + info.items = search(info.doc); + it.setValue(info); + } + + QList<ModelItemInfo> items = info.items; + + foreach (ModelItemInfo info, items) { + if ((hasWildcard && regexp.exactMatch(info.symbolName)) + || (!hasWildcard && matcher.indexIn(info.symbolName) != -1)) { + QVariant id = qVariantFromValue(info); + QuickOpen::FilterEntry filterEntry(this, info.symbolName, id, info.icon); + filterEntry.extraInfo = info.symbolType; + entries.append(filterEntry); + } + } + } + + return entries; +} + +void CppQuickOpenFilter::accept(QuickOpen::FilterEntry selection) const +{ + ModelItemInfo info = qvariant_cast<CppTools::Internal::ModelItemInfo>(selection.internalData); + + TextEditor::BaseTextEditor::openEditorAt(info.fileName, info.line); +} diff --git a/src/plugins/cpptools/cppquickopenfilter.h b/src/plugins/cpptools/cppquickopenfilter.h new file mode 100644 index 00000000000..1375e468f56 --- /dev/null +++ b/src/plugins/cpptools/cppquickopenfilter.h @@ -0,0 +1,118 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPQUICKOPENFILTER_H +#define CPPQUICKOPENFILTER_H + +#include "cppmodelmanager.h" +#include <cplusplus/CppDocument.h> +#include <coreplugin/editormanager/editormanager.h> +#include <quickopen/iquickopenfilter.h> +#include <QtGui/QIcon> +#include <QFile> +#include <QMetaType> + +namespace CppTools { +namespace Internal { + +struct ModelItemInfo +{ + enum ItemType { Enum, Class, Method }; + + ModelItemInfo() + { } + + ModelItemInfo(const QString &symbolName, + const QString &symbolType, + ItemType type, + const QString &fileName, + int line, + const QIcon &icon) + : symbolName(symbolName), + symbolType(symbolType), + type(type), + fileName(fileName), + line(line), + icon(icon) + { } + + QString symbolName; + QString symbolType; + ItemType type; + QString fileName; + int line; + QIcon icon; +}; + +class CppQuickOpenFilter : public QuickOpen::IQuickOpenFilter +{ + Q_OBJECT +public: + CppQuickOpenFilter(CppModelManager *manager, Core::EditorManager *editorManager); + ~CppQuickOpenFilter(); + + QString trName() const { return tr("Classes and Methods"); } + QString name() const { return "Classes and Methods"; } + Priority priority() const { return Medium; } + QList<QuickOpen::FilterEntry> matchesFor(const QString &entry); + void accept(QuickOpen::FilterEntry selection) const; + void refresh(QFutureInterface<void> &future); + +private slots: + void onDocumentUpdated(CPlusPlus::Document::Ptr doc); + void onAboutToRemoveFiles(const QStringList &files); + +private: + CppModelManager *m_manager; + Core::EditorManager *m_editorManager; + + struct Info { + Info(): dirty(true) {} + Info(CPlusPlus::Document::Ptr doc): doc(doc), dirty(true) {} + + CPlusPlus::Document::Ptr doc; + QList<ModelItemInfo> items; + bool dirty; + }; + + QMap<QString, Info> m_searchList; + QList<ModelItemInfo> m_previousResults; + bool m_forceNewSearchList; + QString m_previousEntry; +}; + +} // namespace Internal +} // namespace CppTools + +Q_DECLARE_METATYPE(CppTools::Internal::ModelItemInfo) + +#endif // CPPQUICKOPENFILTER_H diff --git a/src/plugins/cpptools/cpptools.cpp b/src/plugins/cpptools/cpptools.cpp new file mode 100644 index 00000000000..ceca43afaa2 --- /dev/null +++ b/src/plugins/cpptools/cpptools.cpp @@ -0,0 +1,256 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cpptools.h" +#include "cppcodecompletion.h" +#include "cpphoverhandler.h" +#include "cppmodelmanager.h" +#include "cpptoolsconstants.h" +#include "cppquickopenfilter.h" + +#include <coreplugin/icore.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> +#include <cppeditor/cppeditorconstants.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtGui/QMenu> +#include <QtGui/QAction> + +using namespace CppTools::Internal; + +enum { debug = 0 }; + + +CppToolsPlugin *CppToolsPlugin::m_instance = 0; + +CppToolsPlugin::CppToolsPlugin() : + m_core(0), + m_context(-1), + m_modelManager(0) +{ + m_instance = this; +} + +CppToolsPlugin::~CppToolsPlugin() +{ + m_instance = 0; + m_modelManager = 0; // deleted automatically +} + +bool CppToolsPlugin::initialize(const QStringList & /*arguments*/, QString *) +{ + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + Core::ActionManagerInterface *am = m_core->actionManager(); + + // Objects + m_modelManager = new CppModelManager(this); + addAutoReleasedObject(m_modelManager); + CppCodeCompletion *cppcodecompletion = new CppCodeCompletion(m_modelManager, m_core); + addAutoReleasedObject(cppcodecompletion); + CppQuickOpenFilter *quickOpenFilter = new CppQuickOpenFilter(m_modelManager, + m_core->editorManager()); + addAutoReleasedObject(quickOpenFilter); + + // Menus + Core::IActionContainer *mtools = am->actionContainer(Core::Constants::M_TOOLS); + Core::IActionContainer *mcpptools = am->createMenu(CppTools::Constants::M_TOOLS_CPP); + QMenu *menu = mcpptools->menu(); + menu->setTitle(tr("&C++")); + menu->setEnabled(true); + mtools->addMenu(mcpptools); + + // Actions + m_context = m_core->uniqueIDManager()->uniqueIdentifier(CppEditor::Constants::C_CPPEDITOR); + QList<int> context = QList<int>() << m_context; + + QAction *switchAction = new QAction(tr("Switch Header/Source"), this); + Core::ICommand *command = am->registerAction(switchAction, Constants::SWITCH_HEADER_SOURCE, context); + command->setDefaultKeySequence(QKeySequence(Qt::Key_F4)); + mcpptools->addAction(command); + connect(switchAction, SIGNAL(triggered()), this, SLOT(switchHeaderSource())); + + return true; +} + +void CppToolsPlugin::extensionsInitialized() +{ +} + +void CppToolsPlugin::switchHeaderSource() +{ + if (!m_core) + return; + + Core::IEditor *editor = m_core->editorManager()->currentEditor(); + QString otherFile = correspondingHeaderOrSource(editor->file()->fileName()); + if (!otherFile.isEmpty()) { + m_core->editorManager()->openEditor(otherFile); + m_core->editorManager()->ensureEditorManagerVisible(); + } +} + +QFileInfo CppToolsPlugin::findFile(const QDir &dir, const QString &name, + const ProjectExplorer::Project *project) const +{ + if (debug) + qDebug() << Q_FUNC_INFO << dir << name; + + if (project) { + QString pattern = QString(1, QLatin1Char('/')); + pattern += name; + const QStringList projectFiles = project->files(ProjectExplorer::Project::AllFiles); + const QStringList::const_iterator pcend = projectFiles.constEnd(); + for (QStringList::const_iterator it = projectFiles.constBegin(); it != pcend; ++it) + if (it->endsWith(pattern)) + return QFileInfo(*it); + return QFileInfo(); + } + return QFileInfo(dir, name); +} + +// Figure out file type +enum FileType { HeaderFile, C_SourceFile, CPP_SourceFile, UnknownType }; + +static inline FileType fileType(const Core::MimeDatabase *mimeDatase, const QFileInfo & fi) +{ + const Core::MimeType mimeType = mimeDatase->findByFile(fi); + if (!mimeType) + return UnknownType; + const QString typeName = mimeType.type(); + if (typeName == QLatin1String(CppTools::Constants::C_SOURCE_MIMETYPE)) + return C_SourceFile; + if (typeName == QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE)) + return CPP_SourceFile; + if (typeName == QLatin1String(CppTools::Constants::C_HEADER_MIMETYPE) + || typeName == QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE)) + return HeaderFile; + return UnknownType; +} + +// Return the suffixes that should be checked when trying to find a +// source belonging to a header and vice versa +static QStringList matchingCandidateSuffixes(const Core::MimeDatabase *mimeDatase, FileType type) +{ + switch (type) { + case UnknownType: + break; + case HeaderFile: // Note that C/C++ headers are undistinguishable + return mimeDatase->findByType(QLatin1String(CppTools::Constants::C_SOURCE_MIMETYPE)).suffixes() + + mimeDatase->findByType(QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE)).suffixes(); + case C_SourceFile: + return mimeDatase->findByType(QLatin1String(CppTools::Constants::C_HEADER_MIMETYPE)).suffixes(); + case CPP_SourceFile: + return mimeDatase->findByType(QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE)).suffixes(); + } + return QStringList(); +} + +QString CppToolsPlugin::correspondingHeaderOrSourceI(const QString &fileName) const +{ + const Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + const Core::MimeDatabase *mimeDatase = core->mimeDatabase(); + ProjectExplorer::ProjectExplorerPlugin *explorer = + ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::ProjectExplorerPlugin>(); + ProjectExplorer::Project *project = (explorer ? explorer->currentProject() : 0); + + const QFileInfo fi(fileName); + const FileType type = fileType(mimeDatase, fi); + + if (debug) + qDebug() << Q_FUNC_INFO << fileName << type; + + if (type == UnknownType) + return QString(); + + const QDir absoluteDir = fi.absoluteDir(); + const QString baseName = fi.baseName(); + const QStringList suffixes = matchingCandidateSuffixes(mimeDatase, type); + + const QString privateHeaderSuffix = QLatin1String("_p"); + const QChar dot = QLatin1Char('.'); + QStringList candidates; + // Check base matches 'source.h'-> 'source.cpp' and vice versa + const QStringList::const_iterator scend = suffixes.constEnd(); + for (QStringList::const_iterator it = suffixes.constBegin(); it != scend; ++it) { + QString candidate = baseName; + candidate += dot; + candidate += *it; + const QFileInfo candidateFi = findFile(absoluteDir, candidate, project); + if (candidateFi.isFile()) + return candidateFi.absoluteFilePath(); + } + if (type == HeaderFile) { + // 'source_p.h': try 'source.cpp' + if (baseName.endsWith(privateHeaderSuffix)) { + QString sourceBaseName = baseName; + sourceBaseName.truncate(sourceBaseName.size() - privateHeaderSuffix.size()); + for (QStringList::const_iterator it = suffixes.constBegin(); it != scend; ++it) { + QString candidate = sourceBaseName; + candidate += dot; + candidate += *it; + const QFileInfo candidateFi = findFile(absoluteDir, candidate, project); + if (candidateFi.isFile()) + return candidateFi.absoluteFilePath(); + } + } + } else { + // 'source.cpp': try 'source_p.h' + const QStringList::const_iterator scend = suffixes.constEnd(); + for (QStringList::const_iterator it = suffixes.constBegin(); it != scend; ++it) { + QString candidate = baseName; + candidate += privateHeaderSuffix; + candidate += dot; + candidate += *it; + const QFileInfo candidateFi = findFile(absoluteDir, candidate, project); + if (candidateFi.isFile()) + return candidateFi.absoluteFilePath(); + } + } + return QString(); +} + +QString CppToolsPlugin::correspondingHeaderOrSource(const QString &fileName) const +{ + const QString rc = correspondingHeaderOrSourceI(fileName); + if (debug) + qDebug() << Q_FUNC_INFO << fileName << rc; + return rc; +} + +Q_EXPORT_PLUGIN(CppToolsPlugin) diff --git a/src/plugins/cpptools/cpptools.h b/src/plugins/cpptools/cpptools.h new file mode 100644 index 00000000000..3d0f195309e --- /dev/null +++ b/src/plugins/cpptools/cpptools.h @@ -0,0 +1,86 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPTOOLS_H +#define CPPTOOLS_H + +#include <extensionsystem/iplugin.h> +#include <projectexplorer/ProjectExplorerInterfaces> + +QT_BEGIN_NAMESPACE +class QFileInfo; +class QDir; +QT_END_NAMESPACE + +namespace Core { +class ICore; +} + +namespace CppTools { +namespace Internal { + +class CppCodeCompletion; +class CppModelManager; + +class CppToolsPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + static CppToolsPlugin *instance() { return m_instance; } + + CppToolsPlugin(); + ~CppToolsPlugin(); + + bool initialize(const QStringList &arguments, QString *error_message); + void extensionsInitialized(); + CppModelManager *cppModelManager() { return m_modelManager; } + QString correspondingHeaderOrSource(const QString &fileName) const; + +private slots: + void switchHeaderSource(); + +private: + QString correspondingHeaderOrSourceI(const QString &fileName) const; + QFileInfo findFile(const QDir &dir, const QString &name, const ProjectExplorer::Project *project) const; + + Core::ICore *m_core; + int m_context; + CppModelManager *m_modelManager; + + static CppToolsPlugin *m_instance; +}; + +} // namespace Internal +} // namespace CppTools + +#endif // CPPTOOLS_H diff --git a/src/plugins/cpptools/cpptools.pri b/src/plugins/cpptools/cpptools.pri new file mode 100644 index 00000000000..1dffbfc5560 --- /dev/null +++ b/src/plugins/cpptools/cpptools.pri @@ -0,0 +1,3 @@ +include(cpptools_dependencies.pri) + +LIBS *= -l$$qtLibraryTarget(CppTools) diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro new file mode 100644 index 00000000000..17b72496a92 --- /dev/null +++ b/src/plugins/cpptools/cpptools.pro @@ -0,0 +1,40 @@ +TEMPLATE = lib +TARGET = CppTools +include(../../qworkbenchplugin.pri) +include(../../plugins/quickopen/quickopen.pri) +include(cpptools_dependencies.pri) + +#DEFINES += QT_NO_CAST_FROM_ASCII +DEFINES += QT_NO_CAST_TO_ASCII +unix:QMAKE_CXXFLAGS_DEBUG+=-O3 + +INCLUDEPATH += . + +DEFINES += CPPTOOLS_LIBRARY + +CONFIG += help +include(rpp/rpp.pri)|error("Can't find RPP") + +HEADERS += \ + cpptools_global.h \ + cppquickopenfilter.h + +SOURCES += \ + cppquickopenfilter.cpp \ + cpptoolseditorsupport.cpp + +# Input +SOURCES += cpptools.cpp \ + cppmodelmanager.cpp \ + cppcodecompletion.cpp \ + cpphoverhandler.cpp + +HEADERS += cpptools.h \ + cppmodelmanager.h \ + cppcodecompletion.h \ + cpphoverhandler.h \ + cppmodelmanagerinterface.h \ + cpptoolseditorsupport.h \ + cpptoolsconstants.h + +RESOURCES += cpptools.qrc diff --git a/src/plugins/cpptools/cpptools.qrc b/src/plugins/cpptools/cpptools.qrc new file mode 100644 index 00000000000..a750578a4b0 --- /dev/null +++ b/src/plugins/cpptools/cpptools.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/cpptools" > + <file>images/f1.svg</file> + </qresource> +</RCC> diff --git a/src/plugins/cpptools/cpptools_dependencies.pri b/src/plugins/cpptools/cpptools_dependencies.pri new file mode 100644 index 00000000000..e12a33bc465 --- /dev/null +++ b/src/plugins/cpptools/cpptools_dependencies.pri @@ -0,0 +1,3 @@ +include(../../libs/cplusplus/cplusplus.pri) +include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/texteditor/texteditor.pri) diff --git a/src/plugins/cpptools/cpptools_global.h b/src/plugins/cpptools/cpptools_global.h new file mode 100644 index 00000000000..294a54ceb2e --- /dev/null +++ b/src/plugins/cpptools/cpptools_global.h @@ -0,0 +1,42 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPTOOLS_GLOBAL_H +#define CPPTOOLS_GLOBAL_H + +#if defined(CPPTOOLS_LIBRARY) +# define CPPTOOLS_EXPORT Q_DECL_EXPORT +#else +# define CPPTOOLS_EXPORT Q_DECL_IMPORT +#endif + +#endif // CPPTOOLS_GLOBAL_H diff --git a/src/plugins/cpptools/cpptoolsconstants.h b/src/plugins/cpptools/cpptoolsconstants.h new file mode 100644 index 00000000000..8a3e92cf2d6 --- /dev/null +++ b/src/plugins/cpptools/cpptoolsconstants.h @@ -0,0 +1,49 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPTOOLSCONSTANTS_H +#define CPPTOOLSCONSTANTS_H + +namespace CppTools { +namespace Constants { + +const char * const M_TOOLS_CPP = "CppTools.Tools.Menu"; +const char * const SWITCH_HEADER_SOURCE = "CppTools.SwitchHeaderSource"; +const char * const TASK_INDEX = "CppTools.Task.Index"; +const char * const C_SOURCE_MIMETYPE = "text/x-csrc"; +const char * const C_HEADER_MIMETYPE = "text/x-chdr"; +const char * const CPP_SOURCE_MIMETYPE = "text/x-c++src"; +const char * const CPP_HEADER_MIMETYPE = "text/x-c++hdr"; +} +} + +#endif //CPPTOOLSCONSTANTS_H diff --git a/src/plugins/cpptools/cpptoolseditorsupport.cpp b/src/plugins/cpptools/cpptoolseditorsupport.cpp new file mode 100644 index 00000000000..ca36045422c --- /dev/null +++ b/src/plugins/cpptools/cpptoolseditorsupport.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "cpptoolseditorsupport.h" +#include "cppmodelmanager.h" + +#include <texteditor/itexteditor.h> +#include <QTimer> + +using namespace CppTools::Internal; + +CppEditorSupport::CppEditorSupport(CppModelManager *modelManager) + : QObject(modelManager), + _modelManager(modelManager), + _updateDocumentInterval(UPDATE_DOCUMENT_DEFAULT_INTERVAL) +{ + _updateDocumentTimer = new QTimer(this); + _updateDocumentTimer->setSingleShot(true); + _updateDocumentTimer->setInterval(_updateDocumentInterval); + connect(_updateDocumentTimer, SIGNAL(timeout()), this, SLOT(updateDocumentNow())); +} + +CppEditorSupport::~CppEditorSupport() +{ } + +TextEditor::ITextEditor *CppEditorSupport::textEditor() const +{ return _textEditor; } + +void CppEditorSupport::setTextEditor(TextEditor::ITextEditor *textEditor) +{ + _textEditor = textEditor; + + if (! _textEditor) + return; + + connect(_textEditor, SIGNAL(contentsChanged()), this, SLOT(updateDocument())); + updateDocument(); +} + +QString CppEditorSupport::contents() const +{ + if (! _textEditor) + return QString(); + + return _textEditor->contents(); +} + +int CppEditorSupport::updateDocumentInterval() const +{ return _updateDocumentInterval; } + +void CppEditorSupport::setUpdateDocumentInterval(int updateDocumentInterval) +{ _updateDocumentInterval = updateDocumentInterval; } + +void CppEditorSupport::updateDocument() +{ _updateDocumentTimer->start(_updateDocumentInterval); } + +void CppEditorSupport::updateDocumentNow() +{ + if (_documentParser.isRunning()) { + _updateDocumentTimer->start(_updateDocumentInterval); + } else { + _updateDocumentTimer->stop(); + QStringList sourceFiles(_textEditor->file()->fileName()); + _documentParser = _modelManager->refreshSourceFiles(sourceFiles); + } +} + diff --git a/src/plugins/cpptools/cpptoolseditorsupport.h b/src/plugins/cpptools/cpptoolseditorsupport.h new file mode 100644 index 00000000000..51a905e895b --- /dev/null +++ b/src/plugins/cpptools/cpptoolseditorsupport.h @@ -0,0 +1,87 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPTOOLSEDITORSUPPORT_H +#define CPPTOOLSEDITORSUPPORT_H + +#include <QObject> +#include <QPointer> +#include <QFuture> + +QT_BEGIN_NAMESPACE +class QTimer; +class QByteArray; +QT_END_NAMESPACE + +namespace TextEditor { + class ITextEditor; +} // end of namespace TextEditor + +namespace CppTools { +namespace Internal { + +class CppModelManager; + +class CppEditorSupport: public QObject +{ + Q_OBJECT + +public: + CppEditorSupport(CppModelManager *modelManager); + virtual ~CppEditorSupport(); + + TextEditor::ITextEditor *textEditor() const; + void setTextEditor(TextEditor::ITextEditor *textEditor); + + int updateDocumentInterval() const; + void setUpdateDocumentInterval(int updateDocumentInterval); + + QString contents() const; + +private Q_SLOTS: + void updateDocument(); + void updateDocumentNow(); + +private: + enum { UPDATE_DOCUMENT_DEFAULT_INTERVAL = 150 }; + + CppModelManager *_modelManager; + QPointer<TextEditor::ITextEditor> _textEditor; + QTimer *_updateDocumentTimer; + int _updateDocumentInterval; + QFuture<void> _documentParser; +}; + +} // end of namespace Internal +} // end of namespace CppTools + +#endif // CPPTOOLSEDITORSUPPORT_H diff --git a/src/plugins/cpptools/images/f1.svg b/src/plugins/cpptools/images/f1.svg new file mode 100644 index 00000000000..468594cb774 --- /dev/null +++ b/src/plugins/cpptools/images/f1.svg @@ -0,0 +1,175 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="24" + height="24" + id="svg2411" + sodipodi:version="0.32" + inkscape:version="0.46" + version="1.0" + sodipodi:docname="f1.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs2413"> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient001" + id="linearGradient3201" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.6285394,0,0,3.6290105,-1258.7023,-359.38242)" + spreadMethod="pad" + x1="375.31006" + y1="88.869247" + x2="466.8873" + y2="180.4346" /> + <linearGradient + id="linearGradient001"> + <stop + id="stop608" + offset="0.000000" + style="stop-color:#cfcfcf;stop-opacity:1;" /> + <stop + id="stop609" + offset="1.000000" + style="stop-color:#efefef;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient001" + id="linearGradient3207" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.8401167,0,0,3.8424815,-1348.031,-388.46373)" + spreadMethod="pad" + x1="470.3931" + y1="136.23064" + x2="374.90988" + y2="136.23064" /> + <linearGradient + id="linearGradient002"> + <stop + id="stop566" + offset="0.000000" + style="stop-color:#9d9d9f;stop-opacity:1;" /> + <stop + id="stop567" + offset="1.000000" + style="stop-color:#e5e5e5;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient002" + id="linearGradient2419" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.6611924,0,0,3.6628816,-1231.7325,-383.72165)" + spreadMethod="pad" + x1="471.00525" + y1="201.05208" + x2="348.94803" + y2="79.051147" /> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 526.18109 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="744.09448 : 526.18109 : 1" + inkscape:persp3d-origin="372.04724 : 350.78739 : 1" + id="perspective2419" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="11.2" + inkscape:cx="22.801892" + inkscape:cy="10.456883" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="963" + inkscape:window-height="667" + inkscape:window-x="207" + inkscape:window-y="207" /> + <metadata + id="metadata2416"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Ebene 1" + inkscape:groupmode="layer" + id="layer1"> + <g + transform="matrix(4.3636364e-2,0,0,4.3636364e-2,0,6.1090908)" + id="g2404"> + <rect + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + y="-140" + x="2.4832567e-14" + width="550" + style="font-size:12px;fill:#b0b0b0;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.81658993pt;stroke-opacity:1" + ry="81.511414" + id="rect621" + height="550" /> + <rect + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="c:\Documents and Settings\aportale\Desktop\rect621.png" + y="-128.55069" + x="11.458333" + width="527.08331" + style="font-size:12px;fill:url(#linearGradient2419);fill-rule:evenodd;stroke:none;stroke-width:1.03125;stroke-miterlimit:4;stroke-dasharray:none" + ry="78.116447" + id="rect2417" + height="527.09235" /> + <rect + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="c:\Documents and Settings\aportale\Desktop\rect621.png" + y="-71.25" + x="68.950378" + width="412.29962" + style="font-size:12px;fill:url(#linearGradient3207);fill-rule:evenodd;stroke:none;stroke-width:2.4979167;stroke-miterlimit:4;stroke-dasharray:none" + ry="39.785442" + rx="34.514793" + id="rect3205" + height="412.5" /> + <rect + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="c:\Documents and Settings\aportale\Desktop\rect621.png" + y="-59.791668" + x="80.208336" + width="389.58334" + style="font-size:12px;fill:url(#linearGradient3201);fill-rule:evenodd;stroke:none;stroke-width:25.41458321;stroke-miterlimit:4;stroke-dasharray:none" + ry="25.400656" + rx="22.902761" + id="rect3199" + height="389.58334" /> + <path + sodipodi:nodetypes="ccccccccccccccccsccc" + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="c:\Documents and Settings\aportale\Desktop\rect621.png" + id="text3219" + d="M 137.5,157.91667 L 137.5,-2.4752517 L 229.16667,-2.5 L 229.16667,20.416667 L 160.41667,20.416667 L 160.41667,66.25 L 206.25,66.25 L 206.25,89.166667 L 160.41667,89.166667 L 160.41667,157.91667 L 137.5,157.91667 z M 320.89291,157.91667 L 297.91667,157.91667 L 297.91667,40.04211 C 284.08134,48.574912 275.21133,59.2993 252.08333,66.25 L 252.08333,47.355939 C 263.04835,44.807707 286.00818,25.67917 293.48823,18.312241 C 300.9682,10.945593 306.26379,4.4293192 309.375,-2.5 L 320.83333,-2.5 L 320.89291,157.91667 z" + style="font-size:261.65481567px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans" /> + </g> + </g> +</svg> diff --git a/src/plugins/cpptools/rpp/pp-cctype.h b/src/plugins/cpptools/rpp/pp-cctype.h new file mode 100644 index 00000000000..aff4fb2c519 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-cctype.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <[email protected]> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_CCTYPE_H +#define PP_CCTYPE_H + +#include <cctype> + +namespace rpp { + +inline bool pp_isalpha (int __ch) +{ return std::isalpha ((unsigned char) __ch) != 0; } + +inline bool pp_isalnum (int __ch) +{ return std::isalnum ((unsigned char) __ch) != 0; } + +inline bool pp_isdigit (int __ch) +{ return std::isdigit ((unsigned char) __ch) != 0; } + +inline bool pp_isspace (int __ch) +{ return std::isspace ((unsigned char) __ch) != 0; } + +} // namespace rpp + +#endif // PP_CCTYPE_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/src/plugins/cpptools/rpp/pp-client.h b/src/plugins/cpptools/rpp/pp-client.h new file mode 100644 index 00000000000..13d9eda7139 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-client.h @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PP_CLIENT_H +#define PP_CLIENT_H + +#include <QByteArray> +#include <QString> +#include <QFile> + +namespace rpp { + +class Client +{ + Client(const Client &other); + void operator=(const Client &other); + +public: + enum IncludeType { + IncludeLocal, + IncludeGlobal + }; + +public: + Client() + { } + + virtual ~Client() + { } + + virtual void macroAdded(const QByteArray ¯oId, const QByteArray &text) = 0; + virtual void sourceNeeded(QString &fileName, IncludeType mode) = 0; // ### FIX the signature. + + virtual void startSkippingBlocks(unsigned offset) = 0; + virtual void stopSkippingBlocks(unsigned offset) = 0; +}; + +} // end of namespace rpp + +#endif // PP_CLIENT_H diff --git a/src/plugins/cpptools/rpp/pp-engine.cpp b/src/plugins/cpptools/rpp/pp-engine.cpp new file mode 100644 index 00000000000..97e168f0c24 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-engine.cpp @@ -0,0 +1,1085 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <[email protected]> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "pp.h" +#include <Lexer.h> +#include <Token.h> +#include <QtDebug> + +using namespace rpp; +using namespace CPlusPlus; + +namespace { + +class RangeLexer +{ + const Token *first; + const Token *last; + Token trivial; + +public: + inline RangeLexer(const Token *first, const Token *last) + : first(first), last(last) + { + // WARN: `last' must be a valid iterator. + trivial.offset = last->offset; + } + + inline operator bool() const + { return first != last; } + + inline bool isValid() const + { return first != last; } + + inline int size() const + { return std::distance(first, last); } + + inline const Token *dot() const + { return first; } + + inline const Token &operator*() const + { + if (first != last) + return *first; + + return trivial; + } + + inline const Token *operator->() const + { + if (first != last) + return first; + + return &trivial; + } + + inline RangeLexer &operator++() + { + ++first; + return *this; + } +}; + +class ExpressionEvaluator +{ + ExpressionEvaluator(const ExpressionEvaluator &other); + void operator = (const ExpressionEvaluator &other); + +public: + ExpressionEvaluator(Environment *env) + : env(env), _lex(0) + { } + + Value operator()(const Token *firstToken, const Token *lastToken, + const QByteArray &source) + { + this->source = source; + const Value previousValue = switchValue(Value()); + RangeLexer tmp(firstToken, lastToken); + RangeLexer *previousLex = _lex; + _lex = &tmp; + process_expression(); + _lex = previousLex; + return switchValue(previousValue); + } + +protected: + Value switchValue(const Value &value) + { + Value previousValue = _value; + _value = value; + return previousValue; + } + + bool isTokenDefined() const + { + if ((*_lex)->isNot(T_IDENTIFIER)) + return false; + const QByteArray spell = tokenSpell(); + if (spell.size() != 7) + return false; + return spell == "defined"; + } + + QByteArray tokenSpell() const + { + const QByteArray text = QByteArray::fromRawData(source.constData() + (*_lex)->offset, + (*_lex)->length); + return text; + } + + bool process_expression() + { return process_constant_expression(); } + + bool process_primary() + { + if ((*_lex)->is(T_INT_LITERAL)) { + _value.set_long(tokenSpell().toLong()); + ++(*_lex); + return true; + } else if (isTokenDefined()) { + ++(*_lex); + if ((*_lex)->is(T_IDENTIFIER)) { + _value.set_long(env->resolve(tokenSpell()) != 0); + ++(*_lex); + return true; + } else if ((*_lex)->is(T_LPAREN)) { + ++(*_lex); + if ((*_lex)->is(T_IDENTIFIER)) { + _value.set_long(env->resolve(tokenSpell()) != 0); + ++(*_lex); + if ((*_lex)->is(T_RPAREN)) { + ++(*_lex); + return true; + } + } + return false; + } + return true; + } else if ((*_lex)->is(T_IDENTIFIER)) { + _value.set_long(0); + ++(*_lex); + return true; + } else if ((*_lex)->is(T_MINUS)) { + ++(*_lex); + process_primary(); + _value.set_long(- _value.l); + return true; + } else if ((*_lex)->is(T_PLUS)) { + ++(*_lex); + process_primary(); + return true; + } else if ((*_lex)->is(T_EXCLAIM)) { + ++(*_lex); + process_primary(); + _value.set_long(_value.is_zero()); + return true; + } else if ((*_lex)->is(T_LPAREN)) { + ++(*_lex); + process_expression(); + if ((*_lex)->is(T_RPAREN)) + ++(*_lex); + return true; + } + + return false; + } + + bool process_multiplicative() + { + process_primary(); + + while ((*_lex)->is(T_STAR) || (*_lex)->is(T_SLASH) || (*_lex)->is(T_PERCENT)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_primary(); + + if (op.is(T_STAR)) { + _value = left * _value; + } else if (op.is(T_SLASH)) { + if (_value.is_zero()) + _value.set_long(0); + else + _value = left / _value; + } else if (op.is(T_PERCENT)) { + if (_value.is_zero()) + _value.set_long(0); + else + _value = left % _value; + } + } + + return true; + } + + bool process_additive() + { + process_multiplicative(); + + while ((*_lex)->is(T_PLUS) || (*_lex)->is(T_MINUS)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_multiplicative(); + + if (op.is(T_PLUS)) + _value = left + _value; + else if (op.is(T_MINUS)) + _value = left - _value; + } + + return true; + } + + bool process_shift() + { + process_additive(); + + while ((*_lex)->is(T_MINUS_MINUS) || (*_lex)->is(T_GREATER_GREATER)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_additive(); + + if (op.is(T_MINUS_MINUS)) + _value = left << _value; + else if (op.is(T_GREATER_GREATER)) + _value = left >> _value; + } + + return true; + } + + bool process_relational() + { + process_shift(); + + while ((*_lex)->is(T_LESS) || (*_lex)->is(T_LESS_EQUAL) || + (*_lex)->is(T_GREATER) || (*_lex)->is(T_GREATER_EQUAL)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_shift(); + + if (op.is(T_LESS)) + _value = left < _value; + else if (op.is(T_LESS_EQUAL)) + _value = left <= _value; + else if (op.is(T_GREATER)) + _value = left > _value; + else if (op.is(T_GREATER_EQUAL)) + _value = left >= _value; + } + + return true; + } + + bool process_equality() + { + process_relational(); + + while ((*_lex)->is(T_EXCLAIM_EQUAL) || (*_lex)->is(T_EQUAL_EQUAL)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_relational(); + + if (op.is(T_EXCLAIM_EQUAL)) + _value = left != _value; + else if (op.is(T_EQUAL_EQUAL)) + _value = left == _value; + } + + return true; + } + + bool process_and() + { + process_equality(); + + while ((*_lex)->is(T_AMPER)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_equality(); + + _value = left & _value; + } + + return true; + } + + bool process_xor() + { + process_and(); + + while ((*_lex)->is(T_CARET)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_and(); + + _value = left ^ _value; + } + + return true; + } + + bool process_or() + { + process_xor(); + + while ((*_lex)->is(T_CARET)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_xor(); + + _value = left | _value; + } + + return true; + } + + bool process_logical_and() + { + process_or(); + + while ((*_lex)->is(T_AMPER_AMPER)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_or(); + + _value = left && _value; + } + + return true; + } + + bool process_logical_or() + { + process_logical_and(); + + while ((*_lex)->is(T_PIPE_PIPE)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_logical_and(); + + _value = left || _value; + } + + return true; + } + + bool process_constant_expression() + { + process_logical_or(); + const Value cond = _value; + if ((*_lex)->is(T_QUESTION)) { + ++(*_lex); + process_constant_expression(); + Value left = _value, right; + if ((*_lex)->is(T_COLON)) { + ++(*_lex); + process_constant_expression(); + right = _value; + } + _value = ! cond.is_zero() ? left : right; + } + + return true; + } + +private: + Environment *env; + QByteArray source; + RangeLexer *_lex; + Value _value; +}; + +} // end of anonymous namespace + + +pp::pp (Client *client, Environment &env) + : client(client), + env(env), + expand(env) +{ + resetIfLevel (); +} + +void pp::pushState(const State &s) +{ + _savedStates.append(state()); + _source = s.source; + _tokens = s.tokens; + _dot = s.dot; +} + +pp::State pp::state() const +{ + State state; + state.source = _source; + state.tokens = _tokens; + state.dot = _dot; + return state; +} + +void pp::popState() +{ + const State &state = _savedStates.last(); + _source = state.source; + _tokens = state.tokens; + _dot = state.dot; + _savedStates.removeLast(); +} + +void pp::operator () (const QByteArray &filename, + const QByteArray &source, + QByteArray *result) +{ + const QByteArray previousFile = env.current_file; + env.current_file = filename; + + operator () (source, result); + + env.current_file = previousFile; +} + +pp::State pp::createStateFromSource(const QByteArray &source) const +{ + State state; + state.source = source; + Lexer lex(state.source.constBegin(), state.source.constEnd()); + lex.setScanKeywords(false); + Token tok; + do { + lex(&tok); + state.tokens.append(tok); + } while (tok.isNot(T_EOF_SYMBOL)); + state.dot = state.tokens.constBegin(); + return state; +} + +void pp::operator()(const QByteArray &source, QByteArray *result) +{ + pushState(createStateFromSource(source)); + + const unsigned previousCurrentLine = env.currentLine; + env.currentLine = 0; + + while (true) { + if (env.currentLine != _dot->lineno) { + if (env.currentLine > _dot->lineno) { + result->append('\n'); + result->append('#'); + result->append(QByteArray::number(_dot->lineno)); + result->append(' '); + result->append('"'); + result->append(env.current_file); + result->append('"'); + result->append('\n'); + } else { + for (unsigned i = env.currentLine; i < _dot->lineno; ++i) + result->append('\n'); + } + env.currentLine = _dot->lineno; + } + + if (_dot->is(T_EOF_SYMBOL)) { + break; + } else if (_dot->is(T_POUND) && (! _dot->joined && _dot->newline)) { + TokenIterator start = _dot; + do { + ++_dot; + } while (_dot->isNot(T_EOF_SYMBOL) && (_dot->joined || ! _dot->newline)); + + //qDebug() << QByteArray(first + beginPP.offset, + //tokens.last().end() - beginPP.offset); + + const bool skippingBlocks = _skipping[iflevel]; + + processDirective(start, _dot); + + if (client && skippingBlocks != _skipping[iflevel]) { + unsigned offset = start->offset; + if (_skipping[iflevel]) { + if (_dot->newline) + ++offset; + client->startSkippingBlocks(offset); + } else { + if (offset) + --offset; + client->stopSkippingBlocks(offset); + } + } + } else if (skipping()) { + do { + ++_dot; + } while (_dot->isNot(T_EOF_SYMBOL) && (_dot->joined || ! _dot->newline)); + } else { + if (_dot->joined) + result->append("\\\n"); + else if (_dot->newline) { + result->append('\n'); + result->append('#'); + result->append(QByteArray::number(_dot->lineno)); + result->append(' '); + result->append('"'); + result->append(env.current_file); + result->append('"'); + result->append('\n'); + } + else if (_dot->whitespace) + result->append(' '); + + if (_dot->isNot(T_IDENTIFIER)) { + result->append(tokenSpell(*_dot)); + ++_dot; + } else { + const TokenIterator identifierToken = _dot; + ++_dot; // skip T_IDENTIFIER + + const QByteArray spell = tokenSpell(*identifierToken); + if (env.isBuiltinMacro(spell)) { + expand(spell.constBegin(), spell.constEnd(), result); + continue; + } + + Macro *m = env.resolve(spell); + if (! m) { + result->append(spell); + } else { + if (! m->function_like) { + if (_dot->isNot(T_LPAREN)) { + expand(m->definition.constBegin(), + m->definition.constEnd(), + result); + continue; + } else { + QByteArray tmp; + expand(m->definition.constBegin(), + m->definition.constEnd(), + &tmp); + + m = 0; // reset the active the macro + + pushState(createStateFromSource(tmp)); + if (_dot->is(T_IDENTIFIER)) { + const QByteArray id = tokenSpell(*_dot); + Macro *macro = env.resolve(id); + if (macro && macro->function_like) + m = macro; + } + popState(); + + if (! m) { + result->append(tmp); + continue; + } + } + } + + // collect the actual arguments + if (_dot->isNot(T_LPAREN)) { + // ### warnng expected T_LPAREN + result->append(m->name); + continue; + } + + int count = 0; + while (_dot->isNot(T_EOF_SYMBOL)) { + if (_dot->is(T_LPAREN)) + ++count; + else if (_dot->is(T_RPAREN)) { + if (! --count) + break; + } + ++_dot; + } + if (_dot->isNot(T_RPAREN)) { + // ### warning expected T_RPAREN + } else { + const char *beginOfText = startOfToken(*identifierToken); + const char *endOfText = endOfToken(*_dot); + ++_dot; // skip T_RPAREN + expand(beginOfText, endOfText, result); + } + } + } + } + } + + popState(); + env.currentLine = previousCurrentLine; +} + +const char *pp::startOfToken(const Token &token) const +{ return _source.constBegin() + token.begin(); } + +const char *pp::endOfToken(const Token &token) const +{ return _source.constBegin() + token.end(); } + +QByteArray pp::tokenSpell(const Token &token) const +{ + const QByteArray text = QByteArray::fromRawData(_source.constBegin() + token.offset, + token.length); + return text; +} + +QByteArray pp::tokenText(const Token &token) const +{ + const QByteArray text(_source.constBegin() + token.offset, + token.length); + return text; +} + +void pp::processDirective(TokenIterator firstToken, TokenIterator lastToken) +{ + RangeLexer tk(firstToken, lastToken); + ++tk; // skip T_POUND + + if (tk->is(T_IDENTIFIER)) { + const QByteArray directive = tokenSpell(*tk); + switch (PP_DIRECTIVE_TYPE d = classifyDirective(directive)) { + case PP_DEFINE: + if (! skipping()) + processDefine(firstToken, lastToken); + break; + + case PP_INCLUDE: + case PP_INCLUDE_NEXT: + if (! skipping()) + processInclude(d == PP_INCLUDE_NEXT, firstToken, lastToken); + break; + + case PP_UNDEF: + if (! skipping()) + processUndef(firstToken, lastToken); + break; + + case PP_ELIF: + processElif(firstToken, lastToken); + break; + + case PP_ELSE: + processElse(firstToken, lastToken); + break; + + case PP_ENDIF: + processEndif(firstToken, lastToken); + break; + + case PP_IF: + processIf(firstToken, lastToken); + break; + + case PP_IFDEF: + case PP_IFNDEF: + processIfdef(d == PP_IFNDEF, firstToken, lastToken); + break; + + default: + break; + } // switch + } +} + +QVector<Token> pp::tokenize(const QByteArray &text) const +{ + QVector<Token> tokens; + Lexer lex(text.constBegin(), text.constEnd()); + lex.setScanKeywords(false); + Token tk; + do { + lex(&tk); + tokens.append(tk); + } while (tk.isNot(T_EOF_SYMBOL)); + return tokens; +} + +void pp::processInclude(bool skipCurentPath, + TokenIterator firstToken, TokenIterator lastToken, + bool acceptMacros) +{ + RangeLexer tk(firstToken, lastToken); + ++tk; // skip T_POUND + ++tk; // skip `include|nclude_next' + + if (acceptMacros && tk->is(T_IDENTIFIER)) { +#if 0 + QByteArray name; + name.reserve(256); + MacroExpander expandInclude(env); + expandInclude(startOfToken(tokens.at(2)), + startOfToken(tokens.last()), + &name); + const QByteArray previousSource = switchSource(name); + //processInclude(skipCurentPath, tokenize(name), /*accept macros=*/ false); + (void) switchSource(previousSource); +#endif + } else if (tk->is(T_LESS)) { + TokenIterator start = tk.dot(); + for (; tk->isNot(T_EOF_SYMBOL); ++tk) { + if (tk->is(T_GREATER)) + break; + } + const char *beginOfPath = endOfToken(*start); + const char *endOfPath = startOfToken(*tk); + const QByteArray path = QByteArray::fromRawData(beginOfPath, + endOfPath - beginOfPath); + + QString fn = QString::fromUtf8(path.constData(), path.length()); + + if (client) + client->sourceNeeded(fn, Client::IncludeGlobal); + } else if (tk->is(T_ANGLE_STRING_LITERAL) || tk->is(T_STRING_LITERAL)) { + const QByteArray spell = tokenSpell(*tk); + const char *beginOfPath = spell.constBegin(); + const char *endOfPath = spell.constEnd(); + const char quote = *beginOfPath; + if (beginOfPath + 1 != endOfPath && ((quote == '"' && endOfPath[-1] == '"') || + (quote == '<' && endOfPath[-1] == '>'))) { + const QByteArray path = QByteArray::fromRawData(beginOfPath + 1, + spell.length() - 2); + QString fn = QString::fromUtf8(path.constData(), path.length()); + + if (client) + client->sourceNeeded(fn, Client::IncludeLocal); + } + } +} + +void pp::processDefine(TokenIterator firstToken, TokenIterator lastToken) +{ + RangeLexer tk(firstToken, lastToken); + + if (tk.size() < 3) + return; // nothing to do + + ++tk; // skip T_POUND + ++tk; // skip T_DEFINE + + if (tk->isNot(T_IDENTIFIER)) { + // ### warning expected an `identifier' + return; + } + + Macro macro; + macro.name = tokenText(*tk); + ++tk; // skip T_IDENTIFIER + + if (tk->is(T_LPAREN) && ! tk->whitespace) { + // a function-like macro definition + macro.function_like = true; + + ++tk; // skip T_LPAREN + if (tk->is(T_IDENTIFIER)) { + macro.formals.append(tokenText(*tk)); + ++tk; // skip T_IDENTIFIER + while (tk->is(T_COMMA)) { + ++tk;// skip T_COMMA + if (tk->isNot(T_IDENTIFIER)) + break; + macro.formals.append(tokenText(*tk)); + ++tk; // skip T_IDENTIFIER + } + } + + if (tk->is(T_DOT_DOT_DOT)) { + macro.variadics = true; + ++tk; // skip T_DOT_DOT_DOT + } + + if (tk->isNot(T_RPAREN)) { + // ### warning expected `)' + return; + } + + ++tk; // skip T_RPAREN + } + + QByteArray macroId = macro.name; + const bool isQtWord = isQtReservedWord(macroId); + + if (macro.function_like) { + macroId += '('; + for (int i = 0; i < macro.formals.size(); ++i) { + if (i != 0) + macroId += ", "; + + const QByteArray formal = macro.formals.at(i); + macroId += formal; + } + macroId += ')'; + } + + if (isQtWord) + macro.definition = macroId; + else { + const char *startOfDefinition = startOfToken(*tk); + const char *endOfDefinition = startOfToken(*lastToken); + macro.definition.append(startOfDefinition, + endOfDefinition - startOfDefinition); + macro.definition.replace("\\\n", " "); + } + + env.bind(macro); + + QByteArray macroText; + macroText.reserve(64); + macroText += "#define "; + + macroText += macroId; + macroText += ' '; + macroText += macro.definition; + macroText += '\n'; + + client->macroAdded(macroId, macroText); +} + +void pp::processIf(TokenIterator firstToken, TokenIterator lastToken) +{ + RangeLexer tk(firstToken, lastToken); + + ++tk; // skip T_POUND + ++tk; // skipt `if' + + if (testIfLevel()) { + const char *first = startOfToken(*tk); + const char *last = startOfToken(*lastToken); + + MacroExpander expandCondition (env); + QByteArray condition; + condition.reserve(256); + expandCondition(first, last, &condition); + + QVector<Token> tokens = tokenize(condition); + + const Value result = evalExpression(tokens.constBegin(), + tokens.constEnd() - 1, + condition); + + _true_test[iflevel] = ! result.is_zero (); + _skipping[iflevel] = result.is_zero (); + } +} + +void pp::processElse(TokenIterator firstToken, TokenIterator lastToken) +{ + RangeLexer tk(firstToken, lastToken); + + if (iflevel == 0 && !skipping ()) { + // std::cerr << "*** WARNING #else without #if" << std::endl; + } else if (iflevel > 0 && _skipping[iflevel - 1]) { + _skipping[iflevel] = true; + } else { + _skipping[iflevel] = _true_test[iflevel]; + } +} + +void pp::processElif(TokenIterator firstToken, TokenIterator lastToken) +{ + RangeLexer tk(firstToken, lastToken); + ++tk; // skip T_POUND + ++tk; // skipt `elif' + + if (! (iflevel > 0)) { + // std::cerr << "*** WARNING: " << __FILE__ << __LINE__ << std::endl; + } else if (iflevel == 0 && !skipping()) { + // std::cerr << "*** WARNING #else without #if" << std::endl; + } else if (!_true_test[iflevel] && !_skipping[iflevel - 1]) { + const Value result = evalExpression(tk.dot(), lastToken, _source); + _true_test[iflevel] = ! result.is_zero (); + _skipping[iflevel] = result.is_zero (); + } else { + _skipping[iflevel] = true; + } +} + +void pp::processEndif(TokenIterator, TokenIterator) +{ + if (iflevel == 0 && !skipping()) { + // std::cerr << "*** WARNING #endif without #if" << std::endl; + } else { + _skipping[iflevel] = false; + _true_test[iflevel] = false; + + --iflevel; + } +} + +void pp::processIfdef(bool checkUndefined, + TokenIterator firstToken, TokenIterator lastToken) +{ + RangeLexer tk(firstToken, lastToken); + + ++tk; // skip T_POUND + ++tk; // skip `ifdef' + if (testIfLevel()) { + if (tk->is(T_IDENTIFIER)) { + const QByteArray macroName = tokenSpell(*tk); + bool value = env.resolve(macroName) != 0 || env.isBuiltinMacro(macroName); + + if (checkUndefined) + value = ! value; + + _true_test[iflevel] = value; + _skipping [iflevel] = ! value; + } + } +} + +void pp::processUndef(TokenIterator firstToken, TokenIterator lastToken) +{ + RangeLexer tk(firstToken, lastToken); + + ++tk; // skip T_POUND + ++tk; // skip `undef' + + if (tk->is(T_IDENTIFIER)) { + const QByteArray macroName = tokenText(*tk); + env.remove(macroName); + + QByteArray macroText; + macroText += "#undef "; + macroText += macroName; + macroText += '\n'; + client->macroAdded(macroName, macroText); + } +} + +void pp::resetIfLevel () +{ + iflevel = 0; + _skipping[iflevel] = false; + _true_test[iflevel] = false; +} + +pp::PP_DIRECTIVE_TYPE pp::classifyDirective (const QByteArray &__directive) const +{ + switch (__directive.size()) + { + case 2: + if (__directive[0] == 'i' && __directive[1] == 'f') + return PP_IF; + break; + + case 4: + if (__directive[0] == 'e' && __directive == "elif") + return PP_ELIF; + else if (__directive[0] == 'e' && __directive == "else") + return PP_ELSE; + break; + + case 5: + if (__directive[0] == 'i' && __directive == "ifdef") + return PP_IFDEF; + else if (__directive[0] == 'u' && __directive == "undef") + return PP_UNDEF; + else if (__directive[0] == 'e' && __directive == "endif") + return PP_ENDIF; + break; + + case 6: + if (__directive[0] == 'i' && __directive == "ifndef") + return PP_IFNDEF; + else if (__directive[0] == 'd' && __directive == "define") + return PP_DEFINE; + break; + + case 7: + if (__directive[0] == 'i' && __directive == "include") + return PP_INCLUDE; + break; + + case 12: + if (__directive[0] == 'i' && __directive == "include_next") + return PP_INCLUDE_NEXT; + break; + + default: + break; + } + + return PP_UNKNOWN_DIRECTIVE; +} + +bool pp::testIfLevel() +{ + const bool result = !_skipping[iflevel++]; + _skipping[iflevel] = _skipping[iflevel - 1]; + _true_test[iflevel] = false; + return result; +} + +int pp::skipping() const +{ return _skipping[iflevel]; } + +Value pp::evalExpression(TokenIterator firstToken, TokenIterator lastToken, + const QByteArray &source) const +{ + ExpressionEvaluator eval(&env); + const Value result = eval(firstToken, lastToken, source); + return result; +} + +bool pp::isQtReservedWord (const QByteArray ¯oId) const +{ + const int size = macroId.size(); + if (size == 9 && macroId.at(0) == 'Q' && macroId == "Q_SIGNALS") + return true; + else if (size == 7 && macroId.at(0) == 'Q' && macroId == "Q_SLOTS") + return true; + else if (size == 6 && macroId.at(0) == 'S' && macroId == "SIGNAL") + return true; + else if (size == 4 && macroId.at(0) == 'S' && macroId == "SLOT") + return true; + else if (size == 7 && macroId.at(0) == 's' && macroId == "signals") + return true; + else if (size == 5 && macroId.at(0) == 's' && macroId == "slots") + return true; + return false; +} diff --git a/src/plugins/cpptools/rpp/pp-engine.h b/src/plugins/cpptools/rpp/pp-engine.h new file mode 100644 index 00000000000..fb3b9a3212e --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-engine.h @@ -0,0 +1,230 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <[email protected]> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_ENGINE_H +#define PP_ENGINE_H + +#include "pp-client.h" +#include <Token.h> +#include <QVector> + +namespace CPlusPlus { + class Token; +} + +namespace rpp { + + struct Value + { + enum Kind { + Kind_Long, + Kind_ULong, + }; + + Kind kind; + + union { + long l; + unsigned long ul; + }; + + + Value() + : kind(Kind_Long), l(0) + { } + + inline bool is_ulong () const + { return kind == Kind_ULong; } + + inline void set_ulong (unsigned long v) + { + ul = v; + kind = Kind_ULong; + } + + inline void set_long (long v) + { + l = v; + kind = Kind_Long; + } + + inline bool is_zero () const + { return l == 0; } + +#define PP_DEFINE_BIN_OP(name, op) \ + inline Value operator op(const Value &other) const \ + { \ + Value v = *this; \ + if (v.is_ulong () || other.is_ulong ()) \ + v.set_ulong (v.ul op other.ul); \ + else \ + v.set_long (v.l op other.l); \ + return v; \ + } + + PP_DEFINE_BIN_OP(op_add, +) + PP_DEFINE_BIN_OP(op_sub, -) + PP_DEFINE_BIN_OP(op_mult, *) + PP_DEFINE_BIN_OP(op_div, /) + PP_DEFINE_BIN_OP(op_mod, %) + PP_DEFINE_BIN_OP(op_lhs, <<) + PP_DEFINE_BIN_OP(op_rhs, >>) + PP_DEFINE_BIN_OP(op_lt, <) + PP_DEFINE_BIN_OP(op_gt, >) + PP_DEFINE_BIN_OP(op_le, <=) + PP_DEFINE_BIN_OP(op_ge, >=) + PP_DEFINE_BIN_OP(op_eq, ==) + PP_DEFINE_BIN_OP(op_ne, !=) + PP_DEFINE_BIN_OP(op_bit_and, &) + PP_DEFINE_BIN_OP(op_bit_or, |) + PP_DEFINE_BIN_OP(op_bit_xor, ^) + PP_DEFINE_BIN_OP(op_and, &&) + PP_DEFINE_BIN_OP(op_or, ||) + +#undef PP_DEFINE_BIN_OP + }; + + class pp + { + Client *client; + Environment &env; + MacroExpander expand; + + enum { MAX_LEVEL = 512 }; + + bool _skipping[MAX_LEVEL]; // ### move in state + bool _true_test[MAX_LEVEL]; // ### move in state + int iflevel; // ### move in state + + enum PP_DIRECTIVE_TYPE + { + PP_UNKNOWN_DIRECTIVE, + PP_DEFINE, + PP_INCLUDE, + PP_INCLUDE_NEXT, + PP_ELIF, + PP_ELSE, + PP_ENDIF, + PP_IF, + PP_IFDEF, + PP_IFNDEF, + PP_UNDEF + }; + + typedef const CPlusPlus::Token *TokenIterator; + + struct State { + QByteArray source; + QVector<CPlusPlus::Token> tokens; + TokenIterator dot; + }; + + QList<State> _savedStates; + + State state() const; + void pushState(const State &state); + void popState(); + + QByteArray _source; + QVector<CPlusPlus::Token> _tokens; + TokenIterator _dot; + + State createStateFromSource(const QByteArray &source) const; + + public: + pp(Client *client, Environment &env); + + void operator()(const QByteArray &filename, + const QByteArray &source, + QByteArray *result); + + void operator()(const QByteArray &source, + QByteArray *result); + + private: + void resetIfLevel(); + bool testIfLevel(); + int skipping() const; + + PP_DIRECTIVE_TYPE classifyDirective(const QByteArray &directive) const; + + Value evalExpression(TokenIterator firstToken, + TokenIterator lastToken, + const QByteArray &source) const; + + QVector<CPlusPlus::Token> tokenize(const QByteArray &text) const; + + const char *startOfToken(const CPlusPlus::Token &token) const; + const char *endOfToken(const CPlusPlus::Token &token) const; + + QByteArray tokenSpell(const CPlusPlus::Token &token) const; + QByteArray tokenText(const CPlusPlus::Token &token) const; // does a deep copy + + void processDirective(TokenIterator dot, TokenIterator lastToken); + void processInclude(bool skipCurrentPath, + TokenIterator dot, TokenIterator lastToken, + bool acceptMacros = true); + void processDefine(TokenIterator dot, TokenIterator lastToken); + void processIf(TokenIterator dot, TokenIterator lastToken); + void processElse(TokenIterator dot, TokenIterator lastToken); + void processElif(TokenIterator dot, TokenIterator lastToken); + void processEndif(TokenIterator dot, TokenIterator lastToken); + void processIfdef(bool checkUndefined, + TokenIterator dot, TokenIterator lastToken); + void processUndef(TokenIterator dot, TokenIterator lastToken); + + bool isQtReservedWord(const QByteArray &name) const; + }; + +} // namespace rpp + +#endif // PP_ENGINE_H diff --git a/src/plugins/cpptools/rpp/pp-environment.cpp b/src/plugins/cpptools/rpp/pp-environment.cpp new file mode 100644 index 00000000000..15037878981 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-environment.cpp @@ -0,0 +1,231 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <[email protected]> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "pp-environment.h" +#include "pp.h" +#include <cstring> + +using namespace rpp; + +Environment::Environment () + : currentLine(0), + hide_next(false), + _macros(0), + _allocated_macros(0), + _macro_count(-1), + _hash(0), + _hash_count(401) +{ +} + +Environment::~Environment () +{ + if (_macros) { + qDeleteAll(firstMacro(), lastMacro()); + free(_macros); + } + + if (_hash) + free(_hash); +} + +unsigned Environment::macroCount () const +{ return _macro_count + 1; } + +Macro *Environment::macroAt (unsigned index) const +{ return _macros[index]; } + +Macro *Environment::bind(const Macro &__macro) +{ + Q_ASSERT(! __macro.name.isEmpty()); + + Macro *m = new Macro (__macro); + m->hashcode = hash_code(m->name); + m->fileName = current_file; + m->line = currentLine; + + if (++_macro_count == _allocated_macros) { + if (! _allocated_macros) + _allocated_macros = 401; + else + _allocated_macros <<= 1; + + _macros = (Macro **) realloc(_macros, sizeof(Macro *) * _allocated_macros); + } + + _macros[_macro_count] = m; + + if (! _hash || _macro_count > (_hash_count >> 1)) { + rehash(); + } else { + const unsigned h = m->hashcode % _hash_count; + m->next = _hash[h]; + _hash[h] = m; + } + + return m; +} + +void Environment::remove (const QByteArray &name) +{ + Macro macro; + macro.name = name; + macro.hidden = true; + bind(macro); +} + +bool Environment::isBuiltinMacro(const QByteArray &s) const +{ + if (s.length() != 8) + return false; + + if (s[0] == '_') { + if (s[1] == '_') { + if (s[2] == 'D') { + if (s[3] == 'A') { + if (s[4] == 'T') { + if (s[5] == 'E') { + if (s[6] == '_') { + if (s[7] == '_') { + return true; + } + } + } + } + } + } + else if (s[2] == 'F') { + if (s[3] == 'I') { + if (s[4] == 'L') { + if (s[5] == 'E') { + if (s[6] == '_') { + if (s[7] == '_') { + return true; + } + } + } + } + } + } + else if (s[2] == 'L') { + if (s[3] == 'I') { + if (s[4] == 'N') { + if (s[5] == 'E') { + if (s[6] == '_') { + if (s[7] == '_') { + return true; + } + } + } + } + } + } + else if (s[2] == 'T') { + if (s[3] == 'I') { + if (s[4] == 'M') { + if (s[5] == 'E') { + if (s[6] == '_') { + if (s[7] == '_') { + return true; + } + } + } + } + } + } + } + } + return false; +} + +Macro *Environment::resolve (const QByteArray &name) const +{ + if (! _macros) + return 0; + + Macro *it = _hash[hash_code (name) % _hash_count]; + for (; it; it = it->next) { + if (it->name != name) + continue; + else if (it->hidden) + return 0; + else break; + } + return it; +} + +unsigned Environment::hash_code (const QByteArray &s) +{ + unsigned hash_value = 0; + + for (int i = 0; i < s.size (); ++i) + hash_value = (hash_value << 5) - hash_value + s.at (i); + + return hash_value; +} + +void Environment::rehash() +{ + if (_hash) { + free(_hash); + _hash_count <<= 1; + } + + _hash = (Macro **) calloc(_hash_count, sizeof(Macro *)); + + for (Macro **it = firstMacro(); it != lastMacro(); ++it) { + Macro *m= *it; + const unsigned h = m->hashcode % _hash_count; + m->next = _hash[h]; + _hash[h] = m; + } +} diff --git a/src/plugins/cpptools/rpp/pp-environment.h b/src/plugins/cpptools/rpp/pp-environment.h new file mode 100644 index 00000000000..cdecf4de61b --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-environment.h @@ -0,0 +1,109 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <[email protected]> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_ENVIRONMENT_H +#define PP_ENVIRONMENT_H + +#include <QVector> +#include <QByteArray> + +namespace rpp { + +struct Macro; + +class Environment +{ +public: + Environment(); + ~Environment(); + + unsigned macroCount() const; + Macro *macroAt(unsigned index) const; + + Macro *bind(const Macro ¯o); + void remove(const QByteArray &name); + + Macro *resolve(const QByteArray &name) const; + bool isBuiltinMacro(const QByteArray &name) const; + + const Macro *const *firstMacro() const + { return _macros; } + + Macro **firstMacro() + { return _macros; } + + const Macro *const *lastMacro() const + { return _macros + _macro_count + 1; } + + Macro **lastMacro() + { return _macros + _macro_count + 1; } + +private: + static unsigned hash_code (const QByteArray &s); + void rehash(); + +public: + QByteArray current_file; + unsigned currentLine; + bool hide_next; + +private: + Macro **_macros; + int _allocated_macros; + int _macro_count; + Macro **_hash; + int _hash_count; +}; + +} // namespace rpp + +#endif // PP_ENVIRONMENT_H diff --git a/src/plugins/cpptools/rpp/pp-fwd.h b/src/plugins/cpptools/rpp/pp-fwd.h new file mode 100644 index 00000000000..05b68774cf8 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-fwd.h @@ -0,0 +1,62 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <[email protected]> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_FWD_H +#define PP_FWD_H + +namespace rpp { + +} // namespace rpp + +#endif // PP_FWD_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/src/plugins/cpptools/rpp/pp-internal.h b/src/plugins/cpptools/rpp/pp-internal.h new file mode 100644 index 00000000000..eed958372c4 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-internal.h @@ -0,0 +1,102 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <[email protected]> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_INTERNAL_H +#define PP_INTERNAL_H + +#include <QByteArray> + +namespace rpp { + + namespace _PP_internal + { + + inline void output_line(const QByteArray &__filename, int __line, QByteArray *__result) + { + QByteArray __msg; + + __msg += "# "; + + char __line_descr[16]; + qsnprintf (__line_descr, 16, "%d", __line); + __msg += __line_descr; + + __msg += " \""; + + if (__filename.isEmpty ()) + __msg += "<editor>"; + else + __msg += __filename; + + __msg += "\"\n"; + __result->append(__msg); + } + + inline bool comment_p (const char *__first, const char *__last) + { + if (__first == __last) + return false; + + if (*__first != '/') + return false; + + if (++__first == __last) + return false; + + return (*__first == '/' || *__first == '*'); + } + + } // _PP_internal + +} // namespace rpp + +#endif // PP_INTERNAL_H diff --git a/src/plugins/cpptools/rpp/pp-macro-expander.cpp b/src/plugins/cpptools/rpp/pp-macro-expander.cpp new file mode 100644 index 00000000000..658f2acc673 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-macro-expander.cpp @@ -0,0 +1,362 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "pp.h" +#include "pp-macro-expander.h" +#include <QDateTime> + +using namespace rpp; + +MacroExpander::MacroExpander (Environment &env, pp_frame *frame) + : env (env), frame (frame), + lines (0), generated_lines (0) +{ } + +const QByteArray *MacroExpander::resolve_formal (const QByteArray &__name) +{ + if (! (frame && frame->expanding_macro)) + return 0; + + const QVector<QByteArray> &formals = frame->expanding_macro->formals; + for (int index = 0; index < formals.size(); ++index) { + const QByteArray formal = formals.at(index); + + if (formal == __name && index < frame->actuals.size()) + return &frame->actuals.at(index); + } + + return 0; +} + +const char *MacroExpander::operator () (const char *__first, const char *__last, + QByteArray *__result) +{ + generated_lines = 0; + __first = skip_blanks (__first, __last); + lines = skip_blanks.lines; + + while (__first != __last) + { + if (*__first == '\n') + { + __result->append('\n'); + __result->append('#'); + __result->append(QByteArray::number(env.currentLine)); + __result->append(' '); + __result->append('"'); + __result->append(env.current_file); + __result->append('"'); + __result->append('\n'); + ++lines; + + __first = skip_blanks (++__first, __last); + lines += skip_blanks.lines; + + if (__first != __last && *__first == '#') + break; + } + else if (*__first == '#') + { + __first = skip_blanks (++__first, __last); + lines += skip_blanks.lines; + + const char *end_id = skip_identifier (__first, __last); + const QByteArray fast_name(__first, end_id - __first); + __first = end_id; + + if (const QByteArray *actual = resolve_formal (fast_name)) + { + __result->append('\"'); + + const char *actual_begin = actual->constData (); + const char *actual_end = actual_begin + actual->size (); + + for (const char *it = skip_whitespaces (actual_begin, actual_end); + it != actual_end; ++it) + { + if (*it == '"' || *it == '\\') + { + __result->append('\\'); + __result->append(*it); + } + else if (*it == '\n') + { + __result->append('"'); + __result->append('\n'); + __result->append('"'); + } + else + __result->append(*it); + } + + __result->append('\"'); + } + else + __result->append('#'); // ### warning message? + } + else if (*__first == '\"') + { + const char *next_pos = skip_string_literal (__first, __last); + lines += skip_string_literal.lines; + __result->append(__first, next_pos - __first); + __first = next_pos; + } + else if (*__first == '\'') + { + const char *next_pos = skip_char_literal (__first, __last); + lines += skip_char_literal.lines; + __result->append(__first, next_pos - __first); + __first = next_pos; + } + else if (_PP_internal::comment_p (__first, __last)) + { + __first = skip_comment_or_divop (__first, __last); + int n = skip_comment_or_divop.lines; + lines += n; + + while (n-- > 0) + __result->append('\n'); + } + else if (pp_isspace (*__first)) + { + for (; __first != __last; ++__first) + { + if (*__first == '\n' || !pp_isspace (*__first)) + break; + } + + __result->append(' '); + } + else if (pp_isdigit (*__first)) + { + const char *next_pos = skip_number (__first, __last); + lines += skip_number.lines; + __result->append(__first, next_pos - __first); + __first = next_pos; + } + else if (pp_isalpha (*__first) || *__first == '_') + { + const char *name_begin = __first; + const char *name_end = skip_identifier (__first, __last); + __first = name_end; // advance + + // search for the paste token + const char *next = skip_blanks (__first, __last); + bool paste = false; + if (next != __last && *next == '#') + { + paste = true; + ++next; + if (next != __last && *next == '#') + __first = skip_blanks(++next, __last); + } + + const QByteArray fast_name(name_begin, name_end - name_begin); + + if (const QByteArray *actual = resolve_formal (fast_name)) + { + const char *begin = actual->constData (); + const char *end = begin + actual->size (); + if (paste) { + for (--end; end != begin - 1; --end) { + if (! pp_isspace(*end)) + break; + } + ++end; + } + __result->append(begin, end - begin); + continue; + } + + Macro *macro = env.resolve (fast_name); + if (! macro || macro->hidden || env.hide_next) + { + if (fast_name.size () == 7 && fast_name [0] == 'd' && fast_name == "defined") + env.hide_next = true; + else + env.hide_next = false; + + if (fast_name.size () == 8 && fast_name [0] == '_' && fast_name [1] == '_') + { + if (fast_name == "__LINE__") + { + char buf [16]; + const size_t count = qsnprintf (buf, 16, "%d", env.currentLine + lines); + __result->append(buf, count); + continue; + } + + else if (fast_name == "__FILE__") + { + __result->append('"'); + __result->append(env.current_file); + __result->append('"'); + continue; + } + + else if (fast_name == "__DATE__") + { + __result->append('"'); + __result->append(QDate::currentDate().toString().toUtf8()); + __result->append('"'); + continue; + } + + else if (fast_name == "__TIME__") + { + __result->append('"'); + __result->append(QTime::currentTime().toString().toUtf8()); + __result->append('"'); + continue; + } + + } + + __result->append(name_begin, name_end - name_begin); + continue; + } + + if (! macro->function_like) + { + Macro *m = 0; + + if (! macro->definition.isEmpty()) + { + macro->hidden = true; + + QByteArray __tmp; + __tmp.reserve (256); + + MacroExpander expand_macro (env); + expand_macro (macro->definition.constBegin (), macro->definition.constEnd (), &__tmp); + generated_lines += expand_macro.lines; + + if (! __tmp.isEmpty ()) + { + const char *__tmp_begin = __tmp.constBegin(); + const char *__tmp_end = __tmp.constEnd(); + const char *__begin_id = skip_whitespaces (__tmp_begin, __tmp_end); + const char *__end_id = skip_identifier (__begin_id, __tmp_end); + + if (__end_id == __tmp_end) + { + const QByteArray __id (__begin_id, __end_id - __begin_id); + m = env.resolve (__id); + } + + if (! m) + *__result += __tmp; + } + + macro->hidden = false; + } + + if (! m) + continue; + + macro = m; + } + + // function like macro + const char *arg_it = skip_whitespaces (__first, __last); + + if (arg_it == __last || *arg_it != '(') + { + __result->append(name_begin, name_end - name_begin); + lines += skip_whitespaces.lines; + __first = arg_it; + continue; + } + + QVector<QByteArray> actuals; + actuals.reserve (5); + ++arg_it; // skip '(' + + MacroExpander expand_actual (env, frame); + + const char *arg_end = skip_argument_variadics (actuals, macro, arg_it, __last); + if (arg_it != arg_end) + { + const QByteArray actual (arg_it, arg_end - arg_it); + QByteArray expanded; + expand_actual (actual.constBegin (), actual.constEnd (), &expanded); + actuals.push_back (expanded); + arg_it = arg_end; + } + + while (arg_it != __last && *arg_end == ',') + { + ++arg_it; // skip ',' + + arg_end = skip_argument_variadics (actuals, macro, arg_it, __last); + const QByteArray actual (arg_it, arg_end - arg_it); + QByteArray expanded; + expand_actual (actual.constBegin (), actual.constEnd (), &expanded); + actuals.push_back (expanded); + arg_it = arg_end; + } + + if (! (arg_it != __last && *arg_it == ')')) + return __last; + + ++arg_it; // skip ')' + __first = arg_it; + + pp_frame frame (macro, actuals); + MacroExpander expand_macro (env, &frame); + macro->hidden = true; + expand_macro (macro->definition.constBegin (), macro->definition.constEnd (), __result); + macro->hidden = false; + generated_lines += expand_macro.lines; + } + else + __result->append(*__first++); + } + + return __first; +} + +const char *MacroExpander::skip_argument_variadics (QVector<QByteArray> const &__actuals, + Macro *__macro, + const char *__first, const char *__last) +{ + const char *arg_end = skip_argument (__first, __last); + + while (__macro->variadics && __first != arg_end && arg_end != __last && *arg_end == ',' + && (__actuals.size () + 1) == __macro->formals.size ()) + { + arg_end = skip_argument (++arg_end, __last); + } + + return arg_end; +} diff --git a/src/plugins/cpptools/rpp/pp-macro-expander.h b/src/plugins/cpptools/rpp/pp-macro-expander.h new file mode 100644 index 00000000000..bdf21a421ba --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-macro-expander.h @@ -0,0 +1,103 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <[email protected]> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_MACRO_EXPANDER_H +#define PP_MACRO_EXPANDER_H + +namespace rpp { + + struct pp_frame + { + Macro *expanding_macro; + const QVector<QByteArray> actuals; + + pp_frame (Macro *expanding_macro, const QVector<QByteArray> &actuals) + : expanding_macro (expanding_macro), + actuals (actuals) + { } + }; + + class MacroExpander + { + Environment &env; + pp_frame *frame; + + pp_skip_number skip_number; + pp_skip_identifier skip_identifier; + pp_skip_string_literal skip_string_literal; + pp_skip_char_literal skip_char_literal; + pp_skip_argument skip_argument; + pp_skip_comment_or_divop skip_comment_or_divop; + pp_skip_blanks skip_blanks; + pp_skip_whitespaces skip_whitespaces; + + const QByteArray *resolve_formal (const QByteArray &name); + + public: + MacroExpander (Environment &env, pp_frame *frame = 0); + + const char *operator () (const char *first, const char *last, + QByteArray *result); + + const char *skip_argument_variadics (const QVector<QByteArray> &actuals, + Macro *macro, + const char *first, const char *last); + + public: // attributes + int lines; + int generated_lines; + }; + +} // namespace rpp + +#endif // PP_MACRO_EXPANDER_H + diff --git a/src/plugins/cpptools/rpp/pp-macro.h b/src/plugins/cpptools/rpp/pp-macro.h new file mode 100644 index 00000000000..b2201689897 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-macro.h @@ -0,0 +1,95 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <[email protected]> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_MACRO_H +#define PP_MACRO_H + +#include <QByteArray> +#include <QVector> + +namespace rpp { + + struct Macro + { + QByteArray name; + QByteArray definition; + QVector<QByteArray> formals; + QByteArray fileName; + int line; + int lines; + Macro *next; + unsigned hashcode; + + union + { + unsigned state; + + struct + { + unsigned hidden: 1; + unsigned function_like: 1; + unsigned variadics: 1; + }; + }; + + inline Macro(): + line(0), + lines(0), + next(0), + hashcode(0), + state(0) + { } + }; + +} // namespace rpp + +#endif // PP_MACRO_H diff --git a/src/plugins/cpptools/rpp/pp-scanner.h b/src/plugins/cpptools/rpp/pp-scanner.h new file mode 100644 index 00000000000..d9036d88558 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-scanner.h @@ -0,0 +1,380 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <[email protected]> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_SCANNER_H +#define PP_SCANNER_H + +namespace rpp { + +struct pp_skip_blanks +{ + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + lines = 0; + + for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) + { + if (*__first == '\\') + { + const char *__begin = __first; + ++__begin; + + if (__begin != __last && *__begin == '\n') + ++__first; + else + break; + } + else if (*__first == '\n' || !pp_isspace (*__first)) + break; + } + + return __first; + } +}; + +struct pp_skip_whitespaces +{ + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + lines = 0; + + for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) + { + if (! pp_isspace (*__first)) + break; + } + + return __first; + } +}; + +struct pp_skip_comment_or_divop +{ + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + enum { + MAYBE_BEGIN, + BEGIN, + MAYBE_END, + END, + IN_COMMENT, + IN_CXX_COMMENT + } state (MAYBE_BEGIN); + + lines = 0; + + for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) + { + switch (state) + { + default: + assert (0); + break; + + case MAYBE_BEGIN: + if (*__first != '/') + return __first; + + state = BEGIN; + break; + + case BEGIN: + if (*__first == '*') + state = IN_COMMENT; + else if (*__first == '/') + state = IN_CXX_COMMENT; + else + return __first; + break; + + case IN_COMMENT: + if (*__first == '*') + state = MAYBE_END; + break; + + case IN_CXX_COMMENT: + if (*__first == '\n') + return __first; + break; + + case MAYBE_END: + if (*__first == '/') + state = END; + else if (*__first != '*') + state = IN_COMMENT; + break; + + case END: + return __first; + } + } + + return __first; + } +}; + +struct pp_skip_identifier +{ + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + lines = 0; + + for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) + { + if (! pp_isalnum (*__first) && *__first != '_') + break; + } + + return __first; + } +}; + +struct pp_skip_number +{ + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + lines = 0; + + for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) + { + if (! pp_isalnum (*__first) && *__first != '.') + break; + } + + return __first; + } +}; + +struct pp_skip_string_literal +{ + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + enum { + BEGIN, + IN_STRING, + QUOTE, + END + } state (BEGIN); + + lines = 0; + + for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) + { + switch (state) + { + default: + assert (0); + break; + + case BEGIN: + if (*__first != '\"') + return __first; + state = IN_STRING; + break; + + case IN_STRING: + if (! (*__first != '\n')) + return __last; + + if (*__first == '\"') + state = END; + else if (*__first == '\\') + state = QUOTE; + break; + + case QUOTE: + state = IN_STRING; + break; + + case END: + return __first; + } + } + + return __first; + } +}; + +struct pp_skip_char_literal +{ + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + enum { + BEGIN, + IN_STRING, + QUOTE, + END + } state (BEGIN); + + lines = 0; + + for (; state != END && __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) + { + switch (state) + { + default: + assert (0); + break; + + case BEGIN: + if (*__first != '\'') + return __first; + state = IN_STRING; + break; + + case IN_STRING: + if (! (*__first != '\n')) + return __last; + + if (*__first == '\'') + state = END; + else if (*__first == '\\') + state = QUOTE; + break; + + case QUOTE: + state = IN_STRING; + break; + } + } + + return __first; + } +}; + +struct pp_skip_argument +{ + pp_skip_identifier skip_number; + pp_skip_identifier skip_identifier; + pp_skip_string_literal skip_string_literal; + pp_skip_char_literal skip_char_literal; + pp_skip_comment_or_divop skip_comment_or_divop; + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + int depth = 0; + lines = 0; + + while (__first != __last) + { + if (!depth && (*__first == ')' || *__first == ',')) + break; + else if (*__first == '(') + ++depth, ++__first; + else if (*__first == ')') + --depth, ++__first; + else if (*__first == '\"') + { + __first = skip_string_literal (__first, __last); + lines += skip_string_literal.lines; + } + else if (*__first == '\'') + { + __first = skip_char_literal (__first, __last); + lines += skip_char_literal.lines; + } + else if (*__first == '/') + { + __first = skip_comment_or_divop (__first, __last); + lines += skip_comment_or_divop.lines; + } + else if (pp_isalpha (*__first) || *__first == '_') + { + __first = skip_identifier (__first, __last); + lines += skip_identifier.lines; + } + else if (pp_isdigit (*__first)) + { + __first = skip_number (__first, __last); + lines += skip_number.lines; + } + else if (*__first == '\n') + { + ++__first; + ++lines; + } + else + ++__first; + } + + return __first; + } +}; + +} // namespace rpp + +#endif // PP_SCANNER_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/src/plugins/cpptools/rpp/pp-symbol.h b/src/plugins/cpptools/rpp/pp-symbol.h new file mode 100644 index 00000000000..37e2a623e5e --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-symbol.h @@ -0,0 +1,52 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <[email protected]> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + diff --git a/src/plugins/cpptools/rpp/pp.h b/src/plugins/cpptools/rpp/pp.h new file mode 100644 index 00000000000..52411f7624b --- /dev/null +++ b/src/plugins/cpptools/rpp/pp.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <[email protected]> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_H +#define PP_H + +#if defined(_WIN64) || defined(WIN64) || defined(__WIN64__) \ + || defined(_WIN32) || defined(WIN32) || defined(__WIN32__) +# define PP_OS_WIN +#endif + +#include <cassert> +#include <cstring> +#include <cctype> + +#include "pp-fwd.h" +#include "pp-cctype.h" +#include "pp-symbol.h" +#include "pp-internal.h" +#include "pp-macro.h" +#include "pp-environment.h" +#include "pp-scanner.h" +#include "pp-macro-expander.h" +#include "pp-engine.h" +#include "pp-client.h" + +#endif // PP_H diff --git a/src/plugins/cpptools/rpp/rpp.pri b/src/plugins/cpptools/rpp/rpp.pri new file mode 100644 index 00000000000..f47976e6fe1 --- /dev/null +++ b/src/plugins/cpptools/rpp/rpp.pri @@ -0,0 +1,20 @@ +DEPENDPATH += $$PWD +INCLUDEPATH += $$PWD + +HEADERS += $$PWD/pp-cctype.h \ + $$PWD/pp-engine.h \ + $$PWD/pp-environment.h \ + $$PWD/pp-fwd.h \ + $$PWD/pp-internal.h \ + $$PWD/pp-macro-expander.h \ + $$PWD/pp-macro.h \ + $$PWD/pp-scanner.h \ + $$PWD/pp-symbol.h \ + $$PWD/pp.h \ + $$PWD/pp-client.h + +SOURCES += $$PWD/pp-engine.cpp \ + $$PWD/pp-environment.cpp \ + $$PWD/pp-macro-expander.cpp + + diff --git a/src/plugins/debugger/Debugger.pluginspec b/src/plugins/debugger/Debugger.pluginspec new file mode 100644 index 00000000000..d68c17517e7 --- /dev/null +++ b/src/plugins/debugger/Debugger.pluginspec @@ -0,0 +1,13 @@ +<plugin name="Debugger" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Debugger integration.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="CppEditor" version="0.9.1"/><!-- Debugger plugin adds items to the editor's context menu --> + <dependency name="ProjectExplorer" version="0.9.1"/> + <dependency name="Core" version="0.9.1"/> + <dependency name="Find" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/debugger/assert.h b/src/plugins/debugger/assert.h new file mode 100644 index 00000000000..a4310040fee --- /dev/null +++ b/src/plugins/debugger/assert.h @@ -0,0 +1,45 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_QWB_ASSERT_H +#define DEBUGGER_QWB_ASSERT_H + +#ifdef Q_OS_UNIX +#define QWB_ASSERT(cond, action) \ + if(cond){}else{qDebug()<<"ASSERTION"<<#cond<<"FAILED"<<__FILE__<<__LINE__;action;} +#else +#define QWB_ASSERT(cond, action) \ + if(cond){}else{qDebug()<<"ASSERTION"<<#cond<<"FAILED";action;} +#endif + +#endif + diff --git a/src/plugins/debugger/attachexternaldialog.cpp b/src/plugins/debugger/attachexternaldialog.cpp new file mode 100644 index 00000000000..5aeec2ec620 --- /dev/null +++ b/src/plugins/debugger/attachexternaldialog.cpp @@ -0,0 +1,344 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "attachexternaldialog.h" + +#include <QDebug> +#include <QDir> +#include <QFile> +#include <QPushButton> +#include <QStandardItemModel> +#include <QHeaderView> + +using namespace Debugger::Internal; + +AttachExternalDialog::AttachExternalDialog(QWidget *parent, const QString &pid) + : QDialog(parent) +{ + setupUi(this); + buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + m_defaultPID = pid; + m_model = new QStandardItemModel(this); + + procView->setSortingEnabled(true); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + connect(procView, SIGNAL(activated(const QModelIndex &)), + this, SLOT(procSelected(const QModelIndex &))); + + + pidLineEdit->setText(m_defaultPID); + rebuildProcessList(); +} + +static bool isProcessName(const QString &procname) +{ + for (int i = 0; i != procname.size(); ++i) + if (!procname.at(i).isDigit()) + return false; + return true; +} + +struct ProcData { + QString ppid; + QString name; + QString state; +}; + +static void insertItem(QStandardItem *root, const QString &pid, + const QMap<QString, ProcData> &procs, QMap<QString, QStandardItem *> &known) +{ + //qDebug() << "HANDLING " << pid; + QStandardItem *parent = 0; + const ProcData &proc = procs[pid]; + if (1 || pid == "0") { + parent = root; + } else { + if (!known.contains(proc.ppid)) + insertItem(root, proc.ppid, procs, known); + parent = known[proc.ppid]; + } + QList<QStandardItem *> row; + row.append(new QStandardItem(pid)); + row.append(new QStandardItem(proc.name)); + //row.append(new QStandardItem(proc.ppid)); + row.append(new QStandardItem(proc.state)); + parent->appendRow(row); + known[pid] = row[0]; +} + +void AttachExternalDialog::rebuildProcessList() +{ + QStringList procnames = QDir("/proc/").entryList(); + if (procnames.isEmpty()) { + procView->hide(); + return; + } + + typedef QMap<QString, ProcData> Procs; + Procs procs; + + foreach (const QString &procname, procnames) { + if (!isProcessName(procname)) + continue; + QString filename = "/proc/" + procname + "/stat"; + QFile file(filename); + file.open(QIODevice::ReadOnly); + QStringList data = QString::fromLocal8Bit(file.readAll()).split(' '); + //qDebug() << filename << data; + ProcData proc; + proc.name = data.at(1); + if (proc.name.startsWith('(') && proc.name.endsWith(')')) + proc.name = proc.name.mid(1, proc.name.size() - 2); + proc.state = data.at(2); + proc.ppid = data.at(3); + procs[procname] = proc; + } + + m_model->clear(); + QMap<QString, QStandardItem *> known; + for (Procs::const_iterator it = procs.begin(); it != procs.end(); ++it) + insertItem(m_model->invisibleRootItem(), it.key(), procs, known); + m_model->setHeaderData(0, Qt::Horizontal, "Process ID", Qt::DisplayRole); + m_model->setHeaderData(1, Qt::Horizontal, "Name", Qt::DisplayRole); + //model->setHeaderData(2, Qt::Horizontal, "Parent", Qt::DisplayRole); + m_model->setHeaderData(2, Qt::Horizontal, "State", Qt::DisplayRole); + + procView->setModel(m_model); + procView->expandAll(); + procView->resizeColumnToContents(0); + procView->resizeColumnToContents(1); +} + +#ifdef Q_OS_WINDOWS + +#include <windows.h> +#include <tlhelp32.h> +#include <tchar.h> +#include <stdio.h> + +// Forward declarations: +BOOL GetProcessList( ); +BOOL ListProcessModules( DWORD dwPID ); +BOOL ListProcessThreads( DWORD dwOwnerPID ); +void printError( TCHAR* msg ); + +BOOL GetProcessList( ) +{ + HANDLE hProcessSnap; + HANDLE hProcess; + PROCESSENTRY32 pe32; + DWORD dwPriorityClass; + + // Take a snapshot of all processes in the system. + hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); + if( hProcessSnap == INVALID_HANDLE_VALUE ) + { + printError( TEXT("CreateToolhelp32Snapshot (of processes)") ); + return( FALSE ); + } + + // Set the size of the structure before using it. + pe32.dwSize = sizeof( PROCESSENTRY32 ); + + // Retrieve information about the first process, + // and exit if unsuccessful + if( !Process32First( hProcessSnap, &pe32 ) ) + { + printError( TEXT("Process32First") ); // show cause of failure + CloseHandle( hProcessSnap ); // clean the snapshot object + return( FALSE ); + } + + // Now walk the snapshot of processes, and + // display information about each process in turn + do + { + printf( "\n\n=====================================================" ); + _tprintf( TEXT("\nPROCESS NAME: %s"), pe32.szExeFile ); + printf( "\n-----------------------------------------------------" ); + + // Retrieve the priority class. + dwPriorityClass = 0; + hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID ); + if( hProcess == NULL ) + printError( TEXT("OpenProcess") ); + else + { + dwPriorityClass = GetPriorityClass( hProcess ); + if( !dwPriorityClass ) + printError( TEXT("GetPriorityClass") ); + CloseHandle( hProcess ); + } + + printf( "\n Process ID = 0x%08X", pe32.th32ProcessID ); + printf( "\n Thread count = %d", pe32.cntThreads ); + printf( "\n Parent process ID = 0x%08X", pe32.th32ParentProcessID ); + printf( "\n Priority base = %d", pe32.pcPriClassBase ); + if( dwPriorityClass ) + printf( "\n Priority class = %d", dwPriorityClass ); + + // List the modules and threads associated with this process + ListProcessModules( pe32.th32ProcessID ); + ListProcessThreads( pe32.th32ProcessID ); + + } while( Process32Next( hProcessSnap, &pe32 ) ); + + CloseHandle( hProcessSnap ); + return( TRUE ); +} + + +BOOL ListProcessModules( DWORD dwPID ) +{ + HANDLE hModuleSnap = INVALID_HANDLE_VALUE; + MODULEENTRY32 me32; + + // Take a snapshot of all modules in the specified process. + hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID ); + if( hModuleSnap == INVALID_HANDLE_VALUE ) + { + printError( TEXT("CreateToolhelp32Snapshot (of modules)") ); + return( FALSE ); + } + + // Set the size of the structure before using it. + me32.dwSize = sizeof( MODULEENTRY32 ); + + // Retrieve information about the first module, + // and exit if unsuccessful + if( !Module32First( hModuleSnap, &me32 ) ) + { + printError( TEXT("Module32First") ); // show cause of failure + CloseHandle( hModuleSnap ); // clean the snapshot object + return( FALSE ); + } + + // Now walk the module list of the process, + // and display information about each module + do + { + _tprintf( TEXT("\n\n MODULE NAME: %s"), me32.szModule ); + _tprintf( TEXT("\n Executable = %s"), me32.szExePath ); + printf( "\n Process ID = 0x%08X", me32.th32ProcessID ); + printf( "\n Ref count (g) = 0x%04X", me32.GlblcntUsage ); + printf( "\n Ref count (p) = 0x%04X", me32.ProccntUsage ); + printf( "\n Base address = 0x%08X", (DWORD) me32.modBaseAddr ); + printf( "\n Base size = %d", me32.modBaseSize ); + + } while( Module32Next( hModuleSnap, &me32 ) ); + + CloseHandle( hModuleSnap ); + return( TRUE ); +} + +BOOL ListProcessThreads( DWORD dwOwnerPID ) +{ + HANDLE hThreadSnap = INVALID_HANDLE_VALUE; + THREADENTRY32 te32; + + // Take a snapshot of all running threads + hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); + if( hThreadSnap == INVALID_HANDLE_VALUE ) + return( FALSE ); + + // Fill in the size of the structure before using it. + te32.dwSize = sizeof(THREADENTRY32 ); + + // Retrieve information about the first thread, + // and exit if unsuccessful + if( !Thread32First( hThreadSnap, &te32 ) ) + { + printError( TEXT("Thread32First") ); // show cause of failure + CloseHandle( hThreadSnap ); // clean the snapshot object + return( FALSE ); + } + + // Now walk the thread list of the system, + // and display information about each thread + // associated with the specified process + do + { + if( te32.th32OwnerProcessID == dwOwnerPID ) + { + printf( "\n\n THREAD ID = 0x%08X", te32.th32ThreadID ); + printf( "\n Base priority = %d", te32.tpBasePri ); + printf( "\n Delta priority = %d", te32.tpDeltaPri ); + } + } while( Thread32Next(hThreadSnap, &te32 ) ); + + CloseHandle( hThreadSnap ); + return( TRUE ); +} + +void printError( TCHAR* msg ) +{ + DWORD eNum; + TCHAR sysMsg[256]; + TCHAR* p; + + eNum = GetLastError( ); + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, eNum, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + sysMsg, 256, NULL ); + + // Trim the end of the line and terminate it with a null + p = sysMsg; + while( ( *p > 31 ) || ( *p == 9 ) ) + ++p; + do { *p-- = 0; } while( ( p >= sysMsg ) && + ( ( *p == '.' ) || ( *p < 33 ) ) ); + + // Display the message + _tprintf( TEXT("\n WARNING: %s failed with error %d (%s)"), msg, eNum, sysMsg ); +} + +#endif + + +void AttachExternalDialog::procSelected(const QModelIndex &index0) +{ + QModelIndex index = index0.sibling(index0.row(), 0); + QStandardItem *item = m_model->itemFromIndex(index); + if (!item) + return; + pidLineEdit->setText(item->text()); + accept(); +} + +int AttachExternalDialog::attachPID() const +{ + return pidLineEdit->text().toInt(); +} diff --git a/src/plugins/debugger/attachexternaldialog.h b/src/plugins/debugger/attachexternaldialog.h new file mode 100644 index 00000000000..6c77d206b6e --- /dev/null +++ b/src/plugins/debugger/attachexternaldialog.h @@ -0,0 +1,65 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ATTACHEXTERNALDIALOG_H +#define ATTACHEXTERNALDIALOG_H + +#include "ui_attachexternaldialog.h" + +QT_BEGIN_NAMESPACE +class QStandardItemModel; +QT_END_NAMESPACE + +namespace Debugger { +namespace Internal { + +class AttachExternalDialog : public QDialog, Ui::AttachExternalDialog +{ + Q_OBJECT + +public: + explicit AttachExternalDialog(QWidget *parent, const QString &pid); + int attachPID() const; + +private slots: + void rebuildProcessList(); + void procSelected(const QModelIndex &); + +private: + QString m_defaultPID; + QStandardItemModel *m_model; +}; + +} // namespace Debugger +} // namespace Internal + +#endif // ATTACHEEXTERNALDIALOG_H diff --git a/src/plugins/debugger/attachexternaldialog.ui b/src/plugins/debugger/attachexternaldialog.ui new file mode 100644 index 00000000000..63f214c51ea --- /dev/null +++ b/src/plugins/debugger/attachexternaldialog.ui @@ -0,0 +1,64 @@ +<ui version="4.0" > + <class>AttachExternalDialog</class> + <widget class="QDialog" name="AttachExternalDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>561</width> + <height>866</height> + </rect> + </property> + <property name="windowTitle" > + <string>Start Debugger</string> + </property> + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>6</number> + </property> + <property name="margin" > + <number>9</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout" > + <item> + <widget class="QLabel" name="pidLabel" > + <property name="text" > + <string>Attach to Process ID:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="pidLineEdit" /> + </item> + </layout> + </item> + <item> + <widget class="QTreeView" name="procView" > + <property name="editTriggers" > + <set>QAbstractItemView::NoEditTriggers</set> + </property> + </widget> + </item> + <item> + <widget class="Line" name="line" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <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> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/debugger/attachremotedialog.cpp b/src/plugins/debugger/attachremotedialog.cpp new file mode 100644 index 00000000000..ffd09e7b789 --- /dev/null +++ b/src/plugins/debugger/attachremotedialog.cpp @@ -0,0 +1,344 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "attachremotedialog.h" + +#include <QDebug> +#include <QDir> +#include <QFile> +#include <QPushButton> +#include <QStandardItemModel> +#include <QHeaderView> + +using namespace Debugger::Internal; + +AttachRemoteDialog::AttachRemoteDialog(QWidget *parent, const QString &pid) + : QDialog(parent) +{ + setupUi(this); + buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + m_defaultPID = pid; + m_model = new QStandardItemModel(this); + + procView->setSortingEnabled(true); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + connect(procView, SIGNAL(activated(const QModelIndex &)), + this, SLOT(procSelected(const QModelIndex &))); + + + pidLineEdit->setText(m_defaultPID); + rebuildProcessList(); +} + +static bool isProcessName(const QString &procname) +{ + for (int i = 0; i != procname.size(); ++i) + if (!procname.at(i).isDigit()) + return false; + return true; +} + +struct ProcData { + QString ppid; + QString name; + QString state; +}; + +static void insertItem(QStandardItem *root, const QString &pid, + const QMap<QString, ProcData> &procs, QMap<QString, QStandardItem *> &known) +{ + //qDebug() << "HANDLING " << pid; + QStandardItem *parent = 0; + const ProcData &proc = procs[pid]; + if (1 || pid == "0") { + parent = root; + } else { + if (!known.contains(proc.ppid)) + insertItem(root, proc.ppid, procs, known); + parent = known[proc.ppid]; + } + QList<QStandardItem *> row; + row.append(new QStandardItem(pid)); + row.append(new QStandardItem(proc.name)); + //row.append(new QStandardItem(proc.ppid)); + row.append(new QStandardItem(proc.state)); + parent->appendRow(row); + known[pid] = row[0]; +} + +void AttachRemoteDialog::rebuildProcessList() +{ + QStringList procnames = QDir("/proc/").entryList(); + if (procnames.isEmpty()) { + procView->hide(); + return; + } + + typedef QMap<QString, ProcData> Procs; + Procs procs; + + foreach (const QString &procname, procnames) { + if (!isProcessName(procname)) + continue; + QString filename = "/proc/" + procname + "/stat"; + QFile file(filename); + file.open(QIODevice::ReadOnly); + QStringList data = QString::fromLocal8Bit(file.readAll()).split(' '); + //qDebug() << filename << data; + ProcData proc; + proc.name = data.at(1); + if (proc.name.startsWith('(') && proc.name.endsWith(')')) + proc.name = proc.name.mid(1, proc.name.size() - 2); + proc.state = data.at(2); + proc.ppid = data.at(3); + procs[procname] = proc; + } + + m_model->clear(); + QMap<QString, QStandardItem *> known; + for (Procs::const_iterator it = procs.begin(); it != procs.end(); ++it) + insertItem(m_model->invisibleRootItem(), it.key(), procs, known); + m_model->setHeaderData(0, Qt::Horizontal, "Process ID", Qt::DisplayRole); + m_model->setHeaderData(1, Qt::Horizontal, "Name", Qt::DisplayRole); + //model->setHeaderData(2, Qt::Horizontal, "Parent", Qt::DisplayRole); + m_model->setHeaderData(2, Qt::Horizontal, "State", Qt::DisplayRole); + + procView->setModel(m_model); + procView->expandAll(); + procView->resizeColumnToContents(0); + procView->resizeColumnToContents(1); +} + +#ifdef Q_OS_WINDOWS + +#include <windows.h> +#include <tlhelp32.h> +#include <tchar.h> +#include <stdio.h> + +// Forward declarations: +BOOL GetProcessList( ); +BOOL ListProcessModules( DWORD dwPID ); +BOOL ListProcessThreads( DWORD dwOwnerPID ); +void printError( TCHAR* msg ); + +BOOL GetProcessList( ) +{ + HANDLE hProcessSnap; + HANDLE hProcess; + PROCESSENTRY32 pe32; + DWORD dwPriorityClass; + + // Take a snapshot of all processes in the system. + hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); + if( hProcessSnap == INVALID_HANDLE_VALUE ) + { + printError( TEXT("CreateToolhelp32Snapshot (of processes)") ); + return( FALSE ); + } + + // Set the size of the structure before using it. + pe32.dwSize = sizeof( PROCESSENTRY32 ); + + // Retrieve information about the first process, + // and exit if unsuccessful + if( !Process32First( hProcessSnap, &pe32 ) ) + { + printError( TEXT("Process32First") ); // show cause of failure + CloseHandle( hProcessSnap ); // clean the snapshot object + return( FALSE ); + } + + // Now walk the snapshot of processes, and + // display information about each process in turn + do + { + printf( "\n\n=====================================================" ); + _tprintf( TEXT("\nPROCESS NAME: %s"), pe32.szExeFile ); + printf( "\n-----------------------------------------------------" ); + + // Retrieve the priority class. + dwPriorityClass = 0; + hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID ); + if( hProcess == NULL ) + printError( TEXT("OpenProcess") ); + else + { + dwPriorityClass = GetPriorityClass( hProcess ); + if( !dwPriorityClass ) + printError( TEXT("GetPriorityClass") ); + CloseHandle( hProcess ); + } + + printf( "\n Process ID = 0x%08X", pe32.th32ProcessID ); + printf( "\n Thread count = %d", pe32.cntThreads ); + printf( "\n Parent process ID = 0x%08X", pe32.th32ParentProcessID ); + printf( "\n Priority base = %d", pe32.pcPriClassBase ); + if( dwPriorityClass ) + printf( "\n Priority class = %d", dwPriorityClass ); + + // List the modules and threads associated with this process + ListProcessModules( pe32.th32ProcessID ); + ListProcessThreads( pe32.th32ProcessID ); + + } while( Process32Next( hProcessSnap, &pe32 ) ); + + CloseHandle( hProcessSnap ); + return( TRUE ); +} + + +BOOL ListProcessModules( DWORD dwPID ) +{ + HANDLE hModuleSnap = INVALID_HANDLE_VALUE; + MODULEENTRY32 me32; + + // Take a snapshot of all modules in the specified process. + hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID ); + if( hModuleSnap == INVALID_HANDLE_VALUE ) + { + printError( TEXT("CreateToolhelp32Snapshot (of modules)") ); + return( FALSE ); + } + + // Set the size of the structure before using it. + me32.dwSize = sizeof( MODULEENTRY32 ); + + // Retrieve information about the first module, + // and exit if unsuccessful + if( !Module32First( hModuleSnap, &me32 ) ) + { + printError( TEXT("Module32First") ); // show cause of failure + CloseHandle( hModuleSnap ); // clean the snapshot object + return( FALSE ); + } + + // Now walk the module list of the process, + // and display information about each module + do + { + _tprintf( TEXT("\n\n MODULE NAME: %s"), me32.szModule ); + _tprintf( TEXT("\n Executable = %s"), me32.szExePath ); + printf( "\n Process ID = 0x%08X", me32.th32ProcessID ); + printf( "\n Ref count (g) = 0x%04X", me32.GlblcntUsage ); + printf( "\n Ref count (p) = 0x%04X", me32.ProccntUsage ); + printf( "\n Base address = 0x%08X", (DWORD) me32.modBaseAddr ); + printf( "\n Base size = %d", me32.modBaseSize ); + + } while( Module32Next( hModuleSnap, &me32 ) ); + + CloseHandle( hModuleSnap ); + return( TRUE ); +} + +BOOL ListProcessThreads( DWORD dwOwnerPID ) +{ + HANDLE hThreadSnap = INVALID_HANDLE_VALUE; + THREADENTRY32 te32; + + // Take a snapshot of all running threads + hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); + if( hThreadSnap == INVALID_HANDLE_VALUE ) + return( FALSE ); + + // Fill in the size of the structure before using it. + te32.dwSize = sizeof(THREADENTRY32 ); + + // Retrieve information about the first thread, + // and exit if unsuccessful + if( !Thread32First( hThreadSnap, &te32 ) ) + { + printError( TEXT("Thread32First") ); // show cause of failure + CloseHandle( hThreadSnap ); // clean the snapshot object + return( FALSE ); + } + + // Now walk the thread list of the system, + // and display information about each thread + // associated with the specified process + do + { + if( te32.th32OwnerProcessID == dwOwnerPID ) + { + printf( "\n\n THREAD ID = 0x%08X", te32.th32ThreadID ); + printf( "\n Base priority = %d", te32.tpBasePri ); + printf( "\n Delta priority = %d", te32.tpDeltaPri ); + } + } while( Thread32Next(hThreadSnap, &te32 ) ); + + CloseHandle( hThreadSnap ); + return( TRUE ); +} + +void printError( TCHAR* msg ) +{ + DWORD eNum; + TCHAR sysMsg[256]; + TCHAR* p; + + eNum = GetLastError( ); + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, eNum, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + sysMsg, 256, NULL ); + + // Trim the end of the line and terminate it with a null + p = sysMsg; + while( ( *p > 31 ) || ( *p == 9 ) ) + ++p; + do { *p-- = 0; } while( ( p >= sysMsg ) && + ( ( *p == '.' ) || ( *p < 33 ) ) ); + + // Display the message + _tprintf( TEXT("\n WARNING: %s failed with error %d (%s)"), msg, eNum, sysMsg ); +} + +#endif + + +void AttachRemoteDialog::procSelected(const QModelIndex &index0) +{ + QModelIndex index = index0.sibling(index0.row(), 0); + QStandardItem *item = m_model->itemFromIndex(index); + if (!item) + return; + pidLineEdit->setText(item->text()); + accept(); +} + +int AttachRemoteDialog::attachPID() const +{ + return pidLineEdit->text().toInt(); +} diff --git a/src/plugins/debugger/attachremotedialog.h b/src/plugins/debugger/attachremotedialog.h new file mode 100644 index 00000000000..ec732cd5150 --- /dev/null +++ b/src/plugins/debugger/attachremotedialog.h @@ -0,0 +1,65 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ATTACHREMOTE_DIALOG_H +#define ATTACHREMOTE_DIALOG_H + +#include "ui_attachremotedialog.h" + +QT_BEGIN_NAMESPACE +class QStandardItemModel; +QT_END_NAMESPACE + +namespace Debugger { +namespace Internal { + +class AttachRemoteDialog : public QDialog, Ui::AttachRemoteDialog +{ + Q_OBJECT + +public: + explicit AttachRemoteDialog(QWidget *parent, const QString &pid); + int attachPID() const; + +private slots: + void rebuildProcessList(); + void procSelected(const QModelIndex &); + +private: + QString m_defaultPID; + QStandardItemModel *m_model; +}; + +} // namespace Debugger +} // namespace Internal + +#endif // ATTACHREMOTEDIALOG_H diff --git a/src/plugins/debugger/attachremotedialog.ui b/src/plugins/debugger/attachremotedialog.ui new file mode 100644 index 00000000000..4d478e3b0f3 --- /dev/null +++ b/src/plugins/debugger/attachremotedialog.ui @@ -0,0 +1,64 @@ +<ui version="4.0" > + <class>AttachRemoteDialog</class> + <widget class="QDialog" name="AttachRemoteDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>561</width> + <height>866</height> + </rect> + </property> + <property name="windowTitle" > + <string>Start Debugger</string> + </property> + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>6</number> + </property> + <property name="margin" > + <number>9</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout" > + <item> + <widget class="QLabel" name="pidLabel" > + <property name="text" > + <string>Attach to Process ID:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="pidLineEdit" /> + </item> + </layout> + </item> + <item> + <widget class="QTreeView" name="procView" > + <property name="editTriggers" > + <set>QAbstractItemView::NoEditTriggers</set> + </property> + </widget> + </item> + <item> + <widget class="Line" name="line" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <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> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/debugger/breakbyfunction.ui b/src/plugins/debugger/breakbyfunction.ui new file mode 100644 index 00000000000..06cedb2e469 --- /dev/null +++ b/src/plugins/debugger/breakbyfunction.ui @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>BreakByFunctionDialog</class> + <widget class="QDialog" name="BreakByFunctionDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>337</width> + <height>101</height> + </rect> + </property> + <property name="windowTitle"> + <string>Start Debugger</string> + </property> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>9</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="functionLabel"> + <property name="text"> + <string>Function to break on:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="functionLineEdit"/> + </item> + </layout> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <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> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/debugger/breakcondition.ui b/src/plugins/debugger/breakcondition.ui new file mode 100644 index 00000000000..4de45fd29da --- /dev/null +++ b/src/plugins/debugger/breakcondition.ui @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>BreakCondition</class> + <widget class="QDialog" name="BreakCondition"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>435</width> + <height>142</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="labelCondition"> + <property name="text"> + <string>Condition:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEditCondition"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="labelIgnoreCount"> + <property name="text"> + <string>Ignore count:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QSpinBox" name="spinBoxIgnoreCount"> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <property name="maximum"> + <number>999999999</number> + </property> + </widget> + </item> + </layout> + </item> + <item> + <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> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>BreakCondition</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>BreakCondition</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/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp new file mode 100644 index 00000000000..3d83b5e323d --- /dev/null +++ b/src/plugins/debugger/breakhandler.cpp @@ -0,0 +1,559 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "breakhandler.h" + +#include "assert.h" +#include "imports.h" // TextEditor::BaseTextMark + +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> + +using namespace Debugger; +using namespace Debugger::Internal; + + +////////////////////////////////////////////////////////////////// +// +// BreakpointMarker +// +////////////////////////////////////////////////////////////////// + +namespace Debugger { +namespace Internal { + + +// The red blob on the left side in the cpp editor. +class BreakpointMarker : public TextEditor::BaseTextMark +{ + Q_OBJECT +public: + BreakpointMarker(BreakpointData *data, const QString &fileName, int lineNumber) + : BaseTextMark(fileName, lineNumber), m_data(data), m_pending(true) + { + //qDebug() << "CREATE MARKER " << fileName << lineNumber; + } + + ~BreakpointMarker() + { + //qDebug() << "REMOVE MARKER "; + m_data = 0; + } + + QIcon icon() const + { + static const QIcon icon(":/gdbdebugger/images/breakpoint.svg"); + static const QIcon icon2(":/gdbdebugger/images/breakpoint_pending.svg"); + return m_pending ? icon2 : icon; + } + + void setPending(bool pending) + { + if (pending == m_pending) + return; + m_pending = pending; + updateMarker(); + } + + void updateBlock(const QTextBlock &) + { + //qDebug() << "BREAKPOINT MARKER UPDATE BLOCK"; + } + + void removedFromEditor() + { + if (!m_data) + return; + + BreakHandler *handler = m_data->handler(); + handler->removeBreakpoint(handler->indexOf(m_data)); + handler->saveBreakpoints(); + handler->updateMarkers(); + } + + void updateLineNumber(int lineNumber) + { + if (!m_data) + return; + //if (m_data->markerLineNumber == lineNumber) + // return; + if (m_data->markerLineNumber != lineNumber) { + m_data->markerLineNumber = lineNumber; + // FIXME: should we tell gdb about the change? + // Ignore it for now, as we would require re-compilation + // and debugger re-start anyway. + if (0 && !m_data->bpLineNumber.isEmpty()) { + if (!m_data->bpNumber.trimmed().isEmpty()) { + m_data->pending = true; + } + } + } + m_data->lineNumber = QString::number(lineNumber); + m_data->handler()->updateMarkers(); + } + +private: + BreakpointData *m_data; + bool m_pending; +}; + +} // namespace Internal +} // namespace Debugger + + + +////////////////////////////////////////////////////////////////// +// +// BreakpointData +// +////////////////////////////////////////////////////////////////// + +BreakpointData::BreakpointData(BreakHandler *handler) +{ + //qDebug() << "CREATE BREAKPOINTDATA" << this; + m_handler = handler; + pending = true; + marker = 0; + markerLineNumber = 0; + bpMultiple = false; +} + +BreakpointData::~BreakpointData() +{ + removeMarker(); + //qDebug() << "DESTROY BREAKPOINTDATA" << this; +} + +void BreakpointData::removeMarker() +{ + BreakpointMarker *m = marker; + marker = 0; + delete m; +} + +void BreakpointData::updateMarker() +{ + if (marker && (markerFileName != marker->fileName() + || markerLineNumber != marker->lineNumber())) + removeMarker(); + + if (!marker && !markerFileName.isEmpty() && markerLineNumber > 0) + marker = new BreakpointMarker(this, markerFileName, markerLineNumber); + + if (marker) + marker->setPending(pending); +} + +QString BreakpointData::toToolTip() const +{ + QString str; + str += "<table>"; + str += "<tr><td>Marker File:</td><td>" + markerFileName + "</td></tr>"; + str += "<tr><td>Marker Line:</td><td>" + QString::number(markerLineNumber) + "</td></tr>"; + str += "<tr><td>BP Number:</td><td>" + bpNumber + "</td></tr>"; + str += "<tr><td>BP Address:</td><td>" + bpAddress + "</td></tr>"; + str += "<tr><td>----------</td><td></td><td></td></tr>"; + str += "<tr><td>Property:</td><td>Wanted:</td><td>Actual:</td></tr>"; + str += "<tr><td></td><td></td><td></td></tr>"; + str += "<tr><td>Internal Number:</td><td>-</td><td>" + bpNumber + "</td></tr>"; + str += "<tr><td>File Name:</td><td>" + fileName + "</td><td>" + bpFileName + "</td></tr>"; + str += "<tr><td>Function Name:</td><td>" + funcName + "</td><td>" + bpFuncName + "</td></tr>"; + str += "<tr><td>Line Number:</td><td>" + lineNumber + "</td><td>" + bpLineNumber + "</td></tr>"; + str += "<tr><td>Condition:</td><td>" + condition + "</td><td>" + bpCondition + "</td></tr>"; + str += "<tr><td>Ignore count:</td><td>" + ignoreCount + "</td><td>" + bpIgnoreCount + "</td></tr>"; + str += "</table>"; + return str; +} + +bool BreakpointData::isLocatedAt(const QString &fileName_, int lineNumber_) const +{ + /* + if (lineNumber != QString::number(lineNumber_)) + return false; + if (fileName == fileName_) + return true; + if (fileName_.endsWith(fileName)) + return true; + return false; + */ + return lineNumber_ == markerLineNumber && fileName_ == markerFileName; +} + +bool BreakpointData::conditionsMatch() const +{ + // same versions of gdb "beautify" the passed condition + QString s1 = condition; + s1.remove(QChar(' ')); + QString s2 = bpCondition; + s2.remove(QChar(' ')); + return s1 == s2; +} + + +////////////////////////////////////////////////////////////////// +// +// BreakHandler +// +////////////////////////////////////////////////////////////////// + +BreakHandler::BreakHandler(QObject *parent) + : QAbstractItemModel(parent) +{ +} + +int BreakHandler::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : 6; +} + +int BreakHandler::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : size(); +} + +void BreakHandler::removeAt(int index) +{ + BreakpointData *data = at(index); + m_bp.removeAt(index); + delete data; +} + +void BreakHandler::clear() +{ + for (int index = size(); --index >= 0; ) + removeAt(index); +} + +int BreakHandler::findBreakpoint(const BreakpointData &needle) +{ + // looks for a breakpoint we might refer to + for (int index = 0; index != size(); ++index) { + const BreakpointData *data = at(index); + // clear hit. + if (data->bpNumber == needle.bpNumber) + return index; + // at least at a position we were looking for + // FIXME: breaks multiple breakpoints at the same location + if (data->fileName == needle.bpFileName + && data->lineNumber == needle.bpLineNumber) + return index; + } + return -1; +} + +int BreakHandler::findBreakpoint(int bpNumber) +{ + for (int index = 0; index != size(); ++index) + if (at(index)->bpNumber == QString::number(bpNumber)) + return index; + return -1; +} + +void BreakHandler::saveBreakpoints() +{ + QList<QVariant> list; + for (int index = 0; index != size(); ++index) { + const BreakpointData *data = at(index); + QMap<QString, QVariant> map; + if (!data->fileName.isEmpty()) + map["filename"] = data->fileName; + if (!data->lineNumber.isEmpty()) + map["linenumber"] = data->lineNumber; + if (!data->funcName.isEmpty()) + map["funcname"] = data->funcName; + if (!data->condition.isEmpty()) + map["condition"] = data->condition; + if (!data->ignoreCount.isEmpty()) + map["ignorecount"] = data->ignoreCount; + list.append(map); + } + setSessionValueRequested("Breakpoints", list); +} + +void BreakHandler::loadBreakpoints() +{ + QVariant value; + sessionValueRequested("Breakpoints", &value); + QList<QVariant> list = value.toList(); + + clear(); + foreach (const QVariant &var, list) { + const QMap<QString, QVariant> map = var.toMap(); + BreakpointData *data = new BreakpointData(this); + data->fileName = map["filename"].toString(); + data->lineNumber = map["linenumber"].toString(); + data->condition = map["condition"].toString(); + data->ignoreCount = map["ignorecount"].toString(); + data->funcName = map["funcname"].toString(); + data->markerFileName = data->fileName; + data->markerLineNumber = data->lineNumber.toInt(); + append(data); + } +} + +void BreakHandler::resetBreakpoints() +{ + for (int index = size(); --index >= 0;) { + BreakpointData *data = at(index); + data->pending = true; + data->bpNumber.clear(); + data->bpFuncName.clear(); + data->bpFileName.clear(); + data->bpLineNumber.clear(); + data->bpCondition.clear(); + data->bpIgnoreCount.clear(); + // keep marker data if it was primary + if (data->markerFileName != data->fileName) + data->markerFileName.clear(); + if (data->markerLineNumber != data->lineNumber.toInt()) + data->markerLineNumber = 0; + } +} + +void BreakHandler::updateMarkers() +{ + for (int index = 0; index != size(); ++index) + at(index)->updateMarker(); + emit layoutChanged(); +} + +QVariant BreakHandler::headerData(int section, + Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + static QString headers[] = { + tr("Number"), tr("Function"), tr("File"), tr("Line"), + tr("Condition"), tr("Ignore") + }; + return headers[section]; + } + return QVariant(); +} + +QVariant BreakHandler::data(const QModelIndex &mi, int role) const +{ + static const QIcon icon(":/gdbdebugger/images/breakpoint.svg"); + static const QIcon icon2(":/gdbdebugger/images/breakpoint_pending.svg"); + static const QString empty = QString(QLatin1Char('-')); + + QWB_ASSERT(mi.isValid(), return QVariant()); + + if (mi.row() >= size()) + return QVariant(); + + const BreakpointData *data = at(mi.row()); + switch (mi.column()) { + case 0: + if (role == Qt::DisplayRole) { + QString str = data->bpNumber; + return str.isEmpty() ? empty : str; + } + if (role == Qt::DecorationRole) + return data->pending ? icon2 : icon; + break; + case 1: + if (role == Qt::DisplayRole) { + QString str = data->pending ? data->funcName : data->bpFuncName; + return str.isEmpty() ? empty : str; + } + break; + case 2: + if (role == Qt::DisplayRole) { + QString str = data->pending ? data->fileName : data->bpFileName; + str = QFileInfo(str).fileName(); + //if (data->bpMultiple && str.isEmpty() && !data->markerFileName.isEmpty()) + // str = data->markerFileName; + return str.isEmpty() ? empty : str; + } + break; + case 3: + if (role == Qt::DisplayRole) { + QString str = data->pending ? data->lineNumber : data->bpLineNumber; + //if (data->bpMultiple && str.isEmpty() && !data->markerFileName.isEmpty()) + // str = data->markerLineNumber; + return str.isEmpty() ? empty : str; + } + break; + case 4: + if (role == Qt::DisplayRole) + return data->pending ? data->condition : data->bpCondition; + if (role == Qt::ToolTipRole) + return tr("Breakpoint will only be hit if this condition is met."); + break; + case 5: + if (role == Qt::DisplayRole) + return data->pending ? data->ignoreCount : data->bpIgnoreCount; + if (role == Qt::ToolTipRole) + return tr("Breakpoint will only be hit after being ignored so many times."); + break; + } + if (role == Qt::ToolTipRole) + return data->toToolTip(); + return QVariant(); +} + +bool BreakHandler::setData(const QModelIndex &mi, const QVariant &value, int role) +{ + if (role != Qt::EditRole) + return false; + + BreakpointData *data = at(mi.row()); + switch (mi.column()) { + case 4: { + QString val = value.toString(); + if (val != data->condition) { + data->condition = val; + dataChanged(mi, mi); + } + return true; + } + case 5: { + QString val = value.toString(); + if (val != data->ignoreCount) { + data->ignoreCount = val; + dataChanged(mi, mi); + } + return true; + } + default: { + return false; + } + } +} + +QList<BreakpointData *> BreakHandler::takeRemovedBreakpoints() +{ + QList<BreakpointData *> result = m_removed; + m_removed.clear(); + return result; +} + +void BreakHandler::removeBreakpointHelper(int index) +{ + BreakpointData *data = m_bp.at(index); + m_bp.removeAt(index); + data->removeMarker(); + m_removed.append(data); +} + + +void BreakHandler::removeBreakpoint(int index) +{ + if (index < 0 || index >= size()) + return; + BreakHandler::removeBreakpointHelper(index); + emit layoutChanged(); + saveBreakpoints(); +} + + +int BreakHandler::indexOf(const QString &fileName, int lineNumber) +{ + for (int index = 0; index != size(); ++index) + if (at(index)->isLocatedAt(fileName, lineNumber)) + return index; + return -1; +} + +void BreakHandler::setBreakpoint(const QString &fileName, int lineNumber) +{ + QFileInfo fi(fileName); + + BreakpointData *data = new BreakpointData(this); + data->fileName = fileName; + data->lineNumber = QString::number(lineNumber); + data->pending = true; + data->markerFileName = fileName; + data->markerLineNumber = lineNumber; + append(data); + emit layoutChanged(); + saveBreakpoints(); + updateMarkers(); +} + +void BreakHandler::removeAllBreakpoints() +{ + for (int index = size(); --index >= 0;) + removeBreakpointHelper(index); + emit layoutChanged(); + saveBreakpoints(); + updateMarkers(); +} + +void BreakHandler::setAllPending() +{ + loadBreakpoints(); + for (int index = size(); --index >= 0;) + at(index)->pending = true; + saveBreakpoints(); + updateMarkers(); +} + +void BreakHandler::saveSessionData() +{ + saveBreakpoints(); + updateMarkers(); +} + +void BreakHandler::loadSessionData() +{ + //resetBreakpoints(); + loadBreakpoints(); + updateMarkers(); +} + +void BreakHandler::activateBreakPoint(int index) +{ + const BreakpointData *data = at(index); + //qDebug() << "BREAKPOINT ACTIVATED: " << data->fileName; + if (!data->markerFileName.isEmpty()) + emit gotoLocation(data->markerFileName, data->markerLineNumber, false); +} + +void BreakHandler::breakByFunction(const QString &functionName) +{ + // One per function is enough for now + for (int index = size(); --index >= 0;) { + const BreakpointData *data = at(index); + QWB_ASSERT(data, break); + if (data->funcName == functionName && data->condition.isEmpty() + && data->ignoreCount.isEmpty()) + return; + } + BreakpointData *data = new BreakpointData(this); + data->funcName = functionName; + append(data); + saveBreakpoints(); + updateMarkers(); +} + +#include "breakhandler.moc" diff --git a/src/plugins/debugger/breakhandler.h b/src/plugins/debugger/breakhandler.h new file mode 100644 index 00000000000..8ce63272b7a --- /dev/null +++ b/src/plugins/debugger/breakhandler.h @@ -0,0 +1,174 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_BREAKHANDLER_H +#define DEBUGGER_BREAKHANDLER_H + +#include <QtCore/QObject> +#include <QtCore/QAbstractItemModel> + +namespace Debugger { +namespace Internal { + +class BreakpointMarker; +class BreakHandler; + +////////////////////////////////////////////////////////////////// +// +// BreakpointData +// +////////////////////////////////////////////////////////////////// + +class BreakpointData +{ +public: + explicit BreakpointData(BreakHandler *handler); + ~BreakpointData(); + + void removeMarker(); + void updateMarker(); + QString toToolTip() const; + BreakHandler *handler() { return m_handler; } + + bool isLocatedAt(const QString &fileName, int lineNumber) const; + bool conditionsMatch() const; + +private: + // Intentionally unimplemented. + // Making it copiable is tricky because of the markers. + void operator=(const BreakpointData &); + BreakpointData(const BreakpointData &); + + // Our owner + BreakHandler *m_handler; // not owned. + +public: + bool pending; // does the debugger engine know about us already? + + // this "user requested information". will get stored in the session + QString fileName; // short name of source file + QString condition; // condition associated with breakpoint + QString ignoreCount; // ignore count associated with breakpoint + QString lineNumber; // line in source file + QString funcName; // name of containing function + + // this is what gdb produced in response + QString bpNumber; // breakpoint number assigned by the debugger engine + QString bpCondition; // condition acknowledged by the debugger engine + QString bpIgnoreCount; // ignore count acknowledged by the debugger engine + QString bpFileName; // file name acknowledged by the debugger engine + QString bpLineNumber; // line number acknowledged by the debugger engine + QString bpFuncName; // function name acknowledged by the debugger engine + QString bpAddress; // address acknowledged by the debugger engine + bool bpMultiple; // happens in constructors/gdb + + // taken from either user input or gdb responses + QString markerFileName; // used to locate the marker + int markerLineNumber; + + // our red blob in the editor + BreakpointMarker *marker; +}; + + +////////////////////////////////////////////////////////////////// +// +// BreakHandler +// +////////////////////////////////////////////////////////////////// + +class BreakHandler : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit BreakHandler(QObject *parent = 0); + + void removeAllBreakpoints(); + void setAllPending(); + void loadSessionData(); + void saveSessionData(); + + QAbstractItemModel *model() { return this; } + + BreakpointData *at(int index) const { return index < size() ? m_bp.at(index) : 0; } + int size() const { return m_bp.size(); } + void append(BreakpointData *data) { m_bp.append(data); } + void removeAt(int index); // also deletes the marker + void clear(); // also deletes all the marker + int indexOf(BreakpointData *data) { return m_bp.indexOf(data); } + int indexOf(const QString &fileName, int lineNumber); + int findBreakpoint(const BreakpointData &data); // returns index + int findBreakpoint(int bpNumber); // returns index + void updateMarkers(); + + QList<BreakpointData *> takeRemovedBreakpoints(); + +public slots: + void setBreakpoint(const QString &fileName, int lineNumber); + void breakByFunction(const QString &functionName); + void activateBreakPoint(int index); + void removeBreakpoint(int index); + +signals: + void gotoLocation(const QString &fileName, int lineNumber, bool setMarker); + + void sessionValueRequested(const QString &name, QVariant *value); + void setSessionValueRequested(const QString &name, const QVariant &value); + +private: + friend class BreakpointMarker; + + // QAbstractItemModel + int columnCount(const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &, int role); + QModelIndex parent(const QModelIndex &) const { return QModelIndex(); } + QModelIndex index(int row, int column, const QModelIndex &) const + { return createIndex(row, column); } + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + + void markerUpdated(BreakpointMarker *, int lineNumber); + void loadBreakpoints(); + void saveBreakpoints(); + void resetBreakpoints(); + void removeBreakpointHelper(int index); + + QList<BreakpointData *> m_bp; + QList<BreakpointData *> m_removed; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_BREAKHANDLER_H diff --git a/src/plugins/debugger/breakwindow.cpp b/src/plugins/debugger/breakwindow.cpp new file mode 100644 index 00000000000..11248f9a588 --- /dev/null +++ b/src/plugins/debugger/breakwindow.cpp @@ -0,0 +1,164 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "breakwindow.h" + +#include "ui_breakcondition.h" + +#include <QAction> +#include <QDir> +#include <QFileInfo> +#include <QFileInfoList> +#include <QHeaderView> +#include <QKeyEvent> +#include <QMenu> +#include <QResizeEvent> +#include <QToolButton> +#include <QTreeView> + +using Debugger::Internal::BreakWindow; + + +BreakWindow::BreakWindow(QWidget *parent) + : QTreeView(parent), m_alwaysResizeColumnsToContents(false) +{ + setWindowTitle(tr("Breakpoints")); + setWindowIcon(QIcon(":/gdbdebugger/images/debugger_breakpoints.png")); + setAlternatingRowColors(true); + setRootIsDecorated(false); + setIconSize(QSize(10, 10)); + + connect(this, SIGNAL(activated(QModelIndex)), + this, SLOT(rowActivated(QModelIndex))); +} + +void BreakWindow::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Delete) + deleteBreakpoint(currentIndex()); + QTreeView::keyPressEvent(event); +} + +void BreakWindow::resizeEvent(QResizeEvent *event) +{ + QHeaderView *hv = header(); + int totalSize = event->size().width() - 180; + hv->resizeSection(0, 60); + hv->resizeSection(1, (totalSize * 30) / 100); + hv->resizeSection(2, (totalSize * 30) / 100); + hv->resizeSection(3, (totalSize * 30) / 100); + hv->resizeSection(4, 70); + hv->resizeSection(5, 50); + QTreeView::resizeEvent(event); +} + +void BreakWindow::contextMenuEvent(QContextMenuEvent *ev) +{ + QMenu menu; + QModelIndex index = indexAt(ev->pos()); + QAction *act0 = new QAction("Delete breakpoint", &menu); + QAction *act1 = new QAction("Adjust column widths to contents", &menu); + QAction *act2 = new QAction("Always adjust column widths to contents", &menu); + QAction *act3 = new QAction("Edit condition...", &menu); + act2->setCheckable(true); + act2->setChecked(m_alwaysResizeColumnsToContents); + if (index.isValid()) { + menu.addAction(act0); + menu.addAction(act3); + menu.addSeparator(); + } + menu.addAction(act1); + menu.addAction(act2); + + QAction *act = menu.exec(ev->globalPos()); + + if (act == act0) + deleteBreakpoint(index); + else if (act == act1) + resizeColumnsToContents(); + else if (act == act2) + setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents); + else if (act == act3) + editCondition(index); +} + +void BreakWindow::deleteBreakpoint(const QModelIndex &idx) +{ + int row = idx.row(); + if (row == model()->rowCount() - 1) + --row; + setCurrentIndex(idx.sibling(row, 0)); + emit breakPointDeleted(idx.row()); +} + +void BreakWindow::editCondition(const QModelIndex &idx) +{ + QDialog dlg(this); + Ui::BreakCondition ui; + ui.setupUi(&dlg); + + int row = idx.row(); + dlg.setWindowTitle(tr("Conditions on Breakpoint %1").arg(row)); + ui.lineEditCondition->setText(model()->data(idx.sibling(row, 4)).toString()); + ui.spinBoxIgnoreCount->setValue(model()->data(idx.sibling(row, 5)).toInt()); + + if (dlg.exec() == QDialog::Rejected) + return; + + model()->setData(idx.sibling(row, 4), ui.lineEditCondition->text()); + model()->setData(idx.sibling(row, 5), ui.spinBoxIgnoreCount->value()); +} + +void BreakWindow::resizeColumnsToContents() +{ + resizeColumnToContents(0); + resizeColumnToContents(1); + resizeColumnToContents(2); + resizeColumnToContents(3); +} + +void BreakWindow::setAlwaysResizeColumnsToContents(bool on) +{ + m_alwaysResizeColumnsToContents = on; + QHeaderView::ResizeMode mode = on + ? QHeaderView::ResizeToContents : QHeaderView::Interactive; + header()->setResizeMode(0, mode); + header()->setResizeMode(1, mode); + header()->setResizeMode(2, mode); + header()->setResizeMode(3, mode); +} + +void BreakWindow::rowActivated(const QModelIndex &index) +{ + emit breakPointActivated(index.row()); +} + diff --git a/src/plugins/debugger/breakwindow.h b/src/plugins/debugger/breakwindow.h new file mode 100644 index 00000000000..8b17b551451 --- /dev/null +++ b/src/plugins/debugger/breakwindow.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_BREAKWINDOW_H +#define DEBUGGER_BREAKWINDOW_H + +#include <QTreeView> + +namespace Debugger { +namespace Internal { + +class BreakWindow : public QTreeView +{ + Q_OBJECT + +public: + BreakWindow(QWidget *parent = 0); + +public slots: + void resizeColumnsToContents(); + void setAlwaysResizeColumnsToContents(bool on); + +signals: + void breakPointDeleted(int index); + void breakPointActivated(int index); + +private slots: + void rowActivated(const QModelIndex &index); + +protected: + void resizeEvent(QResizeEvent *ev); + void contextMenuEvent(QContextMenuEvent *ev); + void keyPressEvent(QKeyEvent *ev); + +private: + void deleteBreakpoint(const QModelIndex &idx); + void editCondition(const QModelIndex &idx); + + bool m_alwaysResizeColumnsToContents; +}; + + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_BREAKWINDOW + diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro new file mode 100644 index 00000000000..d21eb7cdc01 --- /dev/null +++ b/src/plugins/debugger/debugger.pro @@ -0,0 +1,92 @@ +TEMPLATE = lib +TARGET = Debugger + +# CONFIG += single +include(../../qworkbenchplugin.pri) +include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/find/find.pri) +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/texteditor/texteditor.pri) +include(../../plugins/cpptools/cpptools.pri) +include(../../libs/cplusplus/cplusplus.pri) + +# DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII +QT += gui network script + +HEADERS += assert.h \ + attachexternaldialog.h \ + attachremotedialog.h \ + breakhandler.h \ + breakwindow.h \ + debuggerconstants.h \ + debuggermanager.h \ + debuggeroutputwindow.h \ + debuggerplugin.h \ + debuggerrunner.h \ + mode.h \ + disassemblerhandler.h \ + disassemblerwindow.h \ + gdbengine.h \ + gdbmi.h \ + gdboptionpage.h \ + idebuggerengine.h \ + imports.h \ + moduleshandler.h \ + moduleswindow.h \ + procinterrupt.h \ + registerhandler.h \ + registerwindow.h \ + scriptengine.h \ + stackhandler.h \ + stackwindow.h \ + startexternaldialog.h \ + threadswindow.h \ + watchhandler.h \ + watchwindow.h + +SOURCES += attachexternaldialog.cpp \ + attachremotedialog.cpp \ + breakhandler.cpp \ + breakwindow.cpp \ + breakwindow.h \ + debuggermanager.cpp \ + debuggeroutputwindow.cpp \ + debuggerplugin.cpp \ + debuggerrunner.cpp \ + mode.cpp \ + disassemblerhandler.cpp \ + disassemblerwindow.cpp \ + gdbengine.cpp \ + gdbmi.cpp \ + gdboptionpage.cpp \ + gdbtypemacros.cpp \ + gdbengine.h \ + moduleshandler.cpp \ + moduleswindow.cpp \ + procinterrupt.cpp \ + registerhandler.cpp \ + registerwindow.cpp \ + scriptengine.cpp \ + stackhandler.cpp \ + stackwindow.cpp \ + startexternaldialog.cpp \ + threadswindow.cpp \ + watchhandler.cpp \ + watchwindow.cpp + +FORMS += attachexternaldialog.ui \ + attachremotedialog.ui \ + breakbyfunction.ui \ + breakcondition.ui \ + mode.ui \ + gdboptionpage.ui \ + gdbtypemacros.ui \ + startexternaldialog.ui \ + +RESOURCES += debugger.qrc + +false { +SOURCES += $$PWD/modeltest.cpp +HEADERS += $$PWD/modeltest.h +DEFINES += USE_MODEL_TEST=1 +} diff --git a/src/plugins/debugger/debugger.qrc b/src/plugins/debugger/debugger.qrc new file mode 100644 index 00000000000..548c27ac9f1 --- /dev/null +++ b/src/plugins/debugger/debugger.qrc @@ -0,0 +1,24 @@ +<RCC> + <qresource prefix="/gdbdebugger" > + <file>images/breakpoint.svg</file> + <file>images/breakpoint_pending.svg</file> + <file>images/debugger_breakpoints.png</file> + <file>images/debugger_continue_small.png</file> + <file>images/debugger_interrupt_small.png</file> + <file>images/debugger_start.png</file> + <file>images/debugger_start_small.png</file> + <file>images/debugger_stepinto_small.png</file> + <file>images/debugger_stepout_small.png</file> + <file>images/debugger_stepover_small.png</file> + <file>images/debugger_steponeproc_small.png</file> + <file>images/debugger_stepoverproc_small.png</file> + <file>images/debugger_stop_small.png</file> + <file>images/delete.png</file> + <file>images/done.png</file> + <file>images/empty.svg</file> + <file>images/error.png</file> + <file>images/location.svg</file> + <file>images/newitem.png</file> + <file>images/running.png</file> + </qresource> +</RCC> diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h new file mode 100644 index 00000000000..56d790e0740 --- /dev/null +++ b/src/plugins/debugger/debuggerconstants.h @@ -0,0 +1,65 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGERCONSTANTS_H +#define DEBUGGERCONSTANTS_H + +namespace Debugger { +namespace Constants { + +// modes and their priorities +const char * const MODE_DEBUG = "Debugger.Mode.Debug"; +const int P_MODE_DEBUG = 85; + +// common actions +const char * const INTERRUPT = "Debugger.Interrupt"; +const char * const RESET = "Debugger.Reset"; +const char * const STEP = "Debugger.StepLine"; +const char * const STEPOUT = "Debugger.StepOut"; +const char * const NEXT = "Debugger.NextLine"; +const char * const STEPI = "Debugger.StepInstruction"; +const char * const NEXTI = "Debugger.NextInstruction"; + +const char * const M_VIEW_DEBUG = "Debugger.Menu.View.Debug"; +const char * const G_DEBUG = "Debugger.Group.Debug"; +const char * const G_VIEW_DEBUG = "Debugger.Group.View.Debug"; + +const char * const C_GDBDEBUGGER = "Gdb Debugger"; +const char * const GDBRUNNING = "Gdb.Running"; + +const char * const PROPERTY_REGISTER_FORMAT = "Debugger.Property.RegisterFormat"; + +} // namespace Constants +} // namespace Debugger + +#endif // DEBUGGERCONSTANTS_H + diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp new file mode 100644 index 00000000000..bcf314ae411 --- /dev/null +++ b/src/plugins/debugger/debuggermanager.cpp @@ -0,0 +1,1298 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "debuggermanager.h" + +#include "assert.h" +#include "debuggerconstants.h" +#include "idebuggerengine.h" + +#include "breakwindow.h" +#include "disassemblerwindow.h" +#include "debuggeroutputwindow.h" +#include "moduleswindow.h" +#include "registerwindow.h" +#include "stackwindow.h" +#include "threadswindow.h" +#include "watchwindow.h" + +#include "ui_breakbyfunction.h" + +#include "disassemblerhandler.h" +#include "breakhandler.h" +#include "moduleshandler.h" +#include "registerhandler.h" +#include "stackhandler.h" +#include "watchhandler.h" + +#include "startexternaldialog.h" +#include "attachexternaldialog.h" + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QTime> + +#include <QtGui/QAction> +#include <QtGui/QComboBox> +#include <QtGui/QDockWidget> +#include <QtGui/QErrorMessage> +#include <QtGui/QFileDialog> +#include <QtGui/QLabel> +#include <QtGui/QMainWindow> +#include <QtGui/QMessageBox> +#include <QtGui/QPlainTextEdit> +#include <QtGui/QStatusBar> +#include <QtGui/QTextBlock> +#include <QtGui/QTextCursor> +#include <QtGui/QToolBar> +#include <QtGui/QToolButton> +#include <QtGui/QToolTip> + +using namespace Debugger; +using namespace Debugger::Internal; +using namespace Debugger::Constants; + +static const QString tooltipIName = "tooltip"; + +/////////////////////////////////////////////////////////////////////// +// +// BreakByFunctionDialog +// +/////////////////////////////////////////////////////////////////////// + +class BreakByFunctionDialog : public QDialog, Ui::BreakByFunctionDialog +{ + Q_OBJECT + +public: + explicit BreakByFunctionDialog(QWidget *parent) + : QDialog(parent) + { + setupUi(this); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + } + QString functionName() const { return functionLineEdit->text(); } +}; + + +/////////////////////////////////////////////////////////////////////// +// +// DebuggerManager +// +/////////////////////////////////////////////////////////////////////// + +static IDebuggerEngine *gdbEngine = 0; +static IDebuggerEngine *winEngine = 0; +static IDebuggerEngine *scriptEngine = 0; + +extern IDebuggerEngine *createGdbEngine(DebuggerManager *parent); +extern IDebuggerEngine *createWinEngine(DebuggerManager *) { return 0; } +extern IDebuggerEngine *createScriptEngine(DebuggerManager *parent); + +DebuggerManager::DebuggerManager() +{ + init(); +} + +DebuggerManager::~DebuggerManager() +{ + delete gdbEngine; + delete winEngine; + delete scriptEngine; +} + +void DebuggerManager::init() +{ + m_status = -1; + m_busy = false; + + m_attachedPID = 0; + m_startMode = startInternal; + + m_disassemblerHandler = 0; + m_modulesHandler = 0; + m_registerHandler = 0; + + m_breakWindow = new BreakWindow; + m_disassemblerWindow = new DisassemblerWindow; + m_modulesWindow = new ModulesWindow; + m_outputWindow = new DebuggerOutputWindow; + m_registerWindow = new RegisterWindow; + m_stackWindow = new StackWindow; + m_threadsWindow = new ThreadsWindow; + m_localsWindow = new WatchWindow(WatchWindow::LocalsType); + m_watchersWindow = new WatchWindow(WatchWindow::WatchersType); + //m_tooltipWindow = new WatchWindow(WatchWindow::TooltipType); + //m_watchersWindow = new QTreeView; + m_tooltipWindow = new QTreeView; + + m_mainWindow = new QMainWindow; + m_mainWindow->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); + m_mainWindow->setDocumentMode(true); + + // Stack + m_stackHandler = new StackHandler; + QAbstractItemView *stackView = + qobject_cast<QAbstractItemView *>(m_stackWindow); + stackView->setModel(m_stackHandler->stackModel()); + connect(stackView, SIGNAL(frameActivated(int)), + this, SLOT(activateFrame(int))); + + // Threads + m_threadsHandler = new ThreadsHandler; + QAbstractItemView *threadsView = + qobject_cast<QAbstractItemView *>(m_threadsWindow); + threadsView->setModel(m_threadsHandler->threadsModel()); + connect(threadsView, SIGNAL(threadSelected(int)), + this, SLOT(selectThread(int))); + + // Disassembler + m_disassemblerHandler = new DisassemblerHandler; + QAbstractItemView *disassemblerView = + qobject_cast<QAbstractItemView *>(m_disassemblerWindow); + disassemblerView->setModel(m_disassemblerHandler->model()); + + // Breakpoints + m_breakHandler = new BreakHandler; + QAbstractItemView *breakView = + qobject_cast<QAbstractItemView *>(m_breakWindow); + breakView->setModel(m_breakHandler->model()); + connect(breakView, SIGNAL(breakPointActivated(int)), + m_breakHandler, SLOT(activateBreakPoint(int))); + connect(breakView, SIGNAL(breakPointDeleted(int)), + m_breakHandler, SLOT(removeBreakpoint(int))); + connect(m_breakHandler, SIGNAL(gotoLocation(QString,int,bool)), + this, SLOT(gotoLocation(QString,int,bool))); + connect(m_breakHandler, SIGNAL(sessionValueRequested(QString,QVariant*)), + this, SIGNAL(sessionValueRequested(QString,QVariant*))); + connect(m_breakHandler, SIGNAL(setSessionValueRequested(QString,QVariant)), + this, SIGNAL(setSessionValueRequested(QString,QVariant))); + + // Modules + QAbstractItemView *modulesView = + qobject_cast<QAbstractItemView *>(m_modulesWindow); + m_modulesHandler = new ModulesHandler; + modulesView->setModel(m_modulesHandler->model()); + connect(modulesView, SIGNAL(reloadModulesRequested()), + this, SLOT(reloadModules())); + connect(modulesView, SIGNAL(loadSymbolsRequested(QString)), + this, SLOT(loadSymbols(QString))); + connect(modulesView, SIGNAL(loadAllSymbolsRequested()), + this, SLOT(loadAllSymbols())); + + + // Registers + QAbstractItemView *registerView = + qobject_cast<QAbstractItemView *>(m_registerWindow); + m_registerHandler = new RegisterHandler; + registerView->setModel(m_registerHandler->model()); + + + m_watchHandler = new WatchHandler; + + // Locals + QTreeView *localsView = qobject_cast<QTreeView *>(m_localsWindow); + localsView->setModel(m_watchHandler->model()); + connect(localsView, SIGNAL(requestExpandChildren(QModelIndex)), + this, SLOT(expandChildren(QModelIndex))); + connect(localsView, SIGNAL(requestCollapseChildren(QModelIndex)), + this, SLOT(collapseChildren(QModelIndex))); + connect(localsView, SIGNAL(requestAssignValue(QString,QString)), + this, SLOT(assignValueInDebugger(QString,QString))); + connect(localsView, SIGNAL(requestWatchExpression(QString)), + this, SLOT(watchExpression(QString))); + + // Watchers + QTreeView *watchersView = qobject_cast<QTreeView *>(m_watchersWindow); + watchersView->setModel(m_watchHandler->model()); + connect(watchersView, SIGNAL(requestAssignValue(QString,QString)), + this, SLOT(assignValueInDebugger(QString,QString))); + connect(watchersView, SIGNAL(requestExpandChildren(QModelIndex)), + this, SLOT(expandChildren(QModelIndex))); + connect(watchersView, SIGNAL(requestCollapseChildren(QModelIndex)), + this, SLOT(collapseChildren(QModelIndex))); + connect(watchersView, SIGNAL(requestWatchExpression(QString)), + this, SLOT(watchExpression(QString))); + connect(watchersView, SIGNAL(requestRemoveWatchExpression(QString)), + this, SLOT(removeWatchExpression(QString))); + + // Tooltip + QTreeView *tooltipView = qobject_cast<QTreeView *>(m_tooltipWindow); + tooltipView->setModel(m_watchHandler->model()); + + connect(m_watchHandler, SIGNAL(watchModelUpdateRequested()), + this, SLOT(updateWatchModel())); + + m_startExternalAction = new QAction(this); + m_startExternalAction->setText(tr("Start and Debug External Application...")); + + m_attachExternalAction = new QAction(this); + m_attachExternalAction->setText(tr("Attach to Running External Application...")); + + m_continueAction = new QAction(this); + m_continueAction->setText(tr("Continue")); + m_continueAction->setIcon(QIcon(":/gdbdebugger/images/debugger_continue_small.png")); + + m_stopAction = new QAction(this); + m_stopAction->setText(tr("Interrupt")); + m_stopAction->setIcon(QIcon(":/gdbdebugger/images/debugger_interrupt_small.png")); + + m_resetAction = new QAction(this); + m_resetAction->setText(tr("Reset Debugger")); + + m_nextAction = new QAction(this); + m_nextAction->setText(tr("Step Over")); + //m_nextAction->setShortcut(QKeySequence(tr("F6"))); + m_nextAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stepover_small.png")); + + m_stepAction = new QAction(this); + m_stepAction->setText(tr("Step Into")); + //m_stepAction->setShortcut(QKeySequence(tr("F7"))); + m_stepAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stepinto_small.png")); + + m_nextIAction = new QAction(this); + m_nextIAction->setText(tr("Step Over Instruction")); + //m_nextIAction->setShortcut(QKeySequence(tr("Shift+F6"))); + m_nextIAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stepoverproc_small.png")); + + m_stepIAction = new QAction(this); + m_stepIAction->setText(tr("Step One Instruction")); + //m_stepIAction->setShortcut(QKeySequence(tr("Shift+F9"))); + m_stepIAction->setIcon(QIcon(":/gdbdebugger/images/debugger_steponeproc_small.png")); + + m_stepOutAction = new QAction(this); + m_stepOutAction->setText(tr("Step Out")); + //m_stepOutAction->setShortcut(QKeySequence(tr("Shift+F7"))); + m_stepOutAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stepout_small.png")); + + m_runToLineAction = new QAction(this); + m_runToLineAction->setText(tr("Run to Line")); + + m_runToFunctionAction = new QAction(this); + m_runToFunctionAction->setText(tr("Run to Outermost Function")); + + m_jumpToLineAction = new QAction(this); + m_jumpToLineAction->setText(tr("Jump to Line")); + + m_breakAction = new QAction(this); + m_breakAction->setText(tr("Toggle Breakpoint")); + + m_breakByFunctionAction = new QAction(this); + m_breakByFunctionAction->setText(tr("Set Breakpoint at Function...")); + + m_breakAtMainAction = new QAction(this); + m_breakAtMainAction->setText(tr("Set Breakpoint at Function 'main'")); + + m_debugDumpersAction = new QAction(this); + m_debugDumpersAction->setText(tr("Debug Custom Dumpers")); + m_debugDumpersAction->setCheckable(true); + + m_skipKnownFramesAction = new QAction(this); + m_skipKnownFramesAction->setText(tr("Skip Known Frames When Stepping")); + m_skipKnownFramesAction->setCheckable(true); + + m_useCustomDumpersAction = new QAction(this); + m_useCustomDumpersAction->setText(tr("Use Custom Display for Qt Objects")); + m_useCustomDumpersAction->setToolTip(tr("Checking this will make the debugger " + "try to use code to format certain data (QObject, QString, ...) nicely. ")); + m_useCustomDumpersAction->setCheckable(true); + m_useCustomDumpersAction->setChecked(true); + + m_useCustomDumpersAction = new QAction(this); + m_useCustomDumpersAction->setText(tr("Use Custom Display for Qt Objects")); + m_useCustomDumpersAction->setToolTip(tr("Checking this will make the debugger " + "try to use code to format certain data (QObject, QString, ...) nicely. ")); + m_useCustomDumpersAction->setCheckable(true); + m_useCustomDumpersAction->setChecked(true); + + m_useFastStartAction = new QAction(this); + m_useFastStartAction->setText(tr("Fast Debugger Start")); + m_useFastStartAction->setToolTip(tr("Checking this will make the debugger " + "start fast by loading only very few debug symbols on start up. This " + "might lead to situations where breakpoints can not be set properly. " + "So uncheck this option if you experience breakpoint related problems.")); + m_useFastStartAction->setCheckable(true); + m_useFastStartAction->setChecked(true); + + // FIXME + m_useFastStartAction->setChecked(false); + m_useFastStartAction->setEnabled(false); + + m_dumpLogAction = new QAction(this); + m_dumpLogAction->setText(tr("Dump Log File for Debugging Purposes")); + + m_watchAction = new QAction(this); + m_watchAction->setText(tr("Add to Watch Window")); + + // For usuage hints oin focus{In,Out} + //connect(m_outputWindow, SIGNAL(statusMessageRequested(QString,int)), + // this, SLOT(showStatusMessage(QString,int))); + + connect(m_continueAction, SIGNAL(triggered()), + this, SLOT(continueExec())); + + connect(m_startExternalAction, SIGNAL(triggered()), + this, SLOT(startExternalApplication())); + connect(m_attachExternalAction, SIGNAL(triggered()), + this, SLOT(attachExternalApplication())); + + connect(m_stopAction, SIGNAL(triggered()), + this, SLOT(interruptDebuggingRequest())); + connect(m_resetAction, SIGNAL(triggered()), + this, SLOT(exitDebugger())); + connect(m_nextAction, SIGNAL(triggered()), + this, SLOT(nextExec())); + connect(m_stepAction, SIGNAL(triggered()), + this, SLOT(stepExec())); + connect(m_nextIAction, SIGNAL(triggered()), + this, SLOT(nextIExec())); + connect(m_stepIAction, SIGNAL(triggered()), + this, SLOT(stepIExec())); + connect(m_stepOutAction, SIGNAL(triggered()), + this, SLOT(stepOutExec())); + connect(m_runToLineAction, SIGNAL(triggered()), + this, SLOT(runToLineExec())); + connect(m_runToFunctionAction, SIGNAL(triggered()), + this, SLOT(runToFunctionExec())); + connect(m_jumpToLineAction, SIGNAL(triggered()), + this, SLOT(jumpToLineExec())); + connect(m_watchAction, SIGNAL(triggered()), + this, SLOT(addToWatchWindow())); + connect(m_breakAction, SIGNAL(triggered()), + this, SLOT(toggleBreakpoint())); + connect(m_breakByFunctionAction, SIGNAL(triggered()), + this, SLOT(breakByFunction())); + connect(m_breakAtMainAction, SIGNAL(triggered()), + this, SLOT(breakAtMain())); + + connect(m_useFastStartAction, SIGNAL(triggered()), + this, SLOT(saveSessionData())); + connect(m_useCustomDumpersAction, SIGNAL(triggered()), + this, SLOT(saveSessionData())); + connect(m_skipKnownFramesAction, SIGNAL(triggered()), + this, SLOT(saveSessionData())); + connect(m_dumpLogAction, SIGNAL(triggered()), + this, SLOT(dumpLog())); + + connect(m_outputWindow, SIGNAL(commandExecutionRequested(QString)), + this, SLOT(executeDebuggerCommand(QString))); + + + m_breakDock = createDockForWidget(m_breakWindow); + + m_disassemblerDock = createDockForWidget(m_disassemblerWindow); + connect(m_disassemblerDock->toggleViewAction(), SIGNAL(toggled(bool)), + this, SLOT(reloadDisassembler()), Qt::QueuedConnection); + + m_modulesDock = createDockForWidget(m_modulesWindow); + connect(m_modulesDock->toggleViewAction(), SIGNAL(toggled(bool)), + this, SLOT(reloadModules()), Qt::QueuedConnection); + + m_registerDock = createDockForWidget(m_registerWindow); + connect(m_registerDock->toggleViewAction(), SIGNAL(toggled(bool)), + this, SLOT(reloadRegisters()), Qt::QueuedConnection); + + m_outputDock = createDockForWidget(m_outputWindow); + + m_stackDock = createDockForWidget(m_stackWindow); + + m_threadsDock = createDockForWidget(m_threadsWindow); + + setStatus(DebuggerProcessNotReady); + gdbEngine = createGdbEngine(this); + winEngine = createWinEngine(this); + scriptEngine = createScriptEngine(this); + setDebuggerType(GdbDebugger); +} + +void DebuggerManager::setDebuggerType(DebuggerType type) +{ + switch (type) { + case GdbDebugger: + m_engine = gdbEngine; + break; + case ScriptDebugger: + m_engine = scriptEngine; + break; + case WinDebugger: + m_engine = winEngine; + break; + } +} + +IDebuggerEngine *DebuggerManager::engine() +{ + return m_engine; +} + +IDebuggerManagerAccessForEngines *DebuggerManager::engineInterface() +{ + return dynamic_cast<IDebuggerManagerAccessForEngines *>(this); +} + +IDebuggerManagerAccessForDebugMode *DebuggerManager::debugModeInterface() +{ + return dynamic_cast<IDebuggerManagerAccessForDebugMode *>(this); +} + +void DebuggerManager::createDockWidgets() +{ + QSplitter *localsAndWatchers = new QSplitter(Qt::Vertical, 0); + localsAndWatchers->setWindowTitle(m_localsWindow->windowTitle()); + localsAndWatchers->addWidget(m_localsWindow); + localsAndWatchers->addWidget(m_watchersWindow); + localsAndWatchers->setStretchFactor(0, 3); + localsAndWatchers->setStretchFactor(1, 1); + m_watchDock = createDockForWidget(localsAndWatchers); +} + +QDockWidget *DebuggerManager::createDockForWidget(QWidget *widget) +{ + QDockWidget *dockWidget = new QDockWidget(widget->windowTitle(), m_mainWindow); + dockWidget->setObjectName(widget->windowTitle()); + //dockWidget->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea); + dockWidget->setAllowedAreas(Qt::AllDockWidgetAreas); // that space is needed. + //dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures); + dockWidget->setFeatures(QDockWidget::AllDockWidgetFeatures); + dockWidget->setTitleBarWidget(new QWidget(dockWidget)); + dockWidget->setWidget(widget); + connect(dockWidget->toggleViewAction(), SIGNAL(toggled(bool)), + this, SLOT(dockToggled(bool)), Qt::QueuedConnection); + m_dockWidgets.append(dockWidget); + return dockWidget; +} + +void DebuggerManager::setSimpleDockWidgetArrangement() +{ + foreach (QDockWidget *dockWidget, m_dockWidgets) + m_mainWindow->removeDockWidget(dockWidget); + + foreach (QDockWidget *dockWidget, m_dockWidgets) { + m_mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dockWidget); + dockWidget->show(); + } + + m_mainWindow->tabifyDockWidget(m_watchDock, m_breakDock); + m_mainWindow->tabifyDockWidget(m_watchDock, m_disassemblerDock); + m_mainWindow->tabifyDockWidget(m_watchDock, m_modulesDock); + m_mainWindow->tabifyDockWidget(m_watchDock, m_outputDock); + m_mainWindow->tabifyDockWidget(m_watchDock, m_registerDock); + m_mainWindow->tabifyDockWidget(m_watchDock, m_threadsDock); + + // They are rarely used even in ordinary debugging. Hiding them also saves + // cycles since the corresponding information won't be retrieved. + m_registerDock->hide(); + m_disassemblerDock->hide(); + m_modulesDock->hide(); + m_outputDock->hide(); +} + +void DebuggerManager::setLocked(bool locked) +{ + const QDockWidget::DockWidgetFeatures features = + (locked) ? QDockWidget::NoDockWidgetFeatures : + QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable; + + foreach (QDockWidget *dockWidget, m_dockWidgets) { + QWidget *titleBarWidget = dockWidget->titleBarWidget(); + if (locked && !titleBarWidget) + titleBarWidget = new QWidget(dockWidget); + else if (!locked && titleBarWidget) { + delete titleBarWidget; + titleBarWidget = 0; + } + dockWidget->setTitleBarWidget(titleBarWidget); + dockWidget->setFeatures(features); + } +} + +void DebuggerManager::dockToggled(bool on) +{ + QDockWidget *dw = qobject_cast<QDockWidget *>(sender()->parent()); + if (on && dw) + dw->raise(); +} + +QAbstractItemModel *DebuggerManager::threadsModel() +{ + return qobject_cast<ThreadsWindow*>(m_threadsWindow)->model(); +} + +void DebuggerManager::showStatusMessage(const QString &msg, int timeout) +{ + Q_UNUSED(timeout) + //qDebug() << "STATUS: " << msg; + showDebuggerOutput("status:", msg); + mainWindow()->statusBar()->showMessage(msg, timeout); +#if 0 + QString currentTime = QTime::currentTime().toString("hh:mm:ss.zzz"); + + ICore *core = m_pm->getObject<Core::ICore>(); + //qDebug() << qPrintable(currentTime) << "Setting status: " << msg; + if (msg.isEmpty()) + core->messageManager()->displayStatusBarMessage(msg); + else if (timeout == -1) + core->messageManager()->displayStatusBarMessage(tr("Debugger: ") + msg); + else + core->messageManager()->displayStatusBarMessage(tr("Debugger: ") + msg, timeout); +#endif +} + +void DebuggerManager::notifyStartupFinished() +{ + setStatus(DebuggerProcessReady); + showStatusMessage(tr("Startup finished. Debugger ready."), -1); + if (m_startMode == attachExternal) { + // we continue the execution + engine()->continueInferior(); + } else { + engine()->runInferior(); + } +} + +void DebuggerManager::notifyInferiorStopped() +{ + resetLocation(); + setStatus(DebuggerInferiorStopped); + showStatusMessage(tr("Stopped."), 5000); +} + +void DebuggerManager::notifyInferiorUpdateFinished() +{ + setStatus(DebuggerInferiorReady); + showStatusMessage(tr("Stopped."), 5000); +} + +void DebuggerManager::notifyInferiorRunningRequested() +{ + setStatus(DebuggerInferiorRunningRequested); + showStatusMessage(tr("Running..."), 5000); +} + +void DebuggerManager::notifyInferiorRunning() +{ + setStatus(DebuggerInferiorRunning); + showStatusMessage(tr("Running..."), 5000); +} + +void DebuggerManager::notifyInferiorExited() +{ + setStatus(DebuggerProcessReady); + showStatusMessage(tr("Stopped."), 5000); +} + +void DebuggerManager::notifyInferiorPidChanged(int pid) +{ + //QMessageBox::warning(0, "PID", "PID: " + QString::number(pid)); + //qDebug() << "PID: " << pid; + emit inferiorPidChanged(pid); +} + +void DebuggerManager::showApplicationOutput(const QString &prefix, const QString &str) +{ + applicationOutputAvailable(prefix, str); +} + +void DebuggerManager::shutdown() +{ + //qDebug() << "DEBUGGER_MANAGER SHUTDOWN START"; + engine()->shutdown(); + // Delete these manually before deleting the manager + // (who will delete the models for most views) + delete m_breakWindow; + delete m_disassemblerWindow; + delete m_modulesWindow; + delete m_outputWindow; + delete m_registerWindow; + delete m_stackWindow; + delete m_threadsWindow; + delete m_tooltipWindow; + delete m_watchersWindow; + delete m_localsWindow; + // These widgets are all in some layout which will take care of deletion. + m_breakWindow = 0; + m_disassemblerWindow = 0; + m_modulesWindow = 0; + m_outputWindow = 0; + m_registerWindow = 0; + m_stackWindow = 0; + m_threadsWindow = 0; + m_tooltipWindow = 0; + m_watchersWindow = 0; + m_localsWindow = 0; + + delete m_breakHandler; + delete m_disassemblerHandler; + delete m_modulesHandler; + delete m_registerHandler; + delete m_stackHandler; + delete m_watchHandler; + m_breakHandler = 0; + m_disassemblerHandler = 0; + m_modulesHandler = 0; + m_registerHandler = 0; + m_stackHandler = 0; + m_watchHandler = 0; + //qDebug() << "DEBUGGER_MANAGER SHUTDOWN END"; +} + +void DebuggerManager::toggleBreakpoint() +{ + QString fileName; + int lineNumber = -1; + queryCurrentTextEditor(&fileName, &lineNumber, 0); + if (lineNumber == -1) + return; + toggleBreakpoint(fileName, lineNumber); +} + +void DebuggerManager::toggleBreakpoint(const QString &fileName, int lineNumber) +{ + int index = m_breakHandler->indexOf(fileName, lineNumber); + if (index == -1) + breakHandler()->setBreakpoint(fileName, lineNumber); + else + breakHandler()->removeBreakpoint(index); + engine()->attemptBreakpointSynchronization(); +} + +void DebuggerManager::setToolTipExpression(const QPoint &pos, const QString &exp) +{ + engine()->setToolTipExpression(pos, exp); +} + +void DebuggerManager::updateWatchModel() +{ + engine()->updateWatchModel(); +} + +void DebuggerManager::expandChildren(const QModelIndex &idx) +{ + watchHandler()->expandChildren(idx); +} + +void DebuggerManager::collapseChildren(const QModelIndex &idx) +{ + watchHandler()->collapseChildren(idx); +} + +void DebuggerManager::removeWatchExpression(const QString &iname) +{ + watchHandler()->removeWatchExpression(iname); +} + +QVariant DebuggerManager::sessionValue(const QString &name) +{ + QVariant value; + emit sessionValueRequested(name, &value); + return value; +} + +void DebuggerManager::querySessionValue(const QString &name, QVariant *value) +{ + emit sessionValueRequested(name, value); +} + +void DebuggerManager::setSessionValue(const QString &name, const QVariant &value) +{ + emit setSessionValueRequested(name, value); +} + +QVariant DebuggerManager::configValue(const QString &name) +{ + QVariant value; + emit configValueRequested(name, &value); + return value; +} + +void DebuggerManager::queryConfigValue(const QString &name, QVariant *value) +{ + emit configValueRequested(name, value); +} + +void DebuggerManager::setConfigValue(const QString &name, const QVariant &value) +{ + emit setConfigValueRequested(name, value); +} + +void DebuggerManager::startExternalApplication() +{ + if (!startNewDebugger(startExternal)) + emit debuggingFinished(); +} + +void DebuggerManager::attachExternalApplication() +{ + if (!startNewDebugger(attachExternal)) + emit debuggingFinished(); +} + +bool DebuggerManager::startNewDebugger(StartMode mode) +{ + m_startMode = mode; + // FIXME: Clean up + + if (startMode() == startExternal) { + StartExternalDialog dlg(mainWindow()); + dlg.setExecutableFile( + configValue(QLatin1String("LastExternalExecutableFile")).toString()); + dlg.setExecutableArguments( + configValue(QLatin1String("LastExternalExecutableArguments")).toString()); + if (dlg.exec() != QDialog::Accepted) + return false; + setConfigValue(QLatin1String("LastExternalExecutableFile"), + dlg.executableFile()); + setConfigValue(QLatin1String("LastExternalExecutableArguments"), + dlg.executableArguments()); + m_executable = dlg.executableFile(); + m_processArgs = dlg.executableArguments().split(' '); + m_workingDir = QString(); + m_attachedPID = -1; + } else if (startMode() == attachExternal) { + QString pid; + AttachExternalDialog dlg(mainWindow(), pid); + if (dlg.exec() != QDialog::Accepted) + return false; + m_executable = QString(); + m_processArgs = QStringList(); + m_workingDir = QString(); + m_attachedPID = dlg.attachPID(); + } else if (startMode() == startInternal) { + if (m_executable.isEmpty()) { + QString startDirectory = m_executable; + if (m_executable.isEmpty()) { + QString fileName; + emit currentTextEditorRequested(&fileName, 0, 0); + if (!fileName.isEmpty()) { + const QFileInfo editorFile(fileName); + startDirectory = editorFile.dir().absolutePath(); + } + } + StartExternalDialog dlg(mainWindow()); + dlg.setExecutableFile(startDirectory); + if (dlg.exec() != QDialog::Accepted) + return false; + m_executable = dlg.executableFile(); + m_processArgs = dlg.executableArguments().split(' '); + m_workingDir = QString(); + m_attachedPID = 0; + } else { + //m_executable = QDir::convertSeparators(m_executable); + //m_processArgs = sd.processArgs.join(QLatin1String(" ")); + m_attachedPID = 0; + } + } + + emit debugModeRequested(); + + if (m_executable.endsWith(".js")) + setDebuggerType(ScriptDebugger); + else + setDebuggerType(GdbDebugger); + + if (!engine()->startDebugger()) + return false; + + m_busy = false; + setStatus(DebuggerProcessStartingUp); + return true; +} + +void DebuggerManager::cleanupViews() +{ + resetLocation(); + breakHandler()->setAllPending(); + stackHandler()->removeAll(); + threadsHandler()->removeAll(); + disassemblerHandler()->removeAll(); + modulesHandler()->removeAll(); + watchHandler()->cleanup(); +} + +void DebuggerManager::exitDebugger() +{ + engine()->exitDebugger(); + cleanupViews(); + setStatus(DebuggerProcessNotReady); + setBusyCursor(false); + emit debuggingFinished(); +} + +void DebuggerManager::assignValueInDebugger(const QString &expr, const QString &value) +{ + engine()->assignValueInDebugger(expr, value); +} + +void DebuggerManager::activateFrame(int index) +{ + engine()->activateFrame(index); +} + +void DebuggerManager::selectThread(int index) +{ + engine()->selectThread(index); +} + +void DebuggerManager::loadAllSymbols() +{ + engine()->loadAllSymbols(); +} + +void DebuggerManager::loadSymbols(const QString &module) +{ + engine()->loadSymbols(module); +} + +void DebuggerManager::stepExec() +{ + resetLocation(); + engine()->stepExec(); +} + +void DebuggerManager::stepOutExec() +{ + resetLocation(); + engine()->stepOutExec(); +} + +void DebuggerManager::nextExec() +{ + resetLocation(); + engine()->nextExec(); +} + +void DebuggerManager::stepIExec() +{ + resetLocation(); + engine()->stepIExec(); +} + +void DebuggerManager::nextIExec() +{ + resetLocation(); + engine()->nextIExec(); +} + +void DebuggerManager::executeDebuggerCommand(const QString &command) +{ + engine()->executeDebuggerCommand(command); +} + +void DebuggerManager::sessionLoaded() +{ + exitDebugger(); + loadSessionData(); +} + +void DebuggerManager::aboutToSaveSession() +{ + saveSessionData(); +} + +void DebuggerManager::loadSessionData() +{ + m_breakHandler->loadSessionData(); + + QVariant value; + querySessionValue(QLatin1String("UseFastStart"), &value); + m_useFastStartAction->setChecked(value.toBool()); + querySessionValue(QLatin1String("UseCustomDumpers"), &value); + m_useCustomDumpersAction->setChecked(!value.isValid() || value.toBool()); + querySessionValue(QLatin1String("SkipKnownFrames"), &value); + m_skipKnownFramesAction->setChecked(value.toBool()); + engine()->loadSessionData(); +} + +void DebuggerManager::saveSessionData() +{ + m_breakHandler->saveSessionData(); + + setSessionValue(QLatin1String("UseFastStart"), + m_useFastStartAction->isChecked()); + setSessionValue(QLatin1String("UseCustomDumpers"), + m_useCustomDumpersAction->isChecked()); + setSessionValue(QLatin1String("SkipKnownFrames"), + m_skipKnownFramesAction->isChecked()); + engine()->saveSessionData(); +} + +void DebuggerManager::dumpLog() +{ + QString fileName = QFileDialog::getSaveFileName(mainWindow(), + tr("Save Debugger Log"), QDir::tempPath()); + if (fileName.isEmpty()) + return; + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly)) + return; + QTextStream ts(&file); + ts << m_outputWindow->inputContents(); + ts << "\n\n=======================================\n\n"; + ts << m_outputWindow->combinedContents(); +} + +#if 0 +// call after m_gdbProc exited. +void GdbEngine::procFinished() +{ + //qDebug() << "GDB PROCESS FINISHED"; + setStatus(DebuggerProcessNotReady); + showStatusMessage(tr("Done"), 5000); + q->m_breakHandler->procFinished(); + q->m_watchHandler->cleanup(); + m_stackHandler->m_stackFrames.clear(); + m_stackHandler->resetModel(); + m_threadsHandler->resetModel(); + if (q->m_modulesHandler) + q->m_modulesHandler->procFinished(); + q->resetLocation(); + setStatus(DebuggerProcessNotReady); + emit q->previousModeRequested(); + emit q->debuggingFinished(); + //exitDebugger(); + //showStatusMessage("Gdb killed"); + m_shortToFullName.clear(); + m_fullToShortName.clear(); + m_shared = 0; + q->m_busy = false; +} +#endif + +void DebuggerManager::addToWatchWindow() +{ + // requires a selection, but that's the only case we want... + QObject *ob = 0; + queryCurrentTextEditor(0, 0, &ob); + QPlainTextEdit *editor = qobject_cast<QPlainTextEdit*>(ob); + if (!editor) + return; + QTextCursor tc = editor->textCursor(); + watchExpression(tc.selectedText()); +} + +void DebuggerManager::watchExpression(const QString &expression) +{ + watchHandler()->watchExpression(expression); + //engine()->updateWatchModel(); +} + +void DebuggerManager::setBreakpoint(const QString &fileName, int lineNumber) +{ + breakHandler()->setBreakpoint(fileName, lineNumber); + engine()->attemptBreakpointSynchronization(); +} + +void DebuggerManager::breakByFunction(const QString &functionName) +{ + breakHandler()->breakByFunction(functionName); + engine()->attemptBreakpointSynchronization(); +} + +void DebuggerManager::breakByFunction() +{ + BreakByFunctionDialog dlg(m_mainWindow); + if (dlg.exec()) + breakByFunction(dlg.functionName()); +} + +void DebuggerManager::breakAtMain() +{ +#ifdef Q_OS_WIN + breakByFunction("qMain"); +#else + breakByFunction("main"); +#endif +} + +void DebuggerManager::setStatus(int status) +{ + //qDebug() << "STATUS CHANGE: from" << m_status << "to" << status; + + if (status == m_status) + return; + + m_status = status; + + const bool started = status == DebuggerInferiorRunning + || status == DebuggerInferiorRunningRequested + || status == DebuggerInferiorStopRequested + || status == DebuggerInferiorStopped + || status == DebuggerInferiorUpdating + || status == DebuggerInferiorUpdateFinishing + || status == DebuggerInferiorReady; + + const bool starting = status == DebuggerProcessStartingUp; + const bool running = status == DebuggerInferiorRunning; + const bool ready = status == DebuggerInferiorStopped + || status == DebuggerInferiorReady + || status == DebuggerProcessReady; + + m_startExternalAction->setEnabled(!started && !starting); + m_attachExternalAction->setEnabled(!started && !starting); + m_watchAction->setEnabled(ready); + m_breakAction->setEnabled(true); + + bool interruptIsExit = !running; + if (interruptIsExit) { + m_stopAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stop_small.png")); + m_stopAction->setText(tr("Stop Debugger")); + } else { + m_stopAction->setIcon(QIcon(":/gdbdebugger/images/debugger_interrupt_small.png")); + m_stopAction->setText(tr("Interrupt")); + } + + m_stopAction->setEnabled(started); + m_resetAction->setEnabled(true); + + m_stepAction->setEnabled(ready); + m_stepOutAction->setEnabled(ready); + m_runToLineAction->setEnabled(ready); + m_runToFunctionAction->setEnabled(ready); + m_jumpToLineAction->setEnabled(ready); + m_nextAction->setEnabled(ready); + m_stepIAction->setEnabled(ready); + m_nextIAction->setEnabled(ready); + //showStatusMessage(QString("started: %1, running: %2").arg(started).arg(running)); + emit statusChanged(m_status); + const bool notbusy = ready || status == DebuggerProcessNotReady; + setBusyCursor(!notbusy); +} + +void DebuggerManager::setBusyCursor(bool busy) +{ + if (busy == m_busy) + return; + //qDebug() << "BUSY: " << busy; + m_busy = busy; + + QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor); + m_breakWindow->setCursor(cursor); + m_disassemblerWindow->setCursor(cursor); + m_localsWindow->setCursor(cursor); + m_modulesWindow->setCursor(cursor); + m_outputWindow->setCursor(cursor); + m_registerWindow->setCursor(cursor); + m_stackWindow->setCursor(cursor); + m_threadsWindow->setCursor(cursor); + m_tooltipWindow->setCursor(cursor); + m_watchersWindow->setCursor(cursor); +} + +bool DebuggerManager::skipKnownFrames() const +{ + return m_skipKnownFramesAction->isChecked(); +} + +bool DebuggerManager::debugDumpers() const +{ + return m_debugDumpersAction->isChecked(); +} + +bool DebuggerManager::useCustomDumpers() const +{ + return m_useCustomDumpersAction->isChecked(); +} + +bool DebuggerManager::useFastStart() const +{ + return 0; // && m_useFastStartAction->isChecked(); +} + +void DebuggerManager::queryCurrentTextEditor(QString *fileName, int *lineNumber, + QObject **object) +{ + emit currentTextEditorRequested(fileName, lineNumber, object); +} + +void DebuggerManager::continueExec() +{ + engine()->continueInferior(); +} + +void DebuggerManager::interruptDebuggingRequest() +{ + //qDebug() << "INTERRUPTING AT" << status(); + bool interruptIsExit = (status() != DebuggerInferiorRunning); + if (interruptIsExit) + exitDebugger(); + else { + setStatus(DebuggerInferiorStopRequested); + engine()->interruptInferior(); + } +} + + +void DebuggerManager::runToLineExec() +{ + QString fileName; + int lineNumber = -1; + emit currentTextEditorRequested(&fileName, &lineNumber, 0); + if (!fileName.isEmpty()) + engine()->runToLineExec(fileName, lineNumber); +} + +void DebuggerManager::runToFunctionExec() +{ + QString fileName; + int lineNumber = -1; + QObject *object = 0; + emit currentTextEditorRequested(&fileName, &lineNumber, &object); + QPlainTextEdit *ed = qobject_cast<QPlainTextEdit*>(object); + if (!ed) + return; + QTextCursor cursor = ed->textCursor(); + QString functionName = cursor.selectedText(); + if (functionName.isEmpty()) { + const QTextBlock block = cursor.block(); + const QString line = block.text(); + foreach (const QString &str, line.trimmed().split('(')) { + QString a; + for (int i = str.size(); --i >= 0; ) { + if (!str.at(i).isLetterOrNumber()) + break; + a = str.at(i) + a; + } + if (!a.isEmpty()) { + functionName = a; + break; + } + } + } + //qDebug() << "RUN TO FUNCTION " << functionName; + if (!functionName.isEmpty()) + engine()->runToFunctionExec(functionName); +} + +void DebuggerManager::jumpToLineExec() +{ + QString fileName; + int lineNumber = -1; + emit currentTextEditorRequested(&fileName, &lineNumber, 0); + if (!fileName.isEmpty()) + engine()->jumpToLineExec(fileName, lineNumber); +} + +void DebuggerManager::resetLocation() +{ + //m_watchHandler->removeMouseMoveCatcher(editor->widget()); + emit resetLocationRequested(); +} + +void DebuggerManager::gotoLocation(const QString &fileName, int line, + bool setMarker) +{ + emit gotoLocationRequested(fileName, line, setMarker); + //m_watchHandler->installMouseMoveCatcher(editor->widget()); +} + + +////////////////////////////////////////////////////////////////////// +// +// Disassembler specific stuff +// +////////////////////////////////////////////////////////////////////// + +void DebuggerManager::reloadDisassembler() +{ + if (!m_disassemblerDock || !m_disassemblerDock->isVisible()) + return; + engine()->reloadDisassembler(); +} + +void DebuggerManager::disassemblerDockToggled(bool on) +{ + if (on) + reloadDisassembler(); +} + + +////////////////////////////////////////////////////////////////////// +// +// Modules specific stuff +// +////////////////////////////////////////////////////////////////////// + +void DebuggerManager::reloadModules() +{ + if (!m_modulesDock || !m_modulesDock->isVisible()) + return; + engine()->reloadModules(); +} + +void DebuggerManager::modulesDockToggled(bool on) +{ + if (on) + reloadModules(); +} + + +////////////////////////////////////////////////////////////////////// +// +// Output specific stuff +// +////////////////////////////////////////////////////////////////////// + +void DebuggerManager::showDebuggerOutput(const QString &prefix, const QString &msg) +{ + m_outputWindow->showOutput(prefix, msg); +} + +void DebuggerManager::showDebuggerInput(const QString &prefix, const QString &msg) +{ + m_outputWindow->showInput(prefix, msg); +} + + +////////////////////////////////////////////////////////////////////// +// +// Register specific stuff +// +////////////////////////////////////////////////////////////////////// + +void DebuggerManager::registerDockToggled(bool on) +{ + if (on) + reloadRegisters(); +} + +void DebuggerManager::reloadRegisters() +{ + if (!m_registerDock || !m_registerDock->isVisible()) + return; + engine()->reloadRegisters(); +} + + +#include "debuggermanager.moc" diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h new file mode 100644 index 00000000000..beaf876d4b7 --- /dev/null +++ b/src/plugins/debugger/debuggermanager.h @@ -0,0 +1,451 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_DEBUGGERMANAGER_H +#define DEBUGGER_DEBUGGERMANAGER_H + +#include <QtCore/QByteArray> +#include <QtCore/QObject> +#include <QtCore/QPoint> +#include <QtCore/QStringList> +#include <QtCore/QVariant> + +QT_BEGIN_NAMESPACE +class QAction; +class QAbstractItemModel; +class QDockWidget; +class QMainWindow; +class QModelIndex; +class QSplitter; +class QWidget; +QT_END_NAMESPACE + +namespace Debugger { +namespace Internal { + +class DebuggerOutputWindow; +class DebuggerPlugin; +class DebugMode; + +class BreakHandler; +class DisassemblerHandler; +class ModulesHandler; +class RegisterHandler; +class StackHandler; +class ThreadsHandler; +class WatchHandler; +class WatchData; +class BreakpointData; + + +// Note: the Debugger process itself is referred to as 'Debugger', +// whereas the debugged process is referred to as 'Inferior' or 'Debuggee'. + +// DebuggerProcessNotReady +// | +// DebuggerProcessStartingUp +// | +// DebuggerReady [R] [N] +// | <-------------------------------------. +// DebuggerInferiorRunningRequested | +// | | +// DebuggerInferiorRunning | +// | | +// DebuggerInferiorStopRequested | +// | | +// DebuggerInferiorStopped | +// | | +// DebuggerInferiorUpdating | +// | | +// DebuggerInferiorUpdateFinishing | +// | | +// DebuggerInferiorReady [C] [N] | +// | | +// `---------------------------------------' +// +// Allowed actions: +// [R] : Run +// [C] : Continue +// [N] : Step, Next + + + +enum DebuggerStatus +{ + DebuggerProcessNotReady, // Debugger not started + DebuggerProcessStartingUp, // Debugger starting up + DebuggerProcessReady, // Debugger started, Inferior not yet + // running or already finished + + DebuggerInferiorRunningRequested, // Debuggee requested to run + DebuggerInferiorRunning, // Debuggee running + DebuggerInferiorStopRequested, // Debuggee running, stop requested + DebuggerInferiorStopped, // Debuggee stopped + + DebuggerInferiorUpdating, // Debuggee updating data views + DebuggerInferiorUpdateFinishing, // Debuggee updating data views aborting + DebuggerInferiorReady, +}; + + +class IDebuggerEngine; +class GdbEngine; +class ScriptEngine; +class WinEngine; + +// The construct below is not nice but enforces a bit of order. The +// DebuggerManager interfaces a lots of thing: The DebuggerPlugin, +// the DebuggerEngines, the RunMode, the handlers and views. +// Instead of making the whole interface public, we split in into +// smaller parts and grant friend access only to the classes that +// need it. + + +// +// IDebuggerManagerAccessForEngines +// + +class IDebuggerManagerAccessForEngines +{ +public: + virtual ~IDebuggerManagerAccessForEngines() {} + +private: + // This is the part of the interface that's exclusively seen by the + // debugger engines. + friend class GdbEngine; + friend class ScriptEngine; + friend class WinEngine; + + // called from the engines after successful startup + virtual void notifyStartupFinished() = 0; + virtual void notifyInferiorStopped() = 0; + virtual void notifyInferiorUpdateFinished() = 0; + virtual void notifyInferiorRunningRequested() = 0; + virtual void notifyInferiorRunning() = 0; + virtual void notifyInferiorExited() = 0; + virtual void notifyInferiorPidChanged(int) = 0; + + virtual DisassemblerHandler *disassemblerHandler() = 0; + virtual ModulesHandler *modulesHandler() = 0; + virtual BreakHandler *breakHandler() = 0; + virtual RegisterHandler *registerHandler() = 0; + virtual StackHandler *stackHandler() = 0; + virtual ThreadsHandler *threadsHandler() = 0; + virtual WatchHandler *watchHandler() = 0; + + virtual void showApplicationOutput(const QString &prefix, const QString &data) = 0; + virtual QAction *useCustomDumpersAction() const = 0; + virtual QAction *debugDumpersAction() const = 0; + virtual bool skipKnownFrames() const = 0; + virtual bool debugDumpers() const = 0; + virtual bool useCustomDumpers() const = 0; + virtual bool useFastStart() const = 0; + + virtual void reloadDisassembler() = 0; + virtual void reloadModules() = 0; + virtual void reloadRegisters() = 0; +}; + + +// +// IDebuggerManagerAccessForDebugMode +// + +class IDebuggerManagerAccessForDebugMode +{ +public: + virtual ~IDebuggerManagerAccessForDebugMode() {} + +private: + friend class DebugMode; + + virtual QWidget *threadsWindow() = 0; + virtual QList<QDockWidget*> dockWidgets() const = 0; + virtual void createDockWidgets() = 0; +}; + + +// +// DebuggerManager +// + +class DebuggerManager : public QObject, + public IDebuggerManagerAccessForEngines, + public IDebuggerManagerAccessForDebugMode +{ + Q_OBJECT + +public: + DebuggerManager(); + ~DebuggerManager(); + + IDebuggerManagerAccessForEngines *engineInterface(); + IDebuggerManagerAccessForDebugMode *debugModeInterface(); + QMainWindow *mainWindow() const { return m_mainWindow; } + + enum StartMode { startInternal, startExternal, attachExternal }; + enum DebuggerType { GdbDebugger, ScriptDebugger, WinDebugger }; + +public slots: + bool startNewDebugger(StartMode mode); + void exitDebugger(); + + void setSimpleDockWidgetArrangement(); + void setLocked(bool locked); + void dockToggled(bool on); + + void setBusyCursor(bool on); + void queryCurrentTextEditor(QString *fileName, int *lineNumber, QObject **ed); + void querySessionValue(const QString &name, QVariant *value); + void setSessionValue(const QString &name, const QVariant &value); + QVariant configValue(const QString &name); + void queryConfigValue(const QString &name, QVariant *value); + void setConfigValue(const QString &name, const QVariant &value); + QVariant sessionValue(const QString &name); + + void gotoLocation(const QString &file, int line, bool setLocationMarker); + void resetLocation(); + + void interruptDebuggingRequest(); + void startExternalApplication(); + void attachExternalApplication(); + + void jumpToLineExec(); + void runToLineExec(); + void runToFunctionExec(); + void toggleBreakpoint(); + void breakByFunction(); + void breakByFunction(const QString &functionName); + void setBreakpoint(const QString &fileName, int lineNumber); + void watchExpression(const QString &expression); + void breakAtMain(); + void activateFrame(int index); + void selectThread(int index); + + void stepExec(); + void stepOutExec(); + void nextExec(); + void stepIExec(); + void nextIExec(); + void continueExec(); + + void addToWatchWindow(); + void updateWatchModel(); + void removeWatchExpression(const QString &iname); + void expandChildren(const QModelIndex &idx); + void collapseChildren(const QModelIndex &idx); + + void sessionLoaded(); + void aboutToSaveSession(); + + void assignValueInDebugger(const QString &expr, const QString &value); + void executeDebuggerCommand(const QString &command); + + void showStatusMessage(const QString &msg, int timeout); // -1 forever + +private slots: + void showDebuggerOutput(const QString &prefix, const QString &msg); + void showDebuggerInput(const QString &prefix, const QString &msg); + void showApplicationOutput(const QString &prefix, const QString &msg); + + void reloadDisassembler(); + void disassemblerDockToggled(bool on); + + void reloadModules(); + void modulesDockToggled(bool on); + void loadSymbols(const QString &moduleName); + void loadAllSymbols(); + + void reloadRegisters(); + void registerDockToggled(bool on); + void setStatus(int status); + +private: + // + // Implementation of IDebuggerManagerAccessForEngines + // + DisassemblerHandler *disassemblerHandler() { return m_disassemblerHandler; } + ModulesHandler *modulesHandler() { return m_modulesHandler; } + BreakHandler *breakHandler() { return m_breakHandler; } + RegisterHandler *registerHandler() { return m_registerHandler; } + StackHandler *stackHandler() { return m_stackHandler; } + ThreadsHandler *threadsHandler() { return m_threadsHandler; } + WatchHandler *watchHandler() { return m_watchHandler; } + QAction *useCustomDumpersAction() const { return m_useCustomDumpersAction; } + QAction *debugDumpersAction() const { return m_debugDumpersAction; } + bool skipKnownFrames() const; + bool debugDumpers() const; + bool useCustomDumpers() const; + bool useFastStart() const; + + void notifyStartupFinished(); + void notifyInferiorStopped(); + void notifyInferiorUpdateFinished(); + void notifyInferiorRunningRequested(); + void notifyInferiorRunning(); + void notifyInferiorExited(); + void notifyInferiorPidChanged(int); + + void cleanupViews(); + + // + // Implementation of IDebuggerManagerAccessForDebugMode + // + QWidget *threadsWindow() { return m_threadsWindow; } + QList<QDockWidget*> dockWidgets() const { return m_dockWidgets; } + void createDockWidgets(); + + // + // internal implementation + // + Q_SLOT void loadSessionData(); + Q_SLOT void saveSessionData(); + Q_SLOT void dumpLog(); + +public: + // stuff in this block should be made private by moving it to + // one of the interfaces + QAbstractItemModel *threadsModel(); + int status() const { return m_status; } + StartMode startMode() const { return m_startMode; } + +signals: + void debuggingFinished(); + void inferiorPidChanged(qint64 pid); + void statusChanged(int newstatus); + void debugModeRequested(); + void previousModeRequested(); + void statusMessageRequested(const QString &msg, int timeout); // -1 for 'forever' + void gotoLocationRequested(const QString &file, int line, bool setLocationMarker); + void resetLocationRequested(); + void currentTextEditorRequested(QString *fileName, int *lineNumber, QObject **ob); + void currentMainWindowRequested(QWidget **); + void sessionValueRequested(const QString &name, QVariant *value); + void setSessionValueRequested(const QString &name, const QVariant &value); + void configValueRequested(const QString &name, QVariant *value); + void setConfigValueRequested(const QString &name, const QVariant &value); + void applicationOutputAvailable(const QString &prefix, const QString &msg); + + +public: + // FIXME: make private + QString m_executable; + QStringList m_environment; + QString m_workingDir; + QString m_buildDir; + QStringList m_processArgs; + int m_attachedPID; + +private: + void init(); + void setDebuggerType(DebuggerType type); + QDockWidget *createDockForWidget(QWidget *widget); + + void shutdown(); + + void toggleBreakpoint(const QString &fileName, int lineNumber); + void setToolTipExpression(const QPoint &pos, const QString &exp0); + + StartMode m_startMode; + DebuggerType m_debuggerType; + + /// Views + QMainWindow *m_mainWindow; + QDockWidget *m_breakDock; + QDockWidget *m_disassemblerDock; + QDockWidget *m_modulesDock; + QDockWidget *m_outputDock; + QDockWidget *m_registerDock; + QDockWidget *m_stackDock; + QDockWidget *m_threadsDock; + QDockWidget *m_watchDock; + QList<QDockWidget*> m_dockWidgets; + + BreakHandler *m_breakHandler; + DisassemblerHandler *m_disassemblerHandler; + ModulesHandler *m_modulesHandler; + RegisterHandler *m_registerHandler; + StackHandler *m_stackHandler; + ThreadsHandler *m_threadsHandler; + WatchHandler *m_watchHandler; + + /// Actions + friend class DebuggerPlugin; + QAction *m_startExternalAction; + QAction *m_attachExternalAction; + QAction *m_continueAction; + QAction *m_stopAction; + QAction *m_resetAction; // FIXME: Should not be needed in a stable release + QAction *m_stepAction; + QAction *m_stepOutAction; + QAction *m_runToLineAction; + QAction *m_runToFunctionAction; + QAction *m_jumpToLineAction; + QAction *m_nextAction; + QAction *m_watchAction; + QAction *m_breakAction; + QAction *m_breakByFunctionAction; + QAction *m_breakAtMainAction; + QAction *m_sepAction; + QAction *m_stepIAction; + QAction *m_nextIAction; + QAction *m_skipKnownFramesAction; + + QAction *m_debugDumpersAction; + QAction *m_useCustomDumpersAction; + QAction *m_useFastStartAction; + QAction *m_dumpLogAction; + + QWidget *m_breakWindow; + QWidget *m_disassemblerWindow; + QWidget *m_localsWindow; + QWidget *m_registerWindow; + QWidget *m_modulesWindow; + QWidget *m_tooltipWindow; + QWidget *m_stackWindow; + QWidget *m_threadsWindow; + QWidget *m_watchersWindow; + DebuggerOutputWindow *m_outputWindow; + + int m_status; + bool m_busy; + + IDebuggerEngine *engine(); + IDebuggerEngine *m_engine; +}; + + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_DEBUGGERMANAGER_H diff --git a/src/plugins/debugger/debuggeroutputwindow.cpp b/src/plugins/debugger/debuggeroutputwindow.cpp new file mode 100644 index 00000000000..7f6bef85d71 --- /dev/null +++ b/src/plugins/debugger/debuggeroutputwindow.cpp @@ -0,0 +1,318 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "debuggeroutputwindow.h" + +#include <QtCore/QDebug> + +#include <QtGui/QAction> +#include <QtGui/QHBoxLayout> +#include <QtGui/QVBoxLayout> +#include <QtGui/QKeyEvent> +#include <QtGui/QLabel> +#include <QtGui/QLineEdit> +#include <QtGui/QMenu> +#include <QtGui/QSpacerItem> +#include <QtGui/QSplitter> +#include <QtGui/QTextBlock> + +#ifndef GDBDEBUGGERLEAN + +#include <aggregation/aggregate.h> +#include <find/basetextfind.h> + +using namespace Find; + +#endif // GDBDEBUGGERLEAN + +using Debugger::Internal::DebuggerOutputWindow; + +///////////////////////////////////////////////////////////////////// +// +// InputPane +// +///////////////////////////////////////////////////////////////////// + +class DebuggerPane : public QTextEdit +{ +public: + DebuggerPane(QWidget *parent) + : QTextEdit(parent) + { + m_clearContentsAction = new QAction(this); + m_clearContentsAction->setText("Clear contents"); + m_clearContentsAction->setEnabled(true); + m_clearContentsAction->setShortcut(Qt::ControlModifier + Qt::Key_R); + connect(m_clearContentsAction, SIGNAL(triggered(bool)), + parent, SLOT(clearContents())); + + m_saveContentsAction = new QAction(this); + m_saveContentsAction->setText("Save contents"); + m_saveContentsAction->setEnabled(true); + } + + void contextMenuEvent(QContextMenuEvent *ev) + { + QMenu *menu = createStandardContextMenu(); + menu->addAction(m_clearContentsAction); + //menu->addAction(m_saveContentsAction); + addContextActions(menu); + menu->exec(ev->globalPos()); + delete menu; + } + + virtual void addContextActions(QMenu *) {} + +public: + QAction *m_clearContentsAction; + QAction *m_saveContentsAction; +}; + +class InputPane : public DebuggerPane +{ + Q_OBJECT +public: + InputPane(QWidget *parent) : DebuggerPane(parent) + { + m_commandExecutionAction = new QAction(this); + m_commandExecutionAction->setText("Execute line"); + m_commandExecutionAction->setEnabled(true); + //m_commandExecutionAction->setShortcut + // (Qt::ControlModifier + Qt::Key_Return); + + connect(m_commandExecutionAction, SIGNAL(triggered(bool)), + this, SLOT(executeCommand())); + } + +signals: + void commandExecutionRequested(const QString &); + void clearContentsRequested(); + void statusMessageRequested(const QString &, int); + void commandSelected(int); + +private slots: + void executeCommand() + { + emit commandExecutionRequested(textCursor().block().text()); + } + +private: + void keyPressEvent(QKeyEvent *ev) + { + if (ev->modifiers() == Qt::ControlModifier && ev->key() == Qt::Key_Return) + emit commandExecutionRequested(textCursor().block().text()); + else if (ev->modifiers() == Qt::ControlModifier && ev->key() == Qt::Key_R) + emit clearContentsRequested(); + else + QTextEdit::keyPressEvent(ev); + } + + void mouseDoubleClickEvent(QMouseEvent *ev) + { + QString line = cursorForPosition(ev->pos()).block().text(); + int n = 0; + + // cut time string + if (line.size() > 18 && line.at(0) == '[') + line = line.mid(18); + //qDebug() << line; + + for (int i = 0; i != line.size(); ++i) { + QChar c = line.at(i); + if (!c.isDigit()) + break; + n = 10 * n + c.unicode() - '0'; + } + emit commandSelected(n); + } + + void addContextActions(QMenu *menu) + { + menu->addAction(m_commandExecutionAction); + } + + void focusInEvent(QFocusEvent *ev) + { + emit statusMessageRequested("Type Ctrl-<Return> to execute a line.", -1); + QTextEdit::focusInEvent(ev); + } + + void focusOutEvent(QFocusEvent *ev) + { + emit statusMessageRequested(QString(), -1); + QTextEdit::focusOutEvent(ev); + } + + QAction *m_commandExecutionAction; +}; + + +///////////////////////////////////////////////////////////////////// +// +// CombinedPane +// +///////////////////////////////////////////////////////////////////// + +class CombinedPane : public DebuggerPane +{ + Q_OBJECT +public: + CombinedPane(QWidget *parent) + : DebuggerPane(parent) + {} + +public slots: + void gotoResult(int i) + { + QString needle = QString::number(i) + '^'; + QString needle2 = "stdout:" + needle; + QTextCursor cursor(document()); + do { + const QString line = cursor.block().text(); + if (line.startsWith(needle) || line.startsWith(needle2)) { + setFocus(); + setTextCursor(cursor); + ensureCursorVisible(); + cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor); + setTextCursor(cursor); + break; + } + } while (cursor.movePosition(QTextCursor::Down)); + } +}; + + +///////////////////////////////////////////////////////////////////// +// +// DebuggerOutputWindow +// +///////////////////////////////////////////////////////////////////// + +DebuggerOutputWindow::DebuggerOutputWindow(QWidget *parent) + : QWidget(parent) +{ + setWindowTitle(tr("Gdb")); + + QSplitter *m_splitter = new QSplitter(Qt::Horizontal, this); + // mixed input/output + m_combinedText = new CombinedPane(this); + m_combinedText->setReadOnly(true); + m_combinedText->setReadOnly(false); + m_combinedText->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + + // input only + m_inputText = new InputPane(this); + m_inputText->setReadOnly(false); + m_inputText->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + + m_splitter->addWidget(m_inputText); + m_splitter->addWidget(m_combinedText); + + QGridLayout *layout = new QGridLayout(this); + layout->setMargin(0); + layout->addWidget(m_splitter); + setLayout(layout); + +#ifndef GDBDEBUGGERLEAN + Aggregation::Aggregate *aggregate = new Aggregation::Aggregate; + aggregate->add(m_combinedText); + aggregate->add(new BaseTextFind(m_combinedText)); + + aggregate = new Aggregation::Aggregate; + aggregate->add(m_inputText); + aggregate->add(new BaseTextFind(m_inputText)); +#endif + + connect(m_inputText, SIGNAL(commandExecutionRequested(QString)), + this, SIGNAL(commandExecutionRequested(QString))); + connect(m_inputText, SIGNAL(statusMessageRequested(QString,int)), + this, SIGNAL(statusMessageRequested(QString,int))); + connect(m_inputText, SIGNAL(commandSelected(int)), + m_combinedText, SLOT(gotoResult(int))); +}; + +void DebuggerOutputWindow::onReturnPressed() +{ + emit commandExecutionRequested(m_commandEdit->text()); +} + +void DebuggerOutputWindow::showOutput(const QString &prefix, const QString &output) +{ + if (output.isEmpty()) + return; + foreach (QString line, output.split("\n")) { + // FIXME: QTextEdit asserts on really long lines... + const int n = 3000; + if (line.size() > n) + line = line.left(n) + " [...] <cut off>"; + m_combinedText->append(prefix + line); + } + QTextCursor cursor = m_combinedText->textCursor(); + cursor.movePosition(QTextCursor::End); + m_combinedText->setTextCursor(cursor); + m_combinedText->ensureCursorVisible(); +} + +void DebuggerOutputWindow::showInput(const QString &prefix, const QString &input) +{ + m_inputText->append(input); + QTextCursor cursor = m_inputText->textCursor(); + cursor.movePosition(QTextCursor::End); + m_inputText->setTextCursor(cursor); + m_inputText->ensureCursorVisible(); + showOutput("input:", input); +} + +void DebuggerOutputWindow::clearContents() +{ + m_combinedText->clear(); + m_inputText->clear(); +} + +void DebuggerOutputWindow::setCursor(const QCursor &cursor) +{ + m_combinedText->setCursor(cursor); + m_inputText->setCursor(cursor); + QWidget::setCursor(cursor); +} + +QString DebuggerOutputWindow::combinedContents() const +{ + return m_combinedText->toPlainText(); +} + +QString DebuggerOutputWindow::inputContents() const +{ + return m_inputText->toPlainText(); +} + +#include "debuggeroutputwindow.moc" diff --git a/src/plugins/debugger/debuggeroutputwindow.h b/src/plugins/debugger/debuggeroutputwindow.h new file mode 100644 index 00000000000..f844c0fff97 --- /dev/null +++ b/src/plugins/debugger/debuggeroutputwindow.h @@ -0,0 +1,87 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_OUTPUTWINDOW_H +#define DEBUGGER_OUTPUTWINDOW_H + +#include <QtGui/QLineEdit> +#include <QtGui/QSplitter> +#include <QtGui/QTextEdit> +#include <QtGui/QWidget> + +namespace Debugger { +namespace Internal { + +class DebuggerOutputWindow : public QWidget +{ + Q_OBJECT + +public: + DebuggerOutputWindow(QWidget *parent = 0); + + QWidget *outputWidget(QWidget *) { return this; } + QList<QWidget*> toolBarWidgets(void) const { return QList<QWidget *>(); } + + QString name() const { return windowTitle(); } + void visibilityChanged(bool /*visible*/) {} + + void bringPaneToForeground() { emit showPage(); } + void setCursor(const QCursor &cursor); + + QString combinedContents() const; + QString inputContents() const; + +public slots: + void clearContents(); + void showOutput(const QString &prefix, const QString &output); + void showInput(const QString &prefix, const QString &input); + +signals: + void showPage(); + void statusMessageRequested(const QString &msg, int); + void commandExecutionRequested(const QString &cmd); + +private slots: + void onReturnPressed(); + +private: + QTextEdit *m_combinedText; // combined input/output + QTextEdit *m_inputText; // scriptable input alone + QLineEdit *m_commandEdit; +}; + + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_OUTPUTWINDOW_H + diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp new file mode 100644 index 00000000000..8fe6cb0250d --- /dev/null +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -0,0 +1,610 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "debuggerplugin.h" + +#include "assert.h" +#include "debuggerconstants.h" +#include "debuggermanager.h" +#include "debuggerrunner.h" +#include "gdboptionpage.h" +#include "gdbengine.h" +#include "mode.h" + +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/messagemanager.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <cplusplus/ExpressionUnderCursor.h> +#include <cppeditor/cppeditorconstants.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/session.h> +#include <texteditor/basetextmark.h> +#include <texteditor/itexteditor.h> +#include <texteditor/texteditorconstants.h> +#include <texteditor/basetexteditor.h> + +#include <QtCore/QDebug> +#include <QtCore/qplugin.h> +#include <QtCore/QObject> +#include <QtCore/QPoint> +#include <QtCore/QSettings> +#include <QtGui/QPlainTextEdit> +#include <QtGui/QTextBlock> +#include <QtGui/QTextCursor> + + +using namespace Debugger::Internal; +using namespace Debugger::Constants; +using namespace TextEditor; +using namespace Core; +using namespace ProjectExplorer; +using namespace CPlusPlus; + + +namespace Debugger { +namespace Constants { + +const char * const STARTEXTERNAL = "Debugger.StartExternal"; +const char * const ATTACHEXTERNAL = "Debugger.AttachExternal"; + +const char * const RUN_TO_LINE = "Debugger.RunToLine"; +const char * const RUN_TO_FUNCTION = "Debugger.RunToFunction"; +const char * const JUMP_TO_LINE = "Debugger.JumpToLine"; +const char * const TOGGLE_BREAK = "Debugger.ToggleBreak"; +const char * const BREAK_BY_FUNCTION = "Debugger.BreakByFunction"; +const char * const BREAK_AT_MAIN = "Debugger.BreakAtMain"; +const char * const DEBUG_DUMPERS = "Debugger.DebugDumpers"; +const char * const ADD_TO_WATCH = "Debugger.AddToWatch"; +const char * const USE_CUSTOM_DUMPERS = "Debugger.UseCustomDumpers"; +const char * const USE_FAST_START = "Debugger.UseFastStart"; +const char * const SKIP_KNOWN_FRAMES = "Debugger.SkipKnownFrames"; +const char * const DUMP_LOG = "Debugger.DumpLog"; + +#ifdef Q_OS_MAC +const char * const INTERRUPT_KEY = "Shift+F5"; +const char * const RESET_KEY = "Ctrl+Shift+F5"; +const char * const STEP_KEY = "F7"; +const char * const STEPOUT_KEY = "Shift+F7"; +const char * const NEXT_KEY = "F6"; +const char * const STEPI_KEY = "Shift+F9"; +const char * const NEXTI_KEY = "Shift+F6"; +const char * const RUN_TO_LINE_KEY = "Shift+F8"; +const char * const RUN_TO_FUNCTION_KEY = "Ctrl+F6"; +const char * const JUMP_TO_LINE_KEY = "Alt+D,Alt+L"; +const char * const TOGGLE_BREAK_KEY = "F8"; +const char * const BREAK_BY_FUNCTION_KEY = "Alt+D,Alt+F"; +const char * const BREAK_AT_MAIN_KEY = "Alt+D,Alt+M"; +const char * const ADD_TO_WATCH_KEY = "Alt+D,Alt+W"; +#else +const char * const INTERRUPT_KEY = "Shift+F5"; +const char * const RESET_KEY = "Ctrl+Shift+F5"; +const char * const STEP_KEY = "F11"; +const char * const STEPOUT_KEY = "Shift+F11"; +const char * const NEXT_KEY = "F10"; +const char * const STEPI_KEY = ""; +const char * const NEXTI_KEY = ""; +const char * const RUN_TO_LINE_KEY = ""; +const char * const RUN_TO_FUNCTION_KEY = ""; +const char * const JUMP_TO_LINE_KEY = ""; +const char * const TOGGLE_BREAK_KEY = "F9"; +const char * const BREAK_BY_FUNCTION_KEY = ""; +const char * const BREAK_AT_MAIN_KEY = ""; +const char * const ADD_TO_WATCH_KEY = "Ctrl+Alt+Q"; +#endif + +} // namespace Constants +} // namespace Debugger + + +/////////////////////////////////////////////////////////////////////// +// +// LocationMark +// +/////////////////////////////////////////////////////////////////////// + +class Debugger::Internal::LocationMark + : public TextEditor::BaseTextMark +{ + Q_OBJECT + +public: + LocationMark(const QString &fileName, int linenumber) + : BaseTextMark(fileName, linenumber) + { + } + ~LocationMark(); + + QIcon icon() const; + void updateLineNumber(int /*lineNumber*/) {} + void updateBlock(const QTextBlock & /*block*/) {} + void removedFromEditor() { deleteLater(); } +private: +}; + +LocationMark::~LocationMark() +{ + //qDebug() << "LOCATIONMARK DESTRUCTOR" << m_editor; +} + +QIcon LocationMark::icon() const +{ + static const QIcon icon(":/gdbdebugger/images/location.svg"); + return icon; +} + +/////////////////////////////////////////////////////////////////////// +// +// DebuggerPlugin +// +/////////////////////////////////////////////////////////////////////// + +DebuggerPlugin::DebuggerPlugin() +{ + m_pm = 0; + m_generalOptionPage = 0; + m_typeMacroPage = 0; + m_locationMark = 0; + m_manager = 0; +} + +DebuggerPlugin::~DebuggerPlugin() +{} + +void DebuggerPlugin::shutdown() +{ + if (m_debugMode) + m_debugMode->shutdown(); // saves state including manager information + QWB_ASSERT(m_manager, /**/); + if (m_manager) + m_manager->shutdown(); + + //qDebug() << "DebuggerPlugin::~DebuggerPlugin"; + removeObject(m_debugMode); + removeObject(m_generalOptionPage); + removeObject(m_typeMacroPage); + + // FIXME: when using the line below, BreakWindow etc gets deleted twice. + // so better leak for now... + delete m_debugMode; + m_debugMode = 0; + + delete m_generalOptionPage; + m_generalOptionPage = 0; + + delete m_typeMacroPage; + m_typeMacroPage = 0; + + delete m_locationMark; + m_locationMark = 0; + + delete m_manager; + m_manager = 0; +} + +bool DebuggerPlugin::initialize(const QStringList &arguments, QString *error_message) +{ + Q_UNUSED(arguments); + Q_UNUSED(error_message); + + m_manager = new DebuggerManager; + + m_pm = ExtensionSystem::PluginManager::instance(); + + ICore *core = m_pm->getObject<Core::ICore>(); + QWB_ASSERT(core, return false); + + Core::ActionManagerInterface *actionManager = core->actionManager(); + QWB_ASSERT(actionManager, return false); + + Core::UniqueIDManager *uidm = core->uniqueIDManager(); + QWB_ASSERT(uidm, return false); + + QList<int> globalcontext; + globalcontext << Core::Constants::C_GLOBAL_ID; + + QList<int> cppcontext; + cppcontext << uidm->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX); + + QList<int> debuggercontext; + debuggercontext << uidm->uniqueIdentifier(C_GDBDEBUGGER); + + QList<int> cppeditorcontext; + cppeditorcontext << uidm->uniqueIdentifier(CppEditor::Constants::C_CPPEDITOR); + + QList<int> texteditorcontext; + texteditorcontext << uidm->uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR); + + m_gdbRunningContext = uidm->uniqueIdentifier(Constants::GDBRUNNING); + + //Core::IActionContainer *mcppcontext = + // actionManager->actionContainer(CppEditor::Constants::M_CONTEXT); + + Core::IActionContainer *mdebug = + actionManager->actionContainer(ProjectExplorer::Constants::M_DEBUG); + + Core::ICommand *cmd = 0; + cmd = actionManager->registerAction(m_manager->m_startExternalAction, + Constants::STARTEXTERNAL, globalcontext); + mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE); + +#ifndef Q_OS_WIN + cmd = actionManager->registerAction(m_manager->m_attachExternalAction, + Constants::ATTACHEXTERNAL, globalcontext); + mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE); +#endif + + cmd = actionManager->registerAction(m_manager->m_continueAction, + ProjectExplorer::Constants::DEBUG, QList<int>()<< m_gdbRunningContext); + + cmd = actionManager->registerAction(m_manager->m_stopAction, + Constants::INTERRUPT, globalcontext); + cmd->setAttribute(Core::ICommand::CA_UpdateText); + cmd->setAttribute(Core::ICommand::CA_UpdateIcon); + cmd->setDefaultKeySequence(QKeySequence(Constants::INTERRUPT_KEY)); + cmd->setDefaultText(tr("Stop Debugger/Interrupt Debugger")); + mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE); + + cmd = actionManager->registerAction(m_manager->m_resetAction, + Constants::RESET, globalcontext); + cmd->setAttribute(Core::ICommand::CA_UpdateText); + cmd->setDefaultKeySequence(QKeySequence(Constants::RESET_KEY)); + cmd->setDefaultText(tr("Reset Debugger")); + //disabled mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE); + + QAction *sep = new QAction(this); + sep->setSeparator(true); + cmd = actionManager->registerAction(sep, + QLatin1String("GdbDebugger.Sep1"), globalcontext); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_nextAction, + Constants::NEXT, debuggercontext); + cmd->setDefaultKeySequence(QKeySequence(Constants::NEXT_KEY)); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_stepAction, + Constants::STEP, debuggercontext); + cmd->setDefaultKeySequence(QKeySequence(Constants::STEP_KEY)); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_stepOutAction, + Constants::STEPOUT, debuggercontext); + cmd->setDefaultKeySequence(QKeySequence(Constants::STEPOUT_KEY)); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_nextIAction, + Constants::NEXTI, debuggercontext); + cmd->setDefaultKeySequence(QKeySequence(Constants::NEXTI_KEY)); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_stepIAction, + Constants::STEPI, debuggercontext); + cmd->setDefaultKeySequence(QKeySequence(Constants::STEPI_KEY)); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_runToLineAction, + Constants::RUN_TO_LINE, debuggercontext); + cmd->setDefaultKeySequence(QKeySequence(Constants::RUN_TO_LINE_KEY)); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_runToFunctionAction, + Constants::RUN_TO_FUNCTION, debuggercontext); + cmd->setDefaultKeySequence(QKeySequence(Constants::RUN_TO_FUNCTION_KEY)); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_jumpToLineAction, + Constants::JUMP_TO_LINE, debuggercontext); + mdebug->addAction(cmd); + + sep = new QAction(this); + sep->setSeparator(true); + cmd = actionManager->registerAction(sep, + QLatin1String("GdbDebugger.Sep3"), globalcontext); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_breakAction, + Constants::TOGGLE_BREAK, cppeditorcontext); + cmd->setDefaultKeySequence(QKeySequence(Constants::TOGGLE_BREAK_KEY)); + mdebug->addAction(cmd); + //mcppcontext->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_breakByFunctionAction, + Constants::BREAK_BY_FUNCTION, globalcontext); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_breakAtMainAction, + Constants::BREAK_AT_MAIN, globalcontext); + mdebug->addAction(cmd); + + sep = new QAction(this); + sep->setSeparator(true); + cmd = actionManager->registerAction(sep, + QLatin1String("GdbDebugger.Sep2"), globalcontext); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_skipKnownFramesAction, + Constants::SKIP_KNOWN_FRAMES, globalcontext); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_useCustomDumpersAction, + Constants::USE_CUSTOM_DUMPERS, globalcontext); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_useFastStartAction, + Constants::USE_FAST_START, globalcontext); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_dumpLogAction, + Constants::DUMP_LOG, globalcontext); + //cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+D,Ctrl+L"))); + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+F11"))); + mdebug->addAction(cmd); + +#ifdef QT_DEBUG + cmd = actionManager->registerAction(m_manager->m_debugDumpersAction, + Constants::DEBUG_DUMPERS, debuggercontext); + mdebug->addAction(cmd); +#endif + + sep = new QAction(this); + sep->setSeparator(true); + cmd = actionManager->registerAction(sep, + QLatin1String("GdbDebugger.Sep4"), globalcontext); + mdebug->addAction(cmd); + + cmd = actionManager->registerAction(m_manager->m_watchAction, + Constants::ADD_TO_WATCH, cppeditorcontext); + //cmd->setDefaultKeySequence(QKeySequence(tr("ALT+D,ALT+W"))); + mdebug->addAction(cmd); + + m_generalOptionPage = 0; + m_typeMacroPage = 0; + + // FIXME: + m_generalOptionPage = new GdbOptionPage(&theGdbSettings()); + addObject(m_generalOptionPage); + m_typeMacroPage = new TypeMacroPage(&theGdbSettings()); + addObject(m_typeMacroPage); + + m_locationMark = 0; + + m_debugMode = new DebugMode(m_manager, this); + //addAutoReleasedObject(m_debugMode); + addObject(m_debugMode); + + addAutoReleasedObject(new DebuggerRunner(m_manager)); + + // ProjectExplorer + connect(projectExplorer()->session(), SIGNAL(sessionLoaded()), + m_manager, SLOT(sessionLoaded())); + connect(projectExplorer()->session(), SIGNAL(aboutToSaveSession()), + m_manager, SLOT(aboutToSaveSession())); + + // EditorManager + QObject *editorManager = core->editorManager(); + connect(editorManager, SIGNAL(editorAboutToClose(Core::IEditor*)), + this, SLOT(editorAboutToClose(Core::IEditor*))); + connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)), + this, SLOT(editorOpened(Core::IEditor*))); + + // Application interaction + connect(m_manager, SIGNAL(currentTextEditorRequested(QString*,int*,QObject**)), + this, SLOT(queryCurrentTextEditor(QString*,int*,QObject**))); + + connect(m_manager, SIGNAL(setSessionValueRequested(QString,QVariant)), + this, SLOT(setSessionValue(QString,QVariant))); + connect(m_manager, SIGNAL(sessionValueRequested(QString,QVariant*)), + this, SLOT(querySessionValue(QString,QVariant*))); + connect(m_manager, SIGNAL(setConfigValueRequested(QString,QVariant)), + this, SLOT(setConfigValue(QString,QVariant))); + connect(m_manager, SIGNAL(configValueRequested(QString,QVariant*)), + this, SLOT(queryConfigValue(QString,QVariant*))); + + connect(m_manager, SIGNAL(resetLocationRequested()), + this, SLOT(resetLocation())); + connect(m_manager, SIGNAL(gotoLocationRequested(QString,int,bool)), + this, SLOT(gotoLocation(QString,int,bool))); + connect(m_manager, SIGNAL(statusChanged(int)), + this, SLOT(changeStatus(int))); + connect(m_manager, SIGNAL(previousModeRequested()), + this, SLOT(activatePreviousMode())); + connect(m_manager, SIGNAL(debugModeRequested()), + this, SLOT(activateDebugMode())); + + return true; +} + +void DebuggerPlugin::extensionsInitialized() +{ +} + +ProjectExplorer::ProjectExplorerPlugin *DebuggerPlugin::projectExplorer() const +{ + return m_pm->getObject<ProjectExplorer::ProjectExplorerPlugin>(); +} + +/*! Activates the previous mode when the current mode is the debug mode. */ +void DebuggerPlugin::activatePreviousMode() +{ + ICore *core = m_pm->getObject<Core::ICore>(); + Core::ModeManager *const modeManager = core->modeManager(); + + if (modeManager->currentMode() == modeManager->mode(Constants::MODE_DEBUG) + && !m_previousMode.isEmpty()) { + modeManager->activateMode(m_previousMode); + m_previousMode.clear(); + } +} + +void DebuggerPlugin::activateDebugMode() +{ + ICore *core = m_pm->getObject<Core::ICore>(); + Core::ModeManager *modeManager = core->modeManager(); + m_previousMode = QLatin1String(modeManager->currentMode()->uniqueModeName()); + modeManager->activateMode(QLatin1String(MODE_DEBUG)); +} + +void DebuggerPlugin::queryCurrentTextEditor(QString *fileName, int *lineNumber, QObject **object) +{ + ICore *core = m_pm->getObject<Core::ICore>(); + if (!core || !core->editorManager()) + return; + Core::IEditor *editor = core->editorManager()->currentEditor(); + ITextEditor *textEditor = qobject_cast<ITextEditor*>(editor); + if (!textEditor) + return; + if (fileName) + *fileName = textEditor->file()->fileName(); + if (lineNumber) + *lineNumber = textEditor->currentLine(); + if (object) + *object = textEditor->widget(); +} + +void DebuggerPlugin::editorOpened(Core::IEditor *editor) +{ + if (ITextEditor *textEditor = qobject_cast<ITextEditor *>(editor)) { + connect(textEditor, SIGNAL(markRequested(TextEditor::ITextEditor*,int)), + this, SLOT(requestMark(TextEditor::ITextEditor*,int))); + connect(editor, SIGNAL(tooltipRequested(TextEditor::ITextEditor*,QPoint,int)), + this, SLOT(showToolTip(TextEditor::ITextEditor*,QPoint,int))); + } +} + +void DebuggerPlugin::editorAboutToClose(Core::IEditor *editor) +{ + if (ITextEditor *textEditor = qobject_cast<ITextEditor *>(editor)) { + disconnect(textEditor, SIGNAL(markRequested(TextEditor::ITextEditor*,int)), + this, SLOT(requestMark(TextEditor::ITextEditor*,int))); + disconnect(editor, SIGNAL(tooltipRequested(TextEditor::ITextEditor*,QPoint,int)), + this, SLOT(showToolTip(TextEditor::ITextEditor*,QPoint,int))); + } +} + +void DebuggerPlugin::requestMark(TextEditor::ITextEditor *editor, int lineNumber) +{ + m_manager->toggleBreakpoint(editor->file()->fileName(), lineNumber); +} + +void DebuggerPlugin::showToolTip(TextEditor::ITextEditor *editor, + const QPoint &point, int pos) +{ + QPlainTextEdit *plaintext = qobject_cast<QPlainTextEdit*>(editor->widget()); + if (!plaintext) + return; + + QString expr = plaintext->textCursor().selectedText(); + if (expr.isEmpty()) { + QTextCursor tc(plaintext->document()); + tc.setPosition(pos); + + const QChar ch = editor->characterAt(pos); + if (ch.isLetterOrNumber() || ch == QLatin1Char('_')) + tc.movePosition(QTextCursor::EndOfWord); + + // Fetch the expression's code. + ExpressionUnderCursor expressionUnderCursor; + expr = expressionUnderCursor(tc); + } + //qDebug() << " TOOLTIP EXPR " << expr; + m_manager->setToolTipExpression(point, expr); +} + +void DebuggerPlugin::setSessionValue(const QString &name, const QVariant &value) +{ + //qDebug() << "SET SESSION VALUE" << name << value; + ProjectExplorerPlugin *pe = projectExplorer(); + if (pe->session()) + pe->session()->setValue(name, value); + else + qDebug() << "FIXME: Session does not exist yet"; +} + +void DebuggerPlugin::querySessionValue(const QString &name, QVariant *value) +{ + ProjectExplorerPlugin *pe = projectExplorer(); + *value = pe->session()->value(name); + //qDebug() << "GET SESSION VALUE: " << name << value; +} + + +void DebuggerPlugin::setConfigValue(const QString &name, const QVariant &value) +{ + QWB_ASSERT(m_debugMode, return); + m_debugMode->settings()->setValue(name, value); +} + +void DebuggerPlugin::queryConfigValue(const QString &name, QVariant *value) +{ + QWB_ASSERT(m_debugMode, return); + *value = m_debugMode->settings()->value(name); +} + +void DebuggerPlugin::resetLocation() +{ + //qDebug() << "RESET_LOCATION: current:" << currentTextEditor(); + //qDebug() << "RESET_LOCATION: locations:" << m_locationMark; + //qDebug() << "RESET_LOCATION: stored:" << m_locationMark->editor(); + delete m_locationMark; + m_locationMark = 0; +} + +void DebuggerPlugin::gotoLocation(const QString &fileName, int lineNumber, + bool setMarker) +{ + TextEditor::BaseTextEditor::openEditorAt(fileName, lineNumber); + if (setMarker) { + resetLocation(); + m_locationMark = new LocationMark(fileName, lineNumber); + } +} + +void DebuggerPlugin::changeStatus(int status) +{ + bool startIsContinue = (status == DebuggerInferiorStopped); + ICore *core = m_pm->getObject<Core::ICore>(); + if (startIsContinue) { + core->addAdditionalContext(m_gdbRunningContext); + core->updateContext(); + } else { + core->removeAdditionalContext(m_gdbRunningContext); + core->updateContext(); + } +} + +#include "debuggerplugin.moc" + +Q_EXPORT_PLUGIN(DebuggerPlugin) diff --git a/src/plugins/debugger/debuggerplugin.h b/src/plugins/debugger/debuggerplugin.h new file mode 100644 index 00000000000..0d7cf83bd33 --- /dev/null +++ b/src/plugins/debugger/debuggerplugin.h @@ -0,0 +1,111 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGERPLUGIN_H +#define DEBUGGERPLUGIN_H + +#include <projectexplorer/projectexplorer.h> +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE +class QAction; +class QCursor; +class QAbstractItemView; +QT_END_NAMESPACE + +namespace Core { class IEditor; } +namespace TextEditor { class ITextEditor; } + +namespace Debugger { +namespace Internal { + +class DebuggerManager; +class DebugMode; +class GdbOptionPage; +class TypeMacroPage; +class LocationMark; + +class DebuggerPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + DebuggerPlugin(); + ~DebuggerPlugin(); + +private: + bool initialize(const QStringList &arguments, QString *error_message); + void shutdown(); + void extensionsInitialized(); + +private slots: + void activatePreviousMode(); + void activateDebugMode(); + void queryCurrentTextEditor(QString *fileName, int *line, QObject **object); + void editorOpened(Core::IEditor *); + void editorAboutToClose(Core::IEditor *); + void changeStatus(int status); + void requestMark(TextEditor::ITextEditor *editor, int lineNumber); + void showToolTip(TextEditor::ITextEditor *editor, const QPoint &pnt, int pos); + + void querySessionValue(const QString &name, QVariant *value); + void setSessionValue(const QString &name, const QVariant &value); + void queryConfigValue(const QString &name, QVariant *value); + void setConfigValue(const QString &name, const QVariant &value); + + void resetLocation(); + void gotoLocation(const QString &fileName, int line, bool setMarker); + +private: + friend class DebuggerManager; + friend class DebugMode; // FIXME: Just a hack now so that it can access the views + + ProjectExplorer::ProjectExplorerPlugin *projectExplorer() const; + + DebuggerManager *m_manager; + DebugMode *m_debugMode; + + ExtensionSystem::PluginManager *m_pm; + GdbOptionPage *m_generalOptionPage; + TypeMacroPage *m_typeMacroPage; + + QString m_previousMode; + LocationMark *m_locationMark; + int m_gdbRunningContext; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGERPLUGIN_H diff --git a/src/plugins/debugger/debuggerrunner.cpp b/src/plugins/debugger/debuggerrunner.cpp new file mode 100644 index 00000000000..0254bebb9be --- /dev/null +++ b/src/plugins/debugger/debuggerrunner.cpp @@ -0,0 +1,161 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "debuggerrunner.h" + +#include "assert.h" +#include "debuggermanager.h" + +#include <projectexplorer/applicationrunconfiguration.h> +#include <projectexplorer/environment.h> +#include <projectexplorer/project.h> +#include <projectexplorer/projectexplorerconstants.h> + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> + +using namespace Debugger::Internal; + +using ProjectExplorer::RunConfiguration; +using ProjectExplorer::RunControl; +using ProjectExplorer::ApplicationRunConfiguration; + + +//////////////////////////////////////////////////////////////////////// +// +// DebuggerRunner +// +//////////////////////////////////////////////////////////////////////// + +DebuggerRunner::DebuggerRunner(DebuggerManager *manager) + : m_manager(manager) +{} + +bool DebuggerRunner::canRun(RunConfigurationPtr runConfiguration, const QString &mode) +{ + return mode == ProjectExplorer::Constants::DEBUGMODE + && !qSharedPointerCast<ApplicationRunConfiguration>(runConfiguration).isNull(); +} + +QString DebuggerRunner::displayName() const +{ + return QObject::tr("Debug"); +} + +RunControl* DebuggerRunner::run(RunConfigurationPtr runConfiguration, const QString &mode) +{ + Q_UNUSED(mode); + Q_ASSERT(mode == ProjectExplorer::Constants::DEBUGMODE); + ApplicationRunConfigurationPtr rc = + qSharedPointerCast<ApplicationRunConfiguration>(runConfiguration); + Q_ASSERT(rc); + //qDebug() << "***** Debugging" << rc->name() << rc->executable(); + return new DebuggerRunControl(m_manager, rc); +} + +QWidget *DebuggerRunner::configurationWidget(RunConfigurationPtr runConfiguration) +{ + // NBS TODO: Add GDB-specific configuration widget + Q_UNUSED(runConfiguration); + return 0; +} + + + +//////////////////////////////////////////////////////////////////////// +// +// DebuggerRunControl +// +//////////////////////////////////////////////////////////////////////// + + +DebuggerRunControl::DebuggerRunControl(DebuggerManager *manager, + QSharedPointer<ApplicationRunConfiguration> runConfiguration) + : RunControl(runConfiguration), m_manager(manager), m_running(false) +{ + connect(m_manager, SIGNAL(debuggingFinished()), + this, SLOT(debuggingFinished())); + connect(m_manager, SIGNAL(applicationOutputAvailable(QString, QString)), + this, SLOT(slotAddToOutputWindow(QString, QString))); + connect(m_manager, SIGNAL(inferiorPidChanged(qint64)), + this, SLOT(bringApplicationToForeground(qint64))); +} + +void DebuggerRunControl::start() +{ + m_running = true; + ApplicationRunConfigurationPtr rc = + qSharedPointerCast<ApplicationRunConfiguration>(runConfiguration()); + QWB_ASSERT(rc, return); + ProjectExplorer::Project *project = rc->project(); + QWB_ASSERT(project, return); + + m_manager->m_executable = rc->executable(); + m_manager->m_environment = rc->environment().toStringList(); + m_manager->m_workingDir = rc->workingDirectory(); + m_manager->m_processArgs = rc->commandLineArguments(); + m_manager->m_buildDir = + project->buildDirectory(project->activeBuildConfiguration()); + //<daniel> andre: + "\qtc-gdbmacros\" + + //emit addToOutputWindow(this, tr("Debugging %1").arg(m_executable)); + if (m_manager->startNewDebugger(DebuggerManager::startInternal)) + emit started(); + else + debuggingFinished(); +} + +void DebuggerRunControl::slotAddToOutputWindow(const QString &prefix, const QString &line) +{ + Q_UNUSED(prefix); + foreach (const QString &l, line.split('\n')) + emit addToOutputWindow(this, prefix + l); + //emit addToOutputWindow(this, prefix + line); +} + +void DebuggerRunControl::stop() +{ + m_manager->exitDebugger(); +} + +void DebuggerRunControl::debuggingFinished() +{ + m_running = false; + //emit addToOutputWindow(this, tr("Debugging %1 finished").arg(m_executable)); + emit finished(); +} + +bool DebuggerRunControl::isRunning() const +{ + return m_running; +} diff --git a/src/plugins/debugger/debuggerrunner.h b/src/plugins/debugger/debuggerrunner.h new file mode 100644 index 00000000000..cf8a8d2184d --- /dev/null +++ b/src/plugins/debugger/debuggerrunner.h @@ -0,0 +1,96 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGERRUNNER_H +#define DEBUGGERRUNNER_H + +#include <projectexplorer/runconfiguration.h> + +namespace ProjectExplorer { +class ApplicationRunConfiguration; +} + +namespace Debugger { +namespace Internal { + +class DebuggerManager; +class StartData; + +typedef QSharedPointer<ProjectExplorer::RunConfiguration> + RunConfigurationPtr; + +typedef QSharedPointer<ProjectExplorer::ApplicationRunConfiguration> + ApplicationRunConfigurationPtr; + +class DebuggerRunner : public ProjectExplorer::IRunConfigurationRunner +{ + Q_OBJECT + +public: + explicit DebuggerRunner(DebuggerManager *manager); + + virtual bool canRun(RunConfigurationPtr runConfiguration, const QString &mode); + virtual QString displayName() const; + virtual ProjectExplorer::RunControl *run(RunConfigurationPtr runConfiguration, + const QString &mode); + virtual QWidget *configurationWidget(RunConfigurationPtr runConfiguration); + +private: + DebuggerManager *m_manager; +}; + + +class DebuggerRunControl : public ProjectExplorer::RunControl +{ + Q_OBJECT + +public: + DebuggerRunControl(DebuggerManager *manager, + ApplicationRunConfigurationPtr runConfiguration); + + virtual void start(); + virtual void stop(); + virtual bool isRunning() const; + +private slots: + void debuggingFinished(); + void slotAddToOutputWindow(const QString &prefix, const QString &line); + +private: + DebuggerManager *m_manager; + bool m_running; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGERRUNNER_H diff --git a/src/plugins/debugger/disassemblerhandler.cpp b/src/plugins/debugger/disassemblerhandler.cpp new file mode 100644 index 00000000000..02d31722550 --- /dev/null +++ b/src/plugins/debugger/disassemblerhandler.cpp @@ -0,0 +1,187 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "disassemblerhandler.h" + +#include "assert.h" + +#include <QtCore/QDebug> +#include <QtCore/QAbstractTableModel> + +#include <QtGui/QIcon> + +using namespace Debugger; +using namespace Debugger::Internal; + + +////////////////////////////////////////////////////////////////// +// +// DisassemblerModel +// +////////////////////////////////////////////////////////////////// + +/*! A model to represent the stack in a QTreeView. */ +class Debugger::Internal::DisassemblerModel : public QAbstractTableModel +{ +public: + DisassemblerModel(QObject *parent); + + // ItemModel + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + // Properties + void setLines(const QList<DisassemblerLine> &lines); + QList<DisassemblerLine> lines() const; + void setCurrentLine(int line) { m_currentLine = line; } + +private: + friend class DisassemblerHandler; + QList<DisassemblerLine> m_lines; + int m_currentLine; + QIcon m_positionIcon; + QIcon m_emptyIcon; +}; + +DisassemblerModel::DisassemblerModel(QObject *parent) + : QAbstractTableModel(parent), m_currentLine(0) +{ + m_emptyIcon = QIcon(":/gdbdebugger/images/empty.svg"); + m_positionIcon = QIcon(":/gdbdebugger/images/location.svg"); +} + +int DisassemblerModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return m_lines.size(); +} + +int DisassemblerModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 3; +} + +QVariant DisassemblerModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= m_lines.size()) + return QVariant(); + + const DisassemblerLine &line = m_lines.at(index.row()); + + if (role == Qt::DisplayRole) { + switch (index.column()) { + case 0: + return line.addressDisplay; + case 1: + return line.symbolDisplay; + case 2: + return line.mnemonic; + } + } else if (role == Qt::ToolTipRole) { + return QString(); + } else if (role == Qt::DecorationRole && index.column() == 0) { + // Return icon that indicates whether this is the active stack frame + return (index.row() == m_currentLine) ? m_positionIcon : m_emptyIcon; + } + + return QVariant(); +} + +QVariant DisassemblerModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + static const char * const headers[] = { + QT_TR_NOOP("Address"), + QT_TR_NOOP("Symbol"), + QT_TR_NOOP("Mnemonic"), + }; + if (section < 3) + return tr(headers[section]); + } + return QVariant(); +} + +void DisassemblerModel::setLines(const QList<DisassemblerLine> &lines) +{ + m_lines = lines; + if (m_currentLine >= m_lines.size()) + m_currentLine = m_lines.size() - 1; + reset(); +} + +QList<DisassemblerLine> DisassemblerModel::lines() const +{ + return m_lines; +} + + + +////////////////////////////////////////////////////////////////// +// +// DisassemblerHandler +// +////////////////////////////////////////////////////////////////// + +DisassemblerHandler::DisassemblerHandler() +{ + m_model = new DisassemblerModel(this); +} + +void DisassemblerHandler::removeAll() +{ + m_model->m_lines.clear(); +} + +QAbstractItemModel *DisassemblerHandler::model() const +{ + return m_model; +} + +void DisassemblerHandler::setLines(const QList<DisassemblerLine> &lines) +{ + m_model->setLines(lines); +} + +QList<DisassemblerLine> DisassemblerHandler::lines() const +{ + return m_model->lines(); +} + +void DisassemblerHandler::setCurrentLine(int line) +{ + m_model->setCurrentLine(line); +} diff --git a/src/plugins/debugger/disassemblerhandler.h b/src/plugins/debugger/disassemblerhandler.h new file mode 100644 index 00000000000..9de497a1b4c --- /dev/null +++ b/src/plugins/debugger/disassemblerhandler.h @@ -0,0 +1,78 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DISASSEMBLERHANDLER_H +#define DISASSEMBLERHANDLER_H + +#include <QtCore/QObject> +#include <QtCore/QAbstractItemModel> + +#include <QtGui/QIcon> + +namespace Debugger { +namespace Internal { + +class DisassemblerLine +{ +public: + QString address; + QString symbol; + QString addressDisplay; + QString symbolDisplay; + QString mnemonic; +}; + +class DisassemblerModel; + +class DisassemblerHandler : public QObject +{ + Q_OBJECT + +public: + DisassemblerHandler(); + QAbstractItemModel *model() const; + +public slots: + void removeAll(); + + void setLines(const QList<DisassemblerLine> &lines); + QList<DisassemblerLine> lines() const; + void setCurrentLine(int line); + +private: + DisassemblerModel *m_model; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DISASSEMBLERHANDLER_H diff --git a/src/plugins/debugger/disassemblerwindow.cpp b/src/plugins/debugger/disassemblerwindow.cpp new file mode 100644 index 00000000000..8130031d705 --- /dev/null +++ b/src/plugins/debugger/disassemblerwindow.cpp @@ -0,0 +1,140 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "disassemblerwindow.h" + +#include <QAction> +#include <QDebug> +#include <QHeaderView> +#include <QMenu> +#include <QResizeEvent> + + +using namespace Debugger::Internal; + +DisassemblerWindow::DisassemblerWindow() + : m_alwaysResizeColumnsToContents(true), m_alwaysReloadContents(false) +{ + setWindowTitle(tr("Disassembler")); + setSortingEnabled(false); + setAlternatingRowColors(true); + setRootIsDecorated(false); + header()->hide(); + //setIconSize(QSize(10, 10)); + //setWindowIcon(QIcon(":/gdbdebugger/images/debugger_breakpoints.png")); + //QHeaderView *hv = header(); + //hv->setDefaultAlignment(Qt::AlignLeft); + //hv->setClickable(true); + //hv->setSortIndicatorShown(true); +} + +void DisassemblerWindow::resizeEvent(QResizeEvent *ev) +{ + //QHeaderView *hv = header(); + //int totalSize = ev->size().width() - 110; + //hv->resizeSection(0, 60); + //hv->resizeSection(1, (totalSize * 50) / 100); + //hv->resizeSection(2, (totalSize * 50) / 100); + //hv->resizeSection(3, 50); + QTreeView::resizeEvent(ev); +} + +void DisassemblerWindow::contextMenuEvent(QContextMenuEvent *ev) +{ + QMenu menu; + //QTreeWidgetItem *item = itemAt(ev->pos()); + QAction *act1 = new QAction("Adjust column widths to contents", &menu); + QAction *act2 = new QAction("Always adjust column widths to contents", &menu); + act2->setCheckable(true); + act2->setChecked(m_alwaysResizeColumnsToContents); + QAction *act3 = new QAction("Reload disassembler listing", &menu); + QAction *act4 = new QAction("Always reload disassembler listing", &menu); + act4->setCheckable(true); + act4->setChecked(m_alwaysReloadContents); + //if (item) { + // menu.addAction(act0); + // menu.addSeparator(); + //} + menu.addAction(act3); + //menu.addAction(act4); + menu.addSeparator(); + menu.addAction(act1); + menu.addAction(act2); + + QAction *act = menu.exec(ev->globalPos()); + + if (act == act1) + resizeColumnsToContents(); + else if (act == act2) + setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents); + else if (act == act3) + reloadContents(); + else if (act == act2) + setAlwaysReloadContents(!m_alwaysReloadContents); +} + +void DisassemblerWindow::resizeColumnsToContents() +{ + resizeColumnToContents(0); + resizeColumnToContents(1); + resizeColumnToContents(2); +} + +void DisassemblerWindow::setAlwaysResizeColumnsToContents(bool on) +{ + m_alwaysResizeColumnsToContents = on; + QHeaderView::ResizeMode mode = on + ? QHeaderView::ResizeToContents : QHeaderView::Interactive; + header()->setResizeMode(0, mode); + header()->setResizeMode(1, mode); + header()->setResizeMode(2, mode); +} + +void DisassemblerWindow::setAlwaysReloadContents(bool on) +{ + m_alwaysReloadContents = on; + if (m_alwaysReloadContents) + reloadContents(); +} + +void DisassemblerWindow::reloadContents() +{ + emit reloadDisassemblerRequested(); +} + + +void DisassemblerWindow::setModel(QAbstractItemModel *model) +{ + QTreeView::setModel(model); + setAlwaysResizeColumnsToContents(true); +} + diff --git a/src/plugins/debugger/disassemblerwindow.h b/src/plugins/debugger/disassemblerwindow.h new file mode 100644 index 00000000000..3715afca213 --- /dev/null +++ b/src/plugins/debugger/disassemblerwindow.h @@ -0,0 +1,71 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_DISASSEMBLERWINDOW_H +#define DEBUGGER_DISASSEMBLERWINDOW_H + +#include <QTreeView> + +namespace Debugger { +namespace Internal { + +class DisassemblerWindow : public QTreeView +{ + Q_OBJECT + +public: + DisassemblerWindow(); + void setModel(QAbstractItemModel *model); + +signals: + void reloadDisassemblerRequested(); + +public slots: + void resizeColumnsToContents(); + void setAlwaysResizeColumnsToContents(bool on); + void reloadContents(); + void setAlwaysReloadContents(bool on); + +protected: + void resizeEvent(QResizeEvent *ev); + void contextMenuEvent(QContextMenuEvent *ev); + +private: + bool m_alwaysResizeColumnsToContents; + bool m_alwaysReloadContents; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_DISASSEMBLERWINDOW_H + diff --git a/src/plugins/debugger/gdbengine.cpp b/src/plugins/debugger/gdbengine.cpp new file mode 100644 index 00000000000..9a29bc8675c --- /dev/null +++ b/src/plugins/debugger/gdbengine.cpp @@ -0,0 +1,4035 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "gdbengine.h" + +#include "assert.h" +#include "debuggerconstants.h" +#include "debuggermanager.h" +#include "gdbmi.h" +#include "procinterrupt.h" + +#include "disassemblerhandler.h" +#include "breakhandler.h" +#include "moduleshandler.h" +#include "registerhandler.h" +#include "stackhandler.h" +#include "watchhandler.h" + +#include "startexternaldialog.h" +#include "attachexternaldialog.h" + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QTime> +#include <QtCore/QTimer> + +#include <QtGui/QAction> +#include <QtGui/QLabel> +#include <QtGui/QMainWindow> +#include <QtGui/QMessageBox> +#include <QtGui/QToolTip> + +#if defined(Q_OS_LINUX) || defined(Q_OS_MAC) +#include <unistd.h> +#include <dlfcn.h> +#endif + +using namespace Debugger; +using namespace Debugger::Internal; +using namespace Debugger::Constants; + +Q_DECLARE_METATYPE(Debugger::Internal::GdbMi); + +//#define DEBUG_PENDING 1 +//#define DEBUG_SUBITEM 1 + +#if DEBUG_PENDING +# define PENDING_DEBUG(s) qDebug() << s +#else +# define PENDING_DEBUG(s) +#endif + +static const QString tooltipIName = "tooltip"; + +/////////////////////////////////////////////////////////////////////// +// +// GdbSettings +// +/////////////////////////////////////////////////////////////////////// + +GdbSettings &Debugger::Internal::theGdbSettings() +{ + static GdbSettings settings; + return settings; +} + + +/////////////////////////////////////////////////////////////////////// +// +// GdbCommandType +// +/////////////////////////////////////////////////////////////////////// + +enum GdbCommandType +{ + GdbInvalidCommand = 0, + + GdbShowVersion = 100, + GdbFileExecAndSymbols, + GdbQueryPwd, + GdbQuerySources, + GdbQuerySources2, + GdbAsyncOutput2, + GdbExecRun, + GdbExecRunToFunction, + GdbExecStep, + GdbExecNext, + GdbExecStepI, + GdbExecNextI, + GdbExecContinue, + GdbExecFinish, + GdbExecJumpToLine, + GdbExecInterrupt, + GdbInfoShared, + GdbInfoProc, + GdbQueryDataDumper1, + GdbQueryDataDumper2, + GdbInitializeSocket1, + + BreakCondition = 200, + BreakEnablePending, + BreakSetAnnotate, + BreakDelete, + BreakList, + BreakIgnore, + BreakInfo, + BreakInsert, + BreakInsert1, + + DisassemblerList = 300, + + ModulesList = 400, + + RegisterListNames = 500, + RegisterListValues, + + StackSelectThread = 600, + StackListThreads, + StackListFrames, + StackListLocals, + StackListArguments, + + WatchVarAssign = 700, // data changed by user + WatchVarListChildren, + WatchVarCreate, + WatchEvaluateExpression, + WatchToolTip, + WatchDumpCustomSetup, + WatchDumpCustomValue1, // waiting for gdb ack + WatchDumpCustomValue2, // waiting for actual data + WatchDumpCustomEditValue, +}; + +QString dotEscape(QString str) +{ + str.replace(' ', '.'); + str.replace('\\', '.'); + str.replace('/', '.'); + return str; +} + +QString currentTime() +{ + return QTime::currentTime().toString("hh:mm:ss.zzz"); +} + +static int ¤tToken() +{ + static int token = 0; + return token; +} + +static bool isSkippableFunction(const QString &funcName, const QString &fileName) +{ + if (fileName.endsWith("kernel/qobject.cpp")) + return true; + if (fileName.endsWith("kernel/moc_qobject.cpp")) + return true; + if (fileName.endsWith("kernel/qmetaobject.cpp")) + return true; + if (fileName.endsWith(".moc")) + return true; + + if (funcName.endsWith("::qt_metacall")) + return true; + + return false; +} + +static bool isLeavableFunction(const QString &funcName, const QString &fileName) +{ + if (funcName.endsWith("QObjectPrivate::setCurrentSender")) + return true; + if (fileName.endsWith("kernel/qmetaobject.cpp") + && funcName.endsWith("QMetaObject::methodOffset")) + return true; + if (fileName.endsWith("kernel/qobject.h")) + return true; + if (fileName.endsWith("kernel/qobject.cpp") + && funcName.endsWith("QObjectConnectionListVector::at")) + return true; + if (fileName.endsWith("kernel/qobject.cpp") + && funcName.endsWith("~QObject")) + return true; + if (fileName.endsWith("thread/qmutex.cpp")) + return true; + if (fileName.endsWith("thread/qthread.cpp")) + return true; + if (fileName.endsWith("thread/qthread_unix.cpp")) + return true; + if (fileName.endsWith("thread/qmutex.h")) + return true; + if (fileName.contains("thread/qbasicatomic")) + return true; + if (fileName.contains("thread/qorderedmutexlocker_p")) + return true; + if (fileName.contains("arch/qatomic")) + return true; + if (fileName.endsWith("tools/qvector.h")) + return true; + if (fileName.endsWith("tools/qlist.h")) + return true; + if (fileName.endsWith("tools/qhash.h")) + return true; + if (fileName.endsWith("tools/qmap.h")) + return true; + if (fileName.endsWith("tools/qstring.h")) + return true; + if (fileName.endsWith("global/qglobal.h")) + return true; + + return false; +} + + +/////////////////////////////////////////////////////////////////////// +// +// GdbEngine +// +/////////////////////////////////////////////////////////////////////// + +GdbEngine::GdbEngine(DebuggerManager *parent) +{ + q = parent; + qq = parent->engineInterface(); + init(); +} + +GdbEngine::~GdbEngine() +{ +} + +void GdbEngine::init() +{ + m_pendingRequests = 0; + m_gdbVersion = 100; + m_shared = 0; + qq->debugDumpersAction()->setChecked(false); + + m_oldestAcceptableToken = -1; + + // Gdb Process interaction + connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)), this, + SLOT(gdbProcError(QProcess::ProcessError))); + connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()), this, + SLOT(readGdbStandardOutput())); + connect(&m_gdbProc, SIGNAL(readyReadStandardError()), this, + SLOT(readGdbStandardError())); + connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)), q, + SLOT(exitDebugger())); + + // Custom dumpers + //m_dumperServerConnection = 0; + //m_dumperServer = new DumperServer(this); + //QString name = "gdb-" + + // QDateTime::currentDateTime().toString("yyyy_MM_dd-hh_mm_ss_zzz"); + //m_dumperServer->listen(name); + //connect(m_dumperServer, SIGNAL(newConnection()), + // this, SLOT(acceptConnection())); + + //if (!m_dumperServer->isListening()) { + // QMessageBox::critical(q->mainWindow(), tr("Dumper Server Setup Failed"), + // tr("Unable to create server listening for data: %1.\n" + // "Server name: %2").arg(m_dumperServer->errorString(), name), + // QMessageBox::Retry | QMessageBox::Cancel); + // } + + connect(qq->debugDumpersAction(), SIGNAL(toggled(bool)), + this, SLOT(setDebugDumpers(bool))); + + connect(qq->useCustomDumpersAction(), SIGNAL(toggled(bool)), + this, SLOT(setCustomDumpersWanted(bool))); + + // Output + connect(this, SIGNAL(gdbResponseAvailable()), + this, SLOT(handleResponse()), Qt::QueuedConnection); + + connect(this, SIGNAL(gdbOutputAvailable(QString,QString)), + q, SLOT(showDebuggerOutput(QString,QString)), + Qt::QueuedConnection); + connect(this, SIGNAL(gdbInputAvailable(QString,QString)), + q, SLOT(showDebuggerInput(QString,QString)), + Qt::QueuedConnection); + connect(this, SIGNAL(applicationOutputAvailable(QString,QString)), + q, SLOT(showApplicationOutput(QString,QString)), + Qt::QueuedConnection); +} + +void GdbEngine::gdbProcError(QProcess::ProcessError error) +{ + QString msg; + switch (error) { + case QProcess::FailedToStart: + msg = QString(tr("The Gdb process failed to start. Either the " + "invoked program '%1' is missing, or you may have insufficient " + "permissions to invoke the program.")).arg(theGdbSettings().m_gdbCmd); + break; + case QProcess::Crashed: + msg = tr("The Gdb process crashed some time after starting " + "successfully."); + break; + case QProcess::Timedout: + msg = tr("The last waitFor...() function timed out. " + "The state of QProcess is unchanged, and you can try calling " + "waitFor...() again."); + break; + case QProcess::WriteError: + msg = tr("An error occurred when attempting to write " + "to the Gdb process. For example, the process may not be running, " + "or it may have closed its input channel."); + break; + case QProcess::ReadError: + msg = tr("An error occurred when attempting to read from " + "the Gdb process. For example, the process may not be running."); + break; + default: + msg = tr("An unknown error in the Gdb process occurred. " + "This is the default return value of error()."); + } + + q->showStatusMessage(msg, 5000); + QMessageBox::critical(q->mainWindow(), tr("Error"), msg); + // act as if it was closed by the core + q->exitDebugger(); +} + +static void skipSpaces(const char *&from, const char *to) +{ + while (from != to && QChar(*from).isSpace()) + ++from; +} + +static inline bool isNameChar(char c) +{ + // could be 'stopped' or 'shlibs-added' + return (c >= 'a' && c <= 'z') || c == '-'; +} + +#if 0 +static void dump(const char *first, const char *middle, const QString & to) +{ + QByteArray ba(first, middle - first); + Q_UNUSED(to); + // note that qDebug cuts off output after a certain size... (bug?) + qDebug("\n>>>>> %s\n%s\n====\n%s\n<<<<<\n", + qPrintable(currentTime()), + qPrintable(QString(ba).trimmed()), + qPrintable(to.trimmed())); + //qDebug() << ""; + //qDebug() << qPrintable(currentTime()) + // << " Reading response: " << QString(ba).trimmed() << "\n"; +} +#endif + +static void skipTerminator(const char *&from, const char *to) +{ + skipSpaces(from, to); + // skip '(gdb)' + if (from[0] == '(' && from[1] == 'g' && from[3] == 'b' && from[4] == ')') + from += 5; + skipSpaces(from, to); +} + +// called asyncronously as response to Gdb stdout output in +// gdbResponseAvailable() +void GdbEngine::handleResponse() +{ + static QTime lastTime; + + emit gdbOutputAvailable(" ", currentTime()); + emit gdbOutputAvailable("stdout:", m_inbuffer); + +#if 0 + qDebug() // << "#### start response handling #### " + << currentTime() + << lastTime.msecsTo(QTime::currentTime()) << "ms," + << "buf: " << m_inbuffer.left(1500) << "..." + //<< "buf: " << m_inbuffer + << "size:" << m_inbuffer.size(); +#else + //qDebug() << "buf: " << m_inbuffer; +#endif + + lastTime = QTime::currentTime(); + + while (1) { + if (m_inbuffer.isEmpty()) + break; + + const char *from = m_inbuffer.constData(); + // FIXME: check for line ending in '\n(gdb)\n' + const char *to = from + m_inbuffer.size(); + const char *inner; + + //const char *oldfrom = from; + + //skipSpaces(from, to); + skipTerminator(from, to); + int token = -1; + + // token is a sequence of numbers + for (inner = from; inner != to; ++inner) + if (*inner < '0' || *inner > '9') + break; + if (from != inner) { + token = QString(QByteArray(from, inner - from)).toInt(); + from = inner; + //qDebug() << "found token " << token; + } + + if (from == to) { + //qDebug() << "Returning: " << toString(); + break; + } + + if (token == -1 && *from != '&' && *from != '~') { + // FIXME: On Linux the application's std::out is merged in here. + // High risk of falsely interpreting this as MI output. + // We assume that we _always_ use tokens, so not finding a token + // is a positive indication for the presence of application output. + QString s; + while (from != to && *from != '\n') + s += *from++; + //qDebug() << "UNREQUESTED DATA " << s << " TAKEN AS APPLICATION OUTPUT"; + s += '\n'; + + m_inbuffer = QByteArray(from, to - from); + emit applicationOutputAvailable("app-stdout: ", s); + continue; + } + + // next char decides kind of record + const char c = *from++; + //qDebug() << "CODE:" << c; + + switch (c) { + case '*': + case '+': + case '=': { + QByteArray asyncClass; + for (; from != to; ++from) { + const char c = *from; + if (!isNameChar(c)) + break; + asyncClass += *from; + } + //qDebug() << "ASYNCCLASS" << asyncClass; + + GdbMi record; + while (from != to && *from == ',') { + ++from; // skip ',' + GdbMi data; + data.parseResultOrValue(from, to); + if (data.isValid()) { + //qDebug() << "parsed response: " << data.toString(); + record.m_children += data; + record.m_type = GdbMi::Tuple; + } + } + //dump(oldfrom, from, record.toString()); + skipTerminator(from, to); + m_inbuffer = QByteArray(from, to - from); + if (asyncClass == "stopped") { + handleAsyncOutput(record); + } else { + qDebug() << "INGNORED ASYNC OUTPUT " << record.toString(); + } + break; + } + + case '~': + case '@': + case '&': { + QString data = GdbMi::parseCString(from, to); + handleStreamOutput(data, c); + //dump(oldfrom, from, record.toString()); + m_inbuffer = QByteArray(from, to - from); + break; + } + + case '#': { + //qDebug() << "CUSTOM OUTPUT, TOKEN" << token; + QString str; + for (; from != to && *from >= '0' && *from <= '9'; ++from) + str += QLatin1Char(*from); + ++from; // skip the ' ' + int len = str.toInt(); + QByteArray ba(from, len); + from += len; + m_inbuffer = QByteArray(from, to - from); + m_customOutputForToken[token] += QString(ba); + break; + } + + case '^': { + GdbResultRecord record; + + record.token = token; + + for (inner = from; inner != to; ++inner) + if (*inner < 'a' || *inner > 'z') + break; + + QByteArray resultClass(from, inner - from); + + if (resultClass == "done") + record.resultClass = GdbResultDone; + else if (resultClass == "running") + record.resultClass = GdbResultRunning; + else if (resultClass == "connected") + record.resultClass = GdbResultConnected; + else if (resultClass == "error") + record.resultClass = GdbResultError; + else if (resultClass == "exit") + record.resultClass = GdbResultExit; + else + record.resultClass = GdbResultUnknown; + + from = inner; + skipSpaces(from, to); + if (from != to && *from == ',') { + ++from; + record.data.parseTuple_helper(from, to); + record.data.m_type = GdbMi::Tuple; + record.data.m_name = "data"; + } + skipSpaces(from, to); + skipTerminator(from, to); + + //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput; + //qDebug() << "\nTARGET STREAM:" + m_pendingTargetStreamOutput; + //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput; + record.data.setStreamOutput("logstreamoutput", + m_pendingLogStreamOutput); + record.data.setStreamOutput("targetstreamoutput", + m_pendingTargetStreamOutput); + record.data.setStreamOutput("consolestreamoutput", + m_pendingConsoleStreamOutput); + QByteArray custom = m_customOutputForToken[token]; + if (!custom.isEmpty()) + record.data.setStreamOutput("customvaluecontents", + '{' + custom + '}'); + //m_customOutputForToken.remove(token); + m_pendingLogStreamOutput.clear(); + m_pendingTargetStreamOutput.clear(); + m_pendingConsoleStreamOutput.clear(); + + //dump(oldfrom, from, record.toString()); + m_inbuffer = QByteArray(from, to - from); + handleResultRecord(record); + break; + } + default: { + qDebug() << "FIXME: UNKNOWN CODE: " << c << " IN " << m_inbuffer; + m_inbuffer = QByteArray(from, to - from); + break; + } + } + } + + //qDebug() << "##### end response handling ####\n\n\n" + // << currentTime() << lastTime.msecsTo(QTime::currentTime()); + lastTime = QTime::currentTime(); +} + +#ifdef Q_OS_MAC +static void fixMac(QByteArray &out) +{ + // HACK: gdb on Mac mixes MI1 and MI2 syntax. Not nice. + // it returns: 9^done,locals={{name="a"},{name="w"}} + // instead of: 9^done,locals=[{name="a"},{name="w"}] + if (!out.contains("locals={{name")) + return; + + static const QByteArray termArray("(gdb) "); + int pos = out.indexOf(termArray); + if (pos == -1) + return; + + int pos1 = out.indexOf("={{"); + if (pos1 == -1) + return; + + int pos2 = out.indexOf("]]"); + if (pos2 == -1) + return; + + if (pos1 < pos && pos2 < pos) { + out[pos1 + 1] = '['; + out[pos2 + 1] = ']'; + } +} +#endif + +void GdbEngine::readGdbStandardError() +{ + QByteArray err = m_gdbProc.readAllStandardError(); + emit applicationOutputAvailable("app-stderr:", err); +} + +void GdbEngine::readGdbStandardOutput() +{ + // This is the function called whenever the Gdb process created + // output. As a rule of thumb, stdout contains _real_ Gdb output + // as responses to our command (with exception of the data dumpers) + // and "spontaneous" events like messages on loaded shared libraries. + // Otoh, stderr contains application output produced by qDebug etc. + // There is no organized way to pass application stdout output + + // The result of custom data dumpers arrives over the socket _before_ + // the corresponding Gdb "^done" message arrives here over stdout + // and is merged into the response via m_pendingCustomValueContents. + + // Note that this code here runs syncronized to the arriving + // output. The completed response will be signalled by a queued + // connection to the handlers. + + QByteArray out = m_gdbProc.readAllStandardOutput(); + + //qDebug() << "\n\n\nPLUGIN OUT: '" << out.data() << "'\n\n\n"; + + #ifdef Q_OS_MAC + fixMac(out); + #endif + + m_inbuffer.append(out); + //QWB_ASSERT(!m_inbuffer.isEmpty(), return); + + char c = m_inbuffer[m_inbuffer.size() - 1]; + static const QByteArray termArray("(gdb) "); + if (out.indexOf(termArray) == -1 && c != 10 && c != 13) { + //qDebug() << "\n\nBuffer not yet filled, waiting for more data to arrive"; + //qDebug() << m_inbuffer.data() << m_inbuffer.size(); + //qDebug() << "\n\n"; + return; + } + + emit gdbResponseAvailable(); +} + +void GdbEngine::interruptInferior() +{ + if (m_gdbProc.state() == QProcess::NotRunning) + return; + + if (q->m_attachedPID) { + if (interruptProcess(q->m_attachedPID)) + qq->notifyInferiorStopped(); + return; + } + +#ifdef Q_OS_MAC + sendCommand("-exec-interrupt", GdbExecInterrupt); + qq->notifyInferiorStopped(); +#else + if (interruptChildProcess(m_gdbProc.pid())) + qq->notifyInferiorStopped(); +#endif +} + +void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0) +{ + int pid = pid0.toInt(); + if (pid == 0) { + qDebug() << "Cannot parse PID from " << pid0; + return; + } + if (pid == m_inferiorPid) + return; + m_inferiorPid = pid; + qq->notifyInferiorPidChanged(pid); +} + +void GdbEngine::sendSynchronizedCommand(const QString & command, + int type, const QVariant &cookie, bool needStop) +{ + sendCommand(command, type, cookie, needStop, true); +} + +void GdbEngine::sendCommand(const QString &command, int type, + const QVariant &cookie, bool needStop, bool synchronized) +{ + if (m_gdbProc.state() == QProcess::NotRunning) { + //qDebug() << "NO GDB PROCESS RUNNING, CMD IGNORED:" << command; + return; + } + + bool temporarilyStopped = false; + if (needStop && q->status() == DebuggerInferiorRunning) { + q->showStatusMessage(tr("Temporarily stopped"), -1); + interruptInferior(); + temporarilyStopped = true; + } + + ++currentToken(); + if (synchronized) { + ++m_pendingRequests; + PENDING_DEBUG(" TYPE " << type << " INCREMENTS PENDING TO: " + << m_pendingRequests << command); + } else { + PENDING_DEBUG(" UNKNOWN TYPE " << type << " LEAVES PENDING AT: " + << m_pendingRequests << command); + } + + GdbCookie cmd; + cmd.synchronized = synchronized; + cmd.command = command; + cmd.command = QString::number(currentToken()) + cmd.command; + if (cmd.command.contains("%1")) + cmd.command = cmd.command.arg(currentToken()); + cmd.type = type; + cmd.cookie = cookie; + + m_cookieForToken[currentToken()] = cmd; + + //qDebug() << ""; + if (!command.isEmpty()) { + //qDebug() << qPrintable(currentTime()) << "RUNNING << cmd.command; + m_gdbProc.write(cmd.command.toLatin1() + "\r\n"); + //emit gdbInputAvailable(QString(), " " + currentTime()); + emit gdbInputAvailable(QString(), "[" + currentTime() + "] " + cmd.command); + //emit gdbInputAvailable(QString(), cmd.command); + } + + if (temporarilyStopped) + sendCommand("-exec-continue"); + + // slows down + //qApp->processEvents(); +} + +void GdbEngine::handleResultRecord(const GdbResultRecord &record) +{ + //qDebug() << "TOKEN: " << record.token + // << " ACCEPTABLE: " << m_oldestAcceptableToken; + //qDebug() << ""; + //qDebug() << qPrintable(currentTime()) << "Reading response: " + // << record.toString() << "\n"; + //qDebug() << "\nRESULT" << record.token << record.toString(); + + int token = record.token; + if (token == -1) + return; + + if (!m_cookieForToken.contains(token)) { + qDebug() << "NO SUCH TOKEN (ANYMORE): " << token; + return; + } + + GdbCookie cmd = m_cookieForToken.take(token); + + // FIXME: this falsely rejects results from the custom dumper recognition + // procedure, too... + if (record.token < m_oldestAcceptableToken) { + //qDebug() << "### SKIPPING OLD RESULT " << record.toString(); + //QMessageBox::information(m_mainWindow, tr("Skipped"), "xxx"); + return; + } + + // We get _two_ results for a '-exec-foo' command: First a + // 'running' notification, then a 'stopped' or similar. + // So put it back. + if (record.resultClass == GdbResultRunning) + m_cookieForToken[token] = cmd; + +#if 0 + qDebug() << "# handleOutput, " + << "cmd type: " << cmd.type + << "cmd synchronized: " << cmd.synchronized + << "\n record: " << record.toString(); +#endif + + // << "\n data: " << record.data.toString(true); + + if (cmd.type != GdbInvalidCommand) + handleResult(record, cmd.type, cmd.cookie); + + if (cmd.synchronized) { + --m_pendingRequests; + PENDING_DEBUG(" TYPE " << cmd.type << " DECREMENTS PENDING TO: " + << m_pendingRequests << cmd.command); + if (m_pendingRequests == 0) + updateWatchModel2(); + } else { + PENDING_DEBUG(" UNKNOWN TYPE " << cmd.type << " LEAVES PENDING AT: " + << m_pendingRequests << cmd.command); + } +} + +void GdbEngine::handleResult(const GdbResultRecord & record, int type, + const QVariant & cookie) +{ + switch (type) { + case GdbExecNext: + case GdbExecStep: + case GdbExecNextI: + case GdbExecStepI: + case GdbExecContinue: + case GdbExecFinish: + // evil code sharing + case GdbExecRun: + handleExecRun(record); + break; + case GdbInfoProc: + handleInfoProc(record); + break; + + case GdbShowVersion: + handleShowVersion(record); + break; + case GdbFileExecAndSymbols: + handleFileExecAndSymbols(record); + break; + case GdbExecRunToFunction: + // that should be "^running". We need to handle the resulting + // "Stopped" + //handleExecRunToFunction(record); + break; + case GdbExecInterrupt: + break; + case GdbExecJumpToLine: + handleExecJumpToLine(record); + break; + case GdbQueryPwd: + handleQueryPwd(record); + break; + case GdbQuerySources: + handleQuerySources(record); + break; + case GdbQuerySources2: + handleQuerySources2(record, cookie); + break; + case GdbAsyncOutput2: + handleAsyncOutput2(cookie.value<GdbMi>()); + break; + case GdbInfoShared: + handleInfoShared(record); + break; + case GdbInitializeSocket1: + //qDebug() << " INIT SOCKET" << record.toString(); + break; + case GdbQueryDataDumper1: + handleQueryDataDumper1(record); + break; + case GdbQueryDataDumper2: + handleQueryDataDumper2(record); + break; + + case BreakList: + handleBreakList(record); + break; + case BreakInsert: + handleBreakInsert(record, cookie.toInt()); + break; + case BreakInsert1: + handleBreakInsert1(record, cookie.toInt()); + break; + case BreakInfo: + handleBreakInfo(record, cookie.toInt()); + break; + case BreakEnablePending: + case BreakDelete: + // nothing + break; + case BreakIgnore: + handleBreakIgnore(record, cookie.toInt()); + break; + case BreakCondition: + handleBreakCondition(record, cookie.toInt()); + break; + + case DisassemblerList: + handleDisassemblerList(record, cookie.toString()); + break; + + case ModulesList: + handleModulesList(record); + break; + + case RegisterListNames: + handleRegisterListNames(record); + break; + case RegisterListValues: + handleRegisterListValues(record); + break; + + case StackListFrames: + handleStackListFrames(record); + break; + case StackListThreads: + handleStackListThreads(record, cookie.toInt()); + break; + case StackSelectThread: + handleStackSelectThread(record, cookie.toInt()); + break; + case StackListLocals: + handleStackListLocals(record); + break; + case StackListArguments: + handleStackListArguments(record); + break; + + case WatchVarListChildren: + handleVarListChildren(record, cookie.value<WatchData>()); + break; + case WatchVarCreate: + handleVarCreate(record, cookie.value<WatchData>()); + break; + case WatchVarAssign: + handleVarAssign(); + break; + case WatchEvaluateExpression: + handleEvaluateExpression(record, cookie.value<WatchData>()); + break; + case WatchToolTip: + handleToolTip(record, cookie.toString()); + break; + case WatchDumpCustomValue1: + handleDumpCustomValue1(record, cookie.value<WatchData>()); + break; + case WatchDumpCustomValue2: + handleDumpCustomValue2(record, cookie.value<WatchData>()); + break; + case WatchDumpCustomSetup: + handleDumpCustomSetup(record); + break; + + default: + qDebug() << "FIXME: GdbEngine::handleResult: " + "should not happen" << type; + break; + } +} + +void GdbEngine::executeDebuggerCommand(const QString &command) +{ + //createGdbProcessIfNeeded(); + if (m_gdbProc.state() == QProcess::NotRunning) { + qDebug() << "NO GDB PROCESS RUNNING, PLAIN CMD IGNORED: " << command; + return; + } + + GdbCookie cmd; + cmd.command = command; + cmd.type = -1; + + //m_cookieForToken[currentToken()] = cmd; + //++currentToken(); + + //qDebug() << ""; + //qDebug() << currentTime() << "Running command: " << cmd.command; + emit gdbInputAvailable(QString(), cmd.command); + m_gdbProc.write(cmd.command.toLatin1() + "\r\n"); +} + +void GdbEngine::handleQueryPwd(const GdbResultRecord &record) +{ + // FIXME: remove this special case as soon as 'pwd' + // is supported by MI + //qDebug() << "PWD OUTPUT:" << record.toString(); + // Gdb responses _unless_ we get an error first. + if (record.resultClass == GdbResultDone) { +#ifdef Q_OS_LINUX + // "5^done,{logstreamoutput="pwd ",consolestreamoutput + // ="Working directory /home/apoenitz/dev/work/test1. "} + m_pwd = record.data.findChild("consolestreamoutput").data(); + int pos = m_pwd.indexOf("Working directory"); + m_pwd = m_pwd.mid(pos + 18); + m_pwd = m_pwd.trimmed(); + if (m_pwd.endsWith('.')) + m_pwd.chop(1); +#endif +#ifdef Q_OS_WIN + // ~"Working directory C:\\Users\\Thomas\\Documents\\WBTest3\\debug.\n" + m_pwd = record.data.findChild("consolestreamoutput").data(); + m_pwd = m_pwd.trimmed(); +#endif + //qDebug() << "PWD RESULT:" << m_pwd; + } +} + +void GdbEngine::handleQuerySources(const GdbResultRecord &record) +{ + if (record.resultClass == GdbResultDone) { + m_shortToFullName.clear(); + m_fullToShortName.clear(); + // "^done,files=[{file="../../../../bin/gdbmacros/gdbmacros.cpp", + // fullname="/data5/dev/ide/main/bin/gdbmacros/gdbmacros.cpp"}, + GdbMi files = record.data.findChild("files"); + foreach (const GdbMi &item, files.children()) { + QString fileName = item.findChild("file").data(); + GdbMi fullName = item.findChild("fullname"); + QString full = fullName.data(); + #ifdef Q_OS_WIN + full = QDir::cleanPath(full); + #endif + if (fullName.isValid() && QFileInfo(full).isReadable()) { + //qDebug() << "STORING 2: " << fileName << full; + m_shortToFullName[fileName] = full; + m_fullToShortName[full] = fileName; + } + } + } +} + +void GdbEngine::handleInfoProc(const GdbResultRecord &record) +{ + if (record.resultClass == GdbResultDone) { + #if defined(Q_OS_MAC) + //^done,process-id="85075" + maybeHandleInferiorPidChanged(record.data.findChild("process-id").data()); + #endif + + #if defined(Q_OS_LINUX) || defined(Q_OS_WIN) + // FIXME: use something more robust + QRegExp re(QLatin1String("process (\\d+)")); + QString data = record.data.findChild("consolestreamoutput").data(); + if (re.indexIn(data) != -1) + maybeHandleInferiorPidChanged(re.cap(1)); + #endif + } +} + +void GdbEngine::handleInfoShared(const GdbResultRecord &record) +{ + if (record.resultClass == GdbResultDone) { + // let the modules handler do the parsing + handleModulesList(record); + QList<Module> modules = qq->modulesHandler()->modules(); + bool reloadNeeded = false; + foreach (const Module &module, modules) { + // FIXME: read this from some list + if (!module.symbolsRead && !module.moduleName.contains("Q")) { + reloadNeeded = true; + sendCommand("sharedlibrary " + dotEscape(module.moduleName)); + } + } + if (reloadNeeded) + reloadModules(); + continueInferior(); + } +} + +void GdbEngine::handleQuerySources2(const GdbResultRecord &record, + const QVariant &cookie) +{ + if (record.resultClass == GdbResultDone) + handleAsyncOutput2(cookie.value<GdbMi>()); +} + +void GdbEngine::handleExecJumpToLine(const GdbResultRecord &record) +{ + // FIXME: remove this special case as soon as 'jump' + // is supported by MI + // "&"jump /home/apoenitz/dev/work/test1/test1.cpp:242" + // ~"Continuing at 0x4058f3." + // ~"run1 (argc=1, argv=0x7fffb213a478) at test1.cpp:242" + // ~"242\t x *= 2;" + //109^done" + qq->notifyInferiorStopped(); + q->showStatusMessage(tr("Jumped. Stopped."), -1); + QString output = record.data.findChild("logstreamoutput").data(); + if (!output.isEmpty()) + return; + QString fileAndLine = output.section(' ', 1, 1); + QString file = fileAndLine.section(':', 0, 0); + int line = fileAndLine.section(':', 1, 1).toInt(); + q->gotoLocation(file, line, true); +} + +void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record) +{ + // FIXME: remove this special case as soon as there's a real + // reason given when the temporary breakpoint is hit. + // reight now we get: + // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4", + // func="foo",args=[{name="str",value="@0x7fff0f450460"}], + // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"} + qq->notifyInferiorStopped(); + q->showStatusMessage(tr("Run to Function finished. Stopped."), -1); + GdbMi frame = record.data.findChild("frame"); + QString file = frame.findChild("fullname").data(); + int line = frame.findChild("line").data().toInt(); + qDebug() << "HIT: " << file << line << " IN " << frame.toString() + << " -- " << record.toString(); + q->gotoLocation(file, line, true); +} + +void GdbEngine::handleStreamOutput(const QString &data, char code) +{ + // Linux + if (data.contains("[New Thread")) { + QRegExp re("\\[New Thread 0x([0-9a-f]*) \\(LWP ([0-9]*)\\)\\]"); + if (re.indexIn(data) != -1) + maybeHandleInferiorPidChanged(re.cap(2)); + } + + // Mac + if (data.contains("[Switching to process ")) { + QRegExp re("\\[Switching to process ([0-9]*) local thread 0x([0-9a-f]*)\\]"); + if (re.indexIn(data) != -1) + maybeHandleInferiorPidChanged(re.cap(1)); + } + + // present it twice: now and together with the next 'real' result + switch (code) { + case '~': + m_pendingConsoleStreamOutput += data; + break; + case '@': + m_pendingTargetStreamOutput += data; + break; + case '&': + m_pendingLogStreamOutput += data; + // On Windows, the contents seem to depend on the debugger + // version and/or OS version used. + if (data.startsWith("warning:")) + qq->showApplicationOutput(QString(), data); + break; + } + +#ifdef Q_OS_LINUX + if (data.startsWith("Pending break") && data.contains("\" resolved")) { + qDebug() << "SCHEDULING -break-list"; + //m_breakListOnStopNeeded = true; + } +#endif + +#if 0 + if (m_slurpingPTypeOutput) + qDebug() << "SLURP: " << output.data; + + // "No symbol \"__dlopen\" in current context." + // "No symbol \"dlopen\" in current context." + if (output.data.startsWith("No symbol ") + && output.data.contains("dlopen")) { + m_dlopened = true; + return; + } + + // output of 'ptype <foo>' + if (output.data.startsWith("type = ")) { + if (output.data.endsWith("{") || output.data.endsWith("{\\n")) { + // multi-line output started here... + m_slurpingPTypeOutput = true; + m_slurpedPTypeOutput = output.data; + } else { + // Happens for simple types. Process it immediately + m_watchHandler->handleTypeContents(output.data); + } + return; + } + if (m_slurpingPTypeOutput) { + m_slurpedPTypeOutput += '\n'; + m_slurpedPTypeOutput += output.data; + if (output.data.startsWith("}")) { + // this is the last line... + m_slurpingPTypeOutput = false; + m_watchHandler->handleTypeContents(m_slurpedPTypeOutput); + m_slurpedPTypeOutput.clear(); + } + return; + } +#endif +} + +static bool isExitedReason(const QString &reason) +{ + return reason == QLatin1String("exited-normally") // inferior exited normally + || reason == QLatin1String("exited-signalled") // inferior exited because of a signal + //|| reason == QLatin1String("signal-received") // inferior received signal + || reason == QLatin1String("exited"); // inferior exited +} + +static bool isStoppedReason(const QString &reason) +{ + return reason == QLatin1String("function-finished") // -exec-finish + || reason == QLatin1String("signal-received") // handled as "isExitedReason" + || reason == QLatin1String("breakpoint-hit") // -exec-continue + || reason == QLatin1String("end-stepping-range") // -exec-next, -exec-step + || reason == QLatin1String("location-reached") // -exec-until + || reason == QLatin1String("access-watchpoint-trigger") + || reason == QLatin1String("read-watchpoint-trigger") +#ifdef Q_OS_MAC + || reason.isEmpty() +#endif + ; +} + +void GdbEngine::handleAsyncOutput(const GdbMi &data) +{ + const QString reason = data.findChild("reason").data(); + + QString console = data.findChild("consolestreamoutput").data(); + if (console.contains("Stopped due to shared library event") || reason.isEmpty()) { + ++m_shared; + //if (m_shared == 2) + // tryLoadCustomDumpers(); + //qDebug() << "SHARED LIBRARY EVENT " << data.toString() << m_shared; + if (qq->useFastStart()) { + if (1 || m_shared <= 16) { // libpthread? + sendCommand("info shared", GdbInfoShared); + //sendCommand("sharedlibrary gdbdebugger "); + //continueInferior(); + } else { + // auto-load from now on + sendCommand("info shared"); + sendCommand("set auto-solib-add on"); + sendCommand("-file-list-exec-source-files", GdbQuerySources); + sendCommand("-break-list", BreakList); + //sendCommand("bt"); + //QVariant var = QVariant::fromValue<GdbMi>(data); + //sendCommand("p 1", GdbAsyncOutput2, var); // dummy + continueInferior(); + } + } else { + // slow start requested. + q->showStatusMessage("Loading " + data.toString(), -1); + continueInferior(); + } + return; + } + + if (isExitedReason(reason)) { + qq->notifyInferiorExited(); + QString msg = "Program exited normally"; + if (reason == "exited") { + msg = "Program exited with exit code " + + data.findChild("exit-code").toString(); + } else if (reason == "exited-signalled") { + msg = "Program exited after receiving signal " + + data.findChild("signal-name").toString(); + } else if (reason == "signal-received") { + msg = "Program exited after receiving signal " + + data.findChild("signal-name").toString(); + } + q->showStatusMessage(msg, -1); + q->exitDebugger(); + return; + } + + tryLoadCustomDumpers(); + + // jump over well-known frames + static int stepCounter = 0; + if (qq->skipKnownFrames()) { + if (reason == "end-stepping-range" || reason == "function-finished") { + GdbMi frame = data.findChild("frame"); + //qDebug() << frame.toString(); + m_currentFrame = frame.findChild("addr").data() + '%' + + frame.findChild("func").data() + '%'; + + QString funcName = frame.findChild("func").data(); + QString fileName = frame.findChild("file").data(); + if (isLeavableFunction(funcName, fileName)) { + //qDebug() << "LEAVING" << funcName; + ++stepCounter; + q->stepOutExec(); + //stepExec(); + return; + } + if (isSkippableFunction(funcName, fileName)) { + //qDebug() << "SKIPPING" << funcName; + ++stepCounter; + q->stepExec(); + return; + } + //if (stepCounter) + // qDebug() << "STEPCOUNTER:" << stepCounter; + stepCounter = 0; + } + } + + if (isStoppedReason(reason) || reason.isEmpty()) { + // Need another round trip + if (reason == "breakpoint-hit") { + GdbMi frame = data.findChild("frame"); + //qDebug() << frame.toString(); + m_currentFrame = frame.findChild("addr").data() + '%' + + frame.findChild("func").data() + '%'; + + QApplication::alert(q->mainWindow(), 200); + sendCommand("-file-list-exec-source-files", GdbQuerySources); + sendCommand("-break-list", BreakList); + QVariant var = QVariant::fromValue<GdbMi>(data); + sendCommand("p 0", GdbAsyncOutput2, var); // dummy + } else { + handleAsyncOutput2(data); + } + return; + } + + qDebug() << "STOPPED FOR UNKNOWN REASON" << data.toString(); + // Ignore it. Will be handled with full response later in the + // JumpToLine or RunToFunction handlers +#if 1 + // FIXME: remove this special case as soon as there's a real + // reason given when the temporary breakpoint is hit. + // reight now we get: + // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4", + // func="foo",args=[{name="str",value="@0x7fff0f450460"}], + // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"} + // + // MAC yields sometimes: + // stdout:3661*stopped,time={wallclock="0.00658",user="0.00142", + // system="0.00136",start="1218810678.805432",end="1218810678.812011"} + q->resetLocation(); + qq->notifyInferiorStopped(); + q->showStatusMessage(tr("Run to Function finished. Stopped."), -1); + GdbMi frame = data.findChild("frame"); + QString file = frame.findChild("fullname").data(); + int line = frame.findChild("line").data().toInt(); + qDebug() << "HIT: " << file << line << " IN " << frame.toString() + << " -- " << data.toString(); + q->gotoLocation(file, line, true); +#endif +} + + +void GdbEngine::handleAsyncOutput2(const GdbMi &data) +{ + qq->notifyInferiorStopped(); + + // + // Breakpoints + // + //qDebug() << "BREAK ASYNC: " << output.toString(); + //sendListBreakpoints(); + //attemptBreakpointSynchronization(); + //if (m_breakListOnStopNeeded) + // sendListBreakpoints(); + + // something reasonably 'invariant' + // Linux: + //"79*stopped,reason="end-stepping-range",reason="breakpoint-hit",bkptno="1", + //thread-id="1",frame={addr="0x0000000000405d8f",func="run1", + //args=[{name="argc",value="1"},{name="argv",value="0x7fffb7c23058"}], + //file="test1.cpp",fullname="/home/apoenitz/dev/work/test1/test1.cpp" + //,line="261"}" + // Mac: (but only sometimes) + // "82*stopped,bkpt={number="0",type="step + // resume",disp="keep",enabled="y",addr="0x43127171",at="<Find:: + // Internal::FindToolWindow::invokeFindIncremental() + // +225>",thread="1",shlib="/Users/epreuss/dev/ide/main/bin/ + // workbench.app/Contents/PlugIns/Trolltech/libFind.1.0.0.dylib", + // frame="0xbfffd800",thread="1",times="1"}, + + // + // Stack + // + qq->stackHandler()->setCurrentIndex(0); + updateLocals(); // Quick shot + + int currentId = data.findChild("thread-id").data().toInt(); + sendSynchronizedCommand("-stack-list-frames", StackListFrames); + if (supportsThreads()) + sendSynchronizedCommand("-thread-list-ids", StackListThreads, currentId); + + // + // Disassembler + // + // Linux: + //"79*stopped,reason="end-stepping-range",reason="breakpoint-hit",bkptno="1", + //thread-id="1",frame={addr="0x0000000000405d8f",func="run1", + //args=[{name="argc",value="1"},{name="argv",value="0x7fffb7c23058"}], + //file="test1.cpp",fullname="/home/apoenitz/dev/work/test1/test1.cpp",line="261"}" + // Mac: (but only sometimes) + m_address = data.findChild("frame").findChild("addr").data(); + qq->reloadDisassembler(); + + // + // Registers + // + qq->reloadRegisters(); +} + +void GdbEngine::handleShowVersion(const GdbResultRecord &response) +{ + if (response.resultClass == GdbResultDone) { + m_gdbVersion = 100; + QString msg = response.data.findChild("consolestreamoutput").data(); + QRegExp supported("GNU gdb 6.[6789]"); + if (msg.indexOf(supported) == -1) { + QStringList list = msg.split("\n"); + while (list.size() > 2) + list.removeLast(); + msg = tr("The debugger you are using identifies itself as:") + + "<p><p>" + list.join("<br>") + "<p><p>" + + tr("This version is not officially supported by Qt Creator.\n" + "Debugging will most likely not work well.\n" + "Using gdb 6.7 or later is strongly recommended."); +#if 0 + // ugly, but 'Show again' check box... + static QErrorMessage *err = new QErrorMessage(m_mainWindow); + err->setMinimumSize(400, 300); + err->showMessage(msg); +#else + //QMessageBox::information(m_mainWindow, tr("Warning"), msg); +#endif + } + int pos = msg.indexOf("GNU gdb 6."); + if (pos != -1) { + m_gdbVersion = 600 + (msg.at(pos + 10).unicode() - '0') * 10; + //qDebug() << "GDB VERSION " << m_gdbVersion << msg; + } + } +} + +void GdbEngine::handleFileExecAndSymbols + (const GdbResultRecord &response) +{ + if (response.resultClass == GdbResultDone) { + //m_breakHandler->clearBreakMarkers(); + } else if (response.resultClass == GdbResultError) { + QString msg = response.data.findChild("msg").data(); + QMessageBox::critical(q->mainWindow(), tr("Error"), + tr("Starting executable failed:\n") + msg); + QWB_ASSERT(q->status() == DebuggerInferiorRunning, /**/); + interruptInferior(); + } +} + +void GdbEngine::handleExecRun(const GdbResultRecord &response) +{ + if (response.resultClass == GdbResultRunning) { + qq->notifyInferiorRunning(); + q->showStatusMessage(tr("Running..."), -1); + //reloadModules(); + } else if (response.resultClass == GdbResultError) { + QString msg = response.data.findChild("msg").data(); + if (msg == "Cannot find bounds of current function") { + qq->notifyInferiorStopped(); + //q->showStatusMessage(tr("No debug information available. " + // "Leaving function..."), -1); + //stepOutExec(); + } else { + QMessageBox::critical(q->mainWindow(), tr("Error"), + tr("Starting executable failed:\n") + msg); + QWB_ASSERT(q->status() == DebuggerInferiorRunning, /**/); + interruptInferior(); + } + } +} + +void GdbEngine::queryFullName(const QString &fileName, QString *full) +{ + *full = fullName(fileName); +} + +QString GdbEngine::shortName(const QString &fullName) +{ + return m_fullToShortName.value(fullName, QString()); +} + +QString GdbEngine::fullName(const QString &fileName) +{ + //QString absName = m_manager->currentWorkingDirectory() + "/" + file; ?? + if (fileName.isEmpty()) + return QString(); + QString full = m_shortToFullName.value(fileName, QString()); + //qDebug() << "RESOLVING: " << fileName << full; + if (!full.isEmpty()) + return full; + QFileInfo fi(fileName); + if (!fi.isReadable()) + return QString(); + full = fi.absoluteFilePath(); + #ifdef Q_OS_WIN + full = QDir::cleanPath(full); + #endif + //qDebug() << "STORING: " << fileName << full; + m_shortToFullName[fileName] = full; + m_fullToShortName[full] = fileName; + return full; +} + +QString GdbEngine::fullName(const QStringList &candidates) +{ + QString full; + foreach (const QString &fileName, candidates) { + full = fullName(fileName); + if (!full.isEmpty()) + return full; + } + foreach (const QString &fileName, candidates) { + if (!fileName.isEmpty()) + return fileName; + } + return full; +} + +void GdbEngine::shutdown() +{ + exitDebugger(); +} + +void GdbEngine::exitDebugger() +{ + //qDebug() << "EXITING: " << m_gdbProc.state(); + if (m_gdbProc.state() == QProcess::Starting) + m_gdbProc.waitForStarted(); + if (m_gdbProc.state() == QProcess::Running) { + interruptInferior(); + sendCommand("kill"); + sendCommand("-gdb-exit"); + // 20s can easily happen when loading webkit debug information + m_gdbProc.waitForFinished(20000); + if (m_gdbProc.state() != QProcess::Running) { + m_gdbProc.terminate(); + m_gdbProc.waitForFinished(20000); + } + } + if (m_gdbProc.state() != QProcess::NotRunning) + qDebug() << "PROBLEM STOPPING DEBUGGER"; + + m_shortToFullName.clear(); + m_fullToShortName.clear(); + m_varToType.clear(); + m_dataDumperState = DataDumperUninitialized; + m_shared = 0; + qq->debugDumpersAction()->setChecked(false); +} + + +int GdbEngine::currentFrame() const +{ + return qq->stackHandler()->currentIndex(); +} + + +bool GdbEngine::startDebugger() +{ + m_inferiorPid = 0; + QStringList gdbArgs; + + QFileInfo fi(q->m_executable); + QString fileName = '"' + fi.absoluteFilePath() + '"'; + + if (m_gdbProc.state() != QProcess::NotRunning) { + qDebug() << "GDB IS ALREADY RUNNING!"; + return false; + } + + //gdbArgs.prepend(QLatin1String("--quiet")); + gdbArgs.prepend(QLatin1String("mi")); + gdbArgs.prepend(QLatin1String("-i")); + + if (!q->m_workingDir.isEmpty()) + m_gdbProc.setWorkingDirectory(q->m_workingDir); + if (!q->m_environment.isEmpty()) + m_gdbProc.setEnvironment(q->m_environment); + + #if 0 + qDebug() << "Command: " << theGdbSettings().m_gdbCmd; + qDebug() << "WorkingDirectory: " << m_gdbProc.workingDirectory(); + qDebug() << "Environment: " << m_gdbProc.environment(); + qDebug() << "Arguments: " << gdbArgs; + qDebug() << "BuildDir: " << q->m_buildDir; + qDebug() << "ExeFile: " << q->m_executable; + #endif + + q->showStatusMessage("Starting Debugger", -1); + emit gdbInputAvailable(QString(), theGdbSettings().m_gdbCmd + ' ' + gdbArgs.join(" ")); + + m_gdbProc.start(theGdbSettings().m_gdbCmd, gdbArgs); + m_gdbProc.waitForStarted(); + + if (m_gdbProc.state() != QProcess::Running) + return false; + + q->showStatusMessage(tr("Gdb Running"), -1); + + sendCommand("show version", GdbShowVersion); + if (qq->useFastStart()) { + sendCommand("set auto-solib-add off"); + sendCommand("set stop-on-solib-events 1"); + } + //sendCommand("-enable-timings"); + //sendCommand("set stop-on-solib-events 1"); + sendCommand("set print static-members off"); // Seemingly doesn't work. + //sendCommand("define hook-stop\n-thread-list-ids\n-stack-list-frames\nend"); + //sendCommand("define hook-stop\nprint 4\nend"); + //sendCommand("define hookpost-stop\nprint 5\nend"); + //sendCommand("define hook-call\nprint 6\nend"); + //sendCommand("define hookpost-call\nprint 7\nend"); + //sendCommand("set print object on"); // works with CLI, but not MI + //sendCommand("set step-mode on"); // we can't work with that yes + //sendCommand("set exec-done-display on"); + //sendCommand("set print pretty on"); + //sendCommand("set confirm off"); + //sendCommand("set pagination off"); + sendCommand("set breakpoint pending on", BreakEnablePending); + + // one of the following is needed to prevent crashes in gdb on code like: + // template <class T> T foo() { return T(0); } + // int main() { return foo<int>(); } + // (gdb) call 'int foo<int>'() + // /build/buildd/gdb-6.8/gdb/valops.c:2069: internal-error: + sendCommand("set overload-resolution off"); + //sendCommand("set demangle-style none"); + + // From the docs: + // Stop means reenter debugger if this signal happens (implies print). + // Print means print a message if this signal happens. + // Pass means let program see this signal; + // otherwise program doesn't know. + // Pass and Stop may be combined. + // We need "print" as otherwise we would get no feedback whatsoever + // Custom Dumper crashs which happen regularily for when accessing + // uninitialized variables. + sendCommand("handle SIGSEGV nopass stop print"); + + // This is useful to kill the inferior whenever gdb dies. + //sendCommand("handle SIGTERM pass nostop print"); + + sendCommand("set unwindonsignal on"); + sendCommand("pwd", GdbQueryPwd); + + #ifdef Q_OS_MAC + sendCommand("-gdb-set inferior-auto-start-cfm off"); + sendCommand("-gdb-set sharedLibrary load-rules " + "dyld \".*libSystem.*\" " + "all dyld \".*libauto.*\" " + "all dyld \".*AppKit.*\" " + "all dyld \".*PBGDBIntrospectionSupport.*\" " + "all dyld \".*Foundation.*\" " + "all dyld \".*CFDataFormatters.*\" " + "all dyld \".*libobjc.*\" " + "all dyld \".*CarbonDataFormatters"); + #endif + + if (q->startMode() == q->attachExternal) { + sendCommand("attach " + QString::number(q->m_attachedPID)); + } + + if (q->startMode() == q->startInternal) { + sendCommand("-file-exec-and-symbols " + fileName, GdbFileExecAndSymbols); + #ifdef Q_OS_MAC + sendCommand("sharedlibrary apply-load-rules all"); + #endif + sendCommand("-file-list-exec-source-files", GdbQuerySources); + //sendCommand("-gdb-set stop-on-solib-events 1"); + } + + sendCommand("-data-list-register-names", RegisterListNames); + + // set all to "pending" + if (q->startMode() == q->attachExternal) + qq->breakHandler()->removeAllBreakpoints(); + else + qq->breakHandler()->setAllPending(); + + QTimer::singleShot(0, this, SLOT(attemptBreakpointSynchronization())); + + return true; +} + +void GdbEngine::continueInferior() +{ + q->resetLocation(); + setTokenBarrier(); + qq->notifyInferiorRunningRequested(); + emit gdbInputAvailable(QString(), QString()); + sendCommand("-exec-continue", GdbExecContinue); +} + +void GdbEngine::runInferior() +{ + q->resetLocation(); + // FIXME: this ignores important startup messages + setTokenBarrier(); + if (!q->m_processArgs.isEmpty()) + sendCommand("-exec-arguments " + q->m_processArgs.join(" ")); + qq->notifyInferiorRunningRequested(); + emit gdbInputAvailable(QString(), QString()); + sendCommand("-exec-run", GdbExecRun); +#if defined(Q_OS_WIN) + sendCommand("info proc", GdbInfoProc); +#endif +#if defined(Q_OS_LINUX) + sendCommand("info proc", GdbInfoProc); +#endif +#if defined(Q_OS_MAC) + sendCommand("info pid", GdbInfoProc, QVariant(), true); +#endif +} + +void GdbEngine::stepExec() +{ + setTokenBarrier(); + qq->notifyInferiorRunningRequested(); + emit gdbInputAvailable(QString(), QString()); + sendCommand("-exec-step", GdbExecStep); +} + +void GdbEngine::stepIExec() +{ + setTokenBarrier(); + qq->notifyInferiorRunningRequested(); + sendCommand("-exec-step-instruction", GdbExecStepI); +} + +void GdbEngine::stepOutExec() +{ + setTokenBarrier(); + qq->notifyInferiorRunningRequested(); + sendCommand("-exec-finish", GdbExecFinish); +} + +void GdbEngine::nextExec() +{ + setTokenBarrier(); + qq->notifyInferiorRunningRequested(); + emit gdbInputAvailable(QString(), QString()); + sendCommand("-exec-next", GdbExecNext); +} + +void GdbEngine::nextIExec() +{ + setTokenBarrier(); + qq->notifyInferiorRunningRequested(); + sendCommand("-exec-next-instruction", GdbExecNextI); +} + +void GdbEngine::runToLineExec(const QString &fileName, int lineNumber) +{ + setTokenBarrier(); + qq->notifyInferiorRunningRequested(); + sendCommand("-exec-until " + fileName + ":" + QString::number(lineNumber)); +} + +void GdbEngine::runToFunctionExec(const QString &functionName) +{ + setTokenBarrier(); + sendCommand("-break-insert -t " + functionName); + qq->notifyInferiorRunningRequested(); + sendCommand("-exec-continue", GdbExecRunToFunction); +} + +void GdbEngine::jumpToLineExec(const QString &fileName, int lineNumber) +{ +#if 1 + // not available everywhere? + //sendCliCommand("tbreak " + fileName + ":" + QString::number(lineNumber)); + sendCommand("-break-insert -t " + fileName + ":" + QString::number(lineNumber)); + sendCommand("jump " + fileName + ":" + QString::number(lineNumber)); + // will produce something like + // &"jump /home/apoenitz/dev/work/test1/test1.cpp:242" + // ~"Continuing at 0x4058f3." + // ~"run1 (argc=1, argv=0x7fffbf1f5538) at test1.cpp:242" + // ~"242\t x *= 2;" + // 23^done" + q->gotoLocation(fileName, lineNumber, true); + //setBreakpoint(); + //sendCommand("jump " + fileName + ":" + QString::number(lineNumber)); +#else + q->gotoLocation(fileName, lineNumber, true); + setBreakpoint(fileName, lineNumber); + sendCommand("jump " + fileName + ":" + QString::number(lineNumber)); +#endif +} + +/*! + \fn void GdbEngine::setTokenBarrier() + \brief Sets up internal structures to handle a new debugger turn. + + This method is called at the beginnign of all step/next/finish etc. + debugger functions. +*/ + +void GdbEngine::setTokenBarrier() +{ + m_oldestAcceptableToken = currentToken(); +} + +void GdbEngine::setDebugDumpers(bool on) +{ + if (on) { + qDebug() << "SWITCHING ON DUMPER DEBUGGING"; + sendCommand("set unwindonsignal off"); + q->breakByFunction("qDumpObjectData440"); + //updateLocals(); + } else { + qDebug() << "SWITCHING OFF DUMPER DEBUGGING"; + sendCommand("set unwindonsignal on"); + } +} + +//QByteArray GdbEngine::dumperChannel() const +//{ +// return m_dumperServer->serverName().toLatin1(); +// //QByteArray ba; +// //ba.setNum(m_dumperServer->serverPort()); +// //return ba; +//} + + +////////////////////////////////////////////////////////////////////// +// +// Breakpoint specific stuff +// +////////////////////////////////////////////////////////////////////// + +void GdbEngine::breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt) +{ + if (!bkpt.isValid()) + return; + if (!data) + return; + data->pending = false; + data->bpMultiple = false; + data->bpCondition.clear(); + QStringList files; + foreach (const GdbMi &child, bkpt.children()) { + if (child.hasName("number")) { + data->bpNumber = child.data(); + } else if (child.hasName("func")) { + data->bpFuncName = child.data(); + } else if (child.hasName("addr")) { + // <MULTIPLE> happens in constructors. In this case there are + // _two_ fields named "addr" in the response. On Linux that is... + if (child.data() == "<MULTIPLE>") + data->bpMultiple = true; + else + data->bpAddress = child.data(); + } else if (child.hasName("file")) { + files.append(child.data()); + } else if (child.hasName("fullname")) { + QString fullName = child.data(); + #ifdef Q_OS_WIN + fullName = QDir::cleanPath(fullName); + #endif + files.prepend(fullName); + } else if (child.hasName("line")) { + data->bpLineNumber = child.data(); + if (child.data().toInt()) + data->markerLineNumber = child.data().toInt(); + } else if (child.hasName("cond")) { + data->bpCondition = child.data(); + // gdb 6.3 likes to "rewrite" conditions. Just accept that fact. + if (data->bpCondition != data->condition && data->conditionsMatch()) + data->condition = data->bpCondition; + } + else if (child.hasName("pending")) { + data->pending = true; + int pos = child.data().lastIndexOf(':'); + if (pos > 0) { + data->bpLineNumber = child.data().mid(pos + 1); + data->markerLineNumber = child.data().mid(pos + 1).toInt(); + files.prepend(child.data().left(pos)); + } else { + files.prepend(child.data()); + } + } + } + // This field is not present. Contents needs to be parsed from + // the plain "ignore" response. + //else if (child.hasName("ignore")) + // data->bpIgnoreCount = child.data(); + + QString name = fullName(files); + if (data->bpFileName.isEmpty()) + data->bpFileName = name; + if (data->markerFileName.isEmpty()) + data->markerFileName = name; +} + +void GdbEngine::sendInsertBreakpoint(int index) +{ + const BreakpointData *data = qq->breakHandler()->at(index); + QString where; + if (data->funcName.isEmpty()) { + where = data->fileName; +#ifdef Q_OS_MAC + // full names do not work on Mac/MI + QFileInfo fi(data->fileName); + where = fi.fileName(); + //where = fi.absoluteFilePath(); +#endif +#ifdef Q_OS_WIN + // full names do not work on Mac/MI + QFileInfo fi(data->fileName); + where = fi.fileName(); + //where = m_manager->shortName(data->fileName); + //if (where.isEmpty()) + // where = data->fileName; +#endif + // we need something like "\"file name.cpp\":100" to + // survive the gdb command line parser with file names intact + where = "\"\\\"" + where + "\\\":" + data->lineNumber + "\""; + } else { + where = data->funcName; + } + + // set up fallback in case of pending breakpoints which aren't handled + // by the MI interface +#ifdef Q_OS_LINUX + QString cmd = "-break-insert "; + //if (!data->condition.isEmpty()) + // cmd += "-c " + data->condition + " "; + cmd += where; +#endif +#ifdef Q_OS_MAC + QString cmd = "-break-insert -l -1 "; + //if (!data->condition.isEmpty()) + // cmd += "-c " + data->condition + " "; + cmd += where; +#endif +#ifdef Q_OS_WIN + QString cmd = "-break-insert "; + //if (!data->condition.isEmpty()) + // cmd += "-c " + data->condition + " "; + cmd += where; +#endif + sendCommand(cmd, BreakInsert, index, true); + //processQueueAndContinue(); +} + +void GdbEngine::handleBreakList(const GdbResultRecord &record) +{ + // 45^done,BreakpointTable={nr_rows="2",nr_cols="6",hdr=[ + // {width="3",alignment="-1",col_name="number",colhdr="Num"}, ... + // body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y", + // addr="0x000000000040109e",func="main",file="app.cpp", + // fullname="/home/apoenitz/dev/work/plugintest/app/app.cpp", + // line="11",times="1"}, + // bkpt={number="2",type="breakpoint",disp="keep",enabled="y", + // addr="<PENDING>",pending="plugin.cpp:7",times="0"}] ... } + + if (record.resultClass == GdbResultDone) { + GdbMi table = record.data.findChild("BreakpointTable"); + if (table.isValid()) + handleBreakList(table); + } +} + +void GdbEngine::handleBreakList(const GdbMi &table) +{ + //qDebug() << "GdbEngine::handleOutput: table: " + // << table.toString(); + GdbMi body = table.findChild("body"); + //qDebug() << "GdbEngine::handleOutput: body: " + // << body.toString(); + QList<GdbMi> bkpts; + if (body.isValid()) { + // Non-Mac + bkpts = body.children(); + } else { + // Mac + bkpts = table.children(); + // remove the 'hdr' and artificial items + //qDebug() << "FOUND " << bkpts.size() << " BREAKPOINTS"; + for (int i = bkpts.size(); --i >= 0; ) { + int num = bkpts.at(i).findChild("number").data().toInt(); + if (num <= 0) { + //qDebug() << "REMOVING " << i << bkpts.at(i).toString(); + bkpts.removeAt(i); + } + } + //qDebug() << "LEFT " << bkpts.size() << " BREAKPOINTS"; + } + + BreakHandler *handler = qq->breakHandler(); + for (int index = 0; index != bkpts.size(); ++index) { + BreakpointData temp(handler); + breakpointDataFromOutput(&temp, bkpts.at(index)); + int found = handler->findBreakpoint(temp); + if (found != -1) + breakpointDataFromOutput(handler->at(found), bkpts.at(index)); + //else + //qDebug() << "CANNOT HANDLE RESPONSE " << bkpts.at(index).toString(); + } + + attemptBreakpointSynchronization(); + handler->updateMarkers(); +} + + +void GdbEngine::handleBreakIgnore(const GdbResultRecord &record, int index) +{ + // gdb 6.8: + // ignore 2 0: + // ~"Will stop next time breakpoint 2 is reached.\n" + // 28^done + // ignore 2 12: + // &"ignore 2 12\n" + // ~"Will ignore next 12 crossings of breakpoint 2.\n" + // 29^done + // + // gdb 6.3 does not produce any console output + BreakHandler *handler = qq->breakHandler(); + if (record.resultClass == GdbResultDone && index < handler->size()) { + QString msg = record.data.findChild("consolestreamoutput").data(); + BreakpointData *data = handler->at(index); + //if (msg.contains("Will stop next time breakpoint")) { + // data->bpIgnoreCount = "0"; + //} else if (msg.contains("Will ignore next")) { + // data->bpIgnoreCount = data->ignoreCount; + //} + // FIXME: this assumes it is doing the right thing... + data->bpIgnoreCount = data->ignoreCount; + attemptBreakpointSynchronization(); + handler->updateMarkers(); + } +} + +void GdbEngine::handleBreakCondition(const GdbResultRecord &record, int index) +{ + BreakHandler *handler = qq->breakHandler(); + if (record.resultClass == GdbResultDone) { + // we just assume it was successful. otherwise we had to parse + // the output stream data + BreakpointData *data = handler->at(index); + //qDebug() << "HANDLE BREAK CONDITION " << index << data->condition; + data->bpCondition = data->condition; + attemptBreakpointSynchronization(); + handler->updateMarkers(); + } else if (record.resultClass == GdbResultError) { + QString msg = record.data.findChild("msg").data(); + // happens on Mac + if (1 || msg.startsWith("Error parsing breakpoint condition. " + " Will try again when we hit the breakpoint.")) { + BreakpointData *data = handler->at(index); + //qDebug() << "ERROR BREAK CONDITION " << index << data->condition; + data->bpCondition = data->condition; + attemptBreakpointSynchronization(); + handler->updateMarkers(); + } + } +} + +void GdbEngine::handleBreakInsert(const GdbResultRecord &record, int index) +{ + BreakHandler *handler = qq->breakHandler(); + if (record.resultClass == GdbResultDone) { + //qDebug() << "HANDLE BREAK INSERT " << index; +//#ifdef Q_OS_MAC + // interesting only on Mac? + BreakpointData *data = handler->at(index); + GdbMi bkpt = record.data.findChild("bkpt"); + //qDebug() << "BKPT: " << bkpt.toString() << " DATA" << data->toToolTip(); + breakpointDataFromOutput(data, bkpt); +//#endif + attemptBreakpointSynchronization(); + handler->updateMarkers(); + } else if (record.resultClass == GdbResultError) { + const BreakpointData *data = handler->at(index); +#ifdef Q_OS_LINUX + //QString where = "\"\\\"" + data->fileName + "\\\":" + // + data->lineNumber + "\""; + QString where = "\"" + data->fileName + "\":" + + data->lineNumber; + sendCommand("break " + where, BreakInsert1, index); +#endif +#ifdef Q_OS_MAC + QFileInfo fi(data->fileName); + QString where = "\"" + fi.fileName() + "\":" + + data->lineNumber; + sendCommand("break " + where, BreakInsert1, index); +#endif +#ifdef Q_OS_WIN + QFileInfo fi(data->fileName); + QString where = "\"" + fi.fileName() + "\":" + + data->lineNumber; + //QString where = m_data->fileName + QLatin1Char(':') + data->lineNumber; + sendCommand("break " + where, BreakInsert1, index); +#endif + } +} + +void GdbEngine::extractDataFromInfoBreak(const QString &output, BreakpointData *data) +{ + data->bpFileName = "<MULTIPLE>"; + + //qDebug() << output; + if (output.isEmpty()) + return; + // "Num Type Disp Enb Address What + // 4 breakpoint keep y <MULTIPLE> 0x00000000004066ad + // 4.1 y 0x00000000004066ad in CTorTester + // at /data5/dev/ide/main/tests/manual/gdbdebugger/simple/app.cpp:124 + // - or - + // everything on a single line on Windows for constructors of classes + // within namespaces. + // Sometimes the path is relative too. + + QRegExp re("MULTIPLE.*(0x[0-9a-f]+) in (.*)\\s+at (.*):([\\d]+)([^\\d]|$)"); + re.setMinimal(true); + + if (re.indexIn(output) != -1) { + data->bpAddress = re.cap(1); + data->bpFuncName = re.cap(2).trimmed(); + data->bpLineNumber = re.cap(4); + QString full = fullName(re.cap(3)); + data->markerLineNumber = data->bpLineNumber.toInt(); + data->markerFileName = full; + data->bpFileName = full; + //qDebug() << "FOUND BREAKPOINT\n" << output + // << re.cap(1) << "\n" << re.cap(2) << "\n" + // << re.cap(3) << "\n" << re.cap(4) << "\n"; + } else { + qDebug() << "COULD NOT MATCH " << re.pattern() << " AND " << output; + data->bpNumber = "<unavailable>"; + } +} + +void GdbEngine::handleBreakInfo(const GdbResultRecord &record, int bpNumber) +{ + BreakHandler *handler = qq->breakHandler(); + if (record.resultClass == GdbResultDone) { + // Old-style output for multiple breakpoints, presumably in a + // constructor + int found = handler->findBreakpoint(bpNumber); + if (found != -1) { + QString str = record.data.findChild("consolestreamoutput").data(); + extractDataFromInfoBreak(str, handler->at(found)); + handler->updateMarkers(); + attemptBreakpointSynchronization(); // trigger "ready" + } + } +} + +void GdbEngine::handleBreakInsert1(const GdbResultRecord &record, int index) +{ + BreakHandler *handler = qq->breakHandler(); + if (record.resultClass == GdbResultDone) { + // Pending breakpoints in dylibs on Mac only? + BreakpointData *data = handler->at(index); + GdbMi bkpt = record.data.findChild("bkpt"); + breakpointDataFromOutput(data, bkpt); + attemptBreakpointSynchronization(); // trigger "ready" + handler->updateMarkers(); + } else if (record.resultClass == GdbResultError) { + qDebug() << "INSERTING BREAKPOINT WITH BASE NAME FAILED. GIVING UP"; + BreakpointData *data = handler->at(index); + data->bpNumber = "<unavailable>"; + attemptBreakpointSynchronization(); // trigger "ready" + handler->updateMarkers(); + } +} + +void GdbEngine::attemptBreakpointSynchronization() +{ + BreakHandler *handler = qq->breakHandler(); + //qDebug() << "BREAKPOINT SYNCHRONIZATION "; + + foreach (BreakpointData *data, handler->takeRemovedBreakpoints()) { + //qDebug() << " SYNCHRONIZATION REMOVING" << data; + QString bpNumber = data->bpNumber; + if (!bpNumber.trimmed().isEmpty()) + sendCommand("-break-delete " + bpNumber, BreakDelete, 0, true); + //else + // qDebug() << "BP HAS NO NUMBER: " << data->markerFileName; + delete data; + } + + bool updateNeeded = false; + + for (int index = 0; index != handler->size(); ++index) { + BreakpointData *data = handler->at(index); + // multiple breakpoints? + if (data->bpMultiple && data->bpFileName.isEmpty()) { + sendCommand(QString("info break %1").arg(data->bpNumber), + BreakInfo, data->bpNumber.toInt()); + updateNeeded = true; + break; + } + } + + for (int index = 0; index != handler->size(); ++index) { + BreakpointData *data = handler->at(index); + // unset breakpoints? + if (data->bpNumber.isEmpty()) { + data->bpNumber = " "; + sendInsertBreakpoint(index); + //qDebug() << "UPDATE NEEDED BECAUSE OF UNKNOWN BREAKPOINT"; + updateNeeded = true; + break; + } + } + + if (!updateNeeded) { + for (int index = 0; index != handler->size(); ++index) { + BreakpointData *data = handler->at(index); + // update conditions if needed + if (data->bpNumber.toInt() && data->condition != data->bpCondition + && !data->conditionsMatch()) { + sendCommand(QString("condition %1 %2").arg(data->bpNumber) + .arg(data->condition), BreakCondition, index); + //qDebug() << "UPDATE NEEDED BECAUSE OF CONDITION" + // << data->condition << data->bpCondition; + updateNeeded = true; + break; + } + // update ignorecount if needed + if (data->bpNumber.toInt() && data->ignoreCount != data->bpIgnoreCount) { + sendCommand(QString("ignore %1 %2").arg(data->bpNumber) + .arg(data->ignoreCount), BreakIgnore, index); + updateNeeded = true; + break; + } + } + } + + for (int index = 0; index != handler->size(); ++index) { + // happens sometimes on Mac. Brush over symptoms + BreakpointData *data = handler->at(index); + if (data->markerFileName.startsWith("../")) { + data->markerFileName = fullName(data->markerFileName); + handler->updateMarkers(); + } + } + + if (updateNeeded) { + //interruptAndContinue(); + //sendListBreakpoints(); + } + + if (!updateNeeded && q->status() == DebuggerProcessStartingUp) + qq->notifyStartupFinished(); +} + + +////////////////////////////////////////////////////////////////////// +// +// Disassembler specific stuff +// +////////////////////////////////////////////////////////////////////// + +void GdbEngine::reloadDisassembler() +{ + emit sendCommand("disassemble", DisassemblerList, m_address); +} + +void GdbEngine::handleDisassemblerList(const GdbResultRecord &record, + const QString &cookie) +{ + QList<DisassemblerLine> lines; + static const QString pad = QLatin1String(" "); + int currentLine = -1; + if (record.resultClass == GdbResultDone) { + QString res = record.data.findChild("consolestreamoutput").data(); + QTextStream ts(&res, QIODevice::ReadOnly); + while (!ts.atEnd()) { + //0x0000000000405fd8 <_ZN11QTextStreamD1Ev@plt+0>: + // jmpq *2151890(%rip) # 0x6135b0 <_GLOBAL_OFFSET_TABLE_+640> + //0x0000000000405fde <_ZN11QTextStreamD1Ev@plt+6>: + // pushq $0x4d + //0x0000000000405fe3 <_ZN11QTextStreamD1Ev@plt+11>: + // jmpq 0x405af8 <_init+24> + //0x0000000000405fe8 <_ZN9QHashData6rehashEi@plt+0>: + // jmpq *2151882(%rip) # 0x6135b8 <_GLOBAL_OFFSET_TABLE_+648> + QString str = ts.readLine(); + if (!str.startsWith(QLatin1String("0x"))) { + //qDebug() << "IGNORING DISASSEMBLER" << str; + continue; + } + DisassemblerLine line; + QTextStream ts(&str, QIODevice::ReadOnly); + ts >> line.address >> line.symbol; + line.mnemonic = ts.readLine().trimmed(); + if (line.symbol.endsWith(QLatin1Char(':'))) + line.symbol.chop(1); + line.addressDisplay = line.address + pad; + if (line.addressDisplay.startsWith(QLatin1String("0x00000000"))) + line.addressDisplay.replace(2, 8, QString()); + line.symbolDisplay = line.symbol + pad; + + if (line.address == cookie) + currentLine = lines.size(); + + lines.append(line); + } + } else { + DisassemblerLine line; + line.addressDisplay = tr("<could not retreive module information>"); + lines.append(line); + } + + qq->disassemblerHandler()->setLines(lines); + if (currentLine != -1) + qq->disassemblerHandler()->setCurrentLine(currentLine); +} + + +////////////////////////////////////////////////////////////////////// +// +// Modules specific stuff +// +////////////////////////////////////////////////////////////////////// + +void GdbEngine::loadSymbols(const QString &moduleName) +{ + // FIXME: gdb does not understand quoted names here (tested with 6.8) + sendCommand("sharedlibrary " + dotEscape(moduleName)); + reloadModules(); +} + +void GdbEngine::loadAllSymbols() +{ + sendCommand("sharedlibrary .*"); + reloadModules(); +} + +void GdbEngine::reloadModules() +{ + sendCommand("info shared", ModulesList, QVariant()); +} + +void GdbEngine::handleModulesList(const GdbResultRecord &record) +{ + QList<Module> modules; + if (record.resultClass == GdbResultDone) { + QString data = record.data.findChild("consolestreamoutput").data(); + QTextStream ts(&data, QIODevice::ReadOnly); + while (!ts.atEnd()) { + QString line = ts.readLine(); + if (!line.startsWith("0x")) + continue; + Module module; + QString symbolsRead; + QTextStream ts(&line, QIODevice::ReadOnly); + ts >> module.startAddress >> module.endAddress >> symbolsRead; + module.moduleName = ts.readLine().trimmed(); + module.symbolsRead = (symbolsRead == "Yes"); + modules.append(module); + } + } + qq->modulesHandler()->setModules(modules); +} + + +////////////////////////////////////////////////////////////////////// +// +// Stack specific stuff +// +////////////////////////////////////////////////////////////////////// + +void GdbEngine::handleStackSelectThread(const GdbResultRecord &record, int) +{ + Q_UNUSED(record); + qDebug("FIXME: StackHandler::handleOutput: SelectThread"); + q->showStatusMessage(tr("Retrieving data for stack view..."), -1); + sendCommand("-stack-list-frames", StackListFrames); +} + + +void GdbEngine::handleStackListFrames(const GdbResultRecord &record) +{ + QList<StackFrame> stackFrames; + + const GdbMi stack = record.data.findChild("stack"); + QString dummy = stack.toString(); + if (!stack.isValid()) { + qDebug() << "FIXME: stack: " << stack.toString(); + return; + } + + int topFrame = -1; + + for (int i = 0; i != stack.childCount(); ++i) { + //qDebug() << "HANDLING FRAME: " << stack.childAt(i).toString(); + const GdbMi frameMi = stack.childAt(i); + StackFrame frame; + frame.level = i; + QStringList files; + files.append(frameMi.findChild("fullname").data()); + files.append(frameMi.findChild("file").data()); + frame.file = fullName(files); + frame.function = frameMi.findChild("func").data(); + frame.from = frameMi.findChild("from").data(); + frame.line = frameMi.findChild("line").data().toInt(); + frame.address = frameMi.findChild("addr").data(); + + stackFrames.append(frame); + +#ifdef Q_OS_WIN + const bool isBogus = + // Assume this is wrong and points to some strange stl_algobase + // implementation. Happens on Karsten's XP system with Gdb 5.50 + (frame.file.endsWith("/bits/stl_algobase.h") && frame.line == 150) + // Also wrong. Happens on Vista with Gdb 5.50 + || (frame.function == "operator new" && frame.line == 151); + + // immediately leave bogus frames + if (topFrame == -1 && isBogus) { + sendCommand("-exec-finish"); + return; + } + +#endif + + // Initialize top frame to the first valid frame + const bool isValid = !frame.file.isEmpty() && !frame.function.isEmpty(); + if (isValid && topFrame == -1) + topFrame = i; + } + + qq->stackHandler()->setFrames(stackFrames); + +#if 0 + if (0 && topFrame != -1) { + // updates of locals already triggered early + const StackFrame &frame = qq->stackHandler()->currentFrame(); + bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable(); + if (usable) + q->gotoLocation(frame.file, frame.line, true); + else + qDebug() << "FULL NAME NOT USABLE 0: " << frame.file; + } else { + activateFrame(topFrame); + } +#else + if (topFrame != -1) { + // updates of locals already triggered early + const StackFrame &frame = qq->stackHandler()->currentFrame(); + bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable(); + if (usable) + q->gotoLocation(frame.file, frame.line, true); + else + qDebug() << "FULL NAME NOT USABLE 0: " << frame.file; + } +#endif +} + +void GdbEngine::selectThread(int index) +{ + //reset location arrow + q->resetLocation(); + + ThreadsHandler *threadsHandler = qq->threadsHandler(); + threadsHandler->setCurrentThread(index); + + QList<ThreadData> threads = threadsHandler->threads(); + QWB_ASSERT(index < threads.size(), return); + int id = threads.at(index).id; + q->showStatusMessage(tr("Retrieving data for stack view..."), -1); + sendCommand(QLatin1String("-thread-select ") + QString::number(id), + StackSelectThread); +} + +void GdbEngine::activateFrame(int frameIndex) +{ + if (q->status() != DebuggerInferiorStopped) + return; + + StackHandler *stackHandler = qq->stackHandler(); + int oldIndex = stackHandler->currentIndex(); + //qDebug() << "ACTIVATE FRAME: " << frameIndex << oldIndex + // << stackHandler->currentIndex(); + + QWB_ASSERT(frameIndex < stackHandler->stackSize(), return); + + if (oldIndex != frameIndex) { + // Assuming this always succeeds saves a roundtrip. + // Otherwise the lines below would need to get triggered + // after a response to this -stack-select-frame here. + sendCommand("-stack-select-frame " + QString::number(frameIndex)); + + stackHandler->setCurrentIndex(frameIndex); + updateLocals(); + } + + const StackFrame &frame = stackHandler->currentFrame(); + + bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable(); + if (usable) + q->gotoLocation(frame.file, frame.line, true); + else + qDebug() << "FULL NAME NOT USABLE: " << frame.file; +} + +void GdbEngine::handleStackListThreads(const GdbResultRecord &record, int id) +{ + // "72^done,{thread-ids={thread-id="2",thread-id="1"},number-of-threads="2"} + const QList<GdbMi> items = record.data.findChild("thread-ids").children(); + QList<ThreadData> threads; + int currentIndex = -1; + for (int index = 0, n = items.size(); index != n; ++index) { + ThreadData thread; + thread.id = items.at(index).data().toInt(); + threads.append(thread); + if (thread.id == id) { + //qDebug() << "SETTING INDEX TO: " << index << " ID: "<< id << "RECOD: "<< record.toString(); + currentIndex = index; + } + } + ThreadsHandler *threadsHandler = qq->threadsHandler(); + threadsHandler->setThreads(threads); + threadsHandler->setCurrentThread(currentIndex); +} + + +////////////////////////////////////////////////////////////////////// +// +// Register specific stuff +// +////////////////////////////////////////////////////////////////////// + +void GdbEngine::reloadRegisters() +{ + QString format = qq->registerHandler()->model()->property(PROPERTY_REGISTER_FORMAT).toString(); + sendCommand("-data-list-register-values " + format, RegisterListValues); +} + +void GdbEngine::handleRegisterListNames(const GdbResultRecord &record) +{ + if (record.resultClass != GdbResultDone) + return; + + QList<Register> registers; + foreach (const GdbMi &item, record.data.findChild("register-names").children()) + registers.append(Register(item.data())); + + qq->registerHandler()->setRegisters(registers); +} + +void GdbEngine::handleRegisterListValues(const GdbResultRecord &record) +{ + if (record.resultClass != GdbResultDone) + return; + + QList<Register> registers = qq->registerHandler()->registers(); + + // 24^done,register-values=[{number="0",value="0xf423f"},...] + foreach (const GdbMi &item, record.data.findChild("register-values").children()) { + int index = item.findChild("number").data().toInt(); + if (index < registers.size()) { + Register ® = registers[index]; + QString value = item.findChild("value").data(); + reg.changed = (value != reg.value); + if (reg.changed) + reg.value = value; + } + } + qq->registerHandler()->setRegisters(registers); +} + + +////////////////////////////////////////////////////////////////////// +// +// Thread specific stuff +// +////////////////////////////////////////////////////////////////////// + +bool GdbEngine::supportsThreads() const +{ + // 6.3 crashes happily on -thread-list-ids. So don't use it. + // The test below is a semi-random pick, 6.8 works fine + return m_gdbVersion > 650; +} + +////////////////////////////////////////////////////////////////////// +// +// Tooltip specific stuff +// +////////////////////////////////////////////////////////////////////// + +static WatchData m_toolTip; +static QString m_toolTipExpression; +static QPoint m_toolTipPos; +static QHash<QString, WatchData> m_toolTipCache; + +static bool hasLetterOrNumber(const QString &exp) +{ + for (int i = exp.size(); --i >= 0; ) + if (exp[i].isLetterOrNumber() || exp[i] == '_') + return true; + return false; +} + +static bool hasSideEffects(const QString &exp) +{ + // FIXME: complete? + return exp.contains("-=") + || exp.contains("+=") + || exp.contains("/=") + || exp.contains("*=") + || exp.contains("&=") + || exp.contains("|=") + || exp.contains("^=") + || exp.contains("--") + || exp.contains("++"); +} + +static bool isKeyWord(const QString &exp) +{ + // FIXME: incomplete + return exp == QLatin1String("class") + || exp == QLatin1String("const") + || exp == QLatin1String("do") + || exp == QLatin1String("if") + || exp == QLatin1String("return") + || exp == QLatin1String("struct") + || exp == QLatin1String("template") + || exp == QLatin1String("void") + || exp == QLatin1String("volatile") + || exp == QLatin1String("while"); +} + +void GdbEngine::setToolTipExpression(const QPoint &pos, const QString &exp0) +{ + //qDebug() << "SET TOOLTIP EXP" << pos << exp0; + if (q->status() != DebuggerInferiorStopped) { + //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED"; + return; + } + + if (qq->debugDumpersAction()->isChecked()) { + // minimize interference + return; + } + + m_toolTipPos = pos; + m_toolTipExpression = exp0; + QString exp = exp0; +/* + if (m_toolTip.isTypePending()) { + qDebug() << "suppressing duplicated tooltip creation"; + return; + } +*/ + if (m_toolTipCache.contains(exp)) { + const WatchData & data = m_toolTipCache[exp]; + // FIXME: qq->watchHandler()->collapseChildren(data.iname); + insertData(data); + return; + } + + QToolTip::hideText(); + if (exp.isEmpty() || exp.startsWith("#")) { + QToolTip::hideText(); + return; + } + + if (!hasLetterOrNumber(exp)) { + QToolTip::showText(m_toolTipPos, + "'" + exp + "' contains no identifier"); + return; + } + + if (isKeyWord(exp)) + return; + + if (exp.startsWith('"') && exp.endsWith('"')) { + QToolTip::showText(m_toolTipPos, "String literal " + exp); + return; + } + + if (exp.startsWith("++") || exp.startsWith("--")) + exp = exp.mid(2); + + if (exp.endsWith("++") || exp.endsWith("--")) + exp = exp.mid(2); + + if (exp.startsWith("<") || exp.startsWith("[")) + return; + + if (hasSideEffects(exp)) { + QToolTip::showText(m_toolTipPos, + "Cowardly refusing to evaluate expression '" + exp + + "' with potential side effects"); + return; + } + + // Gdb crashes when creating a variable object with the name + // of the type of 'this' +/* + for (int i = 0; i != m_currentLocals.childCount(); ++i) { + if (m_currentLocals.childAt(i).exp == "this") { + qDebug() << "THIS IN ROW " << i; + if (m_currentLocals.childAt(i).type.startsWith(exp)) { + QToolTip::showText(m_toolTipPos, + exp + ": type of current 'this'"); + qDebug() << " TOOLTIP CRASH SUPPRESSED"; + return; + } + break; + } + } +*/ + + //if (m_manager->status() != DebuggerInferiorStopped) + // return; + + // FIXME: 'exp' can contain illegal characters + m_toolTip = WatchData(); + //m_toolTip.level = 0; + // m_toolTip.row = 0; + // m_toolTip.parentIndex = 2; + m_toolTip.exp = exp; + m_toolTip.name = exp; + m_toolTip.iname = tooltipIName; + insertData(m_toolTip); + updateWatchModel2(); +} + + +////////////////////////////////////////////////////////////////////// +// +// Watch specific stuff +// +////////////////////////////////////////////////////////////////////// + +static const QString strNotInScope = QLatin1String("<not in scope>"); + +static bool isPointerType(const QString &type) +{ + return type.endsWith("*") || type.endsWith("* const"); +} + +static bool isAccessSpecifier(const QString &str) +{ + static const QStringList items = + QStringList() << "private" << "protected" << "public"; + return items.contains(str); +} + +static bool startsWithDigit(const QString &str) +{ + return !str.isEmpty() && str[0] >= '0' && str[0] <= '9'; +} + +QString stripPointerType(QString type) +{ + if (type.endsWith("*")) + type.chop(1); + if (type.endsWith("* const")) + type.chop(7); + if (type.endsWith(' ')) + type.chop(1); + return type; +} + +static QString gdbQuoteTypes(const QString &type) +{ + // gdb does not understand sizeof(Core::IFile*). + // "sizeof('Core::IFile*')" is also not acceptable, + // it needs to be "sizeof('Core::IFile'*)" + // + // We never will have a perfect solution here (even if we had a full blown + // C++ parser as we do not have information on what is a type and what is + // a vriable name. So "a<b>::c" could either be two comparisons of values + // 'a', 'b' and '::c', or a nested type 'c' in a template 'a<b>'. We + // assume here it is the latter. + //return type; + + // (*('myns::QPointer<myns::QObject>*'*)0x684060)" is not acceptable + // (*('myns::QPointer<myns::QObject>'**)0x684060)" is acceptable + if (isPointerType(type)) + return gdbQuoteTypes(stripPointerType(type)) + "*"; + + QString accu; + QString result; + int templateLevel = 0; + for (int i = 0; i != type.size(); ++i) { + QChar c = type.at(i); + if (c.isLetterOrNumber() || c == '_' || c == ':' || c == ' ') { + accu += c; + } else if (c == '<') { + ++templateLevel; + accu += c; + } else if (c == '<') { + --templateLevel; + accu += c; + } else if (templateLevel > 0) { + accu += c; + } else { + if (accu.contains(':') || accu.contains('<')) + result += '\'' + accu + '\''; + else + result += accu; + accu.clear(); + result += c; + } + } + if (accu.contains(':') || accu.contains('<')) + result += '\'' + accu + '\''; + else + result += accu; + //qDebug() << "GDB_QUOTING" << type << " TO " << result; + + return result; +} + +static void setWatchDataValue(WatchData &data, const GdbMi &mi, + int encoding = 0) +{ + if (mi.isValid()) { + QByteArray ba; + switch (encoding) { + case 0: // unencoded 8 bit data + ba = mi.data(); + break; + case 1: // base64 encoded 8 bit data + ba = QByteArray::fromBase64(mi.data()); + break; + case 2: // base64 encoded 16 bit data + ba = QByteArray::fromBase64(mi.data()); + ba = QString::fromUtf16((ushort *)ba.data(), ba.size() / 2).toUtf8(); + break; + case 3: // base64 encoded 32 bit data + ba = QByteArray::fromBase64(mi.data()); + ba = QString::fromUcs4((uint *)ba.data(), ba.size() / 4).toUtf8(); + break; + } + data.setValue(ba); + } else { + data.setValueNeeded(); + } +} + +static void setWatchDataEditValue(WatchData &data, const GdbMi &mi) +{ + if (mi.isValid()) + data.editvalue = mi.data(); +} + +static void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi) +{ + if (mi.isValid()) + data.setValueToolTip(mi.data()); +} + +static void setWatchDataChildCount(WatchData &data, const GdbMi &mi) +{ + if (mi.isValid()) { + data.childCount = mi.data().toInt(); + data.setChildCountUnneeded(); + if (data.childCount == 0) + data.setChildrenUnneeded(); + } else { + data.childCount = -1; + } +} + +static void setWatchDataValueDisabled(WatchData &data, const GdbMi &mi) +{ + if (mi.data() == "true") + data.valuedisabled = true; + else if (mi.data() == "false") + data.valuedisabled = false; +} + +static void setWatchDataExpression(WatchData &data, const GdbMi &mi) +{ + if (mi.isValid()) + data.exp = "(" + mi.data() + ")"; +} + +static void setWatchDataAddress(WatchData &data, const GdbMi &mi) +{ + if (mi.isValid()) { + data.addr = mi.data(); + if (data.exp.isEmpty()) + data.exp = "(*(" + gdbQuoteTypes(data.type) + "*)" + data.addr + ")"; + } +} + +static bool extractTemplate(const QString &type, QString *tmplate, QString *inner) +{ + // Input "Template<Inner1,Inner2,...>::Foo" will return "Template::Foo" in + // 'tmplate' and "Inner1@Inner2@..." etc in 'inner'. Result indicates + // whether parsing was successful + int level = 0; + for (int i = 0; i != type.size(); ++i) { + QChar c = type[i]; + if (c == '<') { + *(level == 0 ? tmplate : inner) += c; + ++level; + } else if (c == '>') { + --level; + *(level == 0 ? tmplate : inner) += c; + } else if (c == ',') { + *inner += (level == 1) ? '@' : ','; + } else { + *(level == 0 ? tmplate : inner) += c; + } + } + *tmplate = tmplate->trimmed(); + *tmplate = tmplate->remove("<>"); + *inner = inner->trimmed(); + //qDebug() << "EXTRACT TEMPLATE: " << *tmplate << *inner; + return !inner->isEmpty(); +} + +static QString extractTypeFromPTypeOutput(const QString &str) +{ + int pos0 = str.indexOf('='); + int pos1 = str.indexOf('{'); + int pos2 = str.lastIndexOf('}'); + QString res = str; + if (pos0 != -1 && pos1 != -1 && pos2 != -1) + res = str.mid(pos0 + 2, pos1 - 1 - pos0) + + " ... " + str.right(str.size() - pos2); + return res.simplified(); +} + +static bool isIntOrFloatType(const QString &type) +{ + static const QStringList types = QStringList() + << "char" << "int" << "short" << "float" << "double" << "long" + << "bool" << "signed char" << "unsigned" << "unsigned char" + << "unsigned int" << "unsigned long" << "long long" + << "unsigned long long"; + return types.contains(type); +} + +static QString sizeofTypeExpression(const QString &type) +{ + if (type.endsWith('*')) + return "sizeof(void*)"; + if (type.endsWith('>')) + return "sizeof(" + type + ")"; + return "sizeof(" + gdbQuoteTypes(type) + ")"; +} + +void GdbEngine::setCustomDumpersWanted(bool on) +{ + Q_UNUSED(on); + // FIXME: a bit too harsh, but otherwise the treeview + // sometimes look funny + //m_expandedINames.clear(); + updateLocals(); +} + +bool GdbEngine::isCustomValueDumperAvailable(const QString &type) const +{ + if (!qq->useCustomDumpers()) + return false; + if (qq->debugDumpersAction()->isChecked() + && qq->stackHandler()->isDebuggingDumpers()) + return false; + if (m_dataDumperState != DataDumperAvailable) + return false; + if (m_availableSimpleDumpers.contains(type)) + return true; + + QString tmplate; + QString inner; + if (extractTemplate(type, &tmplate, &inner)) { + if (type.startsWith(m_namespace)) { + tmplate = tmplate.mid(m_namespace.size()); + if (tmplate == "QList") + return true; + if (tmplate == "QVector") + return true; + if (tmplate == "QHash") + return true; + if (tmplate == "QHashNode") + return true; + if (tmplate == "QMap") + return true; + if (tmplate == "QMapNode") + return true; + if (tmplate == "QSet") + return true; + } + if (tmplate == "std::vector" && inner != "bool") + return true; + if (tmplate == "std::basic_string") { + if (inner.startsWith("char@") || inner.startsWith("wchar_t@")) + return true; + } + } + + return false; +} + +void GdbEngine::runCustomDumper(const WatchData & data0, bool dumpChildren) +{ + WatchData data = data0; + QWB_ASSERT(!data.exp.isEmpty(), return); + QString tmplate; + QString inner; + bool isTemplate = extractTemplate(data.type, &tmplate, &inner); + QStringList inners = inner.split('@'); + if (inners.at(0).isEmpty()) + inners.clear(); + + QString outertype = isTemplate ? tmplate : data.type; + + if (outertype == "QWidget") + outertype = "QObject"; + + QString extraArgs[4]; + extraArgs[0] = "0"; + extraArgs[1] = "0"; + extraArgs[2] = "0"; + extraArgs[3] = "0"; + int extraArgCount = 0; + + // "generic" template dumpers: passing sizeof(argument) + // gives already most information the dumpers need + foreach (const QString &arg, inners) + extraArgs[extraArgCount++] = sizeofTypeExpression(arg); + + // in rare cases we need more or less: + if (outertype == m_namespace + "QObject") { + extraArgs[extraArgCount++] = "(char*)&((('" + + m_namespace + "QObjectPrivate'*)&" + + data.exp + ")->children)-(char*)&" + data.exp; + } else if (outertype == m_namespace + "QVector") { + extraArgs[extraArgCount++] = "(char*)&((" + + data.exp + ").d->array)-(char*)" + data.exp + ".d"; + } else if (outertype == m_namespace + "QObjectSlot" + || outertype == m_namespace + "QObjectSignal") { + // we need the number out of something like + // iname="local.ob.slots.[2]deleteLater()" + int lastOpened = data.iname.lastIndexOf('['); + int lastClosed = data.iname.lastIndexOf(']'); + QString slotNumber = "-1"; + if (lastOpened != -1 && lastClosed != -1) + slotNumber = data.iname.mid(lastOpened + 1, lastClosed - lastOpened - 1); + extraArgs[extraArgCount++] = slotNumber; + } else if (outertype == m_namespace + "QMap") { + QString nodetype = m_namespace + "QMapNode"; + nodetype += data.type.mid(m_namespace.size() + 4); + //qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype; + extraArgs[extraArgCount++] = sizeofTypeExpression(nodetype); + extraArgs[extraArgCount++] = "(size_t)&(('" + nodetype + "'*)0)->value"; + } else if (outertype == m_namespace + "QMapNode") { + extraArgs[extraArgCount++] = sizeofTypeExpression(data.type); + extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value"; + } else if (outertype == "std::vector") { + //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners; + if (inners.at(0) == "bool") { + outertype = "std::vector::bool"; + } else { + //extraArgs[extraArgCount++] = sizeofTypeExpression(data.type); + //extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value"; + } + } else if (outertype == "std::basic_string") { + //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners; + if (inners.at(0) == "char") { + outertype = "std::string"; + } else if (inners.at(0) == "wchar_t") { + outertype = "std::wstring"; + } + extraArgs[0] = "0"; + extraArgs[1] = "0"; + extraArgs[2] = "0"; + extraArgs[3] = "0"; + } + + //int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2; + //int protocol = data.iname.startsWith("watch") ? 3 : 2; + int protocol = 2; + //int protocol = isDisplayedIName(data.iname) ? 3 : 2; + + QString addr; + if (data.addr.startsWith("0x")) + addr = "(void*)" + data.addr; + else + addr = "&(" + data.exp + ")"; + + QByteArray params; + params.append(outertype); + params.append('\0'); + params.append(data.iname); + params.append('\0'); + params.append(data.exp); + params.append('\0'); + params.append(inner); + params.append('\0'); + params.append(data.iname); + params.append('\0'); + + sendWatchParameters(params); + + QString cmd ="call " + + QString("qDumpObjectData440(") + + QString::number(protocol) + + ',' + "%1+1" // placeholder for token + + ',' + addr + + ',' + (dumpChildren ? "1" : "0") + + ',' + extraArgs[0] + + ',' + extraArgs[1] + + ',' + extraArgs[2] + + ',' + extraArgs[3] + ')'; + + sendSynchronizedCommand(cmd, WatchDumpCustomValue1, QVariant::fromValue(data)); + + q->showStatusMessage( + tr("Retrieving data for watch view (%1 requests pending)...") + .arg(m_pendingRequests + 1), -1); + // create response slot for socket data + QVariant var; + var.setValue(data); + sendSynchronizedCommand(QString(), WatchDumpCustomValue2, var); + + // this increases the probability that gdb spits out output it + // has collected so far + //sendCommand("p qDumpInBuffer"); +} + +void GdbEngine::createGdbVariable(const WatchData &data) +{ + sendSynchronizedCommand("-var-delete \"" + data.iname + '"'); + QString exp = data.exp; + if (exp.isEmpty() && data.addr.startsWith("0x")) + exp = "*(" + gdbQuoteTypes(data.type) + "*)" + data.addr; + QVariant val = QVariant::fromValue<WatchData>(data); + sendSynchronizedCommand("-var-create \"" + data.iname + '"' + " * " + + '"' + exp + '"', WatchVarCreate, val); +} + +void GdbEngine::updateSubItem(const WatchData &data0) +{ + WatchData data = data0; + #if DEBUG_SUBITEM + qDebug() << "UPDATE SUBITEM: " << data.toString(); + #endif + QWB_ASSERT(data.isValid(), return); + + // in any case we need the type first + if (data.isTypeNeeded()) { + // This should only happen if we don't have a variable yet. + // Let's play safe, though. + if (!data.variable.isEmpty()) { + // Update: It does so for out-of-scope watchers. + #if 1 + qDebug() << "FIXME: GdbEngine::updateSubItem: " + << data.toString() << "should not happen"; + #else + data.setType("<out of scope>"); + data.setValue("<out of scope>"); + data.setChildCount(0); + insertData(data); + return; + #endif + } + // The WatchVarCreate handler will receive type information + // and re-insert a WatchData item with correct type, so + // we will not re-enter this bit. + // FIXME: Concurrency issues? + createGdbVariable(data); + return; + } + + // we should have a type now. this is relied upon further below + QWB_ASSERT(!data.type.isEmpty(), return); + + // a common case that can be easily solved + if (data.isChildrenNeeded() && isPointerType(data.type) + && !isCustomValueDumperAvailable(data.type)) { + // We sometimes know what kind of children pointers have + #if DEBUG_SUBITEM + qDebug() << "IT'S A POINTER"; + #endif +#if 1 + WatchData data1; + data1.iname = data.iname + ".*"; + data1.name = "*" + data.name; + data1.exp = "(*(" + data.exp + "))"; + data1.type = stripPointerType(data.type); + data1.setValueNeeded(); + insertData(data1); + data.setChildrenUnneeded(); + insertData(data); +#else + // Try automatic dereferentiation + data.exp = "*(" + data.exp + ")"; + data.type = data.type + "."; // FIXME: fragile HACK to avoid recursion + insertData(data); +#endif + return; + } + + if (data.isValueNeeded() && isCustomValueDumperAvailable(data.type)) { + #if DEBUG_SUBITEM + qDebug() << "UPDATE SUBITEM: CUSTOMVALUE"; + #endif + runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname)); + return; + } + +/* + if (data.isValueNeeded() && data.exp.isEmpty()) { + #if DEBUG_SUBITEM + qDebug() << "UPDATE SUBITEM: NO EXPRESSION?"; + #endif + data.setError("<no expression given>"); + insertData(data); + return; + } +*/ + + if (data.isValueNeeded() && data.variable.isEmpty()) { + #if DEBUG_SUBITEM + qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE"; + #endif + createGdbVariable(data); + // the WatchVarCreate handler will re-insert a WatchData + // item, with valueNeeded() set. + return; + } + + if (data.isValueNeeded()) { + QWB_ASSERT(!data.variable.isEmpty(), return); // tested above + #if DEBUG_SUBITEM + qDebug() << "UPDATE SUBITEM: VALUE"; + #endif + QString cmd = "-var-evaluate-expression \"" + data.iname + "\""; + sendSynchronizedCommand(cmd, WatchEvaluateExpression, + QVariant::fromValue(data)); + return; + } + + if (data.isChildrenNeeded() && isCustomValueDumperAvailable(data.type)) { + #if DEBUG_SUBITEM + qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN"; + #endif + runCustomDumper(data, true); + return; + } + + if (data.isChildrenNeeded() && data.variable.isEmpty()) { + #if DEBUG_SUBITEM + qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN"; + #endif + createGdbVariable(data); + // the WatchVarCreate handler will re-insert a WatchData + // item, with childrenNeeded() set. + return; + } + + if (data.isChildrenNeeded()) { + QWB_ASSERT(!data.variable.isEmpty(), return); // tested above + QString cmd = "-var-list-children --all-values \"" + data.variable + "\""; + sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data)); + return; + } + + if (data.isChildCountNeeded() && isCustomValueDumperAvailable(data.type)) { + #if DEBUG_SUBITEM + qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN"; + #endif + runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname)); + return; + } + + if (data.isChildCountNeeded() && data.variable.isEmpty()) { + #if DEBUG_SUBITEM + qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT"; + #endif + createGdbVariable(data); + // the WatchVarCreate handler will re-insert a WatchData + // item, with childrenNeeded() set. + return; + } + + if (data.isChildCountNeeded()) { + QWB_ASSERT(!data.variable.isEmpty(), return); // tested above + QString cmd = "-var-list-children --all-values \"" + data.variable + "\""; + sendCommand(cmd, WatchVarListChildren, QVariant::fromValue(data)); + return; + } + + qDebug() << "FIXME: UPDATE SUBITEM: " << data.toString(); + QWB_ASSERT(false, return); +} + +void GdbEngine::updateWatchModel() +{ + m_pendingRequests = 0; + PENDING_DEBUG("EXTERNAL TRIGGERING UPDATE WATCH MODEL"); + updateWatchModel2(); +} + +void GdbEngine::updateWatchModel2() +{ + PENDING_DEBUG("UPDATE WATCH MODEL"); + QList<WatchData> incomplete = qq->watchHandler()->takeCurrentIncompletes(); + //QWB_ASSERT(incomplete.isEmpty(), /**/); + if (!incomplete.isEmpty()) { + #if DEBUG_PENDING + qDebug() << "##############################################"; + qDebug() << "UPDATE MODEL, FOUND INCOMPLETES:"; + foreach (const WatchData &data, incomplete) + qDebug() << data.toString(); + #endif + + // Bump requests to avoid model rebuilding during the nested + // updateWatchModel runs. + ++m_pendingRequests; + foreach (const WatchData &data, incomplete) + updateSubItem(data); + PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL"); + updateWatchModel2(); + --m_pendingRequests; + + return; + } + + if (m_pendingRequests > 0) { + PENDING_DEBUG("UPDATE MODEL, PENDING: " << m_pendingRequests); + return; + } + + PENDING_DEBUG("REBUILDING MODEL") + emit gdbInputAvailable(QString(), + "[" + currentTime() + "] <Rebuild Watchmodel>"); + q->showStatusMessage(tr("Finished retrieving data."), -1); + qq->watchHandler()->rebuildModel(); + + if (!m_toolTipExpression.isEmpty()) { + WatchData *data = qq->watchHandler()->findData(tooltipIName); + if (data) { + //m_toolTipCache[data->exp] = *data; + QToolTip::showText(m_toolTipPos, + "(" + data->type + ") " + data->exp + " = " + data->value); + } else { + QToolTip::showText(m_toolTipPos, + "Cannot evaluate expression: " + m_toolTipExpression); + } + } + + //qDebug() << "INSERT DATA" << data0.toString(); + //q->showStatusMessage(tr("Stopped."), 5000); +} + +void GdbEngine::handleQueryDataDumper1(const GdbResultRecord &record) +{ + Q_UNUSED(record); +} + +void GdbEngine::handleQueryDataDumper2(const GdbResultRecord &record) +{ + // is this the official gdb response. However, it won't contain + // interesting data other than the information that 'real' data + // either already arrived or is still in the pipe. So we do + // _not_ register this result for counting purposes, this will + // be done by the 'real' result (with resultClass == GdbResultCustomDone) + //qDebug() << "DATA DUMPER TRIAL:" << record.toString(); + GdbMi output = record.data.findChild("customvaluecontents"); + GdbMi contents(output.data()); + GdbMi simple = contents.findChild("simpledumpers"); + m_namespace = contents.findChild("namespace").data(); + //qDebug() << "OUTPUT: " << output.toString(); + //qDebug() << "CONTENTS: " << contents.toString(); + //qDebug() << "SIMPLE DUMPERS: " << simple.toString(); + m_availableSimpleDumpers.clear(); + foreach (const GdbMi &item, simple.children()) + m_availableSimpleDumpers.append(item.data()); + if (m_availableSimpleDumpers.isEmpty()) { + m_dataDumperState = DataDumperUnavailable; + QMessageBox::warning(q->mainWindow(), + tr("Cannot find special data dumpers"), + tr("The debugged binary does not contain information needed for " + "nice display of Qt data types.\n\n" + "Try might want to try include the file\n\n" + ".../ide/main/bin/gdbmacros/gdbmacros.cpp'\n\n" + "into your project directly.") + ); + } else { + m_dataDumperState = DataDumperAvailable; + } + //qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDumpers; +} + +void GdbEngine::sendWatchParameters(const QByteArray ¶ms0) +{ + QByteArray params = params0; + params.append('\0'); + char buf[50]; + sprintf(buf, "set {char[%d]} qDumpInBuffer = {", params.size()); + QByteArray encoded; + encoded.append(buf); + for (int i = 0; i != params.size(); ++i) { + sprintf(buf, "%d,", int(params[i])); + encoded.append(buf); + } + encoded[encoded.size() - 1] = '}'; + + sendCommand(encoded); +} + +void GdbEngine::handleVarAssign() +{ + // everything might have changed, force re-evaluation + // FIXME: Speed this up by re-using variables and only + // marking values as 'unknown' + updateLocals(); +} + +void GdbEngine::setWatchDataType(WatchData &data, const GdbMi &mi) +{ + if (mi.isValid()) { + if (!data.framekey.isEmpty()) + m_varToType[data.framekey] = mi.data(); + data.setType(mi.data()); + } else if (data.type.isEmpty()) { + data.setTypeNeeded(); + } +} + +void GdbEngine::handleVarCreate(const GdbResultRecord &record, + const WatchData &data0) +{ + WatchData data = data0; + // happens e.g. when we already issued a var-evaluate command + if (!data.isValid()) + return; + //qDebug() << "HANDLE VARIABLE CREATION: " << data.toString(); + if (record.resultClass == GdbResultDone) { + data.variable = data.iname; + setWatchDataType(data, record.data.findChild("type")); + if (isCustomValueDumperAvailable(data.type)) { + // we do not trust gdb if we have a custom dumper + if (record.data.findChild("children").isValid()) + data.setChildrenUnneeded(); + else if (qq->watchHandler()->isExpandedIName(data.iname)) + data.setChildrenNeeded(); + insertData(data); + } else { + if (record.data.findChild("children").isValid()) + data.setChildrenUnneeded(); + else if (qq->watchHandler()->isExpandedIName(data.iname)) + data.setChildrenNeeded(); + setWatchDataChildCount(data, record.data.findChild("numchild")); + //if (data.isValueNeeded() && data.childCount > 0) + // data.setValue(QByteArray()); + insertData(data); + } + } else if (record.resultClass == GdbResultError) { + data.setError(record.data.findChild("msg").data()); + if (data.isWatcher()) { + data.value = strNotInScope; + data.type = " "; + data.setAllUnneeded(); + data.setChildCount(0); + data.valuedisabled = true; + insertData(data); + } + } +} + +void GdbEngine::handleEvaluateExpression(const GdbResultRecord &record, + const WatchData &data0) +{ + WatchData data = data0; + QWB_ASSERT(data.isValid(), qDebug() << "HUH?"); + if (record.resultClass == GdbResultDone) { + //if (col == 0) + // data.name = record.data.findChild("value").data(); + //else + setWatchDataValue(data, record.data.findChild("value")); + } else if (record.resultClass == GdbResultError) { + data.setError(record.data.findChild("msg").data()); + } + //qDebug() << "HANDLE EVALUATE EXPRESSION: " << data.toString(); + insertData(data); + //updateWatchModel2(); +} + +void GdbEngine::handleDumpCustomSetup(const GdbResultRecord &record) +{ + qDebug() << "CUSTOM SETUP RESULT: " << record.toString(); + if (record.resultClass == GdbResultDone) { + } else if (record.resultClass == GdbResultError) { + QString msg = record.data.findChild("msg").data(); + qDebug() << "CUSTOM DUMPER SETUP ERROR MESSAGE: " << msg; + } +} + +void GdbEngine::handleDumpCustomValue1(const GdbResultRecord &record, + const WatchData &data0) +{ + WatchData data = data0; + QWB_ASSERT(data.isValid(), return); + if (record.resultClass == GdbResultDone) { + // ignore this case, data will follow + } else if (record.resultClass == GdbResultError) { + // Record an extra result, as the socket result will be lost + // in transmission + --m_pendingRequests; + QString msg = record.data.findChild("msg").data(); + //qDebug() << "CUSTOM DUMPER ERROR MESSAGE: " << msg; +#ifdef QT_DEBUG + // Make debugging of dumers easier + if (qq->debugDumpersAction()->isChecked() + && msg.startsWith("The program being debugged stopped while") + && msg.contains("qDumpObjectData440")) { + // Fake full stop + sendCommand("-file-list-exec-source-files", GdbQuerySources); + sendCommand("-break-list", BreakList); + sendCommand("p 0", GdbAsyncOutput2); // dummy + return; + } +#endif + if (msg.startsWith("The program being debugged was sig")) + msg = strNotInScope; + if (msg.startsWith("The program being debugged stopped while")) + msg = strNotInScope; + data.setError(msg); + insertData(data); + } +} + +void GdbEngine::handleDumpCustomValue2(const GdbResultRecord &record, + const WatchData &data0) +{ + WatchData data = data0; + QWB_ASSERT(data.isValid(), return); + //qDebug() << "CUSTOM VALUE RESULT: " << record.toString(); + //qDebug() << "FOR DATA: " << data.toString() << record.resultClass; + if (record.resultClass == GdbResultDone) { + GdbMi output = record.data.findChild("customvaluecontents"); + //qDebug() << "HANDLE VALUE CONTENTS: " << output.toString(true); + if (!output.isValid()) { + //qDebug() << "INVALID"; + // custom dumper produced no output + if (data.isValueNeeded()) + data.setValue("<unknown>"); + if (data.isTypeNeeded()) + data.setType("<unknown>"); + if (data.isChildrenNeeded()) + data.setChildCount(0); + if (data.isChildCountNeeded()) + data.setChildCount(0); + data.setValueToolTip("<custom dumper produced no output>"); + insertData(data); + } else { + GdbMi contents; + //qDebug() << "OUTPUT" << output.toString(true); + contents.fromString(output.data()); + //qDebug() << "CONTENTS" << contents.toString(true); + setWatchDataType(data, contents.findChild("type")); + setWatchDataValue(data, contents.findChild("value"), + contents.findChild("valueencoded").data().toInt()); + setWatchDataAddress(data, contents.findChild("addr")); + setWatchDataChildCount(data, contents.findChild("numchild")); + setWatchDataValueToolTip(data, contents.findChild("valuetooltip")); + setWatchDataValueDisabled(data, contents.findChild("valuedisabled")); + setWatchDataEditValue(data, contents.findChild("editvalue")); + if (qq->watchHandler()->isDisplayedIName(data.iname)) { + GdbMi editvalue = contents.findChild("editvalue"); + if (editvalue.isValid()) { + setWatchDataEditValue(data, editvalue); + qq->watchHandler()->showEditValue(data); + } + } + if (!qq->watchHandler()->isExpandedIName(data.iname)) + data.setChildrenUnneeded(); + GdbMi children = contents.findChild("children"); + if (children.isValid() || !qq->watchHandler()->isExpandedIName(data.iname)) + data.setChildrenUnneeded(); + data.setValueUnneeded(); + + // try not to repeat data too often + WatchData childtemplate; + setWatchDataType(childtemplate, contents.findChild("childtype")); + setWatchDataChildCount(childtemplate, contents.findChild("childnumchild")); + //qDebug() << "DATA: " << data.toString(); + insertData(data); + foreach (GdbMi item, children.children()) { + WatchData data1 = childtemplate; + data1.name = item.findChild("name").data(); + data1.iname = data.iname + "." + data1.name; + //qDebug() << "NAMEENCODED: " << item.findChild("nameencoded").data() + // << item.findChild("nameencoded").data()[1]; + if (item.findChild("nameencoded").data()[0] == '1') + data1.name = QByteArray::fromBase64(data1.name.toUtf8()); + if (item.findChild("nameisindex").data()[0] == '1') + data1.name = '[' + data1.name + ']'; + setWatchDataType(data1, item.findChild("type")); + setWatchDataExpression(data1, item.findChild("exp")); + setWatchDataChildCount(data1, item.findChild("numchild")); + setWatchDataValue(data1, item.findChild("value"), + item.findChild("valueencoded").data().toInt()); + setWatchDataAddress(data1, item.findChild("addr")); + setWatchDataValueToolTip(data1, item.findChild("valuetooltip")); + setWatchDataValueDisabled(data1, item.findChild("valuedisabled")); + if (!qq->watchHandler()->isExpandedIName(data1.iname)) + data1.setChildrenUnneeded(); + //qDebug() << "HANDLE CUSTOM SUBCONTENTS:" << data1.toString(); + insertData(data1); + } + } + //qDebug() << "HANDLE CUSTOM VALUE CONTENTS: " << data.toString(); + } else if (record.resultClass == GdbResultError) { + // FIXME: Should not happen here, i.e. could be removed + QString msg = record.data.findChild("msg").data(); + //qDebug() << "CUSTOM DUMPER ERROR MESSAGE: " << msg; + if (msg.startsWith("The program being debugged was sig")) + msg = strNotInScope; + if (msg.startsWith("The program being debugged stopped while")) + msg = strNotInScope; + data.setError(msg); + insertData(data); + } else { + qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA: " << data.toString(); + } +} + +void GdbEngine::updateLocals() +{ + setTokenBarrier(); + + m_pendingRequests = 0; + PENDING_DEBUG("\nRESET PENDING"); + m_toolTipCache.clear(); + m_toolTipExpression.clear(); + qq->watchHandler()->reinitializeWatchers(); + + int level = currentFrame(); + // '2' is 'list with type and value' + QString cmd = QString("-stack-list-arguments 2 %1 %2").arg(level).arg(level); + sendSynchronizedCommand(cmd, StackListArguments); // stage 1/2 + // '2' is 'list with type and value' + sendSynchronizedCommand("-stack-list-locals 2", StackListLocals); // stage 2/2 +} + +void GdbEngine::handleStackListArguments(const GdbResultRecord &record) +{ + // stage 1/2 + + // Linux: + // 12^done,stack-args= + // [frame={level="0",args=[ + // {name="argc",type="int",value="1"}, + // {name="argv",type="char **",value="(char **) 0x7..."}]}] + // Mac: + // 78^done,stack-args= + // {frame={level="0",args={ + // varobj= + // {exp="this",value="0x38a2fab0",name="var21",numchild="3", + // type="CurrentDocumentFind * const",typecode="PTR", + // dynamic_type="",in_scope="true",block_start_addr="0x3938e946", + // block_end_addr="0x3938eb2d"}, + // varobj= + // {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}", + // name="var22",numchild="1",type="const QString ...} }}} + // + // In both cases, iterating over the children of stack-args/frame/args + // is ok. + m_currentFunctionArgs.clear(); + if (record.resultClass == GdbResultDone) { + const GdbMi list = record.data.findChild("stack-args"); + const GdbMi frame = list.findChild("frame"); + const GdbMi args = frame.findChild("args"); + m_currentFunctionArgs = args.children(); + } else if (record.resultClass == GdbResultError) { + qDebug() << "FIXME: GdbEngine::handleStackListArguments: should not happen"; + } +} + +void GdbEngine::handleStackListLocals(const GdbResultRecord &record) +{ + // stage 2/2 + + // There could be shadowed variables + QHash<QString, int> seen; + QList<GdbMi> locals = record.data.findChild("locals").children(); + locals += m_currentFunctionArgs; + + //qDebug() << m_varToType; + + foreach (const GdbMi &item, locals) { + #ifdef Q_OS_MAC + QString name = item.findChild("exp").data(); + #else + QString name = item.findChild("name").data(); + #endif + int n = seen.value(name); + if (n) { + seen[name] = n + 1; + WatchData data; + data.iname = "local." + name + QString::number(n + 1); + data.name = name + QString(" <shadowed %1>").arg(n); + //data.setValue("<shadowed>"); + setWatchDataValue(data, item.findChild("value")); + data.setType("<shadowed>"); + data.setChildCount(0); + insertData(data); + } else { + seen[name] = 1; + WatchData data; + data.iname = "local." + name; + data.name = name; + data.exp = name; + data.framekey = m_currentFrame + data.name; + setWatchDataType(data, item.findChild("type")); + // set value only directly if it is simple enough, otherwise + // pass through the insertData() machinery + if (isIntOrFloatType(data.type) || isPointerType(data.type)) + setWatchDataValue(data, item.findChild("value")); + if (!qq->watchHandler()->isExpandedIName(data.iname)) + data.setChildrenUnneeded(); + if (isPointerType(data.type) || data.name == "this") + data.setChildCount(1); + if (0 && m_varToType.contains(data.framekey)) { + qDebug() << "RE-USING " << m_varToType.value(data.framekey); + data.setType(m_varToType.value(data.framekey)); + } + insertData(data); + } + } +} + +void GdbEngine::insertData(const WatchData &data0) +{ + //qDebug() << "INSERT DATA" << data0.toString(); + WatchData data = data0; + if (data.value.startsWith("mi_cmd_var_create:")) { + qDebug() << "BOGUS VALUE: " << data.toString(); + return; + } + qq->watchHandler()->insertData(data); +} + +void GdbEngine::handleTypeContents(const QString &output) +{ + // output.startsWith("type = ") == true + // "type = int" + // "type = class QString {" + // "type = class QStringList : public QList<QString> {" + QString tip; + QString className; + if (output.startsWith("type = class")) { + int posBrace = output.indexOf('{'); + QString head = output.mid(13, posBrace - 13 - 1); + int posColon = head.indexOf(": public"); + if (posColon == -1) + posColon = head.indexOf(": protected"); + if (posColon == -1) + posColon = head.indexOf(": private"); + if (posColon == -1) { + className = head; + tip = "class " + className + " { ... }"; + } else { + className = head.left(posColon - 1); + tip = "class " + head + " { ... }"; + } + //qDebug() << "posColon: " << posColon; + //qDebug() << "posBrace: " << posBrace; + //qDebug() << "head: " << head; + } else { + className = output.mid(7); + tip = className; + } + //qDebug() << "output: " << output.left(100) + "..."; + //qDebug() << "className: " << className; + //qDebug() << "tip: " << tip; + //m_toolTip.type = className; + m_toolTip.type.clear(); + m_toolTip.value = tip; +} + +void GdbEngine::handleVarListChildrenHelper(const GdbMi &item, + const WatchData &parent) +{ + //qDebug() << "VAR_LIST_CHILDREN: PARENT 2" << parent.toString(); + //qDebug() << "VAR_LIST_CHILDREN: APPENDEE " << data.toString(); + QByteArray exp = item.findChild("exp").data(); + QByteArray name = item.findChild("name").data(); + if (isAccessSpecifier(exp)) { + // suppress 'private'/'protected'/'public' level + WatchData data; + data.variable = name; + data.iname = parent.iname; + //data.iname = data.variable; + data.exp = parent.exp; + data.setTypeUnneeded(); + data.setValueUnneeded(); + data.setChildCountUnneeded(); + data.setChildrenUnneeded(); + //qDebug() << "DATA" << data.toString(); + QString cmd = "-var-list-children --all-values \"" + data.variable + "\""; + //iname += '.' + exp; + sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data)); + } else if (item.findChild("numchild").data() == "0") { + // happens for structs without data, e.g. interfaces. + WatchData data; + data.iname = parent.iname + '.' + exp; + data.name = exp; + data.variable = name; + setWatchDataType(data, item.findChild("type")); + setWatchDataValue(data, item.findChild("value")); + setWatchDataAddress(data, item.findChild("addr")); + data.setChildCount(0); + insertData(data); + } else if (parent.iname.endsWith('.')) { + // Happens with anonymous unions + WatchData data; + data.iname = name; + QString cmd = "-var-list-children --all-values \"" + data.variable + "\""; + sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data)); + } else if (exp == "staticMetaObject") { + // && item.findChild("type").data() == "const QMetaObject") + // FIXME: Namespaces? + // { do nothing } FIXME: make coinfigurable? + // special "clever" hack to avoid clutter in the GUI. + // I am not sure this is a good idea... + } else { + WatchData data; + data.iname = parent.iname + '.' + exp; + data.variable = name; + setWatchDataType(data, item.findChild("type")); + setWatchDataValue(data, item.findChild("value")); + setWatchDataAddress(data, item.findChild("addr")); + setWatchDataChildCount(data, item.findChild("numchild")); + if (!qq->watchHandler()->isExpandedIName(data.iname)) + data.setChildrenUnneeded(); + + data.name = exp; + if (isPointerType(parent.type) && data.type == exp) { + data.exp = "*(" + parent.exp + ")"; + data.name = "*" + parent.name; + } else if (data.type == exp) { + // A type we derive from? gdb crashes when creating variables here + data.exp = parent.exp; + } else if (exp.startsWith("*")) { + // A pointer + data.exp = "*(" + parent.exp + ")"; + } else if (startsWithDigit(exp)) { + // An array. No variables needed? + data.name = "[" + data.name + "]"; + data.exp = parent.exp + "[" + exp + "]"; + } else if (0 && parent.name.endsWith('.')) { + // Happens with anonymous unions + data.exp = parent.exp + exp; + //data.name = "<anonymous union>"; + } else if (exp.isEmpty()) { + // Happens with anonymous unions + data.exp = parent.exp; + data.name = "<n/a>"; + data.iname = parent.iname + ".@"; + data.type = "<anonymous union>"; + } else { + // A structure. Hope there's nothing else... + data.exp = parent.exp + '.' + exp; + } + + if (isCustomValueDumperAvailable(data.type)) { + // we do not trust gdb if we have a custom dumper + data.setValueNeeded(); + data.setChildCountNeeded(); + } + + //qDebug() << "VAR_LIST_CHILDREN: PARENT 3" << parent.toString(); + //qDebug() << "VAR_LIST_CHILDREN: APPENDEE " << data.toString(); + insertData(data); + } +} + +void GdbEngine::handleVarListChildren(const GdbResultRecord &record, + const WatchData &data0) +{ + //WatchResultCounter dummy(this, WatchVarListChildren); + WatchData data = data0; + if (!data.isValid()) + return; + if (record.resultClass == GdbResultDone) { + //qDebug() << "VAR_LIST_CHILDREN: PARENT " << data.toString(); + GdbMi children = record.data.findChild("children"); + + foreach (const GdbMi &child, children.children()) + handleVarListChildrenHelper(child, data); + + if (!isAccessSpecifier(data.variable.split('.').takeLast())) { + data.setChildrenUnneeded(); + insertData(data); + } + } else if (record.resultClass == GdbResultError) { + data.setError(record.data.findChild("msg").data()); + } else { + data.setError("Unknown error: " + record.toString()); + } +} + +void GdbEngine::handleToolTip(const GdbResultRecord &record, + const QString &what) +{ + //qDebug() << "HANDLE TOOLTIP: " << what << m_toolTip.toString(); + // << "record: " << record.toString(); + if (record.resultClass == GdbResultError) { + QString msg = record.data.findChild("msg").data(); + if (what == "create") { + sendCommand("ptype " + m_toolTip.exp, WatchToolTip, "ptype"); + return; + } + if (what == "evaluate") { + if (msg.startsWith("Cannot look up value of a typedef")) { + m_toolTip.value = m_toolTip.exp + " is a typedef."; + //return; + } + } + } else if (record.resultClass == GdbResultDone) { + if (what == "create") { + setWatchDataType(m_toolTip, record.data.findChild("type")); + setWatchDataChildCount(m_toolTip, record.data.findChild("numchild")); + if (isCustomValueDumperAvailable(m_toolTip.type)) + runCustomDumper(m_toolTip, false); + else + q->showStatusMessage(tr("Retrieving data for tooltip..."), -1); + sendCommand("-data-evaluate-expression " + m_toolTip.exp, + WatchToolTip, "evaluate"); + //sendToolTipCommand("-var-evaluate-expression tooltip") + return; + } + if (what == "evaluate") { + m_toolTip.value = m_toolTip.type + ' ' + m_toolTip.exp + + " = " + record.data.findChild("value").data(); + //return; + } + if (what == "ptype") { + GdbMi mi = record.data.findChild("consolestreamoutput"); + m_toolTip.value = extractTypeFromPTypeOutput(mi.data()); + //return; + } + } + + m_toolTip.iname = tooltipIName; + m_toolTip.setChildrenUnneeded(); + m_toolTip.setChildCountUnneeded(); + insertData(m_toolTip); + qDebug() << "DATA INSERTED"; + QTimer::singleShot(0, this, SLOT(updateWatchModel2())); + qDebug() << "HANDLE TOOLTIP END"; +} + +#if 0 +void GdbEngine::handleChangedItem(QStandardItem *item) +{ + // HACK: Just store the item for the slot + // handleChangedItem(QWidget *widget) below. + QModelIndex index = item->index().sibling(item->index().row(), 0); + //WatchData data = m_currentSet.takeData(iname); + //m_editedData = inameFromItem(m_model.itemFromIndex(index)).exp; + //qDebug() << "HANDLE CHANGED EXPRESSION: " << m_editedData; +} +#endif + +void GdbEngine::assignValueInDebugger(const QString &expression, const QString &value) +{ + sendCommand("-var-delete assign"); + sendCommand("-var-create assign * " + expression); + sendCommand("-var-assign assign " + value, WatchVarAssign); +} + + +void GdbEngine::tryLoadCustomDumpers() +{ + if (m_dataDumperState != DataDumperUninitialized) + return; + + PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS"); + m_dataDumperState = DataDumperLoadTried; + +#if defined(Q_OS_LINUX) + QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.so"; + if (QFileInfo(lib).isExecutable()) { + //sendCommand("p dlopen"); + if (qq->useFastStart()) + sendCommand("set stop-on-solib-events 0"); + QString flag = QString::number(RTLD_NOW); + sendCommand("call dlopen(\"" + lib + "\", " + flag + ")"); + sendCommand("sharedlibrary " + dotEscape(lib)); + if (qq->useFastStart()) + sendCommand("set stop-on-solib-events 1"); + } +#endif +#if defined(Q_OS_MAC) + QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.dylib"; + if (QFileInfo(lib).isExecutable()) { + //sendCommand("p dlopen"); // FIXME: remove me + if (qq->useFastStart()) + sendCommand("set stop-on-solib-events 0"); + QString flag = QString::number(RTLD_NOW); + sendCommand("call dlopen(\"" + lib + "\", " + flag + ")"); + sendCommand("sharedlibrary " + dotEscape(lib)); + if (qq->useFastStart()) + sendCommand("set stop-on-solib-events 1"); + } +#endif +#if defined(Q_OS_WIN) + QString lib = q->m_buildDir + "/qtc-gdbmacros/debug/gdbmacros.dll"; + if (QFileInfo(lib).exists()) { + if (qq->useFastStart()) + sendCommand("set stop-on-solib-events 0"); + //sendCommand("handle SIGSEGV pass stop print"); + //sendCommand("set unwindonsignal off"); + sendCommand("call LoadLibraryA(\"" + lib + "\")"); + sendCommand("sharedlibrary " + dotEscape(lib)); + if (qq->useFastStart()) + sendCommand("set stop-on-solib-events 1"); + } +#endif + + // retreive list of dumpable classes + sendCommand("call qDumpObjectData440(1,%1+1,0,0,0,0,0,0)", + GdbQueryDataDumper1); + // create response slot for socket data + sendCommand(QString(), GdbQueryDataDumper2); +} + + +IDebuggerEngine *createGdbEngine(DebuggerManager *parent) +{ + return new GdbEngine(parent); +} + diff --git a/src/plugins/debugger/gdbengine.h b/src/plugins/debugger/gdbengine.h new file mode 100644 index 00000000000..bdd59cbca66 --- /dev/null +++ b/src/plugins/debugger/gdbengine.h @@ -0,0 +1,351 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_GDBENGINE_H +#define DEBUGGER_GDBENGINE_H + +#include <QtCore/QByteArray> +#include <QtCore/QHash> +#include <QtCore/QMap> +#include <QtCore/QObject> +#include <QtCore/QProcess> +#include <QtCore/QPoint> +#include <QtCore/QVariant> + +QT_BEGIN_NAMESPACE +class QAction; +class QAbstractItemModel; +class QWidget; +QT_END_NAMESPACE + +#include "idebuggerengine.h" +#include "gdbmi.h" + +namespace Debugger { +namespace Internal { + +class DebuggerManager; +class IDebuggerManagerAccessForEngines; +class GdbResultRecord; +class GdbMi; + +class WatchData; +class BreakpointData; + +struct GdbCookie +{ + GdbCookie() : type(0), synchronized(false) {} + + QString command; + int type; + bool synchronized; + QVariant cookie; +}; + +enum DataDumperState +{ + DataDumperUninitialized, + DataDumperLoadTried, + DataDumperAvailable, + DataDumperUnavailable, +}; + +// FIXME: Move to extra file? +class GdbSettings +{ +public: + GdbSettings() { m_autoRun = m_autoQuit = false; } + +public: + QString m_gdbCmd; + QString m_gdbEnv; + bool m_autoRun; + bool m_autoQuit; + + QString m_scriptFile; + QMap<QString, QVariant> m_typeMacros; +}; + +GdbSettings &theGdbSettings(); + +class GdbEngine : public IDebuggerEngine +{ + Q_OBJECT + +public: + GdbEngine(DebuggerManager *parent); + ~GdbEngine(); + +signals: + void gdbResponseAvailable(); + void gdbInputAvailable(const QString &prefix, const QString &msg); + void gdbOutputAvailable(const QString &prefix, const QString &msg); + void applicationOutputAvailable(const QString &prefix, const QString &msg); + +private: + // + // IDebuggerEngine implementation + // + void stepExec(); + void stepOutExec(); + void nextExec(); + void stepIExec(); + void nextIExec(); + + void shutdown(); + void setToolTipExpression(const QPoint &pos, const QString &exp); + bool startDebugger(); + void exitDebugger(); + + void continueInferior(); + void runInferior(); + void interruptInferior(); + + void runToLineExec(const QString &fileName, int lineNumber); + void runToFunctionExec(const QString &functionName); + void jumpToLineExec(const QString &fileName, int lineNumber); + + void activateFrame(int index); + void selectThread(int index); + + Q_SLOT void attemptBreakpointSynchronization(); + + void loadSessionData() {} + void saveSessionData() {} + + void assignValueInDebugger(const QString &expr, const QString &value); + void executeDebuggerCommand(const QString & command); + + void loadSymbols(const QString &moduleName); + void loadAllSymbols(); + + // + // Own stuff + // + int currentFrame() const; + QString currentWorkingDirectory() const { return m_pwd; } + + bool supportsThreads() const; + + void init(); // called by destructor + void queryFullName(const QString &fileName, QString *fullName); + QString fullName(const QString &fileName); + QString shortName(const QString &fullName); + // get one usable name out of these, try full names first + QString fullName(const QStringList &candidates); + + void handleResult(const GdbResultRecord &, int type, const QVariant &); + + // type and cookie are sender-internal data, opaque for the "event + // queue". resultNeeded == true increments m_pendingResults on + // send and decrements on receipt, effectively preventing + // watch model updates before everything is finished. + void sendCommand(const QString & command, + int type = 0, const QVariant &cookie = QVariant(), + bool needStop = false, bool synchronized = false); + void sendSynchronizedCommand(const QString & command, + int type = 0, const QVariant &cookie = QVariant(), + bool needStop = false); + + void setTokenBarrier(); + + void updateLocals(); + +private slots: + void setDebugDumpers(bool on); + void setCustomDumpersWanted(bool on); + + void handleResponse(); + + void gdbProcError(QProcess::ProcessError error); + void readGdbStandardOutput(); + void readGdbStandardError(); + +private: + int terminationIndex(const QByteArray &buffer, int &length); + void handleStreamOutput(const QString &output, char code); + void handleAsyncOutput2(const GdbMi &data); + void handleAsyncOutput(const GdbMi &data); + void handleResultRecord(const GdbResultRecord &response); + void handleFileExecAndSymbols(const GdbResultRecord &response); + void handleExecRun(const GdbResultRecord &response); + void handleExecJumpToLine(const GdbResultRecord &response); + void handleExecRunToFunction(const GdbResultRecord &response); + void handleInfoShared(const GdbResultRecord &response); + void handleInfoProc(const GdbResultRecord &response); + void handleShowVersion(const GdbResultRecord &response); + void handleQueryPwd(const GdbResultRecord &response); + void handleQuerySources(const GdbResultRecord &response); + void handleQuerySources2(const GdbResultRecord &response, + const QVariant &); + + QByteArray m_inbuffer; + + QProcess m_gdbProc; + + QHash<int, GdbCookie> m_cookieForToken; + QHash<int, QByteArray> m_customOutputForToken; + + QByteArray m_pendingConsoleStreamOutput; + QByteArray m_pendingTargetStreamOutput; + QByteArray m_pendingLogStreamOutput; + //QByteArray m_pendingCustomValueContents; + QString m_pwd; + + // contains the first token number for the current round + // of evaluation. Responses with older tokens are considers + // out of date and discarded. + int m_oldestAcceptableToken; + + int m_gdbVersion; // 6.8.0 is 680 + int m_shared; + + // awful hack to keep track of used files + QHash<QString, QString> m_shortToFullName; + QHash<QString, QString> m_fullToShortName; + + // + // Breakpoint specific stuff + // + void handleBreakList(const GdbResultRecord &record); + void handleBreakList(const GdbMi &table); + void handleBreakIgnore(const GdbResultRecord &record, int index); + void handleBreakInsert(const GdbResultRecord &record, int index); + void handleBreakInsert1(const GdbResultRecord &record, int index); + void handleBreakCondition(const GdbResultRecord &record, int index); + void handleBreakInfo(const GdbResultRecord &record, int index); + void extractDataFromInfoBreak(const QString &output, BreakpointData *data); + void breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt); + void sendInsertBreakpoint(int index); + + + // + // Disassembler specific stuff + // + void handleDisassemblerList(const GdbResultRecord &record, + const QString &cookie); + void reloadDisassembler(); + QString m_address; + + + // + // Modules specific stuff + // + void reloadModules(); + void handleModulesList(const GdbResultRecord &record); + + + // + // Register specific stuff + // + void reloadRegisters(); + void handleRegisterListNames(const GdbResultRecord &record); + void handleRegisterListValues(const GdbResultRecord &record); + + + // + // Stack specific stuff + // + void handleStackListFrames(const GdbResultRecord &record); + void handleStackSelectThread(const GdbResultRecord &record, int cookie); + void handleStackListThreads(const GdbResultRecord &record, int cookie); + + + // + // Tooltip specific stuff + // + void sendToolTipCommand(const QString &command, const QString &cookie); + + + // + // Watch specific stuff + // + // FIXME: BaseClass. called to improve situation for a watch item + void updateSubItem(const WatchData &data); + + void updateWatchModel(); + Q_SLOT void updateWatchModel2(); + + void insertData(const WatchData &data); + void sendWatchParameters(const QByteArray ¶ms0); + void createGdbVariable(const WatchData &data); + + void handleTypeContents(const QString &output); + void maybeHandleInferiorPidChanged(const QString &pid); + + void tryLoadCustomDumpers(); + void runCustomDumper(const WatchData &data, bool dumpChildren); + bool isCustomValueDumperAvailable(const QString &type) const; + + void handleVarListChildren(const GdbResultRecord &record, + const WatchData &cookie); + void handleVarCreate(const GdbResultRecord &record, + const WatchData &cookie); + void handleVarAssign(); + void handleEvaluateExpression(const GdbResultRecord &record, + const WatchData &cookie); + void handleToolTip(const GdbResultRecord &record, + const QString &cookie); + void handleDumpCustomValue1(const GdbResultRecord &record, + const WatchData &cookie); + void handleQueryDataDumper1(const GdbResultRecord &record); + void handleQueryDataDumper2(const GdbResultRecord &record); + void handleDumpCustomValue2(const GdbResultRecord &record, + const WatchData &cookie); + void handleDumpCustomEditValue(const GdbResultRecord &record); + void handleDumpCustomSetup(const GdbResultRecord &record); + void handleStackListLocals(const GdbResultRecord &record); + void handleStackListArguments(const GdbResultRecord &record); + void handleVarListChildrenHelper(const GdbMi &child, + const WatchData &parent); + void setWatchDataType(WatchData &data, const GdbMi &mi); + + QString m_editedData; + int m_pendingRequests; + int m_inferiorPid; + + QStringList m_availableSimpleDumpers; + QString m_namespace; // namespace used in "namespaced Qt"; + + DataDumperState m_dataDumperState; // state of qt creator dumpers + QList<GdbMi> m_currentFunctionArgs; + QString m_currentFrame; + QMap<QString, QString> m_varToType; + + DebuggerManager *q; + IDebuggerManagerAccessForEngines *qq; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_GDBENGINE_H diff --git a/src/plugins/debugger/gdbmi.cpp b/src/plugins/debugger/gdbmi.cpp new file mode 100644 index 00000000000..9091422ad47 --- /dev/null +++ b/src/plugins/debugger/gdbmi.cpp @@ -0,0 +1,473 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gdbmi.h" +#include "assert.h" + +#include <QtCore/QByteArray> +#include <QtCore/QDebug> +#include <QtCore/QTextStream> + +namespace Debugger { +namespace Internal { + +QTextStream & operator<<(QTextStream & os, const GdbMi & mi) +{ + return os << mi.toString(); +} + +//static void skipSpaces(const GdbMi::Char *&from, const GdbMi::Char *to) +//{ +// while (from != to && QChar(*from).isSpace()) +// ++from; +//} + + +void GdbMi::parseResultOrValue(const Char *&from, const Char *to) +{ + //skipSpaces(from, to); + while (from != to && QChar(*from).isSpace()) + ++from; + + //qDebug() << "parseResultOrValue: " << QByteArray::fromLatin1(from, to - from); + parseValue(from, to); + if (isValid()) { + //qDebug() << "no valid result in " << QByteArray::fromLatin1(from, to - from); + return; + } + if (from == to || *from == '(') + return; + const Char *ptr = from; + while (ptr < to && *ptr != '=') { + //qDebug() << "adding" << QChar(*ptr) << "to name"; + ++ptr; + } + m_name = QByteArray(from, ptr - from); + from = ptr; + if (from < to && *from == '=') { + ++from; + parseValue(from, to); + } +} + +QByteArray GdbMi::parseCString(const Char *&from, const Char *to) +{ + QByteArray result; + //qDebug() << "parseCString: " << QByteArray::fromUtf16(from, to - from); + if (*from != '"') { + qDebug() << "MI Parse Error, double quote expected"; + return QByteArray(); + } + const Char *ptr = from; + ++ptr; + while (ptr < to) { + if (*ptr == '"') { + ++ptr; + result = QByteArray(from + 1, ptr - from - 2); + break; + } + if (*ptr == '\\' && ptr < to - 1) + ++ptr; + ++ptr; + } + + if (result.contains('\\')) { + if (result.contains("\\032\\032")) + result.clear(); + else { + result = result.replace("\\n", "\n"); + result = result.replace("\\t", "\t"); + result = result.replace("\\\"", "\""); + } + } + + from = ptr; + return result; +} + +void GdbMi::parseValue(const Char *&from, const Char *to) +{ + //qDebug() << "parseValue: " << QByteArray::fromUtf16(from, to - from); + switch (*from) { + case '{': + parseTuple(from, to); + break; + case '[': + parseList(from, to); + break; + case '"': + m_type = Const; + m_data = parseCString(from, to); + break; + default: + break; + } +} + + +void GdbMi::parseTuple(const Char *&from, const Char *to) +{ + //qDebug() << "parseTuple: " << QByteArray::fromUtf16(from, to - from); + QWB_ASSERT(*from == '{', /**/); + ++from; + parseTuple_helper(from, to); +} + +void GdbMi::parseTuple_helper(const Char *&from, const Char *to) +{ + //qDebug() << "parseTuple_helper: " << QByteArray::fromUtf16(from, to - from); + m_type = Tuple; + while (from < to) { + if (*from == '}') { + ++from; + break; + } + GdbMi child; + child.parseResultOrValue(from, to); + //qDebug() << "\n=======\n" << qPrintable(child.toString()) << "\n========\n"; + if (!child.isValid()) + return; + m_children += child; + if (*from == ',') + ++from; + } +} + +void GdbMi::parseList(const Char *&from, const Char *to) +{ + //qDebug() << "parseList: " << QByteArray::fromUtf16(from, to - from); + QWB_ASSERT(*from == '[', /**/); + ++from; + m_type = List; + while (from < to) { + if (*from == ']') { + ++from; + break; + } + GdbMi child; + child.parseResultOrValue(from, to); + if (child.isValid()) + m_children += child; + if (*from == ',') + ++from; + } +} + +void GdbMi::setStreamOutput(const QByteArray &name, const QByteArray &content) +{ + if (content.isEmpty()) + return; + GdbMi child; + child.m_type = Const; + child.m_name = name; + child.m_data = content; + m_children += child; + if (m_type == Invalid) + m_type = Tuple; +} + +static QByteArray ind(int indent) +{ + return QByteArray(2 * indent, ' '); +} + +void GdbMi::dumpChildren(QByteArray * str, bool multiline, int indent) const +{ + for (int i = 0; i < m_children.size(); ++i) { + if (i != 0) { + *str += ','; + if (multiline) + *str += '\n'; + } + if (multiline) + *str += ind(indent); + *str += m_children.at(i).toString(multiline, indent); + } +} + +QByteArray GdbMi::toString(bool multiline, int indent) const +{ + QByteArray result; + switch (m_type) { + case Invalid: + if (multiline) { + result += ind(indent) + "Invalid\n"; + } else { + result += "Invalid"; + } + break; + case Const: + if (!m_name.isEmpty()) + result += m_name + "="; + if (multiline) { + result += "\"" + m_data + "\""; + } else { + result += "\"" + m_data + "\""; + } + break; + case Tuple: + if (!m_name.isEmpty()) + result += m_name + "="; + if (multiline) { + result += "{\n"; + dumpChildren(&result, multiline, indent + 1); + result += '\n' + ind(indent) + "}"; + } else { + result += "{"; + dumpChildren(&result, multiline, indent + 1); + result += "}"; + } + break; + case List: + if (!m_name.isEmpty()) + result += m_name + "="; + if (multiline) { + result += "[\n"; + dumpChildren(&result, multiline, indent + 1); + result += "]"; + } else { + result += "["; + dumpChildren(&result, multiline, indent + 1); + result += '\n' + ind(indent) + "]"; + } + break; + } + return result; +} + +void GdbMi::fromString(const QByteArray &ba) +{ + const Char *from = ba.constBegin(); + const Char *to = ba.constEnd(); + parseResultOrValue(from, to); +} + +GdbMi GdbMi::findChild(const QByteArray &name) const +{ + for (int i = 0; i < m_children.size(); ++i) + if (m_children.at(i).m_name == name) + return m_children.at(i); + return GdbMi(); +} + + +GdbMi GdbMi::findChild(const QByteArray &name, const QByteArray &defaultData) const +{ + for (int i = 0; i < m_children.size(); ++i) + if (m_children.at(i).m_name == name) + return m_children.at(i); + GdbMi result; + result.m_data = defaultData; + return result; +} + + +////////////////////////////////////////////////////////////////////////////////// +// +// GdbResultRecord +// +////////////////////////////////////////////////////////////////////////////////// + +QByteArray stringFromResultClass(GdbResultClass resultClass) +{ + switch (resultClass) { + case GdbResultDone: return "done"; + case GdbResultRunning: return "running"; + case GdbResultConnected: return "connected"; + case GdbResultError: return "error"; + case GdbResultExit: return "exit"; + default: return "unknown"; + } +}; + +QByteArray GdbResultRecord::toString() const +{ + QByteArray result; + if (token != -1) + result = QByteArray::number(token); + result += '^'; + result += stringFromResultClass(resultClass); + if (data.isValid()) + result += ',' + data.toString(); + result += '\n'; + return result; +} + + +////////////////////////////////////////////////////////////////////////////////// +// +// GdbStreamOutput +// +////////////////////////////////////////////////////////////////////////////////// + +#if 0 + +static const char test1[] = + "1^done,stack=[frame={level=\"0\",addr=\"0x00000000004061ca\"," + "func=\"main\",file=\"test1.cpp\"," + "fullname=\"/home/apoenitz/work/test1/test1.cpp\",line=\"209\"}]\n" + "(gdb)\n"; + +static const char test2[] = + "2^done,stack=[frame={level=\"0\",addr=\"0x00002ac058675840\"," + "func=\"QApplication\",file=\"/home/apoenitz/dev/qt/src/gui/kernel/qapplication.cpp\"," + "fullname=\"/home/apoenitz/dev/qt/src/gui/kernel/qapplication.cpp\",line=\"592\"}," + "frame={level=\"1\",addr=\"0x00000000004061e0\",func=\"main\",file=\"test1.cpp\"," + "fullname=\"/home/apoenitz/work/test1/test1.cpp\",line=\"209\"}]\n" + "(gdb)\n"; + +static const char test3[] = + "3^done,stack=[frame={level=\"0\",addr=\"0x00000000004061ca\"," + "func=\"main\",file=\"test1.cpp\"," + "fullname=\"/home/apoenitz/work/test1/test1.cpp\",line=\"209\"}]\n" + "(gdb)\n"; + +static const char test4[] = + "&\"source /home/apoenitz/dev/ide/main/bin/gdb/qt4macros\\n\"\n" + "4^done\n" + "(gdb)\n"; + + +static const char test5[] = + "1*stopped,reason=\"breakpoint-hit\",bkptno=\"1\",thread-id=\"1\"," + "frame={addr=\"0x0000000000405738\",func=\"main\"," + "args=[{name=\"argc\",value=\"1\"},{name=\"argv\",value=\"0x7fff1ac78f28\"}]," + "file=\"test1.cpp\",fullname=\"/home/apoenitz/work/test1/test1.cpp\"," + "line=\"209\"}\n" + "(gdb)\n"; + +static const char test6[] = + "{u = {u = 2048, v = 16788279, w = -689265400}, a = 1, b = -689265424, c = 11063, s = {static null = {<No data fields>}, static shared_null = {ref = {value = 2}, alloc = 0, size = 0, data = 0x6098da, clean = 0, simpletext = 0, righttoleft = 0, asciiCache = 0, capacity = 0, reserved = 0, array = {0}}, static shared_empty = {ref = {value = 1}, alloc = 0, size = 0, data = 0x2b37d84f8fba, clean = 0, simpletext = 0, righttoleft = 0, asciiCache = 0, capacity = 0, reserved = 0, array = {0}}, d = 0x6098c0, static codecForCStrings = 0x0}}"; + +static const char test8[] = + "8^done,data={locals={{name=\"a\"},{name=\"w\"}}}\n" + "(gdb)\n"; + +static const char test9[] = + "9^done,data={locals=[name=\"baz\",name=\"urgs\",name=\"purgs\"]}\n" + "(gdb)\n"; + + +static const char test10[] = + "16^done,name=\"urgs\",numchild=\"1\",type=\"Urgs\"\n" + "(gdb)\n" + "17^done,name=\"purgs\",numchild=\"1\",type=\"Urgs *\"\n" + "(gdb)\n" + "18^done,name=\"bar\",numchild=\"0\",type=\"int\"\n" + "(gdb)\n" + "19^done,name=\"z\",numchild=\"0\",type=\"int\"\n" + "(gdb)\n"; + +static const char test11[] = + "[{name=\"size\",value=\"1\",type=\"size_t\",readonly=\"true\"}," + "{name=\"0\",value=\"one\",type=\"QByteArray\"}]"; + +static const char test12[] = + "{iname=\"local.hallo\",value=\"\\\"\\\"\",type=\"QByteArray\",numchild=\"0\"}"; + +static struct Tester { + + Tester() { + //test(test10); + test2(test12); + //test(test4); + //apple(); + exit(0); + } + + void test(const char* input) + { + //qDebug("\n<<<<\n%s\n====\n%s\n>>>>\n", input, + //qPrintable(GdbResponse(input).toString())); + } + + void test2(const char* input) + { + GdbMi mi(input); + qDebug("\n<<<<\n%s\n====\n%s\n>>>>\n", input, + qPrintable(mi.toString())); + } + + void apple() + { + QByteArray input(test9); +/* + qDebug() << "input: " << input; + input = input.replace("{{","["); + input = input.replace("},{",","); + input = input.replace("}}","]"); + qDebug() << "input: " << input; + GdbResponse response(input); + qDebug() << "read: " << response.toString(); + GdbMi list = response.results[0].data.findChild("data").findChild("locals"); + QByteArrayList locals; + foreach (const GdbMi &item, list.children()) + locals.append(item.string()); + qDebug() << "Locals (new): " << locals; +*/ + } + void parse(const QByteArray &str) + { + QByteArray result; + result += "\n "; + int indent = 0; + int from = 0; + int to = str.size(); + if (str.size() && str[0] == '{' /*'}'*/) { + ++from; + --to; + } + for (int i = from; i < to; ++i) { + if (str[i] == '{') + result += "{\n" + QByteArray(2*++indent + 1, QChar(' ')); + else if (str[i] == '}') { + if (!result.isEmpty() && result[result.size() - 1] != '\n') + result += "\n"; + result += QByteArray(2*--indent + 1, QChar(' ')) + "}\n"; + } + else if (str[i] == ',') { + if (true || !result.isEmpty() && result[result.size() - 1] != '\n') + result += "\n"; + result += QByteArray(2*indent, QChar(' ')); + } + else + result += str[i]; + } + qDebug() << "result:\n" << result; + } + +} dummy; + +#endif + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/gdbmi.h b/src/plugins/debugger/gdbmi.h new file mode 100644 index 00000000000..381b5ba86bf --- /dev/null +++ b/src/plugins/debugger/gdbmi.h @@ -0,0 +1,183 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#ifndef DEBUGGER_GDBMI_H +#define DEBUGGER_GDBMI_H + +#include <qglobal.h> + +#include <QtCore/QByteArray> +#include <QtCore/QList> + +namespace Debugger { +namespace Internal { + +/* + +output ==> + ( out-of-band-record )* [ result-record ] "(gdb)" nl +result-record ==> + [ token ] "^" result-class ( "," result )* nl +out-of-band-record ==> + async-record | stream-record +async-record ==> + exec-async-output | status-async-output | notify-async-output +exec-async-output ==> + [ token ] "*" async-output +status-async-output ==> + [ token ] "+" async-output +notify-async-output ==> + [ token ] "=" async-output +async-output ==> + async-class ( "," result )* nl +result-class ==> + "done" | "running" | "connected" | "error" | "exit" +async-class ==> + "stopped" | others (where others will be added depending on the needs--this is still in development). +result ==> + variable "=" value +variable ==> + string +value ==> + const | tuple | list +const ==> + c-string +tuple ==> + "{}" | "{" result ( "," result )* "}" +list ==> + "[]" | "[" value ( "," value )* "]" | "[" result ( "," result )* "]" +stream-record ==> + console-stream-output | target-stream-output | log-stream-output +console-stream-output ==> + "~" c-string +target-stream-output ==> + "@" c-string +log-stream-output ==> + "&" c-string +nl ==> + CR | CR-LF +token ==> + any sequence of digits. + + */ + +// FIXME: rename into GdbMiValue +class GdbMi +{ +public: + GdbMi() : m_type(Invalid) {} + explicit GdbMi(const QByteArray &str) { fromString(str); } + + QByteArray m_name; + QByteArray m_data; + QList<GdbMi> m_children; + + enum Type { + Invalid, + Const, + Tuple, + List, + }; + + Type m_type; + + inline Type type() const { return m_type; } + inline QByteArray name() const { return m_name; } + inline bool hasName(const char *name) const { return m_name == name; } + + inline bool isValid() const { return m_type != Invalid; } + inline bool isConst() const { return m_type == Const; } + inline bool isTuple() const { return m_type == Tuple; } + inline bool isList() const { return m_type == List; } + + + inline QByteArray data() const { return m_data; } + inline const QList<GdbMi> &children() const { return m_children; } + inline int childCount() const { return m_children.size(); } + + const GdbMi & childAt(int index) const { return m_children[index]; } + GdbMi & childAt(int index) { return m_children[index]; } + GdbMi findChild(const QByteArray &name) const; + GdbMi findChild(const QByteArray &name, const QByteArray &defaultString) const; + + QByteArray toString(bool multiline = false, int indent = 0) const; + void fromString(const QByteArray &str); + void setStreamOutput(const QByteArray &name, const QByteArray &content); + +private: + friend class GdbResultRecord; + friend class GdbEngine; + + //typedef ushort Char; + typedef char Char; + static QByteArray parseCString(const Char *&from, const Char *to); + void parseResultOrValue(const Char *&from, const Char *to); + void parseValue(const Char *&from, const Char *to); + void parseTuple(const Char *&from, const Char *to); + void parseTuple_helper(const Char *&from, const Char *to); + void parseList(const Char *&from, const Char *to); + + void dumpChildren(QByteArray *str, bool multiline, int indent) const; +}; + +enum GdbResultClass +{ + // "done" | "running" | "connected" | "error" | "exit" + GdbResultUnknown, + GdbResultDone, + GdbResultCustomDone, + GdbResultRunning, + GdbResultConnected, + GdbResultError, + GdbResultExit, +}; + +class GdbResultRecord +{ +public: + GdbResultRecord() : token(-1), resultClass(GdbResultUnknown) {} + QByteArray toString() const; + + int token; + GdbResultClass resultClass; + GdbMi data; +private: + friend class GdbMi; +}; + +} // namespace Internal +} // namespace Debugger + +//Q_DECLARE_METATYPE(GdbDebugger::Internal::GdbMi); + +#endif // DEBUGGER_GDBMI_H diff --git a/src/plugins/debugger/gdboptionpage.cpp b/src/plugins/debugger/gdboptionpage.cpp new file mode 100644 index 00000000000..bee68d1833e --- /dev/null +++ b/src/plugins/debugger/gdboptionpage.cpp @@ -0,0 +1,128 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gdboptionpage.h" + +#include "gdbengine.h" + +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/icore.h> + +#include <QtCore/QSettings> +#include <QtGui/QLineEdit> +#include <QtGui/QFileDialog> + +using namespace Debugger::Internal; + +GdbOptionPage::GdbOptionPage(GdbSettings *settings) +{ + m_pm = ExtensionSystem::PluginManager::instance(); + m_settings = settings; + + Core::ICore *coreIFace = m_pm->getObject<Core::ICore>(); + if (!coreIFace || !coreIFace->settings()) + return; + QSettings *s = coreIFace->settings(); + s->beginGroup("GdbOptions"); + QString defaultCommand("gdb"); +#if defined(Q_OS_WIN32) + defaultCommand.append(".exe"); +#endif + m_settings->m_gdbCmd = s->value("Location", defaultCommand).toString(); + m_settings->m_gdbEnv = s->value("Environment", "").toString(); + m_settings->m_autoRun = s->value("AutoRun", true).toBool(); + m_settings->m_autoQuit = s->value("AutoQuit", true).toBool(); + s->endGroup(); +} + +QString GdbOptionPage::name() const +{ + return tr("Gdb"); +} + +QString GdbOptionPage::category() const +{ + return "Debugger|Gdb"; +} + +QString GdbOptionPage::trCategory() const +{ + return tr("Debugger|Gdb"); +} + +QWidget *GdbOptionPage::createPage(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + m_ui.setupUi(w); + m_ui.gdbEdit->setText(m_settings->m_gdbCmd); + m_ui.envEdit->setText(m_settings->m_gdbEnv); + m_ui.autoStartBox->setChecked(m_settings->m_autoRun); + m_ui.autoQuitBox->setChecked(m_settings->m_autoQuit); + connect(m_ui.pushButtonBrowse, SIGNAL(clicked()), + this, SLOT(browse())); + + return w; +} + +void GdbOptionPage::browse() +{ + QString fileName = QFileDialog::getOpenFileName(m_ui.pushButtonBrowse, + "Browse for gdb executable"); + if (fileName.isEmpty()) + return; + m_settings->m_gdbCmd = fileName; + m_ui.gdbEdit->setText(fileName); +} + +void GdbOptionPage::finished(bool accepted) +{ + if (!accepted) + return; + + m_settings->m_gdbCmd = m_ui.gdbEdit->text(); + m_settings->m_gdbEnv = m_ui.envEdit->text(); + m_settings->m_autoRun = m_ui.autoStartBox->isChecked(); + m_settings->m_autoQuit = m_ui.autoQuitBox->isChecked(); + + Core::ICore *coreIFace = m_pm->getObject<Core::ICore>(); + if (!coreIFace || !coreIFace->settings()) + return; + + QSettings *s = coreIFace->settings(); + + s->beginGroup("GdbOptions"); + s->setValue("Location", m_settings->m_gdbCmd); + s->setValue("Environment", m_settings->m_gdbEnv); + s->setValue("AutoRun", m_settings->m_autoRun); + s->setValue("AutoQuit", m_settings->m_autoQuit); + s->endGroup(); +} diff --git a/src/plugins/debugger/gdboptionpage.h b/src/plugins/debugger/gdboptionpage.h new file mode 100644 index 00000000000..0a835337422 --- /dev/null +++ b/src/plugins/debugger/gdboptionpage.h @@ -0,0 +1,106 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GDBOPTIONPAGE_H +#define GDBOPTIONPAGE_H + +#include "ui_gdboptionpage.h" +#include "ui_gdbtypemacros.h" + +#include <coreplugin/dialogs/ioptionspage.h> + +#include <QtGui/QWidget> + +namespace ExtensionSystem { class PluginManager; } + +namespace Debugger { +namespace Internal { + +class GdbSettings; + +class GdbOptionPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + GdbOptionPage(GdbSettings *settings); + + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + +public slots: + void browse(); + +private: + ExtensionSystem::PluginManager *m_pm; + Ui::GdbOptionPage m_ui; + + GdbSettings *m_settings; +}; + +class TypeMacroPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + TypeMacroPage(GdbSettings *settings); + + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + +private slots: + void onScriptButton(); + void onAddButton(); + void onDelButton(); + void currentItemChanged(QTreeWidgetItem *item); + void updateButtonState(); + +private: + ExtensionSystem::PluginManager *m_pm; + Ui::TypeMacroPage m_ui; + + GdbSettings *m_settings; + QWidget *m_widget; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // GDBOPTIONPAGE_H diff --git a/src/plugins/debugger/gdboptionpage.ui b/src/plugins/debugger/gdboptionpage.ui new file mode 100644 index 00000000000..4b58d5d7140 --- /dev/null +++ b/src/plugins/debugger/gdboptionpage.ui @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>GdbOptionPage</class> + <widget class="QWidget" name="GdbOptionPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>433</width> + <height>216</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>9</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Gdb Debug Options</string> + </property> + <layout class="QGridLayout"> + <property name="margin"> + <number>9</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item row="0" column="1"> + <widget class="QLineEdit" name="gdbEdit"/> + </item> + <item row="1" column="1" colspan="2"> + <widget class="QLineEdit" name="envEdit"/> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Gdb Location:</string> + </property> + <property name="buddy"> + <cstring>gdbEdit</cstring> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Environment:</string> + </property> + <property name="buddy"> + <cstring>envEdit</cstring> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QPushButton" name="pushButtonBrowse"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../coreplugin/core.qrc"> + <normaloff>:/qworkbench/images/fileopen.png</normaloff>:/qworkbench/images/fileopen.png</iconset> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QCheckBox" name="autoStartBox"> + <property name="text"> + <string>Auto run executable on debugger startup</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="autoQuitBox"> + <property name="text"> + <string>Quit debugger when the executable exits</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>415</width> + <height>41</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources> + <include location="../coreplugin/core.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/plugins/debugger/gdbtypemacros.cpp b/src/plugins/debugger/gdbtypemacros.cpp new file mode 100644 index 00000000000..8a35720097b --- /dev/null +++ b/src/plugins/debugger/gdbtypemacros.cpp @@ -0,0 +1,214 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gdboptionpage.h" +#include "gdbengine.h" +#include "imports.h" + +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/icore.h> + +#include <QtCore/QSettings> +#include <QtCore/QByteArray> +#include <QtGui/QFileDialog> + +using namespace Debugger::Internal; + +TypeMacroPage::TypeMacroPage(GdbSettings *settings) +{ + m_pm = ExtensionSystem::PluginManager::instance(); + m_settings = settings; + + Core::ICore *coreIFace = m_pm->getObject<Core::ICore>(); + if (!coreIFace || !coreIFace->settings()) + return; + + QSettings *s = coreIFace->settings(); + s->beginGroup("GdbOptions"); + if (!s->contains("ScriptFile") && !s->contains("TypeMacros")) { + //insert qt4 defaults + m_settings->m_scriptFile = coreIFace->resourcePath() + + QLatin1String("/gdb/qt4macros"); + for (int i=0; i<3; ++i) { + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + switch(i) { + case 0: + stream << QString("printqstring") << (int)1; + m_settings->m_typeMacros.insert(QLatin1String("QString"), data); + break; + case 1: + stream << QString("printqcolor") << (int)0; + m_settings->m_typeMacros.insert(QLatin1String("QColor"), data); + break; + case 2: + stream << QString("printqfont") << (int)1; + m_settings->m_typeMacros.insert(QLatin1String("QFont"), data); + break; + } + } + + s->setValue("ScriptFile", m_settings->m_scriptFile); + s->setValue("TypeMacros", m_settings->m_typeMacros); + } else { + m_settings->m_scriptFile = s->value("ScriptFile", QString()).toString(); + m_settings->m_typeMacros = s->value("TypeMacros", QMap<QString,QVariant>()).toMap(); + } + s->endGroup(); +} + +QString TypeMacroPage::name() const +{ + return tr("Type Macros"); +} + +QString TypeMacroPage::category() const +{ + return "Debugger|Gdb"; +} + +QString TypeMacroPage::trCategory() const +{ + return tr("Debugger|Gdb"); +} + +QWidget *TypeMacroPage::createPage(QWidget *parent) +{ + QString macro; + int index; + + m_widget = new QWidget(parent); + m_ui.setupUi(m_widget); + + connect(m_ui.addButton, SIGNAL(clicked()), + this, SLOT(onAddButton())); + + connect(m_ui.delButton, SIGNAL(clicked()), + this, SLOT(onDelButton())); + + connect(m_ui.scriptButton, SIGNAL(clicked()), + this, SLOT(onScriptButton())); + + connect(m_ui.treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + this, SLOT(currentItemChanged(QTreeWidgetItem *))); + + connect(m_ui.typeEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(updateButtonState())); + + connect(m_ui.macroEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(updateButtonState())); + + QMap<QString, QVariant>::const_iterator i = m_settings->m_typeMacros.constBegin(); + while (i != m_settings->m_typeMacros.constEnd()) { + QTreeWidgetItem *item = new QTreeWidgetItem(m_ui.treeWidget); + QDataStream stream(i.value().toByteArray()); + stream >> macro >> index; + item->setText(0, i.key()); + item->setText(1, macro); + item->setData(0, Qt::UserRole, index); + ++i; + } + + m_ui.scriptEdit->setText(m_settings->m_scriptFile); + + updateButtonState(); + + return m_widget; +} + +void TypeMacroPage::finished(bool accepted) +{ + if (!accepted) + return; + + m_settings->m_typeMacros.clear(); + m_settings->m_scriptFile = m_ui.scriptEdit->text(); + + for (int i=0; i<m_ui.treeWidget->topLevelItemCount(); ++i) { + QTreeWidgetItem *item = m_ui.treeWidget->topLevelItem(i); + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << item->text(1) << item->data(0, Qt::UserRole).toInt(); + m_settings->m_typeMacros.insert(item->text(0), data); + } + + Core::ICore *coreIFace = m_pm->getObject<Core::ICore>(); + if (coreIFace && coreIFace->settings()) { + QSettings *s = coreIFace->settings(); + s->beginGroup("GdbOptions"); + s->setValue("ScriptFile", m_settings->m_scriptFile); + s->setValue("TypeMacros", m_settings->m_typeMacros); + s->endGroup(); + } +} + +void TypeMacroPage::onScriptButton() +{ + QString fileName = QFileDialog::getOpenFileName(m_widget, tr("Select Gdb Script")); + m_ui.scriptEdit->setText(fileName); + updateButtonState(); +} + +void TypeMacroPage::onAddButton() +{ + if (m_ui.typeEdit->text().isEmpty() || m_ui.macroEdit->text().isEmpty()) + return; + + QTreeWidgetItem *item = new QTreeWidgetItem(m_ui.treeWidget); + item->setText(0, m_ui.typeEdit->text()); + item->setText(1, m_ui.macroEdit->text()); + item->setData(0, Qt::UserRole, m_ui.parseAsBox->currentIndex()); + + updateButtonState(); +} + +void TypeMacroPage::onDelButton() +{ + if (QTreeWidgetItem *item = m_ui.treeWidget->currentItem()) + delete item; + updateButtonState(); +} + +void TypeMacroPage::currentItemChanged(QTreeWidgetItem *item) +{ + m_ui.typeEdit->setText(item ? item->text(0) : QString()); + m_ui.macroEdit->setText(item ? item->text(1) : QString()); + m_ui.parseAsBox->setCurrentIndex(item ? item->data(0, Qt::UserRole).toInt() : 0); + updateButtonState(); +} + +void TypeMacroPage::updateButtonState() +{ + m_ui.delButton->setEnabled(m_ui.treeWidget->currentItem() != 0); + m_ui.addButton->setDisabled(m_ui.typeEdit->text().isEmpty() + || m_ui.macroEdit->text().isEmpty()); +} diff --git a/src/plugins/debugger/gdbtypemacros.ui b/src/plugins/debugger/gdbtypemacros.ui new file mode 100644 index 00000000000..aa7215577b4 --- /dev/null +++ b/src/plugins/debugger/gdbtypemacros.ui @@ -0,0 +1,186 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>TypeMacroPage</class> + <widget class="QWidget" name="TypeMacroPage" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>519</width> + <height>238</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox" > + <property name="title" > + <string>Script File</string> + </property> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLineEdit" name="scriptEdit" /> + </item> + <item> + <widget class="QToolButton" name="scriptButton" > + <property name="minimumSize" > + <size> + <width>21</width> + <height>23</height> + </size> + </property> + <property name="text" > + <string>...</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QGridLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="0" column="0" colspan="2" > + <widget class="QTreeWidget" name="treeWidget" > + <property name="rootIsDecorated" > + <bool>false</bool> + </property> + <column> + <property name="text" > + <string>Type</string> + </property> + </column> + <column> + <property name="text" > + <string>Macro</string> + </property> + </column> + </widget> + </item> + <item row="1" column="2" > + <widget class="QToolButton" name="addButton" > + <property name="minimumSize" > + <size> + <width>21</width> + <height>23</height> + </size> + </property> + <property name="text" > + <string>+</string> + </property> + <property name="icon" > + <iconset resource="gdbdebugger.qrc" >:/gdbdebugger/images/newitem.png</iconset> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Macro Name:</string> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Parse as:</string> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLineEdit" name="macroEdit" /> + </item> + <item row="0" column="2" > + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>0</number> + </property> + <item> + <widget class="QToolButton" name="delButton" > + <property name="minimumSize" > + <size> + <width>21</width> + <height>23</height> + </size> + </property> + <property name="text" > + <string>-</string> + </property> + <property name="icon" > + <iconset resource="gdbdebugger.qrc" >:/gdbdebugger/images/delete.png</iconset> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="1" column="1" > + <widget class="QLineEdit" name="typeEdit" /> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Type:</string> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QComboBox" name="parseAsBox" > + <item> + <property name="text" > + <string>ASCII (char *)</string> + </property> + </item> + <item> + <property name="text" > + <string>Unicode (short)</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <pixmapfunction></pixmapfunction> + <resources> + <include location="gdbdebugger.qrc" /> + </resources> + <connections/> +</ui> diff --git a/src/plugins/debugger/idebuggerengine.h b/src/plugins/debugger/idebuggerengine.h new file mode 100644 index 00000000000..84edcb6ca55 --- /dev/null +++ b/src/plugins/debugger/idebuggerengine.h @@ -0,0 +1,88 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_IDEBUGGERENGINE_H +#define DEBUGGER_IDEBUGGERENGINE_H + +#include <QtCore/QObject> + +namespace Debugger { +namespace Internal { + +class IDebuggerEngine : public QObject +{ +public: + IDebuggerEngine(QObject *parent = 0) : QObject(parent) {} + + virtual void shutdown() = 0; + virtual void setToolTipExpression(const QPoint &pos, const QString &exp) = 0; + virtual bool startDebugger() = 0; + virtual void exitDebugger() = 0; + virtual void updateWatchModel() = 0; + + virtual void stepExec() = 0; + virtual void stepOutExec() = 0; + virtual void nextExec() = 0; + virtual void stepIExec() = 0; + virtual void nextIExec() = 0; + + virtual void continueInferior() = 0; + virtual void runInferior() = 0; + virtual void interruptInferior() = 0; + + virtual void runToLineExec(const QString &fileName, int lineNumber) = 0; + virtual void runToFunctionExec(const QString &functionName) = 0; + virtual void jumpToLineExec(const QString &fileName, int lineNumber) = 0; + virtual void assignValueInDebugger(const QString &expr, const QString &value) = 0; + virtual void executeDebuggerCommand(const QString &command) = 0; + + virtual void activateFrame(int index) = 0; + virtual void selectThread(int index) = 0; + + virtual void attemptBreakpointSynchronization() = 0; + + virtual void loadSessionData() = 0; + virtual void saveSessionData() = 0; + + virtual void reloadDisassembler() = 0; + + virtual void reloadModules() = 0; + virtual void loadSymbols(const QString &moduleName) = 0; + virtual void loadAllSymbols() = 0; + + virtual void reloadRegisters() = 0; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_IDEBUGGERENGINE_H diff --git a/src/plugins/debugger/images/breakpoint.svg b/src/plugins/debugger/images/breakpoint.svg new file mode 100644 index 00000000000..e8d63cc9037 --- /dev/null +++ b/src/plugins/debugger/images/breakpoint.svg @@ -0,0 +1,154 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="14" + height="14" + id="svg2270" + sodipodi:version="0.32" + inkscape:version="0.45.1" + version="1.0" + sodipodi:docbase="D:\depot\research\main\editor\images" + sodipodi:docname="breakpoint.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs2272"> + <linearGradient + id="linearGradient7029"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop7031" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop7033" /> + </linearGradient> + <linearGradient + id="linearGradient17794"> + <stop + style="stop-color:#f18383;stop-opacity:1;" + offset="0" + id="stop17798" /> + <stop + id="stop8006" + offset="0.3807947" + style="stop-color:#ed6767;stop-opacity:1;" /> + <stop + style="stop-color:#e62323;stop-opacity:1;" + offset="1" + id="stop17796" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient17794" + id="linearGradient24732" + gradientUnits="userSpaceOnUse" + x1="472.42236" + y1="436.79602" + x2="461.39169" + y2="424.95065" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient17794" + id="linearGradient2438" + gradientUnits="userSpaceOnUse" + x1="472.42236" + y1="436.79602" + x2="461.39169" + y2="424.95065" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient17794" + id="radialGradient6052" + cx="466.73566" + cy="431.19708" + fx="466.73566" + fy="431.19708" + r="9.3095722" + gradientTransform="matrix(1,0,0,1.0057859,0,-2.4948735)" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient7029" + id="linearGradient7035" + x1="6.75" + y1="0.5" + x2="6.75" + y2="12.5" + gradientUnits="userSpaceOnUse" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="32" + inkscape:cx="8.6877264" + inkscape:cy="6.3888789" + inkscape:document-units="px" + inkscape:current-layer="g25843" + width="14px" + height="14px" + inkscape:window-width="1280" + inkscape:window-height="998" + inkscape:window-x="0" + inkscape:window-y="0" + showgrid="true" + gridspacingx="0.5px" + gridspacingy="0.5px" + gridempspacing="2" + inkscape:grid-points="true" /> + <metadata + id="metadata2275"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <g + id="g25843" + transform="matrix(0.7931251,0,0,0.7931251,-372.13374,-408.22195)"> + <path + sodipodi:type="arc" + style="fill:url(#radialGradient6052);fill-opacity:1.0;stroke:#c80000;stroke-width:1.43637741;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path22737" + sodipodi:cx="466.73566" + sodipodi:cy="431.19708" + sodipodi:rx="8.5913839" + sodipodi:ry="8.6452484" + d="M 475.32704 431.19708 A 8.5913839 8.6452484 0 1 1 458.14427,431.19708 A 8.5913839 8.6452484 0 1 1 475.32704 431.19708 z" + transform="matrix(0.8805346,0,0,0.8750503,66.41784,145.57686)" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:url(#linearGradient7035);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path6058" + sodipodi:cx="6.75" + sodipodi:cy="6.5" + sodipodi:rx="5.75" + sodipodi:ry="6" + d="M 12.5 6.5 A 5.75 6 0 1 1 1,6.5 A 5.75 6 0 1 1 12.5 6.5 z" + transform="matrix(0.9867408,0,0,0.6304178,470.73423,515.01579)" /> + </g> + </g> +</svg> diff --git a/src/plugins/debugger/images/breakpoint_pending.svg b/src/plugins/debugger/images/breakpoint_pending.svg new file mode 100644 index 00000000000..e7094068b53 --- /dev/null +++ b/src/plugins/debugger/images/breakpoint_pending.svg @@ -0,0 +1,534 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="14" + height="14" + id="svg2270" + sodipodi:version="0.32" + inkscape:version="0.45.1" + version="1.0" + sodipodi:docbase="c:\depot\research\main\editor\images" + sodipodi:docname="pendingbreakpoint.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs2272"> + <linearGradient + id="linearGradient7029"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop7031" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop7033" /> + </linearGradient> + <linearGradient + id="linearGradient17794"> + <stop + style="stop-color:#f18383;stop-opacity:1;" + offset="0" + id="stop17798" /> + <stop + id="stop8006" + offset="0.3807947" + style="stop-color:#ed6767;stop-opacity:1;" /> + <stop + style="stop-color:#e62323;stop-opacity:1;" + offset="1" + id="stop17796" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient17794" + id="linearGradient24732" + gradientUnits="userSpaceOnUse" + x1="472.42236" + y1="436.79602" + x2="461.39169" + y2="424.95065" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient17794" + id="linearGradient2438" + gradientUnits="userSpaceOnUse" + x1="472.42236" + y1="436.79602" + x2="461.39169" + y2="424.95065" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient17794" + id="radialGradient6052" + cx="466.73566" + cy="431.19708" + fx="466.73566" + fy="431.19708" + r="9.3095722" + gradientTransform="matrix(1,0,0,1.0057859,0,-2.4948735)" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient7029" + id="linearGradient7035" + x1="6.75" + y1="0.5" + x2="6.75" + y2="12.5" + gradientUnits="userSpaceOnUse" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="12.5" + x2="6.75" + y1="0.5" + x1="6.75" + id="linearGradient2228" + xlink:href="#linearGradient7029" + inkscape:collect="always" /> + <radialGradient + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,1.0057859,0,-2.4948735)" + r="9.3095722" + fy="431.19708" + fx="466.73566" + cy="431.19708" + cx="466.73566" + id="radialGradient2226" + xlink:href="#linearGradient17794" + inkscape:collect="always" /> + <linearGradient + y2="424.95065" + x2="461.39169" + y1="436.79602" + x1="472.42236" + gradientUnits="userSpaceOnUse" + id="linearGradient2224" + xlink:href="#linearGradient17794" + inkscape:collect="always" /> + <linearGradient + y2="424.95065" + x2="461.39169" + y1="436.79602" + x1="472.42236" + gradientUnits="userSpaceOnUse" + id="linearGradient2222" + xlink:href="#linearGradient17794" + inkscape:collect="always" /> + <linearGradient + id="linearGradient2214"> + <stop + id="stop2216" + offset="0" + style="stop-color:#f18383;stop-opacity:1;" /> + <stop + style="stop-color:#ed6767;stop-opacity:1;" + offset="0.3807947" + id="stop2218" /> + <stop + id="stop2220" + offset="1" + style="stop-color:#e62323;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient2208"> + <stop + id="stop2210" + offset="0" + style="stop-color:#ffffff;stop-opacity:1;" /> + <stop + id="stop2212" + offset="1" + style="stop-color:#ffffff;stop-opacity:0;" /> + </linearGradient> + <linearGradient + gradientTransform="matrix(1.2661544,0,0,1.2608351,469.23729,510.59508)" + y2="10.60876" + x2="10.981011" + y1="9.9135647" + x1="10.946278" + gradientUnits="userSpaceOnUse" + id="linearGradient4173" + xlink:href="#linearGradient4128" + inkscape:collect="always" /> + <linearGradient + gradientTransform="matrix(1.2608351,0,0,1.2608351,475.7834,516.59183)" + y2="9.578125" + x2="5.859375" + y1="6.609375" + x1="5.953125" + gradientUnits="userSpaceOnUse" + id="linearGradient4159" + xlink:href="#linearGradient4128" + inkscape:collect="always" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="11" + x2="11" + y1="10" + x1="11" + id="linearGradient4156" + xlink:href="#linearGradient4128" + inkscape:collect="always" /> + <linearGradient + y2="521.00476" + x2="472.35138" + y1="519.11353" + x1="472.35138" + gradientTransform="matrix(1,0,0,1.0000093,10.421461,10.71221)" + gradientUnits="userSpaceOnUse" + id="linearGradient4138" + xlink:href="#linearGradient4128" + inkscape:collect="always" /> + <linearGradient + gradientTransform="matrix(1,0,0,0.9687523,10.283691,16.9106)" + gradientUnits="userSpaceOnUse" + y2="521.00476" + x2="472.35138" + y1="519.11353" + x1="472.35138" + id="linearGradient4134" + xlink:href="#linearGradient4128" + inkscape:collect="always" /> + <linearGradient + y2="424.95065" + x2="461.39169" + y1="436.79602" + x1="472.42236" + gradientUnits="userSpaceOnUse" + id="linearGradient2293" + xlink:href="#linearGradient17794" + inkscape:collect="always" /> + <linearGradient + y2="424.95065" + x2="461.39169" + y1="436.79602" + x1="472.42236" + gradientUnits="userSpaceOnUse" + id="linearGradient2291" + xlink:href="#linearGradient17794" + inkscape:collect="always" /> + <linearGradient + id="linearGradient2285"> + <stop + id="stop2287" + offset="0" + style="stop-color:#c80000;stop-opacity:1;" /> + <stop + id="stop2289" + offset="1" + style="stop-color:#ffa0a0;stop-opacity:1;" /> + </linearGradient> + <linearGradient + y2="424.95065" + x2="461.39169" + y1="436.79602" + x1="472.42236" + gradientUnits="userSpaceOnUse" + id="linearGradient25826" + xlink:href="#linearGradient17794" + inkscape:collect="always" /> + <linearGradient + id="linearGradient4128"> + <stop + id="stop4130" + offset="0" + style="stop-color:#ffffff;stop-opacity:0.78431374;" /> + <stop + id="stop4132" + offset="1" + style="stop-color:#ffffff;stop-opacity:0.23529412;" /> + </linearGradient> + <linearGradient + gradientTransform="matrix(1.2661544,0,0,1.2608351,469.23729,510.59508)" + y2="10.60876" + x2="10.981011" + y1="9.9135647" + x1="10.946278" + gradientUnits="userSpaceOnUse" + id="linearGradient2378" + xlink:href="#linearGradient4128" + inkscape:collect="always" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="11.147234" + x2="11.02502" + y1="9.6892195" + x1="10.968282" + id="linearGradient2376" + xlink:href="#linearGradient4128" + inkscape:collect="always" /> + <linearGradient + gradientTransform="matrix(1.2608351,0,0,1.2608351,475.7834,516.59183)" + y2="9.578125" + x2="5.859375" + y1="6.609375" + x1="5.953125" + gradientUnits="userSpaceOnUse" + id="linearGradient2374" + xlink:href="#linearGradient4128" + inkscape:collect="always" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="11" + x2="11" + y1="10" + x1="11" + id="linearGradient2372" + xlink:href="#linearGradient4128" + inkscape:collect="always" /> + <linearGradient + y2="521.00476" + x2="472.35138" + y1="519.11353" + x1="472.35138" + gradientTransform="matrix(1,0,0,0.6666648,10.303108,184.09099)" + gradientUnits="userSpaceOnUse" + id="linearGradient2370" + xlink:href="#linearGradient4128" + inkscape:collect="always" /> + <linearGradient + y2="521.00476" + x2="472.35138" + y1="519.11353" + x1="472.35138" + gradientTransform="matrix(1,0,0,0.6666648,10.342666,173.98441)" + gradientUnits="userSpaceOnUse" + id="linearGradient2368" + xlink:href="#linearGradient4128" + inkscape:collect="always" /> + <linearGradient + y2="521.00476" + x2="472.35138" + y1="519.11353" + x1="472.35138" + gradientTransform="matrix(1,0,0,1.0000093,10.421461,10.71221)" + gradientUnits="userSpaceOnUse" + id="linearGradient2366" + xlink:href="#linearGradient4128" + inkscape:collect="always" /> + <linearGradient + gradientTransform="matrix(1,0,0,0.9687523,10.283691,16.9106)" + gradientUnits="userSpaceOnUse" + y2="521.00476" + x2="472.35138" + y1="519.11353" + x1="472.35138" + id="linearGradient2364" + xlink:href="#linearGradient4128" + inkscape:collect="always" /> + <linearGradient + y2="424.95065" + x2="461.39169" + y1="436.79602" + x1="472.42236" + gradientUnits="userSpaceOnUse" + id="linearGradient2362" + xlink:href="#linearGradient17794" + inkscape:collect="always" /> + <linearGradient + y2="424.95065" + x2="461.39169" + y1="436.79602" + x1="472.42236" + gradientUnits="userSpaceOnUse" + id="linearGradient2360" + xlink:href="#linearGradient17794" + inkscape:collect="always" /> + <linearGradient + id="linearGradient2354"> + <stop + id="stop2356" + offset="0" + style="stop-color:#c80000;stop-opacity:1;" /> + <stop + id="stop2358" + offset="1" + style="stop-color:#ffa0a0;stop-opacity:1;" /> + </linearGradient> + <linearGradient + y2="424.95065" + x2="461.39169" + y1="436.79602" + x1="472.42236" + gradientUnits="userSpaceOnUse" + id="linearGradient2352" + xlink:href="#linearGradient17794" + inkscape:collect="always" /> + <linearGradient + id="linearGradient2346"> + <stop + id="stop2348" + offset="0" + style="stop-color:#ffffff;stop-opacity:0.78431374;" /> + <stop + id="stop2350" + offset="1" + style="stop-color:#ffffff;stop-opacity:0.23529412;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4128" + id="linearGradient2392" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,0.6666648,10.342666,173.98441)" + x1="472.35138" + y1="519.11353" + x2="472.35138" + y2="521.00476" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4128" + id="linearGradient2394" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,0.6666648,10.303108,184.09099)" + x1="472.35138" + y1="519.11353" + x2="472.35138" + y2="521.00476" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4128" + id="linearGradient2396" + gradientUnits="userSpaceOnUse" + x1="10.968282" + y1="9.6892195" + x2="11.02502" + y2="11.147234" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="45.254834" + inkscape:cx="13.678175" + inkscape:cy="6.8291478" + inkscape:document-units="px" + inkscape:current-layer="g25843" + width="14px" + height="14px" + inkscape:window-width="1280" + inkscape:window-height="998" + inkscape:window-x="44" + inkscape:window-y="58" + showgrid="true" + gridspacingx="0.5px" + gridspacingy="0.5px" + gridempspacing="2" + inkscape:grid-points="true" /> + <metadata + id="metadata2275"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <g + id="g25843" + transform="matrix(0.7931251,0,0,0.7931251,-372.13374,-408.22195)"> + <path + sodipodi:type="arc" + style="fill:url(#radialGradient6052);fill-opacity:1.0;stroke:#c80000;stroke-width:1.43637741;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path22737" + sodipodi:cx="466.73566" + sodipodi:cy="431.19708" + sodipodi:rx="8.5913839" + sodipodi:ry="8.6452484" + d="M 475.32704 431.19708 A 8.5913839 8.6452484 0 1 1 458.14427,431.19708 A 8.5913839 8.6452484 0 1 1 475.32704 431.19708 z" + transform="matrix(0.8805346,0,0,0.8750503,66.41784,145.57686)" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:url(#linearGradient7035);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path6058" + sodipodi:cx="6.75" + sodipodi:cy="6.5" + sodipodi:rx="5.75" + sodipodi:ry="6" + d="M 12.5 6.5 A 5.75 6 0 1 1 1,6.5 A 5.75 6 0 1 1 12.5 6.5 z" + transform="matrix(0.9867408,0,0,0.6304178,470.73423,515.01579)" /> + <g + id="g2380" + inkscape:label="Layer 1" + transform="matrix(1.2608365,0,0,1.2633098,469.19929,514.69071)"> + <g + transform="matrix(0.7931251,0,0,0.7931251,-372.13374,-408.22195)" + id="g2382"> + <path + style="fill:#7f7f8c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 481.17723,524.15684 C 481.17723,524.15684 482.11277,524.68766 483.0584,524.68766 C 484.00403,524.68766 484.95974,524.15684 484.95974,524.15684 L 483.4493,526.03249 L 483.4493,527.83201 L 484.8944,528.56151 L 484.95974,531.09144 L 481.17723,531.09144 L 481.16963,528.69627 L 482.71594,527.80266 L 482.72456,526.04216 L 481.17723,524.15684 z " + id="path2442" + sodipodi:nodetypes="czcccccccccc" /> + <path + style="opacity:0.2;fill:#7f7f7f;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 481.17723,521.00476 L 481.17723,523.52643 L 482.43807,526.0481 L 481.17723,528.56977 L 481.17723,531.72186 L 484.95974,531.72186 L 484.95974,528.56977 L 483.6989,526.0481 L 484.95974,523.52643 L 484.95974,521.00476 L 481.17723,521.00476 z " + id="path2444" + sodipodi:nodetypes="ccccccccccc" /> + <path + sodipodi:nodetypes="cccccccccccccccccccccc" + id="path2174" + d="M 480.54682,520.37434 L 480.54681,523.52643 L 481.80765,526.0481 L 480.54681,528.56977 L 480.54682,531.72185 L 485.59016,531.72185 L 485.59015,528.56977 L 484.32932,526.0481 L 485.59015,523.52643 L 485.59016,520.37434 L 480.54682,520.37434 z M 481.80766,521.63517 L 484.32932,521.63517 L 484.32932,523.52643 L 483.46249,526.0481 L 484.32932,528.56977 L 484.32932,530.46102 L 481.80766,530.46102 L 481.80765,528.56977 L 482.72372,526.0481 L 481.80765,523.52643 L 481.80766,521.63517 z " + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <rect + ry="1.2608351" + y="519.7439" + x="479.28598" + height="2.5216701" + width="7.565001" + id="rect2172" + style="opacity:1;fill:#7f2aff;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + ry="1.2608351" + y="529.83063" + x="479.28598" + height="2.5216701" + width="7.565001" + id="rect2170" + style="opacity:1;fill:#6600ff;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + ry="0.63040882" + y="520.0589" + x="480.17267" + height="1.2608176" + width="5.6737618" + id="rect4140" + style="opacity:1;fill:url(#linearGradient2392);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <rect + ry="0.63040882" + y="530.16577" + x="480.13324" + height="1.2608176" + width="5.6737618" + id="rect4144" + style="opacity:1;fill:url(#linearGradient2394);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + sodipodi:nodetypes="cssz" + transform="matrix(1.2608351,0,0,1.2608351,469.1993,514.70058)" + id="path4161" + d="M 11.007811,9.9179701 C 10.386665,9.9257826 9.6414144,10.484263 9.5594939,10.992187 C 9.5017174,11.350414 12.494284,11.43273 12.441847,10.914063 C 12.40754,10.574731 11.628908,9.9101582 11.007811,9.9179701 z " + style="fill:url(#linearGradient2396);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + </g> + </g> + </g> + </g> +</svg> diff --git a/src/plugins/debugger/images/debugger_breakpoints.png b/src/plugins/debugger/images/debugger_breakpoints.png Binary files differnew file mode 100644 index 00000000000..75bb5f15bf7 --- /dev/null +++ b/src/plugins/debugger/images/debugger_breakpoints.png diff --git a/src/plugins/debugger/images/debugger_continue_small.png b/src/plugins/debugger/images/debugger_continue_small.png Binary files differnew file mode 100644 index 00000000000..4a3788c149b --- /dev/null +++ b/src/plugins/debugger/images/debugger_continue_small.png diff --git a/src/plugins/debugger/images/debugger_interrupt_small.png b/src/plugins/debugger/images/debugger_interrupt_small.png Binary files differnew file mode 100644 index 00000000000..815400cb588 --- /dev/null +++ b/src/plugins/debugger/images/debugger_interrupt_small.png diff --git a/src/plugins/debugger/images/debugger_start.png b/src/plugins/debugger/images/debugger_start.png Binary files differnew file mode 100644 index 00000000000..8eed81a899c --- /dev/null +++ b/src/plugins/debugger/images/debugger_start.png diff --git a/src/plugins/debugger/images/debugger_start_small.png b/src/plugins/debugger/images/debugger_start_small.png Binary files differnew file mode 100644 index 00000000000..4a3788c149b --- /dev/null +++ b/src/plugins/debugger/images/debugger_start_small.png diff --git a/src/plugins/debugger/images/debugger_stepinto_small.png b/src/plugins/debugger/images/debugger_stepinto_small.png Binary files differnew file mode 100644 index 00000000000..da36a5f670a --- /dev/null +++ b/src/plugins/debugger/images/debugger_stepinto_small.png diff --git a/src/plugins/debugger/images/debugger_steponeproc_small.png b/src/plugins/debugger/images/debugger_steponeproc_small.png Binary files differnew file mode 100644 index 00000000000..cf164c66047 --- /dev/null +++ b/src/plugins/debugger/images/debugger_steponeproc_small.png diff --git a/src/plugins/debugger/images/debugger_stepout_small.png b/src/plugins/debugger/images/debugger_stepout_small.png Binary files differnew file mode 100644 index 00000000000..e5eeeb32ada --- /dev/null +++ b/src/plugins/debugger/images/debugger_stepout_small.png diff --git a/src/plugins/debugger/images/debugger_stepover_small.png b/src/plugins/debugger/images/debugger_stepover_small.png Binary files differnew file mode 100644 index 00000000000..e8a5d080466 --- /dev/null +++ b/src/plugins/debugger/images/debugger_stepover_small.png diff --git a/src/plugins/debugger/images/debugger_stepoverproc_small.png b/src/plugins/debugger/images/debugger_stepoverproc_small.png Binary files differnew file mode 100644 index 00000000000..34e712da06e --- /dev/null +++ b/src/plugins/debugger/images/debugger_stepoverproc_small.png diff --git a/src/plugins/debugger/images/debugger_stop_small.png b/src/plugins/debugger/images/debugger_stop_small.png Binary files differnew file mode 100644 index 00000000000..1063d089985 --- /dev/null +++ b/src/plugins/debugger/images/debugger_stop_small.png diff --git a/src/plugins/debugger/images/delete.png b/src/plugins/debugger/images/delete.png Binary files differnew file mode 100644 index 00000000000..e4139afc552 --- /dev/null +++ b/src/plugins/debugger/images/delete.png diff --git a/src/plugins/debugger/images/done.png b/src/plugins/debugger/images/done.png Binary files differnew file mode 100644 index 00000000000..b5238f7680a --- /dev/null +++ b/src/plugins/debugger/images/done.png diff --git a/src/plugins/debugger/images/empty.svg b/src/plugins/debugger/images/empty.svg new file mode 100644 index 00000000000..46209de3910 --- /dev/null +++ b/src/plugins/debugger/images/empty.svg @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="14" + height="14" + id="svg2243" + sodipodi:version="0.32" + inkscape:version="0.45.1" + version="1.0" + sodipodi:docbase="c:\depot\research\main\editor\images" + sodipodi:docname="location.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs2245"> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="64" + inkscape:cx="8.3920091" + inkscape:cy="7.4257237" + inkscape:document-units="px" + inkscape:current-layer="layer1" + width="14px" + height="14px" + showborder="true" + inkscape:window-width="1600" + inkscape:window-height="1174" + inkscape:window-x="0" + inkscape:window-y="0" + gridempspacing="2" + showgrid="true" + inkscape:grid-points="true" + gridspacingx="0.5px" + gridspacingy="0.5px" /> + <metadata + id="metadata2248"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + </g> +</svg> diff --git a/src/plugins/debugger/images/error.png b/src/plugins/debugger/images/error.png Binary files differnew file mode 100644 index 00000000000..700530438a5 --- /dev/null +++ b/src/plugins/debugger/images/error.png diff --git a/src/plugins/debugger/images/location.svg b/src/plugins/debugger/images/location.svg new file mode 100644 index 00000000000..afb70052a14 --- /dev/null +++ b/src/plugins/debugger/images/location.svg @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="14" + height="14" + id="svg2243" + sodipodi:version="0.32" + inkscape:version="0.45.1" + version="1.0" + sodipodi:docbase="c:\depot\research\main\editor\images" + sodipodi:docname="location.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs2245"> + <linearGradient + id="linearGradient3134"> + <stop + style="stop-color:#dcdc23;stop-opacity:1;" + offset="0" + id="stop3136" /> + <stop + id="stop5080" + offset="0.64285713" + style="stop-color:#e5d044;stop-opacity:1;" /> + <stop + style="stop-color:#b89354;stop-opacity:1;" + offset="1" + id="stop3138" /> + </linearGradient> + <linearGradient + id="linearGradient3137"> + <stop + style="stop-color:#ffffff;stop-opacity:0.86274511;" + offset="0" + id="stop3139" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop3141" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3137" + id="linearGradient3143" + x1="6.5" + y1="3" + x2="6.515625" + y2="12.180227" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3134" + id="linearGradient3140" + x1="6.5" + y1="3.015625" + x2="6.484375" + y2="11.984375" + gradientUnits="userSpaceOnUse" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="64" + inkscape:cx="8.3920091" + inkscape:cy="7.4257237" + inkscape:document-units="px" + inkscape:current-layer="layer1" + width="14px" + height="14px" + showborder="true" + inkscape:window-width="1600" + inkscape:window-height="1174" + inkscape:window-x="0" + inkscape:window-y="0" + gridempspacing="2" + showgrid="true" + inkscape:grid-points="true" + gridspacingx="0.5px" + gridspacingy="0.5px" /> + <metadata + id="metadata2248"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <path + style="fill:url(#linearGradient3140);fill-opacity:1.0;fill-rule:evenodd;stroke:#b18b1b;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 6.5,3 L 6.5,5.5 L 0.5,5.5 L 0.5,9.5 L 6.5,9.5 L 6.5,12 L 13.125,7.5 L 6.5,3 z " + id="path2216" + sodipodi:nodetypes="cccccccc" /> + <path + style="fill:url(#linearGradient3143);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;opacity:1" + d="M 6.5,3 L 6.5,5.5 L 0.5,5.5 L 0.5,7.5 C 7,6.5 7.5,9.5 13,7.5 L 6.5,3 z " + id="path5066" + sodipodi:nodetypes="cccccc" /> + </g> +</svg> diff --git a/src/plugins/debugger/images/newitem.png b/src/plugins/debugger/images/newitem.png Binary files differnew file mode 100644 index 00000000000..7e26ea9b15b --- /dev/null +++ b/src/plugins/debugger/images/newitem.png diff --git a/src/plugins/debugger/images/running.png b/src/plugins/debugger/images/running.png Binary files differnew file mode 100644 index 00000000000..1cf8888ecb8 --- /dev/null +++ b/src/plugins/debugger/images/running.png diff --git a/src/plugins/debugger/imports.h b/src/plugins/debugger/imports.h new file mode 100644 index 00000000000..8a2edd4c393 --- /dev/null +++ b/src/plugins/debugger/imports.h @@ -0,0 +1,49 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_IMPORTS_H +#define DEBUGGER_IMPORTS_H + +// FIXME: Plan is to remove this file. It's needed to get "harmless" +// replacements for GH internals in the standalone version + +#ifndef GDBDEBUGGERLEAN + +#include <texteditor/basetextmark.h> + +#else + +#include "lean.h" + +#endif + +#endif // DEBUGGER_IMPORTS_H diff --git a/src/plugins/debugger/mode.cpp b/src/plugins/debugger/mode.cpp new file mode 100644 index 00000000000..2023cdd7ee3 --- /dev/null +++ b/src/plugins/debugger/mode.cpp @@ -0,0 +1,233 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "mode.h" + +#include "assert.h" +#include "debuggerconstants.h" +#include "debuggermanager.h" + +#include <coreplugin/coreconstants.h> +#include <coreplugin/icore.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/minisplitter.h> +#include <coreplugin/findplaceholder.h> +#include <coreplugin/outputpane.h> +#include <coreplugin/navigationwidget.h> +#include <coreplugin/rightpane.h> +#include <projectexplorer/projectexplorerconstants.h> + +#include <QtCore/QDebug> +#include <QtCore/QSettings> +#include <QtGui/QDockWidget> +#include <QtGui/QLabel> +#include <QtGui/QMainWindow> +#include <QtGui/QVBoxLayout> +#include <QtGui/QWidget> + +using namespace Core; +using namespace ExtensionSystem; +using namespace Debugger; +using namespace Debugger::Internal; +using namespace Debugger::Constants; + + +DebugMode::DebugMode(DebuggerManager *manager, QObject *parent) + : BaseMode(tr("Debug"), Constants::MODE_DEBUG, + QIcon(":/fancyactionbar/images/mode_Debug.png"), + Constants::P_MODE_DEBUG, 0, parent), + m_manager(manager) +{ + IDebuggerManagerAccessForDebugMode *managerAccess = + m_manager->debugModeInterface(); + UniqueIDManager *uidm = + PluginManager::instance()->getObject<ICore>()->uniqueIDManager(); + QList<int> context; + context.append(uidm->uniqueIdentifier(Core::Constants::C_EDITORMANAGER)); + context.append(uidm->uniqueIdentifier(Constants::C_GDBDEBUGGER)); + context.append(uidm->uniqueIdentifier(Core::Constants::C_NAVIGATION_PANE)); + setContext(context); + + QBoxLayout *editorHolderLayout = new QVBoxLayout; + editorHolderLayout->setMargin(0); + editorHolderLayout->setSpacing(0); + editorHolderLayout->addWidget(new EditorManagerPlaceHolder(this)); + editorHolderLayout->addWidget(new FindToolBarPlaceHolder(this)); + + QWidget *editorAndFindWidget = new QWidget; + editorAndFindWidget->setLayout(editorHolderLayout); + + MiniSplitter *rightPaneSplitter = new MiniSplitter; + rightPaneSplitter->addWidget(editorAndFindWidget); + rightPaneSplitter->addWidget(new RightPanePlaceHolder(this)); + rightPaneSplitter->setStretchFactor(0, 1); + rightPaneSplitter->setStretchFactor(1, 0); + + QWidget *centralWidget = new QWidget; + QBoxLayout *toolBarAddingLayout = new QVBoxLayout(centralWidget); + toolBarAddingLayout->setMargin(0); + toolBarAddingLayout->setSpacing(0); + toolBarAddingLayout->addWidget(rightPaneSplitter); + + m_manager->mainWindow()->setCentralWidget(centralWidget); + + MiniSplitter *splitter = new MiniSplitter; + splitter->addWidget(m_manager->mainWindow()); + splitter->addWidget(new OutputPanePlaceHolder(this)); + splitter->setStretchFactor(0, 10); + splitter->setStretchFactor(1, 0); + splitter->setOrientation(Qt::Vertical); + + MiniSplitter *splitter2 = new MiniSplitter; + splitter2 = new MiniSplitter; + splitter2->addWidget(new NavigationWidgetPlaceHolder(this)); + splitter2->addWidget(splitter); + splitter2->setStretchFactor(0, 0); + splitter2->setStretchFactor(1, 1); + + setWidget(splitter2); + + QToolBar *toolBar = createToolBar(); + toolBarAddingLayout->addWidget(toolBar); + + managerAccess->createDockWidgets(); + m_manager->setSimpleDockWidgetArrangement(); + readSettings(); + + connect(ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode*)), + this, SLOT(focusCurrentEditor(Core::IMode*))); + widget()->setFocusProxy(EditorManager::instance()); +} + +DebugMode::~DebugMode() +{ + // Make sure the editor manager does not get deleted + EditorManager::instance()->setParent(0); +} + +void DebugMode::shutdown() +{ + writeSettings(); +} + +QToolBar *DebugMode::createToolBar() +{ + IDebuggerManagerAccessForDebugMode *managerAccess = + m_manager->debugModeInterface(); + + Core::ActionManagerInterface *am = + ExtensionSystem::PluginManager::instance() + ->getObject<Core::ICore>()->actionManager(); + QToolBar *debugToolBar = new QToolBar; + debugToolBar->addAction(am->command(ProjectExplorer::Constants::DEBUG)->action()); + debugToolBar->addAction(am->command(Constants::INTERRUPT)->action()); + debugToolBar->addAction(am->command(Constants::NEXT)->action()); + debugToolBar->addAction(am->command(Constants::STEP)->action()); + debugToolBar->addAction(am->command(Constants::STEPOUT)->action()); + debugToolBar->addSeparator(); + debugToolBar->addAction(am->command(Constants::STEPI)->action()); + debugToolBar->addAction(am->command(Constants::NEXTI)->action()); + debugToolBar->addSeparator(); + debugToolBar->addWidget(new QLabel(tr("Threads:"))); + + QComboBox *threadBox = new QComboBox; + threadBox->setModel(m_manager->threadsModel()); + connect(threadBox, SIGNAL(activated(int)), + managerAccess->threadsWindow(), SIGNAL(threadSelected(int))); + debugToolBar->addWidget(threadBox); + + QWidget *stretch = new QWidget; + stretch->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + debugToolBar->addWidget(stretch); + + QMenu *viewMenu = new QMenu(debugToolBar); + m_toggleLockedAction = new QAction(tr("Locked"), viewMenu); + m_toggleLockedAction->setCheckable(true); + m_toggleLockedAction->setChecked(true); + connect(m_toggleLockedAction, SIGNAL(toggled(bool)), + m_manager, SLOT(setLocked(bool))); + foreach (QDockWidget *dockWidget, managerAccess->dockWidgets()) + viewMenu->addAction(dockWidget->toggleViewAction()); + viewMenu->addSeparator(); + viewMenu->addAction(m_toggleLockedAction); + viewMenu->addSeparator(); + + QAction *resetToSimpleAction = viewMenu->addAction(tr("Reset to default layout")); + connect(resetToSimpleAction, SIGNAL(triggered()), + m_manager, SLOT(setSimpleDockWidgetArrangement())); + QToolButton *viewMenuButton = new QToolButton(debugToolBar); + viewMenuButton->setText(tr("View ")); + viewMenuButton->setPopupMode(QToolButton::InstantPopup); + viewMenuButton->setMenu(viewMenu); + debugToolBar->addWidget(viewMenuButton); + + return debugToolBar; +} + +void DebugMode::focusCurrentEditor(IMode *mode) +{ + if (mode != this) + return; + + EditorManager *editorManager = EditorManager::instance(); + + if (editorManager->currentEditor()) + editorManager->currentEditor()->widget()->setFocus(); +} + +void DebugMode::writeSettings() const +{ + QSettings *s = settings(); + QWB_ASSERT(m_manager, return); + QWB_ASSERT(m_manager->mainWindow(), return); + s->beginGroup(QLatin1String("DebugMode")); + s->setValue(QLatin1String("State"), m_manager->mainWindow()->saveState()); + s->setValue(QLatin1String("Locked"), m_toggleLockedAction->isChecked()); + s->endGroup(); +} + +void DebugMode::readSettings() +{ + QSettings *s = settings(); + s->beginGroup(QLatin1String("DebugMode")); + m_manager->mainWindow()->restoreState(s->value(QLatin1String("State"), QByteArray()).toByteArray()); + m_toggleLockedAction->setChecked(s->value(QLatin1String("Locked"), true).toBool()); + s->endGroup(); +} + +QSettings *DebugMode::settings() +{ + return PluginManager::instance()->getObject<ICore>()->settings(); +} diff --git a/src/plugins/debugger/mode.h b/src/plugins/debugger/mode.h new file mode 100644 index 00000000000..15506d0cacb --- /dev/null +++ b/src/plugins/debugger/mode.h @@ -0,0 +1,84 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_DEBUGMODE_H +#define DEBUGGER_DEBUGMODE_H + +#include <coreplugin/basemode.h> + +#include <QtCore/QList> +#include <QtCore/QPointer> + +QT_BEGIN_NAMESPACE +class QAction; +class QDockWidget; +class QMainWindow; +class QSettings; +class QSplitter; +class QToolBar; +class QWidget; +QT_END_NAMESPACE + +namespace Debugger { +namespace Internal { + +class DebuggerManager; + +class DebugMode : public Core::BaseMode +{ + Q_OBJECT + +public: + DebugMode(DebuggerManager *manager, QObject *parent = 0); + ~DebugMode(); + + // IMode + void activated(); + void shutdown(); + static QSettings *settings(); + +private slots: + void focusCurrentEditor(Core::IMode *mode); + +private: + QToolBar *createToolBar(); + void writeSettings() const; + void readSettings(); + + QPointer<DebuggerManager> m_manager; + QAction *m_toggleLockedAction; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_DEBUGMODE_H diff --git a/src/plugins/debugger/mode.ui b/src/plugins/debugger/mode.ui new file mode 100644 index 00000000000..da44cf38b4a --- /dev/null +++ b/src/plugins/debugger/mode.ui @@ -0,0 +1,76 @@ +<ui version="4.0" > + <class>DebugMode</class> + <widget class="QWidget" name="DebugMode" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>558</width> + <height>353</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3" > + <property name="spacing" > + <number>0</number> + </property> + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QSplitter" name="vSplitter" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <widget class="QWidget" native="1" name="editorHolder" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Preferred" > + <horstretch>10</horstretch> + <verstretch>10</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QWidget" name="layoutWidget" > + <layout class="QVBoxLayout" name="verticalLayout" > + <property name="spacing" > + <number>0</number> + </property> + <item> + <layout class="QVBoxLayout" name="toolbarLayout" > + <property name="spacing" > + <number>0</number> + </property> + </layout> + </item> + <item> + <widget class="QSplitter" name="hSplitter" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <widget class="QTabWidget" name="bottomTabWidget" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Expanding" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="tabPosition" > + <enum>QTabWidget::South</enum> + </property> + <property name="tabShape" > + <enum>QTabWidget::Rounded</enum> + </property> + </widget> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/debugger/moduleshandler.cpp b/src/plugins/debugger/moduleshandler.cpp new file mode 100644 index 00000000000..458482b3859 --- /dev/null +++ b/src/plugins/debugger/moduleshandler.cpp @@ -0,0 +1,172 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "moduleshandler.h" + +#include "assert.h" + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QList> +#include <QtCore/QTextStream> + +#include <QtGui/QAction> +#include <QtGui/QMainWindow> +#include <QtGui/QStandardItemModel> +#include <QtGui/QSortFilterProxyModel> + +using namespace Debugger; +using namespace Debugger::Internal; + + +////////////////////////////////////////////////////////////////// +// +// ModulesModel +// +////////////////////////////////////////////////////////////////// + +class Debugger::Internal::ModulesModel : public QAbstractItemModel +{ +public: + ModulesModel(ModulesHandler *parent) + : QAbstractItemModel(parent) + {} + + // QAbstractItemModel + int columnCount(const QModelIndex &parent) const + { return parent.isValid() ? 0 : 4; } + int rowCount(const QModelIndex &parent) const + { return parent.isValid() ? 0 : m_modules.size(); } + QModelIndex parent(const QModelIndex &) const { return QModelIndex(); } + QModelIndex index(int row, int column, const QModelIndex &) const + { return createIndex(row, column); } + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role); + + void clearModel() { if (!m_modules.isEmpty()) { m_modules.clear(); update(); } } + void update() { reset(); } + +public: + QList<Module> m_modules; +}; + +QVariant ModulesModel::headerData(int section, + Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + static QString headers[] = { + tr("Module name") + " ", + tr("Symbols read") + " ", + tr("Start address") + " ", + tr("End addAress") + " " + }; + return headers[section]; + } + return QVariant(); +} + +QVariant ModulesModel::data(const QModelIndex &index, int role) const +{ + //static const QIcon icon(":/gdbdebugger/images/breakpoint.svg"); + //static const QIcon icon2(":/gdbdebugger/images/breakpoint_pending.svg"); + + int row = index.row(); + if (row < 0 || row >= m_modules.size()) + return QVariant(); + + const Module &module = m_modules.at(row); + + switch (index.column()) { + case 0: + if (role == Qt::DisplayRole) + return module.moduleName; + // FIXME: add icons + //if (role == Qt::DecorationRole) + // return module.symbolsRead ? icon2 : icon; + break; + case 1: + if (role == Qt::DisplayRole) + return module.symbolsRead ? "yes" : "no"; + break; + case 2: + if (role == Qt::DisplayRole) + return module.startAddress; + break; + case 3: + if (role == Qt::DisplayRole) + return module.endAddress; + break; + } + return QVariant(); +} + +bool ModulesModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + return QAbstractItemModel::setData(index, value, role); +} + + +////////////////////////////////////////////////////////////////// +// +// ModulesHandler +// +////////////////////////////////////////////////////////////////// + +ModulesHandler::ModulesHandler() +{ + m_model = new ModulesModel(this); + m_proxyModel = new QSortFilterProxyModel(this); + m_proxyModel->setSourceModel(m_model); +} + +QAbstractItemModel *ModulesHandler::model() const +{ + return m_proxyModel; +} + +void ModulesHandler::removeAll() +{ + m_model->clearModel(); +} + + +void ModulesHandler::setModules(const QList<Module> &modules) +{ + m_model->m_modules = modules; + m_model->update(); +} + +QList<Module> ModulesHandler::modules() const +{ + return m_model->m_modules; +} diff --git a/src/plugins/debugger/moduleshandler.h b/src/plugins/debugger/moduleshandler.h new file mode 100644 index 00000000000..49efe16a72a --- /dev/null +++ b/src/plugins/debugger/moduleshandler.h @@ -0,0 +1,104 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_MODULESHANDLER_H +#define DEBUGGER_MODULESHANDLER_H + +#include <QtCore/QList> +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE +class QAbstractItemModel; +class QSortFilterProxyModel; +QT_END_NAMESPACE + + +namespace Debugger { +namespace Internal { + +class ModulesModel; + +enum ModulesModelRoles +{ + DisplaySourceRole = Qt::UserRole, + LoadSymbolsRole, + LoadAllSymbolsRole +}; + + +////////////////////////////////////////////////////////////////// +// +// Module +// +////////////////////////////////////////////////////////////////// + +class Module +{ +public: + Module() : symbolsRead(false) {} + +public: + QString moduleName; + bool symbolsRead; + QString startAddress; + QString endAddress; +}; + + +////////////////////////////////////////////////////////////////// +// +// ModulesHandler +// +////////////////////////////////////////////////////////////////// + +class ModulesHandler : public QObject +{ + Q_OBJECT + +public: + ModulesHandler(); + + QAbstractItemModel *model() const; + + void setModules(const QList<Module> &modules); + QList<Module> modules() const; + void removeAll(); + +private: + ModulesModel *m_model; + QSortFilterProxyModel *m_proxyModel; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_MODULESHANDLER_H diff --git a/src/plugins/debugger/moduleswindow.cpp b/src/plugins/debugger/moduleswindow.cpp new file mode 100644 index 00000000000..f98db1ce1f9 --- /dev/null +++ b/src/plugins/debugger/moduleswindow.cpp @@ -0,0 +1,136 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "moduleswindow.h" +#include "moduleshandler.h" // for model roles + +#include <QAction> +#include <QDebug> +#include <QHeaderView> +#include <QMenu> +#include <QResizeEvent> +#include <QToolButton> + + +using Debugger::Internal::ModulesWindow; + +ModulesWindow::ModulesWindow(QWidget *parent) + : QTreeView(parent), m_alwaysResizeColumnsToContents(false) +{ + setWindowTitle(tr("Modules")); + setSortingEnabled(true); + setAlternatingRowColors(true); + setRootIsDecorated(false); + setIconSize(QSize(10, 10)); +} + +void ModulesWindow::resizeEvent(QResizeEvent *event) +{ + //QHeaderView *hv = header(); + //int totalSize = event->size().width() - 110; + //hv->resizeSection(0, totalSize / 4); + //hv->resizeSection(1, totalSize / 4); + //hv->resizeSection(2, totalSize / 4); + //hv->resizeSection(3, totalSize / 4); + //hv->resizeSection(0, 60); + //hv->resizeSection(1, (totalSize * 50) / 100); + //hv->resizeSection(2, (totalSize * 50) / 100); + //hv->resizeSection(3, 50); + //setColumnHidden(3, true); + QTreeView::resizeEvent(event); +} + +void ModulesWindow::contextMenuEvent(QContextMenuEvent *ev) +{ + QModelIndex index = indexAt(ev->pos()); + index = index.sibling(index.row(), 0); + QString name = model()->data(index).toString(); + + QMenu menu; + QAction *act0 = new QAction("Update module list", &menu); + QAction *act1 = new QAction("Adjust column widths to contents", &menu); + QAction *act2 = new QAction("Always adjust column widths to contents", &menu); + act2->setCheckable(true); + act2->setChecked(m_alwaysResizeColumnsToContents); + QAction *act3 = new QAction("Show source files for module " + name, &menu); + QAction *act4 = new QAction("Load symbols for all modules", &menu); + QAction *act5 = new QAction("Load symbols for module " + name, &menu); + act5->setDisabled(name.isEmpty()); + + menu.addAction(act0); + menu.addAction(act4); + menu.addAction(act5); + menu.addSeparator(); + menu.addAction(act1); + menu.addAction(act2); + + QAction *act = menu.exec(ev->globalPos()); + + if (act == act0) + emit reloadModulesRequested(); + else if (act == act1) + resizeColumnsToContents(); + else if (act == act2) + setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents); + else if (act == act3) + emit displaySourceRequested(name); + else if (act == act4) + emit loadAllSymbolsRequested(); + else if (act == act5) + emit loadSymbolsRequested(name); +} + +void ModulesWindow::resizeColumnsToContents() +{ + resizeColumnToContents(0); + resizeColumnToContents(1); + resizeColumnToContents(2); +} + +void ModulesWindow::setAlwaysResizeColumnsToContents(bool on) +{ + m_alwaysResizeColumnsToContents = on; + QHeaderView::ResizeMode mode = on + ? QHeaderView::ResizeToContents : QHeaderView::Interactive; + header()->setResizeMode(0, mode); + header()->setResizeMode(1, mode); + header()->setResizeMode(2, mode); + header()->setResizeMode(3, mode); + //setColumnHidden(3, true); +} + +void ModulesWindow::setModel(QAbstractItemModel *model) +{ + QTreeView::setModel(model); + setAlwaysResizeColumnsToContents(true); +} + diff --git a/src/plugins/debugger/moduleswindow.h b/src/plugins/debugger/moduleswindow.h new file mode 100644 index 00000000000..a014ff8e6c4 --- /dev/null +++ b/src/plugins/debugger/moduleswindow.h @@ -0,0 +1,71 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_MODULESWINDOW_H +#define DEBUGGER_MODULESWINDOW_H + +#include <QTreeView> + +namespace Debugger { +namespace Internal { + +class ModulesWindow : public QTreeView +{ + Q_OBJECT + +public: + explicit ModulesWindow(QWidget *parent = 0); + +signals: + void reloadModulesRequested(); + void displaySourceRequested(const QString &modulesName); + void loadSymbolsRequested(const QString &modulesName); + void loadAllSymbolsRequested(); + +public slots: + void resizeColumnsToContents(); + void setAlwaysResizeColumnsToContents(bool on); + +protected: + void resizeEvent(QResizeEvent *ev); + void contextMenuEvent(QContextMenuEvent *ev); + void setModel(QAbstractItemModel *model); + +private: + bool m_alwaysResizeColumnsToContents; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_MODULESWINDOW_H + diff --git a/src/plugins/debugger/procinterrupt.cpp b/src/plugins/debugger/procinterrupt.cpp new file mode 100644 index 00000000000..29d4d5803da --- /dev/null +++ b/src/plugins/debugger/procinterrupt.cpp @@ -0,0 +1,182 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "procinterrupt.h" + +#ifdef Q_OS_WIN +#include <windows.h> +#include <Tlhelp32.h> + +using namespace Debugger::Internal; + +typedef HANDLE (WINAPI *PtrCreateRemoteThread)( + HANDLE hProcess, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + SIZE_T dwStackSize, + LPTHREAD_START_ROUTINE lpStartAddress, + LPVOID lpParameter, + DWORD dwCreationFlags, + LPDWORD lpThreadId); + +PtrCreateRemoteThread resolveCreateRemoteThread() +{ + HINSTANCE hLib = LoadLibraryA("Kernel32"); + return (PtrCreateRemoteThread)GetProcAddress(hLib, "CreateRemoteThread"); +} + +DWORD findProcessId(DWORD parentId) +{ + HANDLE hProcList = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + PROCESSENTRY32 procEntry; + procEntry.dwSize = sizeof(PROCESSENTRY32); + + DWORD procId = 0; + + BOOL moreProc = Process32First(hProcList, &procEntry); + while (moreProc) { + if (procEntry.th32ParentProcessID == parentId) { + procId = procEntry.th32ProcessID; + break; + } + moreProc = Process32Next(hProcList, &procEntry); + } + + CloseHandle(hProcList); + return procId; +} +#else + +#include <QtCore/QLatin1String> +#include <QtCore/QString> +#include <QtCore/QDir> +#include <QtCore/QFileInfoList> +#include <QtCore/QByteArray> +#include <QtCore/QDebug> + +#include <sys/types.h> +#include <signal.h> + +#include <sys/sysctl.h> + +#define OPProcessValueUnknown UINT_MAX + +/* Mac OS X +int OPParentIDForProcessID(int pid) + // Returns the parent process id for the given process id (pid) +{ + struct kinfo_proc info; + size_t length = sizeof(struct kinfo_proc); + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }; + if (sysctl(mib, 4, &info, &length, NULL, 0) < 0) + return OPProcessValueUnknown; + if (length == 0) + return OPProcessValueUnknown; + return info.kp_eproc.e_ppid; +} +*/ + +int findParentProcess(int procId) +{ + QFile statFile(QLatin1String("/proc/") + QString::number(procId) + + QLatin1String("/stat")); + if (!statFile.open(QIODevice::ReadOnly)) + return -1; + + QByteArray line = statFile.readLine(); + line = line.mid(line.indexOf(')') + 4); + //qDebug() << "1: " << line; + line = line.left(line.indexOf(' ')); + //qDebug() << "2: " << line; + + return QString(line).toInt(); +} + +int findChildProcess(int parentId) +{ + QDir proc(QLatin1String("/proc")); + QFileInfoList procList = proc.entryInfoList(QDir::Dirs); + foreach (const QFileInfo &info, procList) { + int procId = 0; + bool ok = false; + procId = info.baseName().toInt(&ok); + if (!ok || !procId) + continue; + + if (findParentProcess(procId) == parentId) + return procId; + } + + return -1; +} + +#endif + +bool Debugger::Internal::interruptProcess(int pID) +{ +#ifdef Q_OS_WIN + DWORD pid = pID; + if (!pid) + return false; + + PtrCreateRemoteThread libFunc = resolveCreateRemoteThread(); + if (libFunc) { + DWORD dwThreadId = 0; + HANDLE hproc = OpenProcess(PROCESS_ALL_ACCESS, false, pid); + HANDLE hthread = libFunc(hproc, NULL, 0, (LPTHREAD_START_ROUTINE)DebugBreak, 0, 0, &dwThreadId); + CloseHandle(hthread); + if (dwThreadId) + return true; + } +#else + int procId = pID; + if (procId != -1) { + if (kill(procId, 2) == 0) + return true; + } + +#endif + + return false; +} + +bool Debugger::Internal::interruptChildProcess(Q_PID parentPID) +{ +#ifdef WIN32 + DWORD pid = findProcessId(parentPID->dwProcessId); + return interruptProcess(pid); +#else + int procId = findChildProcess(parentPID); + //qDebug() << "INTERRUPTING PROCESS" << procId; + return interruptProcess(procId); +#endif +} diff --git a/src/plugins/debugger/procinterrupt.h b/src/plugins/debugger/procinterrupt.h new file mode 100644 index 00000000000..0fcf27b0f1e --- /dev/null +++ b/src/plugins/debugger/procinterrupt.h @@ -0,0 +1,47 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_PROCINTERRUPT_H +#define DEBUGGER_PROCINTERRUPT_H + +#include <QtCore/QProcess> + +namespace Debugger { +namespace Internal { + +bool interruptProcess(int pID); +bool interruptChildProcess(Q_PID parentPID); + +} // Internal +} // GdbDebugger + +#endif // DEBUGGER_PROCINTERRUPT_H diff --git a/src/plugins/debugger/registerhandler.cpp b/src/plugins/debugger/registerhandler.cpp new file mode 100644 index 00000000000..4c70e2339cc --- /dev/null +++ b/src/plugins/debugger/registerhandler.cpp @@ -0,0 +1,128 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "registerhandler.h" + +#include "assert.h" +#include "debuggerconstants.h" + +#include <QtCore/QAbstractTableModel> +#include <QtCore/QDebug> + +#include <QtGui/QColor> + +using namespace Debugger; +using namespace Debugger::Internal; +using namespace Debugger::Constants; + + + +////////////////////////////////////////////////////////////////// +// +// RegisterHandler +// +////////////////////////////////////////////////////////////////// + +RegisterHandler::RegisterHandler(QObject *parent) + : QAbstractTableModel(parent) +{ + setProperty(PROPERTY_REGISTER_FORMAT, "x"); +} + +int RegisterHandler::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return m_registers.size(); +} + +int RegisterHandler::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 2; +} + +QVariant RegisterHandler::data(const QModelIndex &index, int role) const +{ + static const QVariant red = QColor(200, 0, 0); + if (!index.isValid() || index.row() >= m_registers.size()) + return QVariant(); + + const Register ® = m_registers.at(index.row()); + + if (role == Qt::DisplayRole) { + switch (index.column()) { + case 0: + return reg.name; + case 1: + return reg.value; + } + } + if (role == Qt::TextColorRole && reg.changed && index.column() == 1) + return red; + return QVariant(); +} + +QVariant RegisterHandler::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + static const char * const headers[] = { + QT_TR_NOOP("Name"), + QT_TR_NOOP("Value"), + }; + if (section < 2) + return tr(headers[section]); + } + return QVariant(); +} + +void RegisterHandler::removeAll() +{ + m_registers.clear(); + reset(); +} + +bool RegisterHandler::isEmpty() const +{ + return m_registers.isEmpty(); +} + +void RegisterHandler::setRegisters(const QList<Register> ®isters) +{ + m_registers = registers; + reset(); +} + +QList<Register> RegisterHandler::registers() const +{ + return m_registers; +} diff --git a/src/plugins/debugger/registerhandler.h b/src/plugins/debugger/registerhandler.h new file mode 100644 index 00000000000..933e45aad42 --- /dev/null +++ b/src/plugins/debugger/registerhandler.h @@ -0,0 +1,81 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_REGISTERHANDLER_H +#define DEBUGGER_REGISTERHANDLER_H + +#include <QtCore/QAbstractTableModel> + +namespace Debugger { +namespace Internal { + +class Register +{ +public: + Register() : changed(true) {} + Register(QString const &name_) : name(name_), changed(true) {} + +public: + QString name; + QString value; + bool changed; +}; + +class RegisterHandler : public QAbstractTableModel +{ + Q_OBJECT + +public: + RegisterHandler(QObject *parent = 0); + + void sessionClosed(); + QAbstractItemModel *model() { return this; } + + bool isEmpty() const; // nothing known so far? + void setRegisters(const QList<Register> ®isters); + QList<Register> registers() const; + void removeAll(); + +private: + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + QList<Register> m_registers; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_REGISTERHANDLER_H diff --git a/src/plugins/debugger/registerwindow.cpp b/src/plugins/debugger/registerwindow.cpp new file mode 100644 index 00000000000..5e531392852 --- /dev/null +++ b/src/plugins/debugger/registerwindow.cpp @@ -0,0 +1,181 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "registerwindow.h" + +#include "debuggerconstants.h" + +#include <QAction> +#include <QDebug> +#include <QDir> +#include <QFileInfo> +#include <QFileInfoList> +#include <QHeaderView> +#include <QMenu> +#include <QResizeEvent> +#include <QToolButton> + + +using namespace Debugger::Internal; +using namespace Debugger::Constants; + +RegisterWindow::RegisterWindow() + : m_alwaysResizeColumnsToContents(true), m_alwaysReloadContents(false) +{ + setWindowTitle(tr("Registers")); + setSortingEnabled(true); + setAlternatingRowColors(true); + setRootIsDecorated(false); + //header()->hide(); + //setIconSize(QSize(10, 10)); + //setWindowIcon(QIcon(":/gdbdebugger/images/debugger_breakpoints.png")); + //QHeaderView *hv = header(); + //hv->setDefaultAlignment(Qt::AlignLeft); + //hv->setClickable(true); + //hv->setSortIndicatorShown(true); +} + +void RegisterWindow::resizeEvent(QResizeEvent *ev) +{ + //QHeaderView *hv = header(); + //int totalSize = ev->size().width() - 110; + //hv->resizeSection(0, totalSize / 4); + //hv->resizeSection(1, totalSize / 4); + QTreeView::resizeEvent(ev); +} + +void RegisterWindow::contextMenuEvent(QContextMenuEvent *ev) +{ + enum { Hex, Bin, Dec, Raw, Oct, Nat, + Adjust, AlwaysAdjust, Reload, AlwaysReload, Count }; + + QMenu menu; + QAction *actions[Count]; + //QTreeWidgetItem *item = itemAt(ev->pos()); + QString format = model()->property(PROPERTY_REGISTER_FORMAT).toString(); + qDebug() << "FORMAT: " << format; + + actions[Adjust] = menu.addAction("Adjust column widths to contents"); + + actions[AlwaysAdjust] = menu.addAction("Always adjust column widths to contents"); + actions[AlwaysAdjust]->setCheckable(true); + actions[AlwaysAdjust]->setChecked(m_alwaysResizeColumnsToContents); + + actions[Reload] = menu.addAction("Reload register listing"); + + actions[AlwaysReload] = menu.addAction("Always reload register listing"); + actions[AlwaysReload]->setCheckable(true); + actions[AlwaysReload]->setChecked(m_alwaysReloadContents); + + menu.addSeparator(); + + actions[Hex] = menu.addAction("Hexadecimal"); + actions[Hex]->setCheckable(true); + actions[Hex]->setChecked(format == "h"); + + actions[Bin] = menu.addAction("Binary"); + actions[Bin]->setCheckable(true); + actions[Bin]->setChecked(format == "t"); + + actions[Dec] = menu.addAction("Decimal"); + actions[Dec]->setCheckable(true); + actions[Dec]->setChecked(format == "d"); + + actions[Raw] = menu.addAction("Raw"); + actions[Raw]->setCheckable(true); + actions[Raw]->setChecked(format == "r"); + + actions[Nat] = menu.addAction("Natural"); + actions[Nat]->setCheckable(true); + actions[Nat]->setChecked(format == "N"); + + actions[Oct] = menu.addAction("Octal"); + actions[Oct]->setCheckable(true); + actions[Oct]->setChecked(format == "o"); + + QAction *act = menu.exec(ev->globalPos()); + + if (act == actions[Adjust]) + resizeColumnsToContents(); + else if (act == actions[AlwaysAdjust]) + setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents); + else if (act == actions[Reload]) + reloadContents(); + else if (act == actions[AlwaysReload]) + setAlwaysReloadContents(!m_alwaysReloadContents); + else if (act == actions[Hex]) + model()->setProperty(PROPERTY_REGISTER_FORMAT, "h"); + else if (act == actions[Oct]) + model()->setProperty(PROPERTY_REGISTER_FORMAT, "o"); + else if (act == actions[Bin]) + model()->setProperty(PROPERTY_REGISTER_FORMAT, "t"); + else if (act == actions[Dec]) + model()->setProperty(PROPERTY_REGISTER_FORMAT, "d"); + else if (act == actions[Nat]) + model()->setProperty(PROPERTY_REGISTER_FORMAT, "N"); + +} + +void RegisterWindow::resizeColumnsToContents() +{ + resizeColumnToContents(0); + resizeColumnToContents(1); +} + +void RegisterWindow::setAlwaysResizeColumnsToContents(bool on) +{ + m_alwaysResizeColumnsToContents = on; + QHeaderView::ResizeMode mode = on + ? QHeaderView::ResizeToContents : QHeaderView::Interactive; + header()->setResizeMode(0, mode); + header()->setResizeMode(1, mode); +} + +void RegisterWindow::setAlwaysReloadContents(bool on) +{ + m_alwaysReloadContents = on; + if (m_alwaysReloadContents) + reloadContents(); +} + +void RegisterWindow::reloadContents() +{ + emit reloadRegisterRequested(); +} + + +void RegisterWindow::setModel(QAbstractItemModel *model) +{ + QTreeView::setModel(model); + setAlwaysResizeColumnsToContents(true); +} + diff --git a/src/plugins/debugger/registerwindow.h b/src/plugins/debugger/registerwindow.h new file mode 100644 index 00000000000..4c2cc7b7a62 --- /dev/null +++ b/src/plugins/debugger/registerwindow.h @@ -0,0 +1,71 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_REGISTERWINDOW_H +#define DEBUGGER_REGISTERWINDOW_H + +#include <QTreeView> + +namespace Debugger { +namespace Internal { + +class RegisterWindow : public QTreeView +{ + Q_OBJECT + +public: + RegisterWindow(); + void setModel(QAbstractItemModel *model); + +signals: + void reloadRegisterRequested(); + +public slots: + void resizeColumnsToContents(); + void setAlwaysResizeColumnsToContents(bool on); + void reloadContents(); + void setAlwaysReloadContents(bool on); + +protected: + void resizeEvent(QResizeEvent *ev); + void contextMenuEvent(QContextMenuEvent *ev); + +private: + bool m_alwaysResizeColumnsToContents; + bool m_alwaysReloadContents; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_REGISTERWINDOW_H + diff --git a/src/plugins/debugger/scriptengine.cpp b/src/plugins/debugger/scriptengine.cpp new file mode 100644 index 00000000000..c6c7dd65a56 --- /dev/null +++ b/src/plugins/debugger/scriptengine.cpp @@ -0,0 +1,677 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "scriptengine.h" + +#include "assert.h" +#include "debuggerconstants.h" +#include "debuggermanager.h" + +#include "disassemblerhandler.h" +#include "breakhandler.h" +#include "moduleshandler.h" +#include "registerhandler.h" +#include "stackhandler.h" +#include "watchhandler.h" + +#include "startexternaldialog.h" +#include "attachexternaldialog.h" + +#include <QtCore/QDateTime> +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QTimer> + +#include <QtGui/QAction> +#include <QtGui/QToolTip> + +#include <QtScript/QScriptContext> +#include <QtScript/QScriptClassPropertyIterator> +#include <QtScript/QScriptContextInfo> +#include <QtScript/QScriptEngine> +#include <QtScript/QScriptEngineAgent> +#include <QtScript/QScriptValue> +#include <QtScript/QScriptValueIterator> + +using namespace Debugger; +using namespace Debugger::Internal; +using namespace Debugger::Constants; + + +/////////////////////////////////////////////////////////////////////// +// +// ScriptEngine +// +/////////////////////////////////////////////////////////////////////// + +class Debugger::Internal::ScriptAgent : public QScriptEngineAgent +{ +public: + ScriptAgent(ScriptEngine *debugger, QScriptEngine *script); + ~ScriptAgent() {} + + void contextPop(); + void contextPush(); + void exceptionCatch(qint64 scriptId, const QScriptValue &exception); + void exceptionThrow(qint64 scriptId, const QScriptValue & exception, + bool hasHandler); + void functionEntry(qint64 scriptId); + void functionExit(qint64 scriptId, const QScriptValue &returnValue); + void positionChange(qint64 scriptId, int lineNumber, int columnNumber); + void scriptLoad(qint64 id, const QString &program, const QString &fileName, + int baseLineNumber); + void scriptUnload(qint64 id); + +private: + void maybeBreakNow(bool byFunction); + + ScriptEngine *q; +}; + +ScriptAgent::ScriptAgent(ScriptEngine *debugger, QScriptEngine *script) + : QScriptEngineAgent(script), q(debugger) +{} + +void ScriptAgent::contextPop() +{ + qDebug() << "ScriptAgent::contextPop: "; +} + +void ScriptAgent::contextPush() +{ + qDebug() << "ScriptAgent::contextPush: "; +} + +void ScriptAgent::exceptionCatch(qint64 scriptId, const QScriptValue & exception) +{ + qDebug() << "ScriptAgent::exceptionCatch: " << scriptId << &exception; +} + +void ScriptAgent::exceptionThrow(qint64 scriptId, const QScriptValue &exception, + bool hasHandler) +{ + qDebug() << "ScriptAgent::exceptionThrow: " << scriptId << &exception + << hasHandler; +} + +void ScriptAgent::functionEntry(qint64 scriptId) +{ + Q_UNUSED(scriptId); + q->maybeBreakNow(true); +} + +void ScriptAgent::functionExit(qint64 scriptId, const QScriptValue &returnValue) +{ + qDebug() << "ScriptAgent::functionExit: " << scriptId << &returnValue; +} + +void ScriptAgent::positionChange(qint64 scriptId, int lineNumber, int columnNumber) +{ + //qDebug() << "ScriptAgent::position: " << lineNumber; + Q_UNUSED(scriptId); + Q_UNUSED(lineNumber); + Q_UNUSED(columnNumber); + q->maybeBreakNow(false); +} + +void ScriptAgent::scriptLoad(qint64 scriptId, const QString &program, + const QString &fileName, int baseLineNumber) +{ + Q_UNUSED(scriptId); + Q_UNUSED(program); + Q_UNUSED(fileName); + Q_UNUSED(baseLineNumber); + //qDebug() << "ScriptAgent::scriptLoad: " << program << fileName + // << baseLineNumber; +} + +void ScriptAgent::scriptUnload(qint64 scriptId) +{ + Q_UNUSED(scriptId); + //qDebug() << "ScriptAgent::scriptUnload: " << scriptId; +} + + +/////////////////////////////////////////////////////////////////////// +// +// ScriptEngine +// +/////////////////////////////////////////////////////////////////////// + +ScriptEngine::ScriptEngine(DebuggerManager *parent) +{ + q = parent; + qq = parent->engineInterface(); + m_scriptEngine = new QScriptEngine(this); + m_scriptAgent = new ScriptAgent(this, m_scriptEngine); + m_scriptEngine->setAgent(m_scriptAgent); + m_scriptEngine->setProcessEventsInterval(1 /*ms*/); +} + +ScriptEngine::~ScriptEngine() +{ +} + +void ScriptEngine::executeDebuggerCommand(const QString &command) +{ + Q_UNUSED(command); + qDebug() << "FIXME: ScriptEngine::executeDebuggerCommand()"; +} + +void ScriptEngine::shutdown() +{ + exitDebugger(); +} + +void ScriptEngine::exitDebugger() +{ + //qDebug() << " ScriptEngine::exitDebugger()"; + m_stopped = false; + m_stopOnNextLine = false; + m_scriptEngine->abortEvaluation(); + qq->notifyInferiorExited(); +} + +bool ScriptEngine::startDebugger() +{ + m_stopped = false; + m_stopOnNextLine = false; + m_scriptEngine->abortEvaluation(); + QFileInfo fi(q->m_executable); + m_scriptFileName = fi.absoluteFilePath(); + QFile scriptFile(m_scriptFileName); + if (!scriptFile.open(QIODevice::ReadOnly)) + return false; + QTextStream stream(&scriptFile); + m_scriptContents = stream.readAll(); + scriptFile.close(); + attemptBreakpointSynchronization(); + QTimer::singleShot(0, q, SLOT(notifyStartupFinished())); + return true; +} + +void ScriptEngine::continueInferior() +{ + //qDebug() << "ScriptEngine::continueInferior()"; + m_stopped = false; + m_stopOnNextLine = false; +} + +void ScriptEngine::runInferior() +{ + //qDebug() << "ScriptEngine::runInferior()"; + QScriptValue result = m_scriptEngine->evaluate(m_scriptContents, m_scriptFileName); +} + +void ScriptEngine::interruptInferior() +{ + m_stopped = false; + m_stopOnNextLine = true; + qDebug() << "FIXME: ScriptEngine::interruptInferior()"; +} + +void ScriptEngine::stepExec() +{ + //qDebug() << "FIXME: ScriptEngine::stepExec()"; + m_stopped = false; + m_stopOnNextLine = true; +} + +void ScriptEngine::stepIExec() +{ + //qDebug() << "FIXME: ScriptEngine::stepIExec()"; + m_stopped = false; + m_stopOnNextLine = true; +} + +void ScriptEngine::stepOutExec() +{ + //qDebug() << "FIXME: ScriptEngine::stepOutExec()"; + m_stopped = false; + m_stopOnNextLine = true; +} + +void ScriptEngine::nextExec() +{ + //qDebug() << "FIXME: ScriptEngine::nextExec()"; + m_stopped = false; + m_stopOnNextLine = true; +} + +void ScriptEngine::nextIExec() +{ + //qDebug() << "FIXME: ScriptEngine::nextIExec()"; + m_stopped = false; + m_stopOnNextLine = true; +} + +void ScriptEngine::runToLineExec(const QString &fileName, int lineNumber) +{ + Q_UNUSED(fileName); + Q_UNUSED(lineNumber); + qDebug() << "FIXME: ScriptEngine::runToLineExec()"; +} + +void ScriptEngine::runToFunctionExec(const QString &functionName) +{ + Q_UNUSED(functionName); + qDebug() << "FIXME: ScriptEngine::runToFunctionExec()"; +} + +void ScriptEngine::jumpToLineExec(const QString &fileName, int lineNumber) +{ + Q_UNUSED(fileName); + Q_UNUSED(lineNumber); + qDebug() << "FIXME: ScriptEngine::jumpToLineExec()"; +} + +void ScriptEngine::activateFrame(int index) +{ + Q_UNUSED(index); +} + +void ScriptEngine::selectThread(int index) +{ + Q_UNUSED(index); +} + +void ScriptEngine::attemptBreakpointSynchronization() +{ + BreakHandler *handler = qq->breakHandler(); + bool updateNeeded = false; + for (int index = 0; index != handler->size(); ++index) { + BreakpointData *data = handler->at(index); + if (data->pending) { + data->pending = false; // FIXME + updateNeeded = true; + } + if (data->bpNumber.isEmpty()) { + data->bpNumber = QString::number(index + 1); + updateNeeded = true; + } + if (!data->fileName.isEmpty() && data->markerFileName.isEmpty()) { + data->markerFileName = data->fileName; + data->markerLineNumber = data->lineNumber.toInt(); + updateNeeded = true; + } + } + if (updateNeeded) + handler->updateMarkers(); +} + +void ScriptEngine::reloadDisassembler() +{ +} + +void ScriptEngine::loadSymbols(const QString &moduleName) +{ + Q_UNUSED(moduleName); +} + +void ScriptEngine::loadAllSymbols() +{ +} + +void ScriptEngine::reloadModules() +{ +} + + + +////////////////////////////////////////////////////////////////////// +// +// Tooltip specific stuff +// +////////////////////////////////////////////////////////////////////// + +static WatchData m_toolTip; +static QPoint m_toolTipPos; +static QHash<QString, WatchData> m_toolTipCache; + +static bool hasLetterOrNumber(const QString &exp) +{ + for (int i = exp.size(); --i >= 0; ) + if (exp[i].isLetterOrNumber()) + return true; + return false; +} + +static bool hasSideEffects(const QString &exp) +{ + // FIXME: complete? + return exp.contains("-=") + || exp.contains("+=") + || exp.contains("/=") + || exp.contains("*=") + || exp.contains("&=") + || exp.contains("|=") + || exp.contains("^=") + || exp.contains("--") + || exp.contains("++"); +} + +void ScriptEngine::setToolTipExpression(const QPoint &pos, const QString &exp0) +{ + Q_UNUSED(pos); + Q_UNUSED(exp0); + + if (q->status() != DebuggerInferiorStopped) { + //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED"; + return; + } + + //m_toolTipPos = pos; + QString exp = exp0; + +/* + if (m_toolTipCache.contains(exp)) { + const WatchData & data = m_toolTipCache[exp]; + q->watchHandler()->removeChildren(data.iname); + insertData(data); + return; + } +*/ + + QToolTip::hideText(); + if (exp.isEmpty() || exp.startsWith("#")) { + QToolTip::hideText(); + return; + } + + if (!hasLetterOrNumber(exp)) { + QToolTip::showText(m_toolTipPos, + "'" + exp + "' contains no identifier"); + return; + } + + if (exp.startsWith('"') && exp.endsWith('"')) { + QToolTip::showText(m_toolTipPos, "String literal " + exp); + return; + } + + if (exp.startsWith("++") || exp.startsWith("--")) + exp = exp.mid(2); + + if (exp.endsWith("++") || exp.endsWith("--")) + exp = exp.mid(2); + + if (exp.startsWith("<") || exp.startsWith("[")) + return; + + if (hasSideEffects(exp)) { + QToolTip::showText(m_toolTipPos, + "Cowardly refusing to evaluate expression '" + exp + + "' with potential side effects"); + return; + } + +#if 0 + //if (m_manager->status() != DebuggerInferiorStopped) + // return; + + // FIXME: 'exp' can contain illegal characters + m_toolTip = WatchData(); + m_toolTip.exp = exp; + m_toolTip.name = exp; + m_toolTip.iname = tooltipIName; + insertData(m_toolTip); +#endif +} + + +////////////////////////////////////////////////////////////////////// +// +// Watch specific stuff +// +////////////////////////////////////////////////////////////////////// + +void ScriptEngine::assignValueInDebugger(const QString &expression, + const QString &value) +{ + Q_UNUSED(expression); + Q_UNUSED(value); +} + +void ScriptEngine::maybeBreakNow(bool byFunction) +{ + QScriptContext *context = m_scriptEngine->currentContext(); + QScriptContextInfo info(context); + + // + // Update breakpoints + // + QString functionName = info.functionName(); + QString fileName = info.fileName(); + int lineNumber = info.lineNumber(); + if (byFunction) + lineNumber = info.functionStartLineNumber(); + + BreakHandler *handler = qq->breakHandler(); + + if (m_stopOnNextLine) { + m_stopOnNextLine = false; + } else { + int index = 0; + for (; index != handler->size(); ++index) { + BreakpointData *data = handler->at(index); + if (byFunction) { + if (!functionName.isEmpty() && data->funcName == functionName) + break; + } else { + if (info.lineNumber() == data->lineNumber.toInt() + && fileName == data->fileName) + break; + } + } + + if (index == handler->size()) + return; + + // we just run into a breakpoint + //qDebug() << "RESOLVING BREAKPOINT AT " << fileName << lineNumber; + BreakpointData *data = handler->at(index); + data->bpLineNumber = QString::number(lineNumber); + data->bpFileName = fileName; + data->bpFuncName = functionName; + data->markerLineNumber = lineNumber; + data->markerFileName = fileName; + data->pending = false; + data->updateMarker(); + } + + qq->notifyInferiorStopped(); + q->gotoLocation(fileName, lineNumber, true); + + qq->watchHandler()->reinitializeWatchers(); + //qDebug() << "UPDATE LOCALS"; + + // + // Build stack + // + QList<StackFrame> stackFrames; + int i = 0; + for (QScriptContext *c = context; c; c = c->parentContext(), ++i) { + QScriptContextInfo info(c); + StackFrame frame; + frame.level = i; + frame.file = info.fileName(); + frame.function = info.functionName(); + frame.from = QString::number(info.functionStartLineNumber()); + frame.to = QString::number(info.functionEndLineNumber()); + frame.line = info.lineNumber(); + + if (frame.function.isEmpty()) + frame.function = "<global scope>"; + //frame.address = ...; + stackFrames.append(frame); + } + qq->stackHandler()->setFrames(stackFrames); + + // + // Build locals + // + WatchData data; + data.iname = "local"; + data.name = "local"; + data.scriptValue = context->activationObject(); + qq->watchHandler()->insertData(data); + updateWatchModel(); + + // FIXME: Use an extra thread. This here is evil + m_stopped = true; + while (m_stopped) { + //qDebug() << "LOOPING"; + QApplication::processEvents(); + } + //qDebug() << "RUNNING AGAIN"; +} + +void ScriptEngine::updateWatchModel() +{ + while (true) { + QList<WatchData> list = qq->watchHandler()->takeCurrentIncompletes(); + if (list.isEmpty()) + break; + foreach (const WatchData &data, list) + updateSubItem(data); + } + qq->watchHandler()->rebuildModel(); + q->showStatusMessage(tr("Stopped."), 5000); +} + +void ScriptEngine::updateSubItem(const WatchData &data0) +{ + WatchData data = data0; + //qDebug() << "\nUPDATE SUBITEM: " << data.toString(); + QWB_ASSERT(data.isValid(), return); + + if (data.isTypeNeeded() || data.isValueNeeded()) { + QScriptValue ob = data.scriptValue; + if (ob.isArray()) { + data.setType("Array"); + data.setValue(" "); + } else if (ob.isBool()) { + data.setType("Bool"); + data.setValue(ob.toBool() ? "true" : "false"); + data.setChildCount(0); + } else if (ob.isDate()) { + data.setType("Date"); + data.setValue(ob.toDateTime().toString().toUtf8()); + data.setChildCount(0); + } else if (ob.isError()) { + data.setType("Error"); + data.setValue(" "); + } else if (ob.isFunction()) { + data.setType("Function"); + data.setValue(" "); + } else if (ob.isNull()) { + data.setType("<null>"); + data.setValue("<null>"); + } else if (ob.isNumber()) { + data.setType("Number"); + data.setValue(QString::number(ob.toNumber()).toUtf8()); + data.setChildCount(0); + } else if (ob.isObject()) { + data.setType("Object"); + data.setValue(" "); + } else if (ob.isQMetaObject()) { + data.setType("QMetaObject"); + data.setValue(" "); + } else if (ob.isQObject()) { + data.setType("QObject"); + data.setValue(" "); + } else if (ob.isRegExp()) { + data.setType("RegExp"); + data.setValue(ob.toRegExp().pattern().toUtf8()); + } else if (ob.isString()) { + data.setType("String"); + data.setValue(ob.toString().toUtf8()); + } else if (ob.isVariant()) { + data.setType("Variant"); + data.setValue(" "); + } else if (ob.isUndefined()) { + data.setType("<undefined>"); + data.setValue("<unknown>"); + } else { + data.setType("<unknown>"); + data.setValue("<unknown>"); + } + qq->watchHandler()->insertData(data); + return; + } + + if (data.isChildrenNeeded()) { + int numChild = 0; + QScriptValueIterator it(data.scriptValue); + while (it.hasNext()) { + it.next(); + WatchData data1; + data1.iname = data.iname + "." + it.name(); + data1.name = it.name(); + data1.scriptValue = it.value(); + if (qq->watchHandler()->isExpandedIName(data1.iname)) + data1.setChildrenNeeded(); + else + data1.setChildrenUnneeded(); + qq->watchHandler()->insertData(data1); + ++numChild; + } + //qDebug() << " ... CHILDREN: " << numChild; + data.setChildCount(numChild); + data.setChildrenUnneeded(); + qq->watchHandler()->insertData(data); + return; + } + + if (data.isChildCountNeeded()) { + int numChild = 0; + QScriptValueIterator it(data.scriptValue); + while (it.hasNext()) { + it.next(); + ++numChild; + } + data.setChildCount(numChild); + //qDebug() << " ... CHILDCOUNT: " << numChild; + qq->watchHandler()->insertData(data); + return; + } + + QWB_ASSERT(false, return); +} + +IDebuggerEngine *createScriptEngine(DebuggerManager *parent) +{ + return new ScriptEngine(parent); +} + diff --git a/src/plugins/debugger/scriptengine.h b/src/plugins/debugger/scriptengine.h new file mode 100644 index 00000000000..69cb4405707 --- /dev/null +++ b/src/plugins/debugger/scriptengine.h @@ -0,0 +1,134 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_SCRIPTENGINE_H +#define DEBUGGER_SCRIPTENGINE_H + +#include <QtCore/QByteArray> +#include <QtCore/QHash> +#include <QtCore/QMap> +#include <QtCore/QObject> +#include <QtCore/QProcess> +#include <QtCore/QPoint> +#include <QtCore/QSet> +#include <QtCore/QVariant> + +#include <QtNetwork/QLocalSocket> + +QT_BEGIN_NAMESPACE +class QAction; +class QAbstractItemModel; +class QSplitter; +class QToolBar; +class QScriptEngine; +class QScriptValue; +QT_END_NAMESPACE + +#include "idebuggerengine.h" + +namespace Debugger { +namespace Internal { + +class DebuggerManager; +class IDebuggerManagerAccessForEngines; +class ScriptAgent; +class WatchData; + +class ScriptEngine : public IDebuggerEngine +{ + Q_OBJECT + +public: + ScriptEngine(DebuggerManager *parent); + ~ScriptEngine(); + +private: + // IDebuggerEngine implementation + void stepExec(); + void stepOutExec(); + void nextExec(); + void stepIExec(); + void nextIExec(); + + void shutdown(); + void setToolTipExpression(const QPoint &pos, const QString &exp); + bool startDebugger(); + void exitDebugger(); + + void continueInferior(); + void runInferior(); + void interruptInferior(); + + void runToLineExec(const QString &fileName, int lineNumber); + void runToFunctionExec(const QString &functionName); + void jumpToLineExec(const QString &fileName, int lineNumber); + + void activateFrame(int index); + void selectThread(int index); + + void attemptBreakpointSynchronization(); + + void loadSessionData() {} + void saveSessionData() {} + + void assignValueInDebugger(const QString &expr, const QString &value); + void executeDebuggerCommand(const QString & command); + + void loadSymbols(const QString &moduleName); + void loadAllSymbols(); + void reloadDisassembler(); + void reloadModules(); + void reloadRegisters() {} + + bool supportsThreads() const { return true; } + void maybeBreakNow(bool byFunction); + void updateWatchModel(); + void updateSubItem(const WatchData &data0); + +private: + friend class ScriptAgent; + DebuggerManager *q; + IDebuggerManagerAccessForEngines *qq; + + QScriptEngine *m_scriptEngine; + QString m_scriptContents; + QString m_scriptFileName; + ScriptAgent *m_scriptAgent; + + bool m_stopped; + bool m_stopOnNextLine; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_SCRIPTENGINE_H diff --git a/src/plugins/debugger/stackhandler.cpp b/src/plugins/debugger/stackhandler.cpp new file mode 100644 index 00000000000..d05e259c094 --- /dev/null +++ b/src/plugins/debugger/stackhandler.cpp @@ -0,0 +1,271 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "stackhandler.h" + +#include "assert.h" + +#include <QtCore/QAbstractTableModel> +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> + +using namespace Debugger::Internal; + + +//////////////////////////////////////////////////////////////////////// +// +// StackHandler +// +//////////////////////////////////////////////////////////////////////// + +StackHandler::StackHandler(QObject *parent) + : QAbstractTableModel(parent), m_currentIndex(0) +{ + m_emptyIcon = QIcon(":/gdbdebugger/images/empty.svg"); + m_positionIcon = QIcon(":/gdbdebugger/images/location.svg"); +} + +int StackHandler::rowCount(const QModelIndex &parent) const +{ + // Since the stack is not a tree, row count is 0 for any valid parent + return parent.isValid() ? 0 : m_stackFrames.size(); +} + +int StackHandler::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : 4; +} + +QVariant StackHandler::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= m_stackFrames.size()) + return QVariant(); + + const StackFrame &frame = m_stackFrames.at(index.row()); + + if (role == Qt::DisplayRole) { + switch (index.column()) { + case 0: // Stack frame level + return QString::number(frame.level); + case 1: // Function name + return frame.function; + case 2: // File name + return frame.file.isEmpty() ? frame.from : QFileInfo(frame.file).fileName(); + case 3: // Line number + return frame.line; + case 4: // Address + return frame.address; + } + } else if (role == Qt::ToolTipRole) { + return "<table><tr><td>Address:</td><td>" + frame.address + "</td></tr>" + + "<tr><td>Function: </td><td>" + frame.function + "</td></tr>" + + "<tr><td>File: </td><td>" + frame.file + "</td></tr>" + + "<tr><td>Line: </td><td>" + QString::number(frame.line) + "</td></tr>" + + "<tr><td>From: </td><td>" + frame.from + "</td></tr></table>" + + "<tr><td>To: </td><td>" + frame.to + "</td></tr></table>"; + } else if (role == Qt::DecorationRole && index.column() == 0) { + // Return icon that indicates whether this is the active stack frame + return (index.row() == m_currentIndex) ? m_positionIcon : m_emptyIcon; + } + + return QVariant(); +} + +QVariant StackHandler::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + static const char * const headers[] = { + QT_TR_NOOP("Level"), + QT_TR_NOOP("Function"), + QT_TR_NOOP("File"), + QT_TR_NOOP("Line"), + QT_TR_NOOP("Address") + }; + if (section < 5) + return tr(headers[section]); + } + return QVariant(); +} + +Qt::ItemFlags StackHandler::flags(const QModelIndex &index) const +{ + if (index.row() >= m_stackFrames.size()) + return 0; + const StackFrame &frame = m_stackFrames.at(index.row()); + const bool isValid = !frame.file.isEmpty() && !frame.function.isEmpty(); + return isValid ? QAbstractTableModel::flags(index) : Qt::ItemFlags(0); +} + +StackFrame StackHandler::currentFrame() const +{ + QWB_ASSERT(m_currentIndex >= 0, return StackFrame()); + QWB_ASSERT(m_currentIndex < m_stackFrames.size(), return StackFrame()); + return m_stackFrames.at(m_currentIndex); +} + +void StackHandler::setCurrentIndex(int level) +{ + if (level == m_currentIndex) + return; + + // Emit changed for previous frame + QModelIndex i = index(m_currentIndex, 0); + emit dataChanged(i, i); + + m_currentIndex = level; + + // Emit changed for new frame + i = index(m_currentIndex, 0); + emit dataChanged(i, i); +} + +void StackHandler::removeAll() +{ + m_stackFrames.clear(); + m_currentIndex = 0; + reset(); +} + +void StackHandler::setFrames(const QList<StackFrame> &frames) +{ + m_stackFrames = frames; + if (m_currentIndex >= m_stackFrames.size()) + m_currentIndex = m_stackFrames.size() - 1; + reset(); +} + +QList<StackFrame> StackHandler::frames() const +{ + return m_stackFrames; +} + +bool StackHandler::isDebuggingDumpers() const +{ + for (int i = m_stackFrames.size(); --i >= 0; ) + if (m_stackFrames.at(i).function.startsWith("qDumpObjectData")) + return true; + return false; +} + +//////////////////////////////////////////////////////////////////////// +// +// ThreadsHandler +// +//////////////////////////////////////////////////////////////////////// + +ThreadsHandler::ThreadsHandler(QObject *parent) + : QAbstractTableModel(parent), m_currentIndex(0) +{ + m_emptyIcon = QIcon(":/gdbdebugger/images/empty.svg"); + m_positionIcon = QIcon(":/gdbdebugger/images/location.svg"); +} + +int ThreadsHandler::rowCount(const QModelIndex &parent) const +{ + // Since the stack is not a tree, row count is 0 for any valid parent + return parent.isValid() ? 0 : m_threads.size(); +} + +int ThreadsHandler::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : 1; +} + +QVariant ThreadsHandler::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= m_threads.size()) + return QVariant(); + + if (role == Qt::DisplayRole) { + switch (index.column()) { + case 0: // Thread ID + return m_threads.at(index.row()).id; + case 1: // Function name + return "???"; + } + } else if (role == Qt::ToolTipRole) { + return "Thread: " + QString::number(m_threads.at(index.row()).id); + } else if (role == Qt::DecorationRole && index.column() == 0) { + // Return icon that indicates whether this is the active stack frame + return (index.row() == m_currentIndex) ? m_positionIcon : m_emptyIcon; + } + + return QVariant(); +} + +QVariant ThreadsHandler::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + static const char * const headers[] = { + QT_TR_NOOP("Thread ID"), + }; + if (section < 1) + return tr(headers[section]); + } + return QVariant(); +} + +void ThreadsHandler::setCurrentThread(int index) +{ + if (index == m_currentIndex) + return; + + // Emit changed for previous frame + QModelIndex i = ThreadsHandler::index(m_currentIndex, 0); + emit dataChanged(i, i); + + m_currentIndex = index; + + // Emit changed for new frame + i = ThreadsHandler::index(m_currentIndex, 0); + emit dataChanged(i, i); +} + +void ThreadsHandler::setThreads(const QList<ThreadData> &threads) +{ + m_threads = threads; + if (m_currentIndex >= m_threads.size()) + m_currentIndex = m_threads.size() - 1; + reset(); +} + +QList<ThreadData> ThreadsHandler::threads() const +{ + return m_threads; +} + +void ThreadsHandler::removeAll() +{ + m_threads.clear(); + m_currentIndex = 0; + reset(); +} diff --git a/src/plugins/debugger/stackhandler.h b/src/plugins/debugger/stackhandler.h new file mode 100644 index 00000000000..81a4686f685 --- /dev/null +++ b/src/plugins/debugger/stackhandler.h @@ -0,0 +1,136 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_STACKHANDLER_H +#define DEBUGGER_STACKHANDLER_H + +#include <QtCore/QAbstractTableModel> +#include <QtCore/QObject> + +#include <QtGui/QIcon> + +namespace Debugger { +namespace Internal { + +//////////////////////////////////////////////////////////////////////// +// +// StackModel +// +//////////////////////////////////////////////////////////////////////// + +struct StackFrame +{ + int level; + QString function; + QString file; // we try to put an absolute file name in there + QString from; + QString to; + int line; + QString address; +}; + +/*! A model to represent the stack in a QTreeView. */ +class StackHandler : public QAbstractTableModel +{ +public: + StackHandler(QObject *parent = 0); + + void setFrames(const QList<StackFrame> &frames); + QList<StackFrame> frames() const; + void setCurrentIndex(int index); + int currentIndex() const { return m_currentIndex; } + StackFrame currentFrame() const; + int stackSize() const { return m_stackFrames.size(); } + + // Called from StackHandler after a new stack list has been received + void removeAll(); + QAbstractItemModel *stackModel() { return this; } + bool isDebuggingDumpers() const; + +private: + // QAbstractTableModel + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + QList<StackFrame> m_stackFrames; + int m_currentIndex; + QIcon m_positionIcon; + QIcon m_emptyIcon; +}; + + +//////////////////////////////////////////////////////////////////////// +// +// ThreadsHandler +// +//////////////////////////////////////////////////////////////////////// + +struct ThreadData +{ + int id; +}; + +/*! A model to represent the running threads in a QTreeView or ComboBox */ +class ThreadsHandler : public QAbstractTableModel +{ +public: + ThreadsHandler(QObject *parent = 0); + + void setCurrentThread(int index); + void selectThread(int index); + void setThreads(const QList<ThreadData> &threads); + void removeAll(); + QList<ThreadData> threads() const; + QAbstractItemModel *threadsModel() { return this; } + +private: + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + +private: + friend class StackHandler; + QList<ThreadData> m_threads; + int m_currentIndex; + QIcon m_positionIcon; + QIcon m_emptyIcon; +}; + + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_STACKHANDLER_H diff --git a/src/plugins/debugger/stackwindow.cpp b/src/plugins/debugger/stackwindow.cpp new file mode 100644 index 00000000000..3dc1209a00b --- /dev/null +++ b/src/plugins/debugger/stackwindow.cpp @@ -0,0 +1,117 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "stackwindow.h" + +#include "assert.h" +#include "stackhandler.h" + +#include <QAction> +#include <QComboBox> +#include <QDebug> +#include <QHeaderView> +#include <QMenu> +#include <QResizeEvent> +#include <QTreeView> +#include <QVBoxLayout> + +using Debugger::Internal::StackWindow; + +StackWindow::StackWindow(QWidget *parent) + : QTreeView(parent), m_alwaysResizeColumnsToContents(false) +{ + setWindowTitle(tr("Stack")); + + setAlternatingRowColors(true); + setRootIsDecorated(false); + setIconSize(QSize(10, 10)); + + header()->setDefaultAlignment(Qt::AlignLeft); + + connect(this, SIGNAL(activated(QModelIndex)), + this, SLOT(rowActivated(QModelIndex))); +} + +void StackWindow::resizeEvent(QResizeEvent *event) +{ + QHeaderView *hv = header(); + int totalSize = event->size().width() - 120; + if (totalSize > 10) { + hv->resizeSection(0, 45); + hv->resizeSection(1, totalSize / 2); + hv->resizeSection(2, totalSize / 2); + hv->resizeSection(3, 55); + } + QTreeView::resizeEvent(event); +} + +void StackWindow::rowActivated(const QModelIndex &index) +{ + //qDebug() << "ACTIVATED: " << index.row() << index.column(); + emit frameActivated(index.row()); +} + +void StackWindow::contextMenuEvent(QContextMenuEvent *ev) +{ + QMenu menu; + QAction *act1 = new QAction(tr("Adjust column widths to contents"), &menu); + QAction *act2 = new QAction(tr("Always adjust column widths to contents"), &menu); + act2->setCheckable(true); + act2->setChecked(m_alwaysResizeColumnsToContents); + + menu.addAction(act1); + menu.addAction(act2); + + QAction *act = menu.exec(ev->globalPos()); + + if (act == act1) + resizeColumnsToContents(); + else if (act == act2) + setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents); +} + +void StackWindow::resizeColumnsToContents() +{ + resizeColumnToContents(0); + resizeColumnToContents(1); + resizeColumnToContents(2); +} + +void StackWindow::setAlwaysResizeColumnsToContents(bool on) +{ + m_alwaysResizeColumnsToContents = on; + QHeaderView::ResizeMode mode = on + ? QHeaderView::ResizeToContents : QHeaderView::Interactive; + header()->setResizeMode(0, mode); + header()->setResizeMode(1, mode); + header()->setResizeMode(2, mode); +} diff --git a/src/plugins/debugger/stackwindow.h b/src/plugins/debugger/stackwindow.h new file mode 100644 index 00000000000..5edfeb3fb66 --- /dev/null +++ b/src/plugins/debugger/stackwindow.h @@ -0,0 +1,75 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_STACKWINDOW_H +#define DEBUGGER_STACKWINDOW_H + +#include <QtGui/QTreeView> +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE +class QComboBox; +class QModelIndex; +QT_END_NAMESPACE + +namespace Debugger { +namespace Internal { + +class StackWindow : public QTreeView +{ + Q_OBJECT + +public: + StackWindow(QWidget *parent = 0); + +signals: + void frameActivated(int); + +public slots: + void resizeColumnsToContents(); + void setAlwaysResizeColumnsToContents(bool on); + +private slots: + void rowActivated(const QModelIndex &index); + +private: + void resizeEvent(QResizeEvent *ev); + void contextMenuEvent(QContextMenuEvent *ev); + + bool m_alwaysResizeColumnsToContents; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_STACKWINDOW_H + diff --git a/src/plugins/debugger/startexternaldialog.cpp b/src/plugins/debugger/startexternaldialog.cpp new file mode 100644 index 00000000000..c87982ab1d6 --- /dev/null +++ b/src/plugins/debugger/startexternaldialog.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "startexternaldialog.h" + +#include <QtGui/QFileDialog> +#include <QtGui/QPushButton> + +using namespace Debugger::Internal; + +StartExternalDialog::StartExternalDialog(QWidget *parent) + : QDialog(parent) +{ + setupUi(this); + buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + + //execLabel->setHidden(false); + //execEdit->setHidden(false); + //browseButton->setHidden(false); + + execLabel->setText(tr("Executable:")); + argLabel->setText(tr("Arguments:")); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + connect(browseButton, SIGNAL(clicked()), + this, SLOT(onBrowseButton())); +} + +void StartExternalDialog::setExecutableFile(const QString &str) +{ + execEdit->setText(str); +} + +void StartExternalDialog::setExecutableArguments(const QString &str) +{ + argsEdit->setText(str); +} + +QString StartExternalDialog::executableFile() const +{ + return execEdit->text(); +} + +QString StartExternalDialog::executableArguments() const +{ + return argsEdit->text(); + /* + bool inQuotes = false; + QString args = argsEdit->text(); + QChar current; + QChar last; + QString arg; + + QStringList result; + if (!args.isEmpty()) + result << QLatin1String("--args"); + result << execEdit->text(); + + for(int i=0; i<args.length(); ++i) { + current = args.at(i); + + if (current == QLatin1Char('\"') && last != QLatin1Char('\\')) { + if (inQuotes && !arg.isEmpty()) { + result << arg; + arg.clear(); + } + inQuotes = !inQuotes; + } else if (!inQuotes && current == QLatin1Char(' ')) { + arg = arg.trimmed(); + if (!arg.isEmpty()) { + result << arg; + arg.clear(); + } + } else { + arg += current; + } + + last = current; + } + + if (!arg.isEmpty()) + result << arg; + + return result; + */ +} + +void StartExternalDialog::onBrowseButton() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Select Executable"), + execEdit->text()); + execEdit->setText(fileName); +} diff --git a/src/plugins/debugger/startexternaldialog.h b/src/plugins/debugger/startexternaldialog.h new file mode 100644 index 00000000000..10a43670dbc --- /dev/null +++ b/src/plugins/debugger/startexternaldialog.h @@ -0,0 +1,63 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_STARTEXTERNALDIALOG_H +#define DEBUGGER_STARTEXTERNALDIALOG_H + +#include <QtGui/QDialog> + +#include "ui_startexternaldialog.h" + +namespace Debugger { +namespace Internal { + +class StartExternalDialog : public QDialog, Ui::StartExternalDialog +{ + Q_OBJECT + +public: + StartExternalDialog(QWidget *parent); + + void setExecutableFile(const QString &executable); + void setExecutableArguments(const QString &args); + + QString executableFile() const; + QString executableArguments() const; + +private slots: + void onBrowseButton(); +}; + +} // namespace Debugger +} // namespace Internal + +#endif // DEBUGGER_STARTEXTERNALDIALOG_H diff --git a/src/plugins/debugger/startexternaldialog.ui b/src/plugins/debugger/startexternaldialog.ui new file mode 100644 index 00000000000..7888db2a3e6 --- /dev/null +++ b/src/plugins/debugger/startexternaldialog.ui @@ -0,0 +1,93 @@ +<ui version="4.0" > + <class>StartExternalDialog</class> + <widget class="QDialog" name="StartExternalDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>425</width> + <height>127</height> + </rect> + </property> + <property name="windowTitle" > + <string>Start Debugger</string> + </property> + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>6</number> + </property> + <property name="margin" > + <number>9</number> + </property> + <item> + <layout class="QGridLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="1" column="1" > + <widget class="QLineEdit" name="argsEdit" /> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="execEdit" /> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="execLabel" > + <property name="text" > + <string>Executable:</string> + </property> + </widget> + </item> + <item row="0" column="2" > + <widget class="QToolButton" name="browseButton" > + <property name="text" > + <string>...</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="argLabel" > + <property name="text" > + <string>Arguments:</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>407</width> + <height>16</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="Line" name="line" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <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> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/debugger/threadswindow.cpp b/src/plugins/debugger/threadswindow.cpp new file mode 100644 index 00000000000..beffda84cb2 --- /dev/null +++ b/src/plugins/debugger/threadswindow.cpp @@ -0,0 +1,112 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "threadswindow.h" + +#include "assert.h" +#include "stackhandler.h" + +#include <QAction> +#include <QComboBox> +#include <QDebug> +#include <QHeaderView> +#include <QMenu> +#include <QResizeEvent> +#include <QTreeView> +#include <QVBoxLayout> + +using Debugger::Internal::ThreadsWindow; + +ThreadsWindow::ThreadsWindow(QWidget *parent) + : QTreeView(parent), m_alwaysResizeColumnsToContents(false) +{ + setWindowTitle(tr("Thread")); + + setAlternatingRowColors(true); + setRootIsDecorated(false); + setIconSize(QSize(10, 10)); + + header()->setDefaultAlignment(Qt::AlignLeft); + + connect(this, SIGNAL(activated(const QModelIndex &)), + this, SLOT(rowActivated(const QModelIndex &))); +} + +void ThreadsWindow::resizeEvent(QResizeEvent *event) +{ + //QHeaderView *hv = header(); + //int totalSize = event->size().width() - 120; + //hv->resizeSection(0, 45); + //hv->resizeSection(1, totalSize); + //hv->resizeSection(2, 55); + QTreeView::resizeEvent(event); +} + +void ThreadsWindow::rowActivated(const QModelIndex &index) +{ + //qDebug() << "ACTIVATED: " << index.row() << index.column(); + emit threadSelected(index.row()); +} + +void ThreadsWindow::contextMenuEvent(QContextMenuEvent *ev) +{ + QMenu menu; + QAction *act1 = new QAction(tr("Adjust column widths to contents"), &menu); + QAction *act2 = new QAction(tr("Always adjust column widths to contents"), &menu); + act2->setCheckable(true); + act2->setChecked(m_alwaysResizeColumnsToContents); + menu.addAction(act1); + menu.addAction(act2); + + QAction *act = menu.exec(ev->globalPos()); + + if (act == act1) + resizeColumnsToContents(); + else if (act == act2) + setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents); +} + +void ThreadsWindow::resizeColumnsToContents() +{ + resizeColumnToContents(0); + //resizeColumnToContents(1); +} + +void ThreadsWindow::setAlwaysResizeColumnsToContents(bool on) +{ + m_alwaysResizeColumnsToContents = on; + QHeaderView::ResizeMode mode = on + ? QHeaderView::ResizeToContents : QHeaderView::Interactive; + header()->setResizeMode(0, mode); + //header()->setResizeMode(1, mode); +} + diff --git a/src/plugins/debugger/threadswindow.h b/src/plugins/debugger/threadswindow.h new file mode 100644 index 00000000000..05b94dc531c --- /dev/null +++ b/src/plugins/debugger/threadswindow.h @@ -0,0 +1,68 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_THREADWINDOW_H +#define DEBUGGER_THREADWINDOW_H + +#include <QtGui/QTreeView> + +namespace Debugger { +namespace Internal { + +class ThreadsWindow : public QTreeView +{ + Q_OBJECT + +public: + ThreadsWindow(QWidget *parent = 0); + +signals: + void threadSelected(int); + +public slots: + void resizeColumnsToContents(); + void setAlwaysResizeColumnsToContents(bool on); + +private slots: + void rowActivated(const QModelIndex &index); + +private: + void resizeEvent(QResizeEvent *ev); + void contextMenuEvent(QContextMenuEvent *ev); + + bool m_alwaysResizeColumnsToContents; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_THREADWINDOW_H diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp new file mode 100644 index 00000000000..9147aa308e2 --- /dev/null +++ b/src/plugins/debugger/watchhandler.cpp @@ -0,0 +1,1137 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "watchhandler.h" + +#if USE_MODEL_TEST +#include "modeltest.h" +#endif + +#include "assert.h" + +#include <QtCore/QDebug> +#include <QtCore/QEvent> + +#include <QtGui/QApplication> +#include <QtGui/QLabel> +#include <QtGui/QToolTip> +#include <QtGui/QTextEdit> + +#include <ctype.h> + +// creates debug output regarding pending watch data results +//#define DEBUG_PENDING 1 +// creates debug output for accesses to the itemmodel +//#define DEBUG_MODEL 1 + +#if DEBUG_MODEL +# define MODEL_DEBUG(s) qDebug() << s +#else +# define MODEL_DEBUG(s) +#endif +#define MODEL_DEBUGX(s) qDebug() << s + +using namespace Debugger::Internal; + +static const QString strNotInScope = QLatin1String("<not in scope>"); + +static bool isIntOrFloatType(const QString &type) +{ + static const QStringList types = QStringList() + << "char" << "int" << "short" << "float" << "double" << "long" + << "bool" << "signed char" << "unsigned" << "unsigned char" + << "unsigned int" << "unsigned long" << "long long"; + return types.contains(type); +} + +static bool isPointerType(const QString &type) +{ + return type.endsWith("*") || type.endsWith("* const"); +} + +static QString htmlQuote(const QString &str0) +{ + QString str = str0; + str.replace('&', "&"); + str.replace('<', "<"); + str.replace('>', ">"); + return str; +} + +//////////////////////////////////////////////////////////////////// +// +// WatchData +// +//////////////////////////////////////////////////////////////////// + +WatchData::WatchData() +{ + valuedisabled = false; + state = InitialState; + childCount = -1; + parentIndex = -1; + row = -1; + level = -1; + changed = false; +} + +void WatchData::setError(const QString &msg) +{ + setAllUnneeded(); + value = msg; + setChildCount(0); + valuedisabled = true; +} + +static QByteArray quoteUnprintable(const QByteArray &ba) +{ + QByteArray res; + char buf[10]; + for (int i = 0, n = ba.size(); i != n; ++i) { + char c = ba.at(i); + if (isprint(c)) { + res += c; + } else { + qsnprintf(buf, sizeof(buf) - 1, "\\%x", int(c)); + res += buf; + } + } + return res; +} + +void WatchData::setValue(const QByteArray &value0) +{ + value = quoteUnprintable(value0); + if (value == "{...}") { + value.clear(); + childCount = 1; // at least one... + } + + // avoid duplicated information + if (value.startsWith("(") && value.contains(") 0x")) + value = value.mid(value.lastIndexOf(") 0x") + 2); + + // doubles are sometimes displayed as "@0x6141378: 1.2". + // I don't want that. + if (/*isIntOrFloatType(type) && */ value.startsWith("@0x") + && value.contains(':')) { + value = value.mid(value.indexOf(':') + 2); + setChildCount(0); + } + + // "numchild" is sometimes lying + //MODEL_DEBUG("\n\n\nPOINTER: " << type << value); + if (isPointerType(type)) + setChildCount(value != "0x0" && value != "<null>"); + + // pointer type information is available in the 'type' + // column. No need to duplicate it here. + if (value.startsWith("(" + type + ") 0x")) + value = value.section(" ", -1, -1); + + setValueUnneeded(); +} + +void WatchData::setValueToolTip(const QString &tooltip) +{ + valuetooltip = tooltip; +} + +void WatchData::setType(const QString &str) +{ + type = str.trimmed(); + bool changed = true; + while (changed) { + if (type.endsWith("const")) + type.chop(5); + else if (type.endsWith(" ")) + type.chop(1); + else if (type.endsWith("&")) + type.chop(1); + else if (type.startsWith("const ")) + type = type.mid(6); + else if (type.startsWith("volatile ")) + type = type.mid(9); + else if (type.startsWith("class ")) + type = type.mid(6); + else if (type.startsWith("struct ")) + type = type.mid(6); + else if (type.startsWith(" ")) + type = type.mid(1); + else + changed = false; + } + setTypeUnneeded(); + if (isIntOrFloatType(type)) + setChildCount(0); +} + +void WatchData::setAddress(const QString & str) +{ + addr = str; +} + +QString WatchData::toString() const +{ + QString res = "{"; + + res += "level=\"" + QString::number(level) + "\","; + res += "parent=\"" + QString::number(parentIndex) + "\","; + res += "row=\"" + QString::number(row) + "\","; + res += "child=\""; + foreach (int index, childIndex) + res += QString::number(index) + ","; + if (res.endsWith(',')) + res[res.size() - 1] = '"'; + else + res += '"'; + res += ","; + + + if (!iname.isEmpty()) + res += "iname=\"" + iname + "\","; + if (!exp.isEmpty()) + res += "exp=\"" + exp + "\","; + + if (!variable.isEmpty()) + res += "variable=\"" + variable + "\","; + + if (isValueNeeded()) + res += "value=<needed>,"; + if (isValueKnown() && !value.isEmpty()) + res += "value=\"" + value + "\","; + + if (!editvalue.isEmpty()) + res += "editvalue=\"" + editvalue + "\","; + + if (isTypeNeeded()) + res += "type=<needed>,"; + if (isTypeKnown() && !type.isEmpty()) + res += "type=\"" + type + "\","; + + if (isChildCountNeeded()) + res += "numchild=<needed>,"; + if (isChildCountKnown() && childCount == -1) + res += "numchild=\"" + QString::number(childCount) + "\","; + + if (isChildrenNeeded()) + res += "children=<needed>,"; + + if (res.endsWith(',')) + res[res.size() - 1] = '}'; + else + res += '}'; + + return res; +} + + +static bool iNameSorter(const WatchData &d1, const WatchData &d2) +{ + if (d1.level != d2.level) + return d1.level < d2.level; + + for (int level = 0; level != d1.level; ++level) { + QString name1 = d1.iname.section('.', level, level); + QString name2 = d2.iname.section('.', level, level); + //MODEL_DEBUG(" SORT: " << name1 << name2 << (name1 < name2)); + + if (name1 != name2) { + // This formerly used inames. in this case 'lastIndexOf' probably + // makes more sense. + if (name1.startsWith('[') && name2.startsWith('[')) { + return name1.mid(1, name1.indexOf(']') - 1).toInt() + < name2.mid(1, name2.indexOf(']') - 1).toInt(); + // numbers should be sorted according to their numerical value + //int pos = d1.name.lastIndexOf('.'); + //if (pos != -1 && pos + 1 != d1.name.size() && d1.name.at(pos + 1).isDigit()) + // return d1.name.size() < d2.name.size(); + // fall through + } + return name1 < name2; + } + } + return false; +} + +static QString parentName(const QString &iname) +{ + int pos = iname.lastIndexOf("."); + if (pos == -1) + return QString(); + return iname.left(pos); +} + + +static void insertDataHelper(QList<WatchData> &list, const WatchData &data) +{ + // FIXME: Quadratic algorithm + for (int i = list.size(); --i >= 0; ) { + if (list.at(i).iname == data.iname) { + list[i] = data; + return; + } + } + list.append(data); +} + +static WatchData take(const QString &iname, QList<WatchData> *list) +{ + for (int i = list->size(); --i >= 0;) { + if (list->at(i).iname == iname) { + WatchData res = list->at(i); + (*list)[i] = list->back(); + (void) list->takeLast(); + return res; + //return list->takeAt(i); + } + } + return WatchData(); +} + + +/////////////////////////////////////////////////////////////////////// +// +// WatchHandler +// +/////////////////////////////////////////////////////////////////////// + +WatchHandler::WatchHandler() +{ + m_expandPointers = true; + m_inFetchMore = false; + m_inChange = false; + + cleanModel(); + m_displaySet = m_completeSet; +} + +bool WatchHandler::setData(const QModelIndex &idx, + const QVariant &value, int role) +{ +/* + Q_UNUSED(idx); + Q_UNUSED(value); + Q_UNUSED(role); + if (role == VisualRole) { + QString iname = inameFromIndex(index); + setDisplayedIName(iname, value.toBool()); + return true; + } + return true; +*/ + return QAbstractItemModel::setData(idx, value, role); +} + +static QString niceType(QString type) +{ + if (type.contains("std::")) { + static QRegExp re("std::vector<(.*)\\s*,std::allocator<(.*)>\\s*>"); + re.setMinimal(true); + + type.replace("std::basic_string<char, std::char_traits<char>, " + "std::allocator<char> >", "std::string"); + type.replace("std::basic_string<wchar_t, std::char_traits<wchar_t>, " + "std::allocator<wchar_t> >", "std::wstring"); + + for (int i = 0; i != 10; ++i) { + if (re.indexIn(type) == -1 || re.cap(1) != re.cap(2)) + break; + type.replace(re.cap(0), "std::vector<" + re.cap(1) + ">"); + } + + type.replace(" >", ">"); + } + return type; +} + +QVariant WatchHandler::data(const QModelIndex &idx, int role) const +{ + int node = idx.internalId(); + if (node < 0) + return QVariant(); + + const WatchData &data = m_displaySet.at(node); + + switch (role) { + case Qt::DisplayRole: { + switch (idx.column()) { + case 0: return data.name; + case 1: return data.value; + case 2: return niceType(data.type); + default: break; + } + break; + } + + case Qt::ToolTipRole: { + QString val = data.value; + if (val.size() > 1000) + val = val.left(1000) + " ... <cut off>"; + + QString tt = "<table>"; + //tt += "<tr><td>internal name</td><td> : </td><td>"; + //tt += htmlQuote(iname) + "</td></tr>"; + tt += "<tr><td>expression</td><td> : </td><td>"; + tt += htmlQuote(data.exp) + "</td></tr>"; + tt += "<tr><td>type</td><td> : </td><td>"; + tt += htmlQuote(data.type) + "</td></tr>"; + //if (!valuetooltip.isEmpty()) + // tt += valuetooltip; + //else + tt += "<tr><td>value</td><td> : </td><td>"; + tt += htmlQuote(data.value) + "</td></tr>"; + tt += "<tr><td>addr</td><td> : </td><td>"; + tt += htmlQuote(data.addr) + "</td></tr>"; + tt += "<tr><td>iname</td><td> : </td><td>"; + tt += htmlQuote(data.iname) + "</td></tr>"; + tt += "</table>"; + tt.replace("@value@", htmlQuote(data.value)); + + if (tt.size() > 10000) + tt = tt.left(10000) + " ... <cut off>"; + return tt; + } + + case Qt::ForegroundRole: { + static const QVariant red(QColor(200, 0, 0)); + static const QVariant black(QColor(0, 0, 0)); + static const QVariant gray(QColor(140, 140, 140)); + switch (idx.column()) { + case 0: return black; + case 1: return data.valuedisabled ? gray : data.changed ? red : black; + case 2: return black; + } + break; + } + + case INameRole: + return data.iname; + + case VisualRole: + return m_displayedINames.contains(data.iname); + + default: + break; + } + return QVariant(); +} + +Qt::ItemFlags WatchHandler::flags(const QModelIndex &idx) const +{ + using namespace Qt; + + if (!idx.isValid()) + return ItemFlags(); + + int node = idx.internalId(); + if (node < 0) + return ItemFlags(); + + // enabled, editable, selectable, checkable, and can be used both as the + // source of a drag and drop operation and as a drop target. + + static const ItemFlags DefaultNotEditable = + ItemIsSelectable + | ItemIsDragEnabled + | ItemIsDropEnabled + // | ItemIsUserCheckable + // | ItemIsTristate + | ItemIsEnabled; + + static const ItemFlags DefaultEditable = + DefaultNotEditable | ItemIsEditable; + + const WatchData &data = m_displaySet.at(node); + return idx.column() == 1 && + data.isWatcher() ? DefaultEditable : DefaultNotEditable; +} + +QVariant WatchHandler::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (orientation == Qt::Vertical) + return QVariant(); + if (role == Qt::DisplayRole) { + switch (section) { + case 0: return tr("Name") + " "; + case 1: return tr("Value") + " "; + case 2: return tr("Type") + " "; + } + } + return QVariant(); +} + +QString WatchHandler::toString() const +{ + QString res; + res += "\nIncomplete:\n"; + for (int i = 0, n = m_incompleteSet.size(); i != n; ++i) { + res += QString("%1: ").arg(i); + res += m_incompleteSet.at(i).toString(); + res += '\n'; + } + res += "\nComplete:\n"; + for (int i = 0, n = m_completeSet.size(); i != n; ++i) { + res += QString("%1: ").arg(i); + res += m_completeSet.at(i).toString(); + res += '\n'; + } + res += "\nDisplay:\n"; + for (int i = 0, n = m_displaySet.size(); i != n; ++i) { + res += QString("%1: ").arg(i); + res += m_displaySet.at(i).toString(); + res += '\n'; + } +#if 0 + res += "\nOld:\n"; + for (int i = 0, n = m_oldSet.size(); i != n; ++i) { + res += m_oldSet.at(i).toString(); + res += '\n'; + } +#endif + return res; +} + +WatchData *WatchHandler::findData(const QString &iname) +{ + for (int i = m_completeSet.size(); --i >= 0; ) + if (m_completeSet.at(i).iname == iname) + return &m_completeSet[i]; + return 0; +} + +WatchData WatchHandler::takeData(const QString &iname) +{ + WatchData data = take(iname, &m_incompleteSet); + if (data.isValid()) + return data; + return take(iname, &m_completeSet); +} + +QList<WatchData> WatchHandler::takeCurrentIncompletes() +{ + QList<WatchData> res = m_incompleteSet; + //MODEL_DEBUG("TAKING INCOMPLETES" << toString()); + m_incompleteSet.clear(); + return res; +} + +void WatchHandler::rebuildModel() +{ + if (m_inChange) { + MODEL_DEBUG("RECREATE MODEL IGNORED, CURRENT SET:\n" << toString()); + return; + } + + #ifdef DEBUG_PENDING + MODEL_DEBUG("RECREATE MODEL, CURRENT SET:\n" << toString()); + #endif + + QHash<QString, QString> oldValues; + for (int i = 0, n = m_oldSet.size(); i != n; ++i) { + WatchData &data = m_oldSet[i]; + oldValues[data.iname] = data.value; + } + #ifdef DEBUG_PENDING + MODEL_DEBUG("OLD VALUES: " << oldValues); + #endif + + for (int i = m_completeSet.size(); --i >= 0; ) { + WatchData &data = m_completeSet[i]; + data.level = data.iname.isEmpty() ? 0 : data.iname.count('.') + 1; + data.childIndex.clear(); + } + + qSort(m_completeSet.begin(), m_completeSet.end(), &iNameSorter); + + QHash<QString, int> iname2idx; + + for (int i = m_completeSet.size(); --i > 0; ) { + WatchData &data = m_completeSet[i]; + data.parentIndex = 0; + data.childIndex.clear(); + iname2idx[data.iname] = i; + } + + for (int i = 1; i < m_completeSet.size(); ++i) { + WatchData &data = m_completeSet[i]; + QString parentIName = parentName(data.iname); + data.parentIndex = iname2idx.value(parentIName, 0); + WatchData &parent = m_completeSet[data.parentIndex]; + data.row = parent.childIndex.size(); + parent.childIndex.append(i); + } + + m_oldSet = m_completeSet; + m_oldSet += m_incompleteSet; + + for (int i = 0, n = m_completeSet.size(); i != n; ++i) { + WatchData &data = m_completeSet[i]; + data.changed = !data.value.isEmpty() + && data.value != oldValues[data.iname] + && data.value != strNotInScope; + } + + //emit layoutAboutToBeChanged(); + + m_displaySet = m_completeSet; + + #ifdef DEBUG_PENDING + MODEL_DEBUG("SET " << toString()); + #endif + +#if 1 + // Append dummy item to get the [+] effect + for (int i = 0, n = m_displaySet.size(); i != n; ++i) { + WatchData &data = m_displaySet[i]; + if (data.childCount > 0 && data.childIndex.size() == 0) { + WatchData dummy; + dummy.state = 0; + dummy.row = 0; + dummy.iname = data.iname + ".dummy"; + //dummy.name = data.iname + ".dummy"; + //dummy.name = "<loading>"; + dummy.level = data.level + 1; + dummy.parentIndex = i; + dummy.childCount = 0; + data.childIndex.append(m_displaySet.size()); + m_displaySet.append(dummy); + } + } +#endif + + // Possibly append dummy items to prevent empty views + bool ok = true; + QWB_ASSERT(m_displaySet.size() >= 2, ok = false); + QWB_ASSERT(m_displaySet.at(1).iname == "local", ok = false); + QWB_ASSERT(m_displaySet.at(2).iname == "tooltip", ok = false); + QWB_ASSERT(m_displaySet.at(3).iname == "watch", ok = false); + if (ok) { + for (int i = 1; i <= 3; ++i) { + WatchData &data = m_displaySet[i]; + if (data.childIndex.size() == 0) { + WatchData dummy; + dummy.state = 0; + dummy.row = 0; + if (i == 1) { + dummy.iname = "local.dummy"; + dummy.name = "<No Locals>"; + } else if (i == 2) { + dummy.iname = "tooltip.dummy"; + dummy.name = "<No Tooltip>"; + } else { + dummy.iname = "watch.dummy"; + dummy.name = "<No Watchers>"; + } + dummy.level = 2; + dummy.parentIndex = i; + dummy.childCount = 0; + data.childIndex.append(m_displaySet.size()); + m_displaySet.append(dummy); + } + } + } + + m_inChange = true; + //qDebug() << "WATCHHANDLER: RESET ABOUT TO EMIT"; + emit reset(); + //qDebug() << "WATCHHANDLER: RESET EMITTED"; + m_inChange = false; + //emit layoutChanged(); + //QSet<QString> einames = m_expandedINames; + //einames.insert("local"); + //einames.insert("watch"); + //emit expandedItems(einames); + + #if DEBUG_MODEL + #if USE_MODEL_TEST + //(void) new ModelTest(this, this); + #endif + #endif + + #ifdef DEBUG_PENDING + MODEL_DEBUG("SORTED: " << toString()); + MODEL_DEBUG("EXPANDED INAMES: " << m_expandedINames); + #endif +} + +void WatchHandler::cleanup() +{ + m_oldSet.clear(); + m_expandedINames.clear(); + m_displayedINames.clear(); + cleanModel(); + m_displaySet = m_completeSet; +#if 0 + for (EditWindows::ConstIterator it = m_editWindows.begin(); + it != m_editWindows.end(); ++it) { + if (!it.value().isNull()) + delete it.value(); + } + m_editWindows.clear(); +#endif + emit reset(); +} + +void WatchHandler::collapseChildren(const QModelIndex &idx) +{ + if (m_inChange || m_completeSet.isEmpty()) { + //qDebug() << "WATCHHANDLER: COLLAPSE IGNORED" << idx; + return; + } + QWB_ASSERT(checkIndex(idx.internalId()), return); +#if 0 + QString iname0 = m_displaySet.at(idx.internalId()).iname; + MODEL_DEBUG("COLLAPSE NODE" << iname0); + QString iname1 = iname0 + '.'; + for (int i = m_completeSet.size(); --i >= 0; ) { + QString iname = m_completeSet.at(i).iname; + if (iname.startsWith(iname1)) { + // Better leave it in in case the user re-enters the branch? + (void) m_completeSet.takeAt(i); + MODEL_DEBUG(" REMOVING " << iname); + m_expandedINames.remove(iname); + } + } + m_expandedINames.remove(iname0); + //MODEL_DEBUG(toString()); + //rebuildModel(); +#endif +} + +void WatchHandler::expandChildren(const QModelIndex &idx) +{ + if (m_inChange || m_completeSet.isEmpty()) { + //qDebug() << "WATCHHANDLER: EXPAND IGNORED" << idx; + return; + } + int index = idx.internalId(); + if (index == 0) + return; + QWB_ASSERT(index >= 0, qDebug() << toString() << index; return); + QWB_ASSERT(index < m_completeSet.size(), qDebug() << toString() << index; return); + const WatchData &display = m_displaySet.at(index); + QWB_ASSERT(index >= 0, qDebug() << toString() << index; return); + QWB_ASSERT(index < m_completeSet.size(), qDebug() << toString() << index; return); + const WatchData &complete = m_completeSet.at(index); + MODEL_DEBUG("\n\nEXPAND" << display.iname); + if (display.iname.isEmpty()) { + // This should not happen but the view seems to send spurious + // "expand()" signals folr the root item from time to time. + // Try to handle that gracfully. + //MODEL_DEBUG(toString()); + qDebug() << "FIXME: expandChildren, no data " << display.iname << "found" + << idx; + //rebuildModel(); + return; + } + + //qDebug() << " ... NODE: " << display.toString() + // << complete.childIndex.size() << complete.childCount; + + if (m_expandedINames.contains(display.iname)) + return; + + // This is a performance hack and not strictly necessary. + // Remove it if there are troubles when expanding nodes. + if (0 && complete.childCount > 0 && complete.childIndex.size() > 0) { + MODEL_DEBUG("SKIP FETCHING CHILDREN"); + return; + } + + WatchData data = takeData(display.iname); // remove previous data + m_expandedINames.insert(data.iname); + if (data.iname.contains('.')) // not for top-level items + data.setChildrenNeeded(); + insertData(data); + emit watchModelUpdateRequested(); +} + +void WatchHandler::insertData(const WatchData &data) +{ + //MODEL_DEBUG("INSERTDATA: " << data.toString()); + QWB_ASSERT(data.isValid(), return); + if (data.isSomethingNeeded()) + insertDataHelper(m_incompleteSet, data); + else + insertDataHelper(m_completeSet, data); + //MODEL_DEBUG("INSERT RESULT" << toString()); +} + +void WatchHandler::watchExpression(const QString &exp) +{ + // FIXME: 'exp' can contain illegal characters + //MODEL_DEBUG("WATCH: " << exp); + WatchData data; + data.exp = exp; + data.name = exp; + data.iname = "watch." + exp; + insertData(data); +} + + +void WatchHandler::setDisplayedIName(const QString &iname, bool on) +{ + WatchData *d = findData(iname); + if (!on || !d) { + delete m_editWindows.take(iname); + m_displayedINames.remove(iname); + return; + } + if (d->exp.isEmpty()) { + //emit statusMessageRequested(tr("Sorry. Cannot visualize objects without known address."), 5000); + return; + } + d->setValueNeeded(); + m_displayedINames.insert(iname); + insertData(*d); +} + +void WatchHandler::showEditValue(const WatchData &data) +{ + // editvalue is always base64 encoded + QByteArray ba = QByteArray::fromBase64(data.editvalue); + //QByteArray ba = data.editvalue; + QWidget *w = m_editWindows.value(data.iname); + qDebug() << "SHOW_EDIT_VALUE " << data.toString() << data.type + << data.iname << w; + if (data.type == "QImage") { + if (!w) { + w = new QLabel; + m_editWindows[data.iname] = w; + } + QDataStream ds(&ba, QIODevice::ReadOnly); + QVariant v; + ds >> v; + QString type = QString::fromAscii(v.typeName()); + QImage im = v.value<QImage>(); + if (QLabel *l = qobject_cast<QLabel *>(w)) + l->setPixmap(QPixmap::fromImage(im)); + } else if (data.type == "QPixmap") { + if (!w) { + w = new QLabel; + m_editWindows[data.iname] = w; + } + QDataStream ds(&ba, QIODevice::ReadOnly); + QVariant v; + ds >> v; + QString type = QString::fromAscii(v.typeName()); + QPixmap im = v.value<QPixmap>(); + if (QLabel *l = qobject_cast<QLabel *>(w)) + l->setPixmap(im); + } else if (data.type == "QString") { + if (!w) { + w = new QTextEdit; + m_editWindows[data.iname] = w; + } +#if 0 + QDataStream ds(&ba, QIODevice::ReadOnly); + QVariant v; + ds >> v; + QString type = QString::fromAscii(v.typeName()); + QString str = v.value<QString>(); +#else + MODEL_DEBUG("DATA: " << ba); + QString str = QString::fromUtf16((ushort *)ba.constData(), ba.size()/2); +#endif + if (QTextEdit *t = qobject_cast<QTextEdit *>(w)) + t->setText(str); + } + if (w) + w->show(); +} + +void WatchHandler::removeWatchExpression(const QString &iname) +{ + MODEL_DEBUG("REMOVE WATCH: " << iname); + (void) takeData(iname); + emit watchModelUpdateRequested(); +} + +void WatchHandler::cleanModel() +{ + // This uses data stored in m_oldSet to re-create a new set + // one-by-one + m_completeSet.clear(); + m_incompleteSet.clear(); + + WatchData root; + root.state = 0; + root.level = 0; + root.row = 0; + root.name = "Root"; + root.parentIndex = -1; + root.childIndex.append(1); + root.childIndex.append(2); + root.childIndex.append(3); + m_completeSet.append(root); + + WatchData local; + local.iname = "local"; + local.name = "Locals"; + local.state = 0; + local.level = 1; + local.row = 0; + local.parentIndex = 0; + m_completeSet.append(local); + + WatchData tooltip; + tooltip.iname = "tooltip"; + tooltip.name = "Tooltip"; + tooltip.state = 0; + tooltip.level = 1; + tooltip.row = 1; + tooltip.parentIndex = 0; + m_completeSet.append(tooltip); + + WatchData watch; + watch.iname = "watch"; + watch.name = "Watchers"; + watch.state = 0; + watch.level = 1; + watch.row = 2; + watch.parentIndex = 0; + m_completeSet.append(watch); +} + + +void WatchHandler::reinitializeWatchers() +{ + cleanModel(); + + // copy over all watchers and mark all watchers as incomplete + for (int i = 0, n = m_oldSet.size(); i < n; ++i) { + WatchData data = m_oldSet.at(i); + if (data.isWatcher()) { + data.level = -1; + data.row = -1; + data.parentIndex = -1; + data.variable.clear(); + data.setAllNeeded(); + data.valuedisabled = false; + insertData(data); // properly handles "neededChildren" + } + } +} + +bool WatchHandler::canFetchMore(const QModelIndex &parent) const +{ + MODEL_DEBUG("CAN FETCH MORE: " << parent << "false"); +#if 1 + Q_UNUSED(parent); + return false; +#else + // FIXME: not robust enough. Problem is that fetchMore + // needs to be made synchronous to be useful. Busy loop is no good. + if (!parent.isValid()) + return false; + QWB_ASSERT(checkIndex(parent.internalId()), return false); + const WatchData &data = m_displaySet.at(parent.internalId()); + MODEL_DEBUG("CAN FETCH MORE: " << parent << " children: " << data.childCount + << data.iname); + return data.childCount > 0; +#endif +} + +void WatchHandler::fetchMore(const QModelIndex &parent) +{ + MODEL_DEBUG("FETCH MORE: " << parent); + return; + + QWB_ASSERT(checkIndex(parent.internalId()), return); + QString iname = m_displaySet.at(parent.internalId()).iname; + + if (m_inFetchMore) { + MODEL_DEBUG("LOOP IN FETCH MORE" << iname); + return; + } + m_inFetchMore = true; + + WatchData data = takeData(iname); + MODEL_DEBUG("FETCH MORE: " << parent << ":" << iname << data.name); + + if (!data.isValid()) { + MODEL_DEBUG("FIXME: FETCH MORE, no data " << iname << "found"); + return; + } + + m_expandedINames.insert(data.iname); + if (data.iname.contains('.')) // not for top-level items + data.setChildrenNeeded(); + + MODEL_DEBUG("FETCH MORE: data:" << data.toString()); + insertData(data); + //emit watchUpdateRequested(); + + while (m_inFetchMore) { + QApplication::processEvents(); + } + m_inFetchMore = false; + MODEL_DEBUG("BUSY LOOP FINISHED, data:" << data.toString()); +} + +QModelIndex WatchHandler::index(int row, int col, const QModelIndex &parent) const +{ + #ifdef DEBUG_MODEL + MODEL_DEBUG("INDEX " << row << col << parent); + #endif + //if (col != 0) { + // MODEL_DEBUG(" -> " << QModelIndex() << " (3) "); + // return QModelIndex(); + //} + if (row < 0) { + MODEL_DEBUG(" -> " << QModelIndex() << " (4) "); + return QModelIndex(); + } + if (!parent.isValid()) { + if (row == 0 && col >= 0 && col < 3 && parent.row() == -1) { + MODEL_DEBUG(" -> " << createIndex(0, 0, 0) << " (B) "); + return createIndex(0, col, 0); + } + MODEL_DEBUG(" -> " << QModelIndex() << " (1) "); + return QModelIndex(); + } + int parentIndex = parent.internalId(); + if (parentIndex < 0) { + //MODEL_DEBUG("INDEX " << row << col << parentIndex << "INVALID"); + MODEL_DEBUG(" -> " << QModelIndex() << " (2) "); + return QModelIndex(); + } + QWB_ASSERT(checkIndex(parentIndex), return QModelIndex()); + const WatchData &data = m_displaySet.at(parentIndex); + QWB_ASSERT(row >= 0, qDebug() << "ROW: " << row << "PARENT: " << parent + << data.toString() << toString(); return QModelIndex()); + QWB_ASSERT(row < data.childIndex.size(), + MODEL_DEBUG("ROW: " << row << data.toString() << toString()); + return QModelIndex()); + QModelIndex idx = createIndex(row, col, data.childIndex.at(row)); + QWB_ASSERT(idx.row() == m_displaySet.at(idx.internalId()).row, + return QModelIndex()); + MODEL_DEBUG(" -> " << idx << " (A) "); + return idx; +} + +QModelIndex WatchHandler::parent(const QModelIndex &idx) const +{ + if (!idx.isValid()) { + MODEL_DEBUG(" -> " << QModelIndex() << " (1) "); + return QModelIndex(); + } + MODEL_DEBUG("PARENT " << idx); + int currentIndex = idx.internalId(); + QWB_ASSERT(checkIndex(currentIndex), return QModelIndex()); + QWB_ASSERT(idx.row() == m_displaySet.at(currentIndex).row, + MODEL_DEBUG("IDX: " << idx << toString(); return QModelIndex())); + int parentIndex = m_displaySet.at(currentIndex).parentIndex; + if (parentIndex < 0) { + MODEL_DEBUG(" -> " << QModelIndex() << " (2) "); + return QModelIndex(); + } + QWB_ASSERT(checkIndex(parentIndex), return QModelIndex()); + QModelIndex parent = + createIndex(m_displaySet.at(parentIndex).row, 0, parentIndex); + MODEL_DEBUG(" -> " << parent); + return parent; +} + +int WatchHandler::rowCount(const QModelIndex &idx) const +{ + MODEL_DEBUG("ROW COUNT " << idx); + if (idx.column() > 0) { + MODEL_DEBUG(" -> " << 0 << " (A) "); + return 0; + } + int thisIndex = idx.internalId(); + QWB_ASSERT(checkIndex(thisIndex), return 0); + if (idx.row() == -1 && idx.column() == -1) { + MODEL_DEBUG(" -> " << 3 << " (B) "); + return 1; + } + if (thisIndex < 0) { + MODEL_DEBUG(" -> " << 0 << " (C) "); + return 0; + } + if (thisIndex == 0) { + MODEL_DEBUG(" -> " << 3 << " (D) "); + return 3; + } + const WatchData &data = m_displaySet.at(thisIndex); + int rows = data.childIndex.size(); + MODEL_DEBUG(" -> " << rows << " (E) "); + return rows; + // Try lazy evaluation + //if (rows > 0) + // return rows; + //return data.childCount; +} + +int WatchHandler::columnCount(const QModelIndex &idx) const +{ + MODEL_DEBUG("COLUMN COUNT " << idx); + if (idx == QModelIndex()) { + MODEL_DEBUG(" -> " << 3 << " (C) "); + return 3; + } + if (idx.column() != 0) { + MODEL_DEBUG(" -> " << 0 << " (A) "); + return 0; + } + MODEL_DEBUG(" -> " << 3 << " (B) "); + QWB_ASSERT(checkIndex(idx.internalId()), return 3); + return 3; +} + +bool WatchHandler::hasChildren(const QModelIndex &idx) const +{ + // that's the base implementation: + bool base = rowCount(idx) > 0 && columnCount(idx) > 0; + MODEL_DEBUG("HAS CHILDREN: " << idx << base); + return base; + QWB_ASSERT(checkIndex(idx.internalId()), return false); + const WatchData &data = m_displaySet.at(idx.internalId()); + MODEL_DEBUG("HAS CHILDREN: " << idx << data.toString()); + return data.childCount > 0; // || data.childIndex.size() > 0; +} + +bool WatchHandler::checkIndex(int id) const +{ + if (id < 0) { + MODEL_DEBUG("CHECK INDEX FAILED" << id); + return false; + } + if (id >= m_displaySet.size()) { + MODEL_DEBUG("CHECK INDEX FAILED" << id << toString()); + return false; + } + return true; +} diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h new file mode 100644 index 00000000000..b3db114be8b --- /dev/null +++ b/src/plugins/debugger/watchhandler.h @@ -0,0 +1,219 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_WATCHHANDLER_H +#define DEBUGGER_WATCHHANDLER_H + +#include <QtCore/QPointer> +#include <QtCore/QObject> +#include <QtCore/QHash> +#include <QtCore/QSet> +#include <QtGui/QStandardItem> +#include <QtGui/QStandardItemModel> +#include <QtGui/QTreeView> +#include <QtScript/QScriptValue> + +namespace Debugger { +namespace Internal { + +class WatchData +{ +public: + WatchData(); + + enum State + { + Complete = 0, + ChildCountNeeded = 1, + ValueNeeded = 2, + TypeNeeded = 4, + ChildrenNeeded = 8, + + NeededMask = ValueNeeded + | TypeNeeded + | ChildrenNeeded + | ChildCountNeeded, + + InitialState = ValueNeeded + | TypeNeeded + | ChildrenNeeded + | ChildCountNeeded + }; + + void setValue(const QByteArray &); + void setType(const QString &); + void setValueToolTip(const QString &); + void setError(const QString &); + void setAddress(const QString &address); + + bool isSomethingNeeded() const { return state & NeededMask; } + void setAllNeeded() { state = NeededMask; } + void setAllUnneeded() { state = State(0); } + + bool isTypeNeeded() const { return state & TypeNeeded; } + bool isTypeKnown() const { return !(state & TypeNeeded); } + void setTypeNeeded() { state = State(state | TypeNeeded); } + void setTypeUnneeded() { state = State(state & ~TypeNeeded); } + + bool isValueNeeded() const { return state & ValueNeeded; } + bool isValueKnown() const { return !(state & ValueNeeded); } + void setValueNeeded() { state = State(state | ValueNeeded); } + void setValueUnneeded() { state = State(state & ~ValueNeeded); } + + bool isChildrenNeeded() const { return state & ChildrenNeeded; } + bool isChildrenKnown() const { return !(state & ChildrenNeeded); } + void setChildrenNeeded() { state = State(state | ChildrenNeeded); } + void setChildrenUnneeded() { state = State(state & ~ChildrenNeeded); } + + bool isChildCountNeeded() const { return state & ChildCountNeeded; } + bool isChildCountKnown() const { return !(state & ChildCountNeeded); } + void setChildCountNeeded() { state = State(state | ChildCountNeeded); } + void setChildCountUnneeded() { state = State(state & ~ChildCountNeeded); } + void setChildCount(int n) { childCount = n; setChildCountUnneeded(); + if (n == 0) setChildrenUnneeded(); } + + QString toString() const; + bool isLocal() const { return iname.startsWith(QLatin1String("local.")); } + bool isWatcher() const { return iname.startsWith(QLatin1String("watch.")); }; + bool isValid() const { return !iname.isEmpty(); } + +public: + QString iname; // internal name sth like 'local.baz.public.a' + QString exp; // the expression + QString name; // displayed name + QString value; // displayed value + QByteArray editvalue; // displayed value + QString valuetooltip; // tooltip in value column + QString type; // displayed type + QString variable; // name of internal Gdb variable if created + QString addr; // displayed adress + QString framekey; // key for type cache + QScriptValue scriptValue; // if needed... + int childCount; + bool valuedisabled; // value will be greyed out + +private: + +public: + int state; + + // Model + int parentIndex; + int row; + int level; + QList<int> childIndex; + bool changed; +}; + +enum { INameRole = Qt::UserRole, VisualRole }; + + +class WatchHandler : public QAbstractItemModel +{ + Q_OBJECT + +public: + WatchHandler(); + QAbstractItemModel *model() { return this; } + + // + // QAbstractItemModel + // + bool setData(const QModelIndex &index, const QVariant &value, int role); + QVariant data(const QModelIndex &index, int role) const; + QModelIndex index(int, int, const QModelIndex &idx) const; + QModelIndex parent(const QModelIndex &idx) const; + int rowCount(const QModelIndex &idx) const; + int columnCount(const QModelIndex &idx) const; + bool hasChildren(const QModelIndex &idx) const; + Qt::ItemFlags flags(const QModelIndex &idx) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + bool checkIndex(int id) const; + +//public slots: + void cleanup(); + void watchExpression(const QString &exp); + void removeWatchExpression(const QString &exp); + void reinitializeWatchers(); + + void collapseChildren(const QModelIndex &idx); + void expandChildren(const QModelIndex &idx); + + void rebuildModel(); // unconditionally version of above + void showEditValue(const WatchData &data); + + bool isDisplayedIName(const QString &iname) const + { return m_displayedINames.contains(iname); } + bool isExpandedIName(const QString &iname) const + { return m_expandedINames.contains(iname); } + + void insertData(const WatchData &data); + QList<WatchData> takeCurrentIncompletes(); + + bool canFetchMore(const QModelIndex &parent) const; + void fetchMore(const QModelIndex &parent); + + WatchData *findData(const QString &iname); + +signals: + void watchModelUpdateRequested(); + +private: + WatchData takeData(const QString &iname); + QString toString() const; + void cleanModel(); + + bool m_expandPointers; + bool m_inChange; + + typedef QMap<QString, QPointer<QWidget> > EditWindows; + EditWindows m_editWindows; + + QList<WatchData> m_incompleteSet; + QList<WatchData> m_completeSet; + QList<WatchData> m_oldSet; + QList<WatchData> m_displaySet; + + void setDisplayedIName(const QString &iname, bool on); + QSet<QString> m_expandedINames; // those expanded in the treeview + QSet<QString> m_displayedINames; // those with "external" viewers + + bool m_inFetchMore; +}; + +} // namespace Internal +} // namespace Debugger + +Q_DECLARE_METATYPE(Debugger::Internal::WatchData); + +#endif // DEBUGGER_WATCHHANDLER_H diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp new file mode 100644 index 00000000000..6f153d2de5f --- /dev/null +++ b/src/plugins/debugger/watchwindow.cpp @@ -0,0 +1,253 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "watchwindow.h" + +#include <QtCore/QDebug> +#include <QtCore/QTimer> + +#include <QtGui/QAction> +#include <QtGui/QContextMenuEvent> +#include <QtGui/QHeaderView> +#include <QtGui/QLineEdit> +#include <QtGui/QMenu> +#include <QtGui/QResizeEvent> +#include <QtGui/QSplitter> + +using namespace Debugger::Internal; + +enum { INameRole = Qt::UserRole, VisualRole }; + +///////////////////////////////////////////////////////////////////// +// +// WatchWindow +// +///////////////////////////////////////////////////////////////////// + +WatchWindow::WatchWindow(Type type, QWidget *parent) + : QTreeView(parent), m_type(type) +{ + m_blocked = false; + setWindowTitle(tr("Locals and Watchers")); + setAlternatingRowColors(true); + setIndentation(indentation() * 9/10); + setUniformRowHeights(true); + + connect(itemDelegate(), SIGNAL(commitData(QWidget *)), + this, SLOT(handleChangedItem(QWidget *))); + connect(this, SIGNAL(expanded(QModelIndex)), + this, SLOT(expandNode(QModelIndex))); + connect(this, SIGNAL(collapsed(QModelIndex)), + this, SLOT(collapseNode(QModelIndex))); +} + +void WatchWindow::expandNode(const QModelIndex &idx) +{ + //QModelIndex mi0 = idx.sibling(idx.row(), 0); + //QString iname = model()->data(mi0, INameRole).toString(); + //QString name = model()->data(mi0, Qt::DisplayRole).toString(); + //qDebug() << "\n\nEXPAND NODE " // << iname << name + // << idx << (m_blocked ? "blocked" : "passed"); + //if (isExpanded(idx)) + // return; + //if (m_blocked) + // return; + emit requestExpandChildren(idx); +} + +void WatchWindow::collapseNode(const QModelIndex &idx) +{ + //QModelIndex mi0 = idx.sibling(idx.row(), 0); + //QString iname = model()->data(mi0, INameRole).toString(); + //QString name = model()->data(mi0, Qt::DisplayRole).toString(); + //qDebug() << "COLLAPSE NODE " << idx; + if (m_blocked) + return; + emit requestCollapseChildren(idx); +} + +void WatchWindow::contextMenuEvent(QContextMenuEvent *ev) +{ + QMenu menu; + QAction *act1 = new QAction("Adjust column widths to contents", &menu); + QAction *act2 = new QAction("Always adjust column widths to contents", &menu); + act2->setCheckable(true); + act2->setChecked(m_alwaysResizeColumnsToContents); + + menu.addAction(act1); + menu.addAction(act2); + + QAction *act3 = 0; + QAction *act4 = 0; + QModelIndex idx = indexAt(ev->pos()); + QModelIndex mi0 = idx.sibling(idx.row(), 0); + QString exp = model()->data(mi0).toString(); + QString iname = model()->data(mi0, INameRole).toString(); + QModelIndex mi1 = idx.sibling(idx.row(), 0); + QString value = model()->data(mi1).toString(); + bool visual = false; + if (idx.isValid()) { + menu.addSeparator(); + if (m_type == LocalsType) + act3 = new QAction("Watch expression '" + exp + "'", &menu); + else + act3 = new QAction("Remove expression '" + exp + "'", &menu); + menu.addAction(act3); + + visual = model()->data(mi0, VisualRole).toBool(); + act4 = new QAction("Watch expression '" + exp + "' in separate widget", &menu); + act4->setCheckable(true); + act4->setChecked(visual); + // FIXME: menu.addAction(act4); + } + + QAction *act = menu.exec(ev->globalPos()); + + if (!act) + ; + else if (act == act1) + resizeColumnsToContents(); + else if (act == act2) + setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents); + else if (act == act3) + if (m_type == LocalsType) + emit requestWatchExpression(exp); + else + emit requestRemoveWatchExpression(iname); + else if (act == act4) + model()->setData(mi0, !visual, VisualRole); +} + +void WatchWindow::resizeColumnsToContents() +{ + resizeColumnToContents(0); + resizeColumnToContents(1); +} + +void WatchWindow::setAlwaysResizeColumnsToContents(bool on) +{ + if (!header()) + return; + m_alwaysResizeColumnsToContents = on; + QHeaderView::ResizeMode mode = on + ? QHeaderView::ResizeToContents : QHeaderView::Interactive; + header()->setResizeMode(0, mode); + header()->setResizeMode(1, mode); +} + +void WatchWindow::editItem(const QModelIndex &idx) +{ + Q_UNUSED(idx); // FIXME +} + +void WatchWindow::reset() +{ + int row = 0; + if (m_type == TooltipType) + row = 1; + else if (m_type == WatchersType) + row = 2; + //qDebug() << "WATCHWINDOW::RESET" << row; + QTreeView::reset(); + setRootIndex(model()->index(row, 0, model()->index(0, 0))); + //setRootIndex(model()->index(0, 0)); +} + +void WatchWindow::setModel(QAbstractItemModel *model) +{ + QTreeView::setModel(model); + + setRootIsDecorated(true); + header()->setDefaultAlignment(Qt::AlignLeft); + header()->setResizeMode(QHeaderView::ResizeToContents); + if (m_type != LocalsType) + header()->hide(); + + connect(model, SIGNAL(modelAboutToBeReset()), + this, SLOT(modelAboutToBeReset())); + connect(model, SIGNAL(modelReset()), + this, SLOT(modelReset())); +} + +void WatchWindow::modelAboutToBeReset() +{ + m_blocked = true; + //qDebug() << "Model about to be reset"; + m_expandedItems.clear(); + m_expandedItems.insert("local"); + m_expandedItems.insert("watch"); + modelAboutToBeResetHelper(model()->index(0, 0)); + //qDebug() << " expanded: " << m_expandedItems; +} + +void WatchWindow::modelAboutToBeResetHelper(const QModelIndex &idx) +{ + QString iname = model()->data(idx, INameRole).toString(); + //qDebug() << "Model about to be reset helper" << iname << idx + // << isExpanded(idx); + if (isExpanded(idx)) + m_expandedItems.insert(iname); + for (int i = 0, n = model()->rowCount(idx); i != n; ++i) { + QModelIndex idx1 = model()->index(i, 0, idx); + modelAboutToBeResetHelper(idx1); + } +} + +void WatchWindow::modelReset() +{ + //qDebug() << "Model reset"; + expand(model()->index(0, 0)); + modelResetHelper(model()->index(0, 0)); + m_blocked = false; +} + +void WatchWindow::modelResetHelper(const QModelIndex &idx) +{ + QString name = model()->data(idx, Qt::DisplayRole).toString(); + QString iname = model()->data(idx, INameRole).toString(); + //qDebug() << "Model reset helper" << iname << name; + if (m_expandedItems.contains(iname)) { + expand(idx); + for (int i = 0, n = model()->rowCount(idx); i != n; ++i) { + QModelIndex idx1 = model()->index(i, 0, idx); + modelResetHelper(idx1); + } + } +} + +void WatchWindow::handleChangedItem(QWidget *widget) +{ + QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget); + if (lineEdit) + requestAssignValue("foo", lineEdit->text()); +} + diff --git a/src/plugins/debugger/watchwindow.h b/src/plugins/debugger/watchwindow.h new file mode 100644 index 00000000000..da0ee9ee8f7 --- /dev/null +++ b/src/plugins/debugger/watchwindow.h @@ -0,0 +1,95 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEBUGGER_WATCHWINDOW_H +#define DEBUGGER_WATCHWINDOW_H + +#include <QtGui/QTreeView> + +namespace Debugger { +namespace Internal { + +///////////////////////////////////////////////////////////////////// +// +// WatchWindow +// +///////////////////////////////////////////////////////////////////// + +class WatchWindow : public QTreeView +{ + Q_OBJECT + +public: + enum Type { LocalsType, TooltipType, WatchersType }; + + WatchWindow(Type type, QWidget *parent = 0); + void setType(Type type) { m_type = type; } + Type type() const { return m_type; } + +public slots: + void resizeColumnsToContents(); + void setAlwaysResizeColumnsToContents(bool on = true); + void setModel(QAbstractItemModel *model); + +signals: + void requestWatchExpression(const QString &exp); + void requestRemoveWatchExpression(const QString &iname); + void requestAssignValue(const QString &exp, const QString &value); + void requestExpandChildren(const QModelIndex &idx); + void requestCollapseChildren(const QModelIndex &idx); + +private slots: + void handleChangedItem(QWidget *); + void expandNode(const QModelIndex &index); + void collapseNode(const QModelIndex &index); + void modelAboutToBeReset(); + void modelReset(); + +private: + void contextMenuEvent(QContextMenuEvent *ev); + void editItem(const QModelIndex &idx); + void reset(); /* reimpl */ + + void modelAboutToBeResetHelper(const QModelIndex &idx); + void modelResetHelper(const QModelIndex &idx); + + bool m_alwaysResizeColumnsToContents; + Type m_type; + bool m_blocked; + QSet<QString> m_expandedItems; +}; + + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_WATCHWINDOW_H diff --git a/src/plugins/designer/Designer.mimetypes.xml b/src/plugins/designer/Designer.mimetypes.xml new file mode 100644 index 00000000000..f093afb0e1d --- /dev/null +++ b/src/plugins/designer/Designer.mimetypes.xml @@ -0,0 +1,37 @@ +<?xml version="1.0"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="application/x-designer"> + <sub-class-of type="application/xml"/> + <comment>Qt Designer file</comment> + <glob pattern="*.ui"/> + <comment xml:lang="bg">Файл, формат Qt Designer</comment> + <comment xml:lang="ca">fitxer de Qt Designer</comment> + <comment xml:lang="cs">Soubor Qt Designeru</comment> + <comment xml:lang="cy">Ffeil Qt Designer</comment> + <comment xml:lang="da">Qt Designer-fil</comment> + <comment xml:lang="de">QT Designer-Datei</comment> + <comment xml:lang="el">αρχείο Qt Designer</comment> + <comment xml:lang="eo">dosiero de Qt Designer</comment> + <comment xml:lang="es">fichero de Qt Designer</comment> + <comment xml:lang="eu">Qt Designer Fitxategia</comment> + <comment xml:lang="fi">Qt Designer -tiedosto</comment> + <comment xml:lang="fr">fichier Qt Designer</comment> + <comment xml:lang="hu">Qt Designer-fájl</comment> + <comment xml:lang="it">File Qt Designer</comment> + <comment xml:lang="ja">Qt Designer ファイル</comment> + <comment xml:lang="ko">Qt 디자이너 파일</comment> + <comment xml:lang="lt">Qt Designer rinkmena</comment> + <comment xml:lang="ms">Fail Qt Designer</comment> + <comment xml:lang="nb">Qt Designer-fil</comment> + <comment xml:lang="nl">Qt Designer-bestand</comment> + <comment xml:lang="nn">Qt Designer-fil</comment> + <comment xml:lang="pl">Plik Qt Designera</comment> + <comment xml:lang="pt">ficheiro do Qt Designer</comment> + <comment xml:lang="pt_BR">Arquivo do Qt Designer</comment> + <comment xml:lang="ru">файл Qt Designer</comment> + <comment xml:lang="sq">File Qt Designer</comment> + <comment xml:lang="sr">Qt Designer датотека</comment> + <comment xml:lang="sv">Qt Designer-fil</comment> + <comment xml:lang="uk">Файл програми Qt-дизайнер</comment> + </mime-type> +</mime-info> diff --git a/src/plugins/designer/Designer.pluginspec b/src/plugins/designer/Designer.pluginspec new file mode 100644 index 00000000000..d1df125dbe7 --- /dev/null +++ b/src/plugins/designer/Designer.pluginspec @@ -0,0 +1,12 @@ +<plugin name="Designer" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Qt Designer integration.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> +<!-- For compiling with CPP support enabled --> + <dependency name="CppEditor" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/designer/README.txt b/src/plugins/designer/README.txt new file mode 100644 index 00000000000..eeef2a6c1d8 --- /dev/null +++ b/src/plugins/designer/README.txt @@ -0,0 +1,37 @@ +The subdirectory cpp contains classes for CPP-Editor/Designer integration. +They are dependent on the CPP editor plugin. + +Including cpp.pri in designer.pro enables them and defines +CPP_ENABLED. + +Resource handling: + +When the editor is opened for the ui file we remember the internal qrc list +read from the ui file (m_originalUiQrcPaths). In next step (a call to +updateResources()), we detect if the ui file is inside any project or not. + +In case the ui file is not in any project (standalone), we apply +m_originalUiQrcPaths to the form. + +Otherwise we take the list of qrc files from the project and apply the list +into the form. + +Depending if the form is opened in standalone mode or not we save all +resources or only those which are used inside a form (a call to +setSaveResourcesBehaviour()). + +We need to update resources of the opened form in following cases: +- a new qrc file is added to / removed from the project in which the form + exists (we connect filesAdded()/filesRemoved() to the updateResources() slot) +- a new project is added which contains a form (which was opened in standalone + mode) (we connect foldersAdded()/foldersRemoved() to the updateResources() + slot) +- the opened form is being saved as... with new name, what causes the editor + for the form points to a new file which is not in the project (we connect + changed() to updateResources() slot) + +Changes to qrc file contents are not needed to be handled here, since +designer's internal file watcher updates the changes to qrc files silently. + +A call to setResourceEditingEnabled(false) removes the edit resources action +form resource browser in designer diff --git a/src/plugins/designer/cpp/cpp.pri b/src/plugins/designer/cpp/cpp.pri new file mode 100644 index 00000000000..3827bc836f8 --- /dev/null +++ b/src/plugins/designer/cpp/cpp.pri @@ -0,0 +1,15 @@ +INCLUDEPATH+=$$PWD + +DEFINES+=CPP_ENABLED + +HEADERS+=$$PWD/formclasswizardpage.h \ + $$PWD/formclasswizarddialog.h \ + $$PWD/formclasswizard.h \ + $$PWD/formclasswizardparameters.h + +SOURCES+=$$PWD/formclasswizardpage.cpp \ + $$PWD/formclasswizarddialog.cpp \ + $$PWD/formclasswizard.cpp \ + $$PWD/formclasswizardparameters.cpp + +FORMS+=$$PWD/formclasswizardpage.ui diff --git a/src/plugins/designer/cpp/formclasswizard.cpp b/src/plugins/designer/cpp/formclasswizard.cpp new file mode 100644 index 00000000000..43e2c0f44c3 --- /dev/null +++ b/src/plugins/designer/cpp/formclasswizard.cpp @@ -0,0 +1,117 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "formclasswizard.h" +#include "formclasswizarddialog.h" +#include "designerconstants.h" +#include "formwindoweditor.h" + +#include <coreplugin/icore.h> +#include <cppeditor/cppeditorconstants.h> + +#include <QtCore/QFile> +#include <QtCore/QDir> +#include <QtCore/QDebug> + +enum { debugFormClassWizard = 0 }; + +using namespace Designer; +using namespace Designer::Internal; + +FormClassWizard::FormClassWizard(const BaseFileWizardParameters ¶meters, Core::ICore *core, QObject *parent) : + Core::BaseFileWizard(parameters, core, parent) +{ +} + +QString FormClassWizard::headerSuffix() const +{ + return preferredSuffix(QLatin1String(CppEditor::Constants::CPP_HEADER_MIMETYPE)); +} + +QString FormClassWizard::sourceSuffix() const +{ + return preferredSuffix(QLatin1String(CppEditor::Constants::CPP_SOURCE_MIMETYPE)); +} + +QString FormClassWizard::formSuffix() const +{ + return preferredSuffix(QLatin1String(Constants::FORM_MIMETYPE)); +} + +QWizard *FormClassWizard::createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const +{ + FormClassWizardDialog *wizardDialog = new FormClassWizardDialog(extensionPages, + parent); + wizardDialog->setSuffixes(headerSuffix(), sourceSuffix(), formSuffix()); + wizardDialog->setPath(defaultPath); + return wizardDialog; +} + +Core::GeneratedFiles FormClassWizard::generateFiles(const QWizard *w, QString *errorMessage) const +{ + const FormClassWizardDialog *wizardDialog = qobject_cast<const FormClassWizardDialog *>(w); + const FormClassWizardParameters params = wizardDialog->parameters(); + + if (params.uiTemplate.isEmpty()) { + *errorMessage = tr("Internal error: FormClassWizard::generateFiles: empty template contents"); + return Core::GeneratedFiles(); + } + + // header + const QString formFileName = buildFileName(params.path, params.uiFile, formSuffix()); + const QString headerFileName = buildFileName(params.path, params.headerFile, headerSuffix()); + const QString sourceFileName = buildFileName(params.path, params.sourceFile, sourceSuffix()); + + Core::GeneratedFile headerFile(headerFileName); + headerFile.setEditorKind(QLatin1String(CppEditor::Constants::CPPEDITOR_KIND)); + + // Source + Core::GeneratedFile sourceFile(sourceFileName); + sourceFile.setEditorKind(QLatin1String(CppEditor::Constants::CPPEDITOR_KIND)); + + // UI + Core::GeneratedFile uiFile(formFileName); + uiFile.setContents(params.uiTemplate); + uiFile.setEditorKind(QLatin1String(Constants::C_FORMEDITOR)); + + QString source, header; + params.generateCpp(&header, &source); + sourceFile.setContents(source); + headerFile.setContents(header); + + if (debugFormClassWizard) + qDebug() << Q_FUNC_INFO << '\n' << header << '\n' << source; + + return Core::GeneratedFiles() << headerFile << sourceFile << uiFile; +} diff --git a/src/plugins/designer/cpp/formclasswizard.h b/src/plugins/designer/cpp/formclasswizard.h new file mode 100644 index 00000000000..3fce385ebd8 --- /dev/null +++ b/src/plugins/designer/cpp/formclasswizard.h @@ -0,0 +1,77 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FORMCLASSWIZARD_H +#define FORMCLASSWIZARD_H + +#include "formclasswizardparameters.h" + +#include <coreplugin/basefilewizard.h> + +QT_BEGIN_NAMESPACE +class QWizard; +QT_END_NAMESPACE + +namespace Designer { +namespace Internal { + +struct FormClassWizardParameters; + +class FormClassWizard : public Core::BaseFileWizard +{ + Q_DISABLE_COPY(FormClassWizard) + Q_OBJECT + +public: + typedef Core::BaseFileWizardParameters BaseFileWizardParameters; + + FormClassWizard(const BaseFileWizardParameters ¶meters, Core::ICore *core, QObject *parent); + + QString headerSuffix() const; + QString sourceSuffix() const; + QString formSuffix() const; + +protected: + virtual QWizard *createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const; + + virtual Core::GeneratedFiles generateFiles(const QWizard *w, + QString *errorMessage) const; + +private: +}; + +} // namespace Internal +} // namespace Designer + +#endif // FORMCLASSWIZARD_H diff --git a/src/plugins/designer/cpp/formclasswizarddialog.cpp b/src/plugins/designer/cpp/formclasswizarddialog.cpp new file mode 100644 index 00000000000..e340d5ca883 --- /dev/null +++ b/src/plugins/designer/cpp/formclasswizarddialog.cpp @@ -0,0 +1,114 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "formclasswizarddialog.h" +#include "formtemplatewizardpage.h" +#include "formclasswizardpage.h" +#include "formclasswizardparameters.h" + +#include <coreplugin/icore.h> + +#include <QtCore/QDebug> +#include <QtGui/QAbstractButton> + +enum { FormPageId, ClassPageId }; + +namespace Designer { +namespace Internal { + +// ----------------- FormClassWizardDialog +FormClassWizardDialog::FormClassWizardDialog(const WizardPageList &extensionPages, + QWidget *parent) : + QWizard(parent), + m_formPage(new FormTemplateWizardPagePage), + m_classPage(new FormClassWizardPage) +{ + setWindowTitle(tr("Qt Designer Form Class")); + + setPage(FormPageId, m_formPage); + connect(m_formPage, SIGNAL(templateActivated()), + button(QWizard::NextButton), SLOT(animateClick())); + + setPage(ClassPageId, m_classPage); + + foreach (QWizardPage *p, extensionPages) + addPage(p); + + connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(slotCurrentIdChanged(int))); +} + +void FormClassWizardDialog::setSuffixes(const QString &header, const QString &source, const QString &form) +{ + m_classPage->setSuffixes(header, source, form); +} + +QString FormClassWizardDialog::path() const +{ + return m_classPage->path(); +} + +void FormClassWizardDialog::setPath(const QString &p) +{ + m_classPage->setPath(p); +} + +bool FormClassWizardDialog::validateCurrentPage() +{ + return QWizard::validateCurrentPage(); +} + +void FormClassWizardDialog::slotCurrentIdChanged(int id) +{ + // Switching from form to class page: store XML template and set a suitable + // class name in the class page based on the form base class + if (id == ClassPageId) { + QString formBaseClass; + QString uiClassName; + m_rawFormTemplate = m_formPage->templateContents(); + // Strip namespaces from the ui class and suggest it as a new class + // name + if (FormTemplateWizardPagePage::getUIXmlData(m_rawFormTemplate, &formBaseClass, &uiClassName)) + m_classPage->setClassName(FormTemplateWizardPagePage::stripNamespaces(uiClassName)); + } +} + +FormClassWizardParameters FormClassWizardDialog::parameters() const +{ + FormClassWizardParameters rc; + m_classPage->getParameters(&rc); + // Name the ui class in the Ui namespace after the class specified + rc.uiTemplate = FormTemplateWizardPagePage::changeUiClassName(m_rawFormTemplate, rc.className); + return rc; +} + +} // namespace Internal +} // namespace Designer diff --git a/src/plugins/designer/cpp/formclasswizarddialog.h b/src/plugins/designer/cpp/formclasswizarddialog.h new file mode 100644 index 00000000000..849a1cca6b4 --- /dev/null +++ b/src/plugins/designer/cpp/formclasswizarddialog.h @@ -0,0 +1,83 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FORMCLASSWIZARDDIALOG_H +#define FORMCLASSWIZARDDIALOG_H + +#include <QtGui/QWizard> + +namespace Core { + class ICore; +} + +namespace Designer { +namespace Internal { + +struct FormClassWizardParameters; +class FormClassWizardPage; +class FormTemplateWizardPagePage; + +class FormClassWizardDialog : public QWizard +{ + Q_DISABLE_COPY(FormClassWizardDialog) + Q_OBJECT + +public: + typedef QList<QWizardPage *> WizardPageList; + + explicit FormClassWizardDialog(const WizardPageList &extensionPages, + QWidget *parent = 0); + + void setSuffixes(const QString &header, const QString &source, const QString &form); + + QString path() const; + + FormClassWizardParameters parameters() const; + + bool validateCurrentPage(); + +public slots: + void setPath(const QString &); + +private slots: + void slotCurrentIdChanged(int id); + +private: + FormTemplateWizardPagePage *m_formPage; + FormClassWizardPage *m_classPage; + QString m_rawFormTemplate; +}; + +} // namespace Internal +} // namespace Designer + +#endif // FORMCLASSWIZARDDIALOG_H diff --git a/src/plugins/designer/cpp/formclasswizardpage.cpp b/src/plugins/designer/cpp/formclasswizardpage.cpp new file mode 100644 index 00000000000..a65cc21939a --- /dev/null +++ b/src/plugins/designer/cpp/formclasswizardpage.cpp @@ -0,0 +1,209 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "formclasswizardpage.h" +#include "ui_formclasswizardpage.h" +#include "formclasswizardparameters.h" + +#include <coreplugin/icore.h> +#include <cppeditor/cppeditorconstants.h> + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QSettings> + +#include <QtGui/QMessageBox> +#include <QtGui/QAbstractButton> + +static const char *formClassWizardPageGroupC = "FormClassWizardPage"; +static const char *translationKeyC = "RetranslationSupport"; +static const char *embeddingModeKeyC = "Embedding"; + +namespace Designer { +namespace Internal { + +// ----------------- FormClassWizardPage + +FormClassWizardPage::FormClassWizardPage(QWidget * parent) : + QWizardPage(parent), + m_ui(new Ui::FormClassWizardPage), + m_isValid(false) +{ + m_ui->setupUi(this); + + m_ui->newClassWidget->setBaseClassInputVisible(false); + m_ui->newClassWidget->setNamespacesEnabled(true); + + connect(m_ui->newClassWidget, SIGNAL(validChanged()), this, SLOT(slotValidChanged())); + + m_ui->extensionWidget->setVisible(false); + connect(m_ui->moreButton, SIGNAL(clicked(bool)), m_ui->extensionWidget, SLOT(setVisible(bool))); + + restoreSettings(); +} + +FormClassWizardPage::~FormClassWizardPage() +{ + delete m_ui; +} + +void FormClassWizardPage::setSuffixes(const QString &header, const QString &source, const QString &form) +{ + m_ui->newClassWidget->setSourceExtension(source); + m_ui->newClassWidget->setHeaderExtension(header); + m_ui->newClassWidget->setFormExtension(form); +} + +void FormClassWizardPage::setClassName(const QString &suggestedClassName) +{ + // Is it valid, now? + m_ui->newClassWidget->setClassName(suggestedClassName); + slotValidChanged(); +} + +int FormClassWizardPage::uiClassEmbedding() const +{ + if (m_ui->ptrAggregationRadioButton->isChecked()) + return PointerAggregatedUiClass; + if (m_ui->aggregationButton->isChecked()) + return AggregatedUiClass; + return InheritedUiClass; +} + +void FormClassWizardPage::setUiClassEmbedding(int v) +{ + switch (v) { + case PointerAggregatedUiClass: + m_ui->ptrAggregationRadioButton->setChecked(true); + break; + case AggregatedUiClass: + m_ui->aggregationButton->setChecked(true); + break; + case InheritedUiClass: + m_ui->multipleInheritanceButton->setChecked(true); + break; + } +} + +bool FormClassWizardPage::hasRetranslationSupport() const +{ + return m_ui->retranslateCheckBox->isChecked(); +} + +void FormClassWizardPage::setRetranslationSupport(bool v) +{ + m_ui->retranslateCheckBox->setChecked(v); +} + +QString FormClassWizardPage::path() const +{ + return m_ui->newClassWidget->path(); +} + +void FormClassWizardPage::setPath(const QString &p) +{ + m_ui->newClassWidget->setPath(p); +} + +void FormClassWizardPage::getParameters(FormClassWizardParameters *p) const +{ + p->embedding = static_cast<UiClassEmbedding>(uiClassEmbedding()); + p->languageChange = m_ui->retranslateCheckBox->isChecked(); + p->className = m_ui->newClassWidget->className(); + p->path = path(); + p->sourceFile = m_ui->newClassWidget->sourceFileName(); + p->headerFile = m_ui->newClassWidget->headerFileName(); + p->uiFile = m_ui->newClassWidget-> formFileName(); +} + +void FormClassWizardPage::slotValidChanged() +{ + const bool validNow = m_ui->newClassWidget->isValid(); + if (m_isValid != validNow) { + m_isValid = validNow; + emit completeChanged(); + } +} + +bool FormClassWizardPage::isComplete() const +{ + return m_isValid; +} + +bool FormClassWizardPage::validatePage() +{ + QString errorMessage; + const bool rc = m_ui->newClassWidget->isValid(&errorMessage); + if (rc) { + saveSettings(); + } else { + QMessageBox::critical(this, tr("%1 - Error").arg(title()), errorMessage); + } + return rc; +} + +void FormClassWizardPage::saveSettings() +{ + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + if (QSettings *settings = core->settings()) { + settings->beginGroup(QLatin1String(formClassWizardPageGroupC)); + settings->setValue(QLatin1String(translationKeyC), hasRetranslationSupport()); + settings->setValue(QLatin1String(embeddingModeKeyC), uiClassEmbedding()); + settings->endGroup(); + } +} + +void FormClassWizardPage::restoreSettings() +{ + bool retranslationSupport = true; + int embedding = PointerAggregatedUiClass; + + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + if (QSettings *settings = core->settings()) { + + QString key = QLatin1String(formClassWizardPageGroupC); + key += QLatin1Char('/'); + const int groupLength = key.size(); + + key += QLatin1String(translationKeyC); + retranslationSupport = settings->value(key, retranslationSupport).toBool(); + + key.truncate(groupLength); + key += QLatin1String(embeddingModeKeyC); + embedding = settings->value(key, embedding).toInt(); + } + setUiClassEmbedding(embedding); + setRetranslationSupport(retranslationSupport); +} + +} // namespace Internal +} // namespace Designer diff --git a/src/plugins/designer/cpp/formclasswizardpage.h b/src/plugins/designer/cpp/formclasswizardpage.h new file mode 100644 index 00000000000..1a58feb3699 --- /dev/null +++ b/src/plugins/designer/cpp/formclasswizardpage.h @@ -0,0 +1,87 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FORMCLASSWIZARDPAGE_H +#define FORMCLASSWIZARDPAGE_H + +#include <QtGui/QWizardPage> + +namespace Designer { +namespace Internal { + +namespace Ui { + class FormClassWizardPage; +} + +struct FormClassWizardParameters; + +class FormClassWizardPage : public QWizardPage +{ + Q_DISABLE_COPY(FormClassWizardPage) + Q_OBJECT +public: + explicit FormClassWizardPage(QWidget * parent = 0); + ~FormClassWizardPage(); + + void setSuffixes(const QString &header, const QString &source, const QString &form); + + virtual bool isComplete () const; + virtual bool validatePage(); + + int uiClassEmbedding() const; + bool hasRetranslationSupport() const; + QString path() const; + + // Fill out applicable parameters + void getParameters(FormClassWizardParameters *) const; + +public slots: + void setClassName(const QString &suggestedClassName); + void setPath(const QString &); + void setRetranslationSupport(bool); + void setUiClassEmbedding(int v); + +private slots: + void slotValidChanged(); + +private: + void saveSettings(); + void restoreSettings(); + + Ui::FormClassWizardPage *m_ui; + bool m_isValid; +}; + +} // namespace Internal +} // namespace Designer + +#endif //FORMCLASSWIZARDPAGE_H diff --git a/src/plugins/designer/cpp/formclasswizardpage.ui b/src/plugins/designer/cpp/formclasswizardpage.ui new file mode 100644 index 00000000000..236e2059ca4 --- /dev/null +++ b/src/plugins/designer/cpp/formclasswizardpage.ui @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Designer::Internal::FormClassWizardPage</class> + <widget class="QWizardPage" name="Designer::Internal::FormClassWizardPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>552</width> + <height>498</height> + </rect> + </property> + <property name="title"> + <string>Choose a class name</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QGroupBox" name="classGroupBox"> + <property name="title"> + <string>Class</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="Core::Utils::NewClassWidget" name="newClassWidget" native="true"/> + </item> + </layout> + </widget> + </item> + <item row="0" column="1"> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <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> + <item> + <widget class="QToolButton" name="moreButton"> + <property name="text"> + <string>More</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <widget class="QWidget" name="extensionWidget" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QGroupBox" name="uiclassGroupBox"> + <property name="title"> + <string>Embedding of the UI class</string> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QRadioButton" name="ptrAggregationRadioButton"> + <property name="text"> + <string>Aggregation as a pointer member</string> + </property> + <attribute name="buttonGroup"> + <string>buttonGroup</string> + </attribute> + </widget> + </item> + <item> + <widget class="QRadioButton" name="aggregationButton"> + <property name="text"> + <string>Aggregation</string> + </property> + <attribute name="buttonGroup"> + <string>buttonGroup</string> + </attribute> + </widget> + </item> + <item> + <widget class="QRadioButton" name="multipleInheritanceButton"> + <property name="text"> + <string>Multiple Inheritance</string> + </property> + <attribute name="buttonGroup"> + <string>buttonGroup</string> + </attribute> + </widget> + </item> + </layout> + <zorder>aggregationButton</zorder> + <zorder>multipleInheritanceButton</zorder> + <zorder>ptrAggregationRadioButton</zorder> + </widget> + </item> + <item> + <widget class="QGroupBox" name="variousGroupBox"> + <property name="title"> + <string/> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QCheckBox" name="retranslateCheckBox"> + <property name="text"> + <string>Support for changing languages at runtime</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>57</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item row="1" column="1"> + <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> + </widget> + <customwidgets> + <customwidget> + <class>Core::Utils::NewClassWidget</class> + <extends>QWidget</extends> + <header location="global">utils/newclasswidget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> + <buttongroups> + <buttongroup name="buttonGroup"/> + </buttongroups> +</ui> diff --git a/src/plugins/designer/cpp/formclasswizardparameters.cpp b/src/plugins/designer/cpp/formclasswizardparameters.cpp new file mode 100644 index 00000000000..0d7fc701021 --- /dev/null +++ b/src/plugins/designer/cpp/formclasswizardparameters.cpp @@ -0,0 +1,174 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "formclasswizardparameters.h" +#include "formtemplatewizardpage.h" + +#include <utils/codegeneration.h> + +#include <QtCore/QTextStream> +#include <QtCore/QFileInfo> +#include <QtCore/QDebug> + +static const char *uiMemberC = "m_ui"; +static const char *uiNamespaceC = "Ui"; + +namespace Designer { +namespace Internal { + +FormClassWizardParameters::FormClassWizardParameters() : + embedding(PointerAggregatedUiClass), + languageChange(true) +{ +} + +bool FormClassWizardParameters::generateCpp(QString *header, QString *source, int indentation) const +{ + const QString indent = QString(indentation, QLatin1Char(' ')); + QString formBaseClass; + QString uiClassName; + if (!FormTemplateWizardPagePage::getUIXmlData(uiTemplate, &formBaseClass, &uiClassName)) { + qWarning("Unable to determine the form base class from %s.", uiTemplate.toUtf8().constData()); + return false; + } + + // Do we have namespaces? + QStringList namespaceList = className.split(QLatin1String("::")); + if (namespaceList.empty()) // Paranoia! + return false; + + const QString unqualifiedClassName = namespaceList.takeLast(); + + // Include guards + const QString guard = Core::Utils::headerGuard(unqualifiedClassName); + + QString uiInclude = QLatin1String("ui_"); + uiInclude += QFileInfo(uiFile).baseName(); + uiInclude += QLatin1String(".h"); + + // 1) Header file + QTextStream headerStr(header); + headerStr << "#ifndef " << guard + << "\n#define " << guard << '\n' << '\n'; + + // Include 'ui_' + if (embedding != PointerAggregatedUiClass) { + Core::Utils::writeIncludeFileDirective(uiInclude, false, headerStr); + } else { + // Todo: Can we obtain the header from the code model for custom widgets? + // Alternatively, from Designer. + if (formBaseClass.startsWith(QLatin1Char('Q'))) { + QString baseInclude = QLatin1String("QtGui/"); + baseInclude += formBaseClass; + Core::Utils::writeIncludeFileDirective(baseInclude, true, headerStr); + } + } + + // Forward-declare the UI class + if (embedding == PointerAggregatedUiClass) { + headerStr << "\nnamespace " << uiNamespaceC << " {\n" + << indent << "class " << uiClassName << ";\n}\n"; + } + + const QString namespaceIndent = Core::Utils::writeOpeningNameSpaces(namespaceList, indent, headerStr); + // Class declaration + headerStr << '\n' << namespaceIndent << "class " << unqualifiedClassName + << " : public " << formBaseClass; + if (embedding == InheritedUiClass) { + headerStr << ", private " << uiNamespaceC << "::" << uiClassName; + } + headerStr << " {\n" << namespaceIndent << indent << "Q_OBJECT\n" + << namespaceIndent << indent << "Q_DISABLE_COPY(" << unqualifiedClassName << ")\n" + << namespaceIndent << "public:\n" + << namespaceIndent << indent << "explicit " << unqualifiedClassName << "(QWidget *parent = 0);\n"; + if (embedding == PointerAggregatedUiClass) + headerStr << namespaceIndent << indent << "virtual ~" << unqualifiedClassName << "();\n"; + // retranslation + if (languageChange) + headerStr << '\n' << namespaceIndent << "protected:\n" + << namespaceIndent << indent << "virtual void changeEvent(QEvent *e);\n"; + // Member variable + if (embedding != InheritedUiClass) { + headerStr << '\n' << namespaceIndent << "private:\n" + << namespaceIndent << indent << uiNamespaceC << "::" << uiClassName << ' '; + if (embedding == PointerAggregatedUiClass) + headerStr << '*'; + headerStr << uiMemberC << ";\n"; + } + headerStr << namespaceIndent << "};\n\n"; + Core::Utils::writeClosingNameSpaces(namespaceList, indent, headerStr); + headerStr << "#endif // "<< guard << '\n'; + + // 2) Source file + QTextStream sourceStr(source); + Core::Utils::writeIncludeFileDirective(headerFile, false, sourceStr); + if (embedding == PointerAggregatedUiClass) + Core::Utils::writeIncludeFileDirective(uiInclude, false, sourceStr); + // NameSpaces( + Core::Utils::writeOpeningNameSpaces(namespaceList, indent, sourceStr); + // Constructor with setupUi + sourceStr << '\n' << namespaceIndent << unqualifiedClassName << "::" << unqualifiedClassName << "(QWidget *parent) :\n" + << namespaceIndent << indent << formBaseClass << "(parent)"; + if (embedding == PointerAggregatedUiClass) + sourceStr << ",\n" << namespaceIndent << indent << uiMemberC << "(new " << uiNamespaceC << "::" << uiClassName << ")\n"; + sourceStr << namespaceIndent << "{\n" << namespaceIndent << indent; + if (embedding != InheritedUiClass) + sourceStr << uiMemberC << (embedding == PointerAggregatedUiClass ? "->" : "."); + sourceStr << "setupUi(this);\n" << namespaceIndent << "}\n"; + // Deleting destructor for ptr + if (embedding == PointerAggregatedUiClass) { + sourceStr << '\n' << namespaceIndent << unqualifiedClassName << "::~" << unqualifiedClassName + << "()\n" << namespaceIndent << "{\n" + << namespaceIndent << indent << "delete " << uiMemberC << ";\n" + << namespaceIndent << "}\n"; + } + // retranslation + if (languageChange) { + sourceStr << '\n' << namespaceIndent << "void " << unqualifiedClassName << "::" << "changeEvent(QEvent *e)\n" + << namespaceIndent << "{\n" + << namespaceIndent << indent << "switch(e->type()) {\n" << namespaceIndent << indent << "case QEvent::LanguageChange:\n" + << namespaceIndent << indent << indent; + if (embedding != InheritedUiClass) + sourceStr << uiMemberC << (embedding == PointerAggregatedUiClass ? "->" : "."); + sourceStr << "retranslateUi(this);\n" + << namespaceIndent << indent << indent << "break;\n" + << namespaceIndent << indent << "default:\n" + << namespaceIndent << indent << indent << "break;\n" + << namespaceIndent << indent << "}\n" + << namespaceIndent << "}\n"; + } + Core::Utils::writeClosingNameSpaces(namespaceList, indent, sourceStr); + return true; +} + +} // namespace Internal +} // namespace Designer diff --git a/src/plugins/designer/cpp/formclasswizardparameters.h b/src/plugins/designer/cpp/formclasswizardparameters.h new file mode 100644 index 00000000000..4a51444531e --- /dev/null +++ b/src/plugins/designer/cpp/formclasswizardparameters.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FORMCLASSWIZARDPARAMETERS_H +#define FORMCLASSWIZARDPARAMETERS_H + +#include <QtCore/QString> + +namespace Designer { +namespace Internal { + + +enum UiClassEmbedding { + PointerAggregatedUiClass, + AggregatedUiClass, + InheritedUiClass +}; + +struct FormClassWizardParameters { + explicit FormClassWizardParameters(); + + bool generateCpp(QString *header, QString *source, int indentation = 4) const; + + UiClassEmbedding embedding; + bool languageChange; // Add handling for language change events + QString uiTemplate; + QString className; + + QString path; + QString sourceFile; + QString headerFile; + QString uiFile; +}; + +} // namespace Internal +} // namespace Designer + +#endif // FORMCLASSWIZARDPARAMETERS_H diff --git a/src/plugins/designer/designer.pro b/src/plugins/designer/designer.pro new file mode 100644 index 00000000000..a039bf29915 --- /dev/null +++ b/src/plugins/designer/designer.pro @@ -0,0 +1,57 @@ +TEMPLATE = lib +TARGET = Designer + +include(../../qworkbenchplugin.pri) +include(../../../shared/designerintegrationv2/designerintegration.pri) +include(cpp/cpp.pri) +include(designer_dependencies.pri) + +# -- check the Qt version +TOO_OLD_LIST=$$find(QT_VERSION, ^4\.[0-4]) +count(TOO_OLD_LIST, 1) { + error("Cannot build the designer plugin with a Qt version that old:" $$QT_VERSION) +} +# -- figure out shared dir location +!exists($$[QT_INSTALL_HEADERS]/QtDesigner/private/qdesigner_integration_p.h) { + QT_SOURCE_TREE=$$fromfile($$(QTDIR)/.qmake.cache,QT_SOURCE_TREE) + INCLUDEPATH += $$QT_SOURCE_TREE/include +} + +INCLUDEPATH += $$QMAKE_INCDIR_QT/QtDesigner \ + ../../tools/utils + +qtAddLibrary(QtDesigner) +qtAddLibrary(QtDesignerComponents) + +QT+=xml + +HEADERS += formeditorplugin.h \ + formeditorfactory.h \ + formwindoweditor.h \ + formwindowfile.h \ + formwindowhost.h \ + formwizard.h \ + workbenchintegration.h \ + designerconstants.h \ + settingspage.h \ + editorwidget.h \ + formeditorw.h \ + settingsmanager.h \ + formtemplatewizardpage.h \ + formwizarddialog.h + +SOURCES += formeditorplugin.cpp \ + formeditorfactory.cpp \ + formwindoweditor.cpp \ + formwindowfile.cpp \ + formwindowhost.cpp \ + formwizard.cpp \ + workbenchintegration.cpp \ + settingspage.cpp \ + editorwidget.cpp \ + formeditorw.cpp \ + settingsmanager.cpp \ + formtemplatewizardpage.cpp \ + formwizarddialog.cpp + +RESOURCES += designer.qrc diff --git a/src/plugins/designer/designer.qrc b/src/plugins/designer/designer.qrc new file mode 100644 index 00000000000..7565ed0032c --- /dev/null +++ b/src/plugins/designer/designer.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/formeditor" > + <file>images/qt_ui.png</file> + <file>Designer.mimetypes.xml</file> + </qresource> +</RCC> diff --git a/src/plugins/designer/designer_dependencies.pri b/src/plugins/designer/designer_dependencies.pri new file mode 100644 index 00000000000..4c98d46428c --- /dev/null +++ b/src/plugins/designer/designer_dependencies.pri @@ -0,0 +1,3 @@ +include(../../plugins/cppeditor/cppeditor.pri) +include(../../libs/utils/utils.pri) +include(../../plugins/coreplugin/coreplugin.pri) diff --git a/src/plugins/designer/designerconstants.h b/src/plugins/designer/designerconstants.h new file mode 100644 index 00000000000..28252e608a3 --- /dev/null +++ b/src/plugins/designer/designerconstants.h @@ -0,0 +1,65 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DESIGNERPLUGIN_CONSTANTS_H +#define DESIGNERPLUGIN_CONSTANTS_H + +namespace Designer { + namespace Constants { + // context + const char * const C_FORMEDITOR = "Formeditor"; + const char * const T_FORMEDITOR = "Formeditor.Toolbar"; + const char * const M_FORMEDITOR = "Formeditor.Menu"; + const char * const M_FORMEDITOR_PREVIEW = "Formeditor.Menu.Preview"; + const char * const C_FORMWINDOW = "Formwindow"; + // Wizard type + const char * const FORM_FILE_TYPE = "Qt4FormFiles"; + const char * const FORM_MIMETYPE = "application/x-designer"; + + enum DesignerSubWindows { + WidgetBoxSubWindow, + ObjectInspectorSubWindow, + PropertyEditorSubWindow, + SignalSlotEditorSubWindow, + ActionEditorSubWindow, + DesignerSubWindowCount }; + + enum EditModes { + EditModeWidgetEditor, + EditModeSignalsSlotEditor, + EditModeBuddyEditor, + EditModeTabOrderEditor, + NumEditModes }; + } +} + +#endif //DESIGNERPLUGIN_CONSTANTS_H diff --git a/src/plugins/designer/editorwidget.cpp b/src/plugins/designer/editorwidget.cpp new file mode 100644 index 00000000000..c164ddfe232 --- /dev/null +++ b/src/plugins/designer/editorwidget.cpp @@ -0,0 +1,270 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + + +#include "editorwidget.h" +#include "formeditorw.h" + +#include <QtCore/QEvent> +#include <QtGui/QVBoxLayout> +#include <QtGui/QTabWidget> +#include <coreplugin/minisplitter.h> + +using namespace Designer::Constants; + +enum { ActionEditorTab, SignalSlotEditorTab }; + +enum { wantSignalSlotEditor = 0 }; + +namespace Designer { +namespace Internal { + + SharedSubWindow::SharedSubWindow(QWidget *shared, QWidget *parent) : + QWidget(parent), + m_shared(shared), + m_layout(new QVBoxLayout) + { + Q_ASSERT(m_shared); + m_layout->setContentsMargins(0, 0, 0, 0); + setLayout(m_layout); + } + + void SharedSubWindow::activate() + { + // Take the widget off the other parent + Q_ASSERT(m_shared); + QWidget *currentParent = m_shared->parentWidget(); + if (currentParent == this) + return; + + if (currentParent) { + QVBoxLayout *lt = qobject_cast<QVBoxLayout *>(currentParent->layout()); + Q_ASSERT(lt); + m_shared->setParent(0); + delete lt->takeAt(0); + } + m_layout->addWidget(m_shared); + m_layout->invalidate(); + } + + SharedSubWindow::~SharedSubWindow() + { + // Do not destroy the shared sub window if we currently own it + if (m_layout->count()) { + m_shared->setParent(0); + delete m_layout->takeAt(0); + } + } + + // ---------- Global EditorState + Q_GLOBAL_STATIC(EditorWidgetState, editorWidgetState) + + enum { Version = 1 }; + // Simple conversion of an int list to QVariantList, size as leading element + static void intToVariantList(const QList<int> &il, QVariantList& vl) + { + const int size = il.size(); + vl.push_back(size); + if (size != 0) { + const QList<int>::const_iterator cend = il.constEnd(); + for (QList<int>::const_iterator it = il.constBegin(); it != cend; ++it) + vl.push_back(QVariant(*it)); + } + } + // Simple conversion of a QVariantList portion saved by the above function to int list + bool variantListToIntList(const QVariantList& vl, int &index, QList<int> &list) + { + list.clear(); + if (index >= vl.size()) + return false; + const int size = vl.at(index++).toInt(); + const int end = index + size; + if (end > vl.size()) + return false; + if (size != 0) { + for ( ; index < end; index++) + list.push_back(vl.at(index).toInt()); + } + return true; + } + + // ------------------ EditorWidgetState + QVariant EditorWidgetState::toVariant() const + { + QVariantList rc; + rc.push_back(Version); + intToVariantList(horizontalSizes, rc); + intToVariantList(centerVerticalSizes, rc); + intToVariantList(rightVerticalSizes, rc); + return QVariant(rc); + } + + bool EditorWidgetState::fromVariant(const QVariant &v) + { + // Restore state. The weird thing is that QSettings might return + // a QStringList although it was saved as QVariantList<int>. + if (v.type() != QVariant::List && v.type() != QVariant::StringList) + return false; + const QVariantList vl = v.toList(); + if (vl.empty()) + return false; + int index = 0; + const QVariant &versionV = vl.at(index++); + if (versionV.type() != QVariant::Int && versionV.type() != QVariant::String) + return false; + if (versionV.toInt() > Version) + return false; + return variantListToIntList(vl, index, horizontalSizes) && + variantListToIntList(vl, index, centerVerticalSizes) && + variantListToIntList(vl, index, rightVerticalSizes); + } + + // ---------- EditorWidget + EditorWidget::EditorWidget(QWidget *formWindow) : + Core::MiniSplitter(Qt::Horizontal), + m_centerVertSplitter(new Core::MiniSplitter(Qt::Vertical)), + m_bottomTab(0), + m_rightVertSplitter(new Core::MiniSplitter(Qt::Vertical)) + { + // Get shared sub windows from Form Editor + FormEditorW *few = FormEditorW::instance(); + QWidget * const*subs = few->designerSubWindows(); + // Create shared sub windows except SignalSlotEditor + qFill(m_designerSubWindows, m_designerSubWindows + DesignerSubWindowCount, static_cast<SharedSubWindow*>(0)); + for (int i=0; i < DesignerSubWindowCount; i++) + if (wantSignalSlotEditor || i != SignalSlotEditorSubWindow) + m_designerSubWindows[i] = new SharedSubWindow(subs[i]); + // Create splitter + addWidget(m_designerSubWindows[WidgetBoxSubWindow]); + + // center + m_centerVertSplitter->addWidget(formWindow); + + if (wantSignalSlotEditor) { + m_bottomTab = new QTabWidget; + m_bottomTab->setTabPosition(QTabWidget::South); + m_bottomTab->addTab(m_designerSubWindows[ActionEditorSubWindow], tr("Action editor")); + m_bottomTab->addTab(m_designerSubWindows[SignalSlotEditorSubWindow], tr("Signals and slots editor")); + m_centerVertSplitter->addWidget(m_bottomTab); + } else { + m_centerVertSplitter->addWidget(m_designerSubWindows[ActionEditorSubWindow]); + } + + addWidget(m_centerVertSplitter); + + m_rightVertSplitter->addWidget(m_designerSubWindows[ObjectInspectorSubWindow]); + m_rightVertSplitter->addWidget(m_designerSubWindows[PropertyEditorSubWindow]); + addWidget(m_rightVertSplitter); + } + + void EditorWidget::setInitialSizes() + { + QList<int> sizes; + // center vertical. Either the tab containing signal slot editor/ + // action editor or the action editor itself + const QWidget *bottomWidget = m_bottomTab; + if (!bottomWidget) + bottomWidget = m_designerSubWindows[ActionEditorSubWindow]; + const int tabHeight = bottomWidget->sizeHint().height(); + sizes.push_back(height() - handleWidth() - tabHeight); + sizes.push_back( tabHeight); + m_centerVertSplitter->setSizes(sizes); + // right vert + sizes.clear(); + sizes.push_back(height() /2 - (handleWidth() / 2)); + sizes.push_back(height() / 2 - (handleWidth() / 2)); + m_rightVertSplitter->setSizes(sizes); + // horiz sizes + sizes.clear(); + const int wboxWidth = m_designerSubWindows[WidgetBoxSubWindow]->sizeHint().width(); + const int vSplitterWidth = m_rightVertSplitter->sizeHint().width(); + sizes.push_back(wboxWidth); + sizes.push_back(width() - 2 * handleWidth() - wboxWidth - vSplitterWidth); + sizes.push_back(vSplitterWidth); + setSizes(sizes); + } + + void EditorWidget::activate() + { + for (int i=0; i < DesignerSubWindowCount; i++) + if (SharedSubWindow *sw = m_designerSubWindows[i]) // Signal slot might be deactivated + sw->activate(); + if (!restore(*editorWidgetState())) + setInitialSizes(); + } + + bool EditorWidget::event(QEvent * e) + { + if (e->type() == QEvent::Hide) + *editorWidgetState() = save(); + return QSplitter::event(e); + } + + EditorWidgetState EditorWidget::save() const + { + EditorWidgetState rc; + rc.horizontalSizes = sizes(); + rc.centerVerticalSizes = m_centerVertSplitter->sizes(); + rc.rightVerticalSizes = m_rightVertSplitter->sizes(); + return rc; + } + + bool EditorWidget::restore(const EditorWidgetState &s) + { + if (s.horizontalSizes.size() != count() || + s.centerVerticalSizes.size() != m_centerVertSplitter->count() || + s.rightVerticalSizes.size() != m_rightVertSplitter->count()) + return false; + m_centerVertSplitter->setSizes(s.centerVerticalSizes); + m_rightVertSplitter->setSizes(s.rightVerticalSizes); + setSizes(s.horizontalSizes); + return true; + } + + void EditorWidget::toolChanged(int i) + { + if (m_bottomTab) + m_bottomTab->setCurrentIndex(i == EditModeSignalsSlotEditor ? SignalSlotEditorTab : ActionEditorTab); + } + + EditorWidgetState EditorWidget::state() + { + return *editorWidgetState(); + } + + void EditorWidget::setState(const EditorWidgetState& st) + { + *editorWidgetState() = st; + } +} +} diff --git a/src/plugins/designer/editorwidget.h b/src/plugins/designer/editorwidget.h new file mode 100644 index 00000000000..790550cb3e7 --- /dev/null +++ b/src/plugins/designer/editorwidget.h @@ -0,0 +1,118 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DESIGNER_EDITORWIDGET_H +#define DESIGNER_EDITORWIDGET_H + +#include "designerconstants.h" + +#include <coreplugin/minisplitter.h> + +#include <QtCore/QPointer> +#include <QtCore/QList> +#include <QtCore/QVariant> +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE +class QTabWidget; +class QVBoxLayout; +QT_END_NAMESPACE + +namespace Designer { +namespace Internal { + + /* A widget that shares its embedded sub window with others. For example, + * the designer editors need to share the widget box, etc. */ + class SharedSubWindow : public QWidget { + Q_OBJECT + Q_DISABLE_COPY(SharedSubWindow) + + public: + SharedSubWindow(QWidget *shared, QWidget *parent = 0); + virtual ~SharedSubWindow(); + + public slots: + // Takes the shared widget off the current parent and adds it to its + // layout + void activate(); + + private: + QPointer <QWidget> m_shared; + QVBoxLayout *m_layout; + }; + + /** State of the editor window (splitter sizes) + * Shared as a global struct between the instances and stored + * in QSettings. */ + struct EditorWidgetState { + QVariant toVariant() const; // API to conveniently store in QSettings + bool fromVariant(const QVariant &v); + + QList<int> horizontalSizes; + QList<int> centerVerticalSizes; + QList<int> rightVerticalSizes; + }; + + /* Form editor splitter used as editor window. Contains the shared designer + * windows. */ + class EditorWidget : public Core::MiniSplitter { + Q_OBJECT + Q_DISABLE_COPY(EditorWidget) + public: + explicit EditorWidget(QWidget *formWindow); + + virtual bool event(QEvent * e); + + EditorWidgetState save() const; + bool restore(const EditorWidgetState &s); + + // Get/Set the shared splitter state of all editors of that type for + // settings + static EditorWidgetState state(); + static void setState(const EditorWidgetState&st); + + public slots: + void activate(); + void toolChanged(int); + + private: + void setInitialSizes(); + + SharedSubWindow* m_designerSubWindows[Designer::Constants::DesignerSubWindowCount]; + QSplitter *m_centerVertSplitter; + QTabWidget *m_bottomTab; + QSplitter *m_rightVertSplitter; + }; +} +} + +#endif diff --git a/src/plugins/designer/formeditorfactory.cpp b/src/plugins/designer/formeditorfactory.cpp new file mode 100644 index 00000000000..29ceb8b06c9 --- /dev/null +++ b/src/plugins/designer/formeditorfactory.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "formeditorfactory.h" +#include "formeditorw.h" +#include "formwindoweditor.h" +#include "designerconstants.h" + +#include <coreplugin/icore.h> +#include <coreplugin/fileiconprovider.h> +#include <coreplugin/editormanager/editormanager.h> + +#include <QtCore/QFileInfo> +#include <QtCore/QDebug> + +using namespace Designer::Internal; +using namespace Designer::Constants; + +FormEditorFactory::FormEditorFactory(Core::ICore *core) : + Core::IEditorFactory(core), + m_kind(QLatin1String(C_FORMEDITOR)), + m_mimeTypes(QLatin1String(FORM_MIMETYPE)), + m_core(core) +{ + Core::FileIconProvider *iconProvider = Core::FileIconProvider::instance(); + iconProvider->registerIconForSuffix(QIcon(":/formeditor/images/qt_ui.png"), + QLatin1String("ui")); +} + +QString FormEditorFactory::kind() const +{ + return C_FORMEDITOR; +} + +Core::IFile *FormEditorFactory::open(const QString &fileName) +{ + Core::IEditor *iface = m_core->editorManager()->openEditor(fileName, kind()); + return iface ? iface->file() : 0; +} + +Core::IEditor *FormEditorFactory::createEditor(QWidget *parent) +{ + return FormEditorW::instance()->createFormWindowEditor(parent); +} + +QStringList FormEditorFactory::mimeTypes() const +{ + return m_mimeTypes; +} diff --git a/src/plugins/designer/formeditorfactory.h b/src/plugins/designer/formeditorfactory.h new file mode 100644 index 00000000000..5e8c7c2abf1 --- /dev/null +++ b/src/plugins/designer/formeditorfactory.h @@ -0,0 +1,73 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FORMEDITORFACTORY_H +#define FORMEDITORFACTORY_H + +#include <extensionsystem/ExtensionSystemInterfaces> +#include <coreplugin/editormanager/ieditorfactory.h> + +#include <QtCore/QStringList> + +namespace Core { +class ICore; +class IEditor; +class IFile; +} + +namespace Designer { +namespace Internal { + +class FormEditorFactory : public Core::IEditorFactory +{ + Q_OBJECT + +public: + FormEditorFactory(Core::ICore *core); + + virtual QStringList mimeTypes() const; + //EditorFactory + virtual QString kind() const; + Core::IFile *open(const QString &fileName); + Core::IEditor *createEditor(QWidget *parent); + +private: + const QString m_kind; + const QStringList m_mimeTypes; + Core::ICore *m_core; +}; + +} // namespace Internal +} // namespace Designer + +#endif + diff --git a/src/plugins/designer/formeditorplugin.cpp b/src/plugins/designer/formeditorplugin.cpp new file mode 100644 index 00000000000..eacc1f53123 --- /dev/null +++ b/src/plugins/designer/formeditorplugin.cpp @@ -0,0 +1,146 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "formeditorplugin.h" +#include "formeditorfactory.h" +#include "formeditorw.h" +#include "formwizard.h" + +#ifdef CPP_ENABLED +# include "formclasswizard.h" +# include <cppeditor/cppeditorconstants.h> +#endif + +#include "designerconstants.h" +#if QT_VERSION < 0x040500 +# include "settings.h" +#endif + +#include <coreplugin/icore.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/uniqueidmanager.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QDebug> + +#ifdef CPP_ENABLED +# include <QtGui/QAction> +# include <QtGui/QWizard> +# include <QtGui/QMainWindow> +#endif + +using namespace Designer::Internal; +using namespace Designer::Constants; + +FormEditorPlugin::FormEditorPlugin() : + m_factory(0), + m_formWizard(0), + m_formClassWizard(0) +{ +} + +FormEditorPlugin::~FormEditorPlugin() +{ + if (m_factory) + removeObject(m_factory); + if (m_formWizard) + removeObject(m_formWizard); + if (m_formClassWizard) + removeObject(m_formClassWizard); + delete m_factory; + delete m_formWizard; + delete m_formClassWizard; + FormEditorW::deleteInstance(); +} + +//////////////////////////////////////////////////// +// +// INHERITED FROM ExtensionSystem::Plugin +// +//////////////////////////////////////////////////// +bool FormEditorPlugin::initialize(const QStringList & /*arguments*/, QString *error_message/* = 0*/) // =0; +{ + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/formeditor/Designer.mimetypes.xml"), error_message)) + return false; + + if (!initializeTemplates(error_message)) + return false; + + const int uid = core->uniqueIDManager()->uniqueIdentifier(QLatin1String(C_FORMEDITOR)); + const QList<int> context = QList<int>() << uid; + + m_factory = new FormEditorFactory(core); + addObject(m_factory); + + // Make sure settings pages and action shortcuts are registered + FormEditorW::ensureInitStage(FormEditorW::RegisterPlugins); + + error_message->clear(); + return true; +} + +void FormEditorPlugin::extensionsInitialized() +{ +} + +//////////////////////////////////////////////////// +// +// PRIVATE methods +// +//////////////////////////////////////////////////// + +bool FormEditorPlugin::initializeTemplates(QString * /* error_message */) +{ + + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + FormWizard::BaseFileWizardParameters wizardParameters(Core::IWizard::FileWizard); + wizardParameters.setCategory(QLatin1String("Qt")); + wizardParameters.setTrCategory(tr("Qt")); + const QString formFileType = QLatin1String(Constants::FORM_FILE_TYPE); + wizardParameters.setName(tr("Qt Designer Form")); + wizardParameters.setDescription(tr("This creates a new Qt Designer form file.")); + m_formWizard = new FormWizard(wizardParameters, core, this); + addObject(m_formWizard); + +#ifdef CPP_ENABLED + wizardParameters.setKind(Core::IWizard::ClassWizard); + wizardParameters.setName(tr("Qt Designer Form Class")); + wizardParameters.setDescription(tr("This creates a new Qt Designer form class.")); + m_formClassWizard = new FormClassWizard(wizardParameters, core, this); + addObject(m_formClassWizard); +#endif + return true; +} + +Q_EXPORT_PLUGIN(FormEditorPlugin) diff --git a/src/plugins/designer/formeditorplugin.h b/src/plugins/designer/formeditorplugin.h new file mode 100644 index 00000000000..01dbb1d236a --- /dev/null +++ b/src/plugins/designer/formeditorplugin.h @@ -0,0 +1,74 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FORMEDITORPLUGIN_H +#define FORMEDITORPLUGIN_H + +#include <extensionsystem/iplugin.h> + +namespace Core { + class IWizard; + class ICore; +} + +namespace Designer { +namespace Internal { + +class FormEditorFactory; +class FormWizard; +class FormEditorW; + +class FormEditorPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + FormEditorPlugin(); + ~FormEditorPlugin(); + + //Plugin + bool initialize(const QStringList &arguments, QString *error_message = 0); + void extensionsInitialized(); + +private: + bool initializeTemplates(QString *error_message); + + FormEditorFactory *m_factory; + + Core::IWizard *m_formWizard; + Core::IWizard *m_formClassWizard; +}; + +} // namespace Internal +} // namespace Designer + +#endif // FORMEDITORPLUGIN_H diff --git a/src/plugins/designer/formeditorw.cpp b/src/plugins/designer/formeditorw.cpp new file mode 100644 index 00000000000..546be02afc2 --- /dev/null +++ b/src/plugins/designer/formeditorw.cpp @@ -0,0 +1,720 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "formeditorw.h" +#include "formwindoweditor.h" +#include "designerconstants.h" +#include "settingsmanager.h" +#include "settingspage.h" +#include "editorwidget.h" +#include "workbenchintegration.h" + +#include <coreplugin/coreconstants.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> + +#include <QtDesigner/QDesignerFormEditorPluginInterface> +#include <QtDesigner/private/pluginmanager_p.h> + +#include <QtDesigner/private/iconloader_p.h> // createIconSet +#include <QtDesigner/private/qdesigner_formwindowmanager_p.h> +#include <QtDesigner/private/formwindowbase_p.h> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerComponents> + +#include <QtDesigner/QDesignerWidgetBoxInterface> +#include <QtDesigner/abstractobjectinspector.h> +#include <QtDesigner/QDesignerComponents> +#include <QtDesigner/QDesignerPropertyEditorInterface> +#include <QtDesigner/QDesignerActionEditorInterface> + +#include <QtCore/QPluginLoader> +#include <QtCore/QTemporaryFile> +#include <QtCore/QDir> +#include <QtCore/QTime> +#include <QtGui/QAction> +#include <QtGui/QActionGroup> +#include <QtGui/QApplication> +#include <QtGui/QCursor> +#include <QtGui/QMenu> +#include <QtGui/QMainWindow> +#include <QtGui/QMessageBox> +#include <QtGui/QKeySequence> +#include <QtGui/QPrintDialog> +#include <QtGui/QPrinter> +#include <QtGui/QPainter> +#include <QtGui/QStatusBar> +#include <QtGui/QStyle> +#include <QtGui/QToolBar> + +#include <QtCore/QDebug> +#include <QtCore/QSettings> + +enum { debugFormEditor = 0 }; +enum { wantCodeGenerationAction = 0 }; + +static const char *editorWidgetStateKeyC = "editorWidgetState"; +static const char *settingsGroup = "Designer"; + +static inline QIcon designerIcon(const QString &iconName) +{ + const QIcon icon = qdesigner_internal::createIconSet(iconName); + if (icon.isNull()) + qWarning() << "Unable to locate " << iconName; + return icon; +} + +// Create an action to activate a designer tool +static inline QAction *createEditModeAction(QActionGroup *ag, + const QList<int> &context, + Core::ActionManagerInterface *am, + Core::IActionContainer *medit, + const QString &actionName, + const QString &name, + int toolNumber, + const QString &iconName = QString(), + const QString &keySequence = QString()) +{ + QAction *rc = new QAction(actionName, ag); + rc->setCheckable(true); + if (!iconName.isEmpty()) + rc->setIcon(designerIcon(iconName)); + Core::ICommand *command = am->registerAction(rc, name, context); + if (!keySequence.isEmpty()) + command->setDefaultKeySequence(QKeySequence(keySequence)); + command->setAttribute(Core::ICommand::CA_Hide); + medit->addAction(command, Core::Constants::G_EDIT_OTHER); + rc->setData(toolNumber); + ag->addAction(rc); + return rc; +} + + +// Create a menu separato +static inline QAction * createSeparator(QObject *parent, + Core::ActionManagerInterface *am, + const QList<int> &context, + Core::IActionContainer *container, + const QString &name = QString(), + const QString &group = QString()) +{ + QAction *actSeparator = new QAction(parent); + actSeparator->setSeparator(true); + Core::ICommand *command = am->registerAction(actSeparator, name, context); + container->addAction(command, group); + return actSeparator; +} + +// Create a tool action +static inline void addToolAction(QAction *a, + Core::ActionManagerInterface *am, + const QList<int> &context, + const QString &name, + Core::IActionContainer *c1, + const QString &keySequence = QString()) +{ + Core::ICommand *command = am->registerAction(a, name, context); + if (!keySequence.isEmpty()) + command->setDefaultKeySequence(QKeySequence(keySequence)); + c1->addAction(command); +} + +// --------- FormEditorW + +using namespace Designer::Internal; +using namespace Designer::Constants; + +FormEditorW *FormEditorW::m_self = 0; + +FormEditorW::FormEditorW() : + m_formeditor(QDesignerComponents::createFormEditor(0)), + m_integration(0), + m_fwm(0), + m_core(ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()), + m_initStage(RegisterPlugins), + m_actionGroupEditMode(0), + m_actionPrint(0), + m_actionGenerateCode(0) +{ + if (debugFormEditor) + qDebug() << Q_FUNC_INFO; + Q_ASSERT(!m_self); + m_self = this; + Q_ASSERT(m_core); + + qFill(m_designerSubWindows, m_designerSubWindows + Designer::Constants::DesignerSubWindowCount, + static_cast<QWidget *>(0)); + + m_formeditor->setTopLevel(qobject_cast<QWidget *>(m_core->editorManager())); + m_formeditor->setSettingsManager(new SettingsManager()); + + m_fwm = qobject_cast<qdesigner_internal::QDesignerFormWindowManager*>(m_formeditor->formWindowManager()); + Q_ASSERT(m_fwm); + + const int uid = m_core->uniqueIDManager()->uniqueIdentifier(QLatin1String(C_FORMEDITOR)); + m_context << uid; + + setupActions(); + + foreach (QDesignerOptionsPageInterface *designerPage, m_formeditor->optionsPages()) { + SettingsPage *settingsPage = new SettingsPage(designerPage); + ExtensionSystem::PluginManager::instance()->addObject(settingsPage); + m_settingsPages.append(settingsPage); + } + restoreSettings(m_core->settings()); + + connect(m_core->editorManager(), SIGNAL(currentEditorChanged(Core::IEditor *)), + this, SLOT(currentEditorChanged(Core::IEditor *))); +} + +FormEditorW::~FormEditorW() +{ + saveSettings(m_core->settings()); + + for (int i = 0; i < Designer::Constants::DesignerSubWindowCount; ++i) + delete m_designerSubWindows[i]; + + delete m_formeditor; + foreach (SettingsPage *settingsPage, m_settingsPages) { + ExtensionSystem::PluginManager::instance()->removeObject(settingsPage); + delete settingsPage; + } + delete m_integration; + m_self = 0; +} + +void FormEditorW::fullInit() +{ + Q_ASSERT(m_initStage == RegisterPlugins); + QTime *initTime = 0; + if (debugFormEditor) { + initTime = new QTime; + initTime->start(); + } + + QDesignerComponents::createTaskMenu(m_formeditor, parent()); + QDesignerComponents::initializePlugins(designerEditor()); + QDesignerComponents::initializeResources(); + initDesignerSubWindows(); + m_integration = new WorkbenchIntegration(m_formeditor, this); + m_formeditor->setIntegration(m_integration); + + /** + * This will initialize our TabOrder, Signals and slots and Buddy editors. + */ + QList<QObject*> plugins = QPluginLoader::staticInstances(); + plugins += m_formeditor->pluginManager()->instances(); + foreach (QObject *plugin, plugins) { + if (QDesignerFormEditorPluginInterface *formEditorPlugin = qobject_cast<QDesignerFormEditorPluginInterface*>(plugin)) { + if (!formEditorPlugin->isInitialized()) + formEditorPlugin->initialize(m_formeditor); + } + } + + if (debugFormEditor) { + qDebug() << Q_FUNC_INFO << initTime->elapsed() << "ms"; + delete initTime; + } + m_initStage = FullyInitialized; +} + +void FormEditorW::initDesignerSubWindows() +{ + qFill(m_designerSubWindows, m_designerSubWindows + Designer::Constants::DesignerSubWindowCount, static_cast<QWidget*>(0)); + + QDesignerWidgetBoxInterface *wb = QDesignerComponents::createWidgetBox(m_formeditor, 0); + wb->setWindowTitle(tr("Designer widgetbox")); + m_formeditor->setWidgetBox(wb); + m_designerSubWindows[WidgetBoxSubWindow] = wb; + + QDesignerObjectInspectorInterface *oi = QDesignerComponents::createObjectInspector(m_formeditor, 0); + oi->setWindowTitle(tr("Object inspector")); + m_formeditor->setObjectInspector(oi); + m_designerSubWindows[ObjectInspectorSubWindow] = oi; + + QDesignerPropertyEditorInterface *pe = QDesignerComponents::createPropertyEditor(m_formeditor, 0); + pe->setWindowTitle(tr("Property editor")); + m_formeditor->setPropertyEditor(pe); + m_designerSubWindows[PropertyEditorSubWindow] = pe; + + QWidget *se = QDesignerComponents::createSignalSlotEditor(m_formeditor, 0); + se->setWindowTitle(tr("Signals and slots editor")); + m_designerSubWindows[SignalSlotEditorSubWindow] = se; + + QDesignerActionEditorInterface *ae = QDesignerComponents::createActionEditor(m_formeditor, 0); + ae->setWindowTitle(tr("Action editor")); + m_formeditor->setActionEditor(ae); + m_designerSubWindows[ActionEditorSubWindow] = ae; +} + +void FormEditorW::ensureInitStage(InitializationStage s) +{ + if (debugFormEditor) + qDebug() << Q_FUNC_INFO << s; + if (!m_self) + m_self = new FormEditorW; + if (m_self->m_initStage >= s) + return; + QApplication::setOverrideCursor(Qt::WaitCursor); + m_self->fullInit(); + QApplication::restoreOverrideCursor(); +} + +FormEditorW *FormEditorW::instance() +{ + ensureInitStage(FullyInitialized); + return m_self; +} + +void FormEditorW::deleteInstance() +{ + delete m_self; +} + +void FormEditorW::setupActions() +{ + Core::ActionManagerInterface *am = m_core->actionManager(); + Core::ICommand *command; + + //menus + Core::IActionContainer *medit = + am->actionContainer(Core::Constants::M_EDIT); + Core::IActionContainer *mtools = + am->actionContainer(Core::Constants::M_TOOLS); + + Core::IActionContainer *mformtools = + am->createMenu(M_FORMEDITOR); + mformtools->menu()->setTitle(tr("For&m editor")); + mtools->addMenu(mformtools); + + //overridden actions + am->registerAction(m_fwm->actionUndo(), Core::Constants::UNDO, m_context); + am->registerAction(m_fwm->actionRedo(), Core::Constants::REDO, m_context); + am->registerAction(m_fwm->actionCut(), Core::Constants::CUT, m_context); + am->registerAction(m_fwm->actionCopy(), Core::Constants::COPY, m_context); + am->registerAction(m_fwm->actionPaste(), Core::Constants::PASTE, m_context); + am->registerAction(m_fwm->actionSelectAll(), Core::Constants::SELECTALL, m_context); + + m_actionPrint = new QAction(this); + am->registerAction(m_actionPrint, Core::Constants::PRINT, m_context); + connect(m_actionPrint, SIGNAL(triggered()), this, SLOT(print())); + + //'delete' action + command = am->registerAction(m_fwm->actionDelete(), QLatin1String("FormEditor.Edit.Delete"), m_context); + command->setDefaultKeySequence(QKeySequence::Delete); + command->setAttribute(Core::ICommand::CA_Hide); + medit->addAction(command, Core::Constants::G_EDIT_COPYPASTE); + + //editor Modes. Store ids for editor tool bars + m_actionGroupEditMode = new QActionGroup(this); + m_actionGroupEditMode->setExclusive(true); + connect(m_actionGroupEditMode, SIGNAL(triggered(QAction*)), this, SLOT(activateEditMode(QAction*))); + + m_toolActionIds.push_back(QLatin1String("FormEditor.WidgetEditor")); + createEditModeAction(m_actionGroupEditMode, m_context, am, medit, + QLatin1String("Edit widgets"), m_toolActionIds.back(), + EditModeWidgetEditor, QLatin1String("widgettool.png"), tr("F3")); + + m_toolActionIds.push_back(QLatin1String("FormEditor.SignalsSlotsEditor")); + createEditModeAction(m_actionGroupEditMode, m_context, am, medit, + QLatin1String("Edit signals/slots"), m_toolActionIds.back(), + EditModeSignalsSlotEditor, QLatin1String("signalslottool.png"), tr("F4")); + + m_toolActionIds.push_back(QLatin1String("FormEditor.BuddyEditor")); + createEditModeAction(m_actionGroupEditMode, m_context, am, medit, + QLatin1String("Edit buddies"), m_toolActionIds.back(), + EditModeBuddyEditor, QLatin1String("buddytool.png")); + + m_toolActionIds.push_back(QLatin1String("FormEditor.TabOrderEditor")); + createEditModeAction(m_actionGroupEditMode, m_context, am, medit, + QLatin1String("Edit tab order"), m_toolActionIds.back(), + EditModeTabOrderEditor, QLatin1String("tabordertool.png")); + + //tool actions + m_toolActionIds.push_back(QLatin1String("FormEditor.LayoutHorizontally")); + addToolAction(m_fwm->actionHorizontalLayout(), am, m_context, + m_toolActionIds.back(), mformtools, tr("Ctrl+H")); + + m_toolActionIds.push_back(QLatin1String("FormEditor.LayoutVertically")); + addToolAction(m_fwm->actionVerticalLayout(), am, m_context, + m_toolActionIds.back(), mformtools, tr("Ctrl+L")); + + m_toolActionIds.push_back(QLatin1String("FormEditor.SplitHorizontal")); + addToolAction(m_fwm->actionSplitHorizontal(), am, m_context, + m_toolActionIds.back(), mformtools); + + m_toolActionIds.push_back(QLatin1String("FormEditor.SplitVertical")); + addToolAction(m_fwm->actionSplitVertical(), am, m_context, + m_toolActionIds.back(), mformtools); + + m_toolActionIds.push_back(QLatin1String("FormEditor.LayoutForm")); + addToolAction(m_fwm->actionFormLayout(), am, m_context, + m_toolActionIds.back(), mformtools); + + m_toolActionIds.push_back(QLatin1String("FormEditor.LayoutGrid")); + addToolAction(m_fwm->actionGridLayout(), am, m_context, + m_toolActionIds.back(), mformtools, tr("Ctrl+G")); + + m_toolActionIds.push_back(QLatin1String("FormEditor.LayoutBreak")); + addToolAction(m_fwm->actionBreakLayout(), am, m_context, + m_toolActionIds.back(), mformtools); + + m_toolActionIds.push_back(QLatin1String("FormEditor.LayoutAdjustSize")); + addToolAction(m_fwm->actionAdjustSize(), am, m_context, + m_toolActionIds.back(), mformtools, tr("Ctrl+J")); + + m_toolActionIds.push_back(QLatin1String("FormEditor.SimplifyLayout")); + addToolAction(m_fwm->actionSimplifyLayout(), am, m_context, + m_toolActionIds.back(), mformtools); + + createSeparator(this, am, m_context, mformtools, QLatin1String("FormEditor.Menu.Tools.Separator1")); + + addToolAction(m_fwm->actionLower(), am, m_context, + QLatin1String("FormEditor.Lower"), mformtools); + + addToolAction(m_fwm->actionRaise(), am, m_context, + QLatin1String("FormEditor.Raise"), mformtools); + + // Commands that do not go into the editor toolbar + createSeparator(this, am, m_context, mformtools, QLatin1String("FormEditor.Menu.Tools.Separator2")); + + m_actionPreview = m_fwm->actionDefaultPreview(); + Q_ASSERT(m_actionPreview); + addToolAction(m_actionPreview, am, m_context, + QLatin1String("FormEditor.Preview"), mformtools, tr("Ctrl+Alt+R")); + + // Preview in style... + m_actionGroupPreviewInStyle = m_fwm->actionGroupPreviewInStyle(); + mformtools->addMenu(createPreviewStyleMenu(am, m_actionGroupPreviewInStyle)); + + // Disabled since we cannot reliably locate uic. + if (wantCodeGenerationAction) { + m_actionGenerateCode = new QAction(tr("View &code"), this); + addToolAction(m_actionGenerateCode, am, m_context, + QLatin1String("FormEditor.ViewCode"), mformtools); + connect(m_actionGenerateCode, SIGNAL(triggered()), this, SLOT(generateCode())); + } + // Form settings + createSeparator(this, am, m_context, medit, QLatin1String("FormEditor.Edit.Separator2"), Core::Constants::G_EDIT_OTHER); + +#if QT_VERSION >= 0x040500 + createSeparator(this, am, m_context, mformtools, QLatin1String("FormEditor.Menu.Tools.Separator3")); + QAction *actionFormSettings = m_fwm->actionShowFormWindowSettingsDialog(); + addToolAction(actionFormSettings, am, m_context, QLatin1String("FormEditor.FormSettings"), mformtools); +#endif + // FWM + connect(m_fwm, SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface *)), this, SLOT(activeFormWindowChanged(QDesignerFormWindowInterface *))); +} + + +QToolBar *FormEditorW::createEditorToolBar() const +{ + QToolBar *rc = new QToolBar; + rc->addSeparator(); + Core::ActionManagerInterface *am = m_core->actionManager(); + const QStringList::const_iterator cend = m_toolActionIds.constEnd(); + for (QStringList::const_iterator it = m_toolActionIds.constBegin(); it != cend; ++it) { + Core::ICommand *cmd = am->command(*it); + Q_ASSERT(cmd); + QAction *action = cmd->action(); + if (!action->icon().isNull()) // Simplify grid has no action yet + rc->addAction(action); + } + int size = rc->style()->pixelMetric(QStyle::PM_SmallIconSize); + rc->setIconSize(QSize(size, size)); + return rc; +} + +Core::IActionContainer *FormEditorW::createPreviewStyleMenu(Core::ActionManagerInterface *am, + QActionGroup *actionGroup) +{ + const QString menuId = QLatin1String(M_FORMEDITOR_PREVIEW); + Core::IActionContainer *menuPreviewStyle = am->createMenu(menuId); + menuPreviewStyle->menu()->setTitle(tr("Preview in")); + + // The preview menu is a list of invisible actions for the embedded design + // device profiles (integer data) followed by a separator and the styles + // (string data). Make device profiles update their text and hide them + // in the configuration dialog. + const QList<QAction*> actions = actionGroup->actions(); + + const QString deviceProfilePrefix = QLatin1String("DeviceProfile"); + const QChar dot = QLatin1Char('.'); + + foreach(QAction* a, actions) { + QString name = menuId; + name += dot; + const QVariant data = a->data(); + const bool isDeviceProfile = data.type() == QVariant::Int; + if (isDeviceProfile) { + name += deviceProfilePrefix; + name += dot; + } + name += data.toString(); + Core::ICommand *command = am->registerAction(a, name, m_context); + if (isDeviceProfile) { + command->setAttribute(Core::ICommand::CA_UpdateText); + command->setAttribute(Core::ICommand::CA_NonConfigureable); + } + menuPreviewStyle->addAction(command); + } + return menuPreviewStyle; +} + +void FormEditorW::saveSettings(QSettings *s) +{ + s->beginGroup(settingsGroup); + s->setValue(QLatin1String(editorWidgetStateKeyC), EditorWidget::state().toVariant()); + s->endGroup(); +} + +void FormEditorW::restoreSettings(const QSettings *s) +{ + QString key = QLatin1String(settingsGroup) + QLatin1Char('/') + + QLatin1String(editorWidgetStateKeyC); + const QVariant ev = s->value(key); + if (ev.type() != QVariant::Invalid) { + EditorWidgetState st; + if (st.fromVariant(ev)) + EditorWidget::setState(st); + } +} + +void FormEditorW::generateCode() +{ + const FormWindowEditor *fww = activeFormWindow(); + if (!fww) + return; + + bool ok = false; + QString errorMessage; + + do { + QByteArray header; + if (!fww->generateCode(header, errorMessage)) + break; + + QString tempPattern = QDir::tempPath(); + if (!tempPattern.endsWith(QDir::separator())) // platform-dependant + tempPattern += QDir::separator(); + tempPattern += QLatin1String("ui_headerXXXXXX.h"); + QTemporaryFile headerFile(tempPattern); + headerFile.setAutoRemove (false); + if (!headerFile.open() || !headerFile.write(header)) { + errorMessage = tr("Unable to write to a temporary file."); + break; + } + const QString headerFileName = headerFile.fileName(); + headerFile.close(); + Core::IEditor *eif = m_core->editorManager()->openEditor(headerFileName); + if (!eif) { + errorMessage = tr("Unable open %1.").arg(headerFileName); + break; + } + ok = true; + } while (false); + if (!ok) + critical(errorMessage); +} + +void FormEditorW::critical(const QString &errorMessage) +{ + QMessageBox::critical(m_core->mainWindow(), tr("Designer"), errorMessage); +} + +FormWindowEditor *FormEditorW::createFormWindowEditor(QWidget* parentWidget) +{ + m_fwm->closeAllPreviews(); + QDesignerFormWindowInterface *form = m_fwm->createFormWindow(0); + connect(form, SIGNAL(toolChanged(int)), this, SLOT(toolChanged(int))); + qdesigner_internal::FormWindowBase::setupDefaultAction(form); + FormWindowEditor *fww = new FormWindowEditor(m_core, m_context, form, parentWidget); + // Store a pointer to all form windows so we can unselect all other formwindows except the active one. + m_formWindows.append(fww); + connect(fww, SIGNAL(destroyed()), this, SLOT(editorDestroyed())); + return fww; +} + +void FormEditorW::editorDestroyed() +{ + QObject *source = sender(); + + if (debugFormEditor) + qDebug() << "FormEditorW::editorDestroyed()" << source; + + for (EditorList::iterator it = m_formWindows.begin(); it != m_formWindows.end(); ) { + if (*it == source) { + it = m_formWindows.erase(it); + break; + } else { + ++it; + } + } +} + +void FormEditorW::currentEditorChanged(Core::IEditor *editor) +{ + if (debugFormEditor) + qDebug() << "FormEditorW::currentEditorChanged" << editor << " of " << m_fwm->formWindowCount(); + + // Deactivate Designer if a non-form is being edited + if (editor && !qstrcmp(editor->kind(), Constants::C_FORMWINDOW)) { + FormWindowEditor *fw = qobject_cast<FormWindowEditor *>(editor); + Q_ASSERT(fw); + fw->activate(); + m_fwm->setActiveFormWindow(fw->formWindow()); + } else { + m_fwm->setActiveFormWindow(0); + } +} + +void FormEditorW::activeFormWindowChanged(QDesignerFormWindowInterface *afw) +{ + if (debugFormEditor) + qDebug() << "FormEditorW::activeFormWindowChanged" << afw << " of " << m_fwm->formWindowCount() << m_formWindows; + + m_fwm->closeAllPreviews(); + + bool foundFormWindow = false; + // Display form selection handles only on active window + EditorList::const_iterator cend = m_formWindows.constEnd(); + for (EditorList::const_iterator it = m_formWindows.constBegin(); it != cend ; ++it) { + FormWindowEditor *fwe = *it; + const bool active = fwe->formWindow() == afw; + if (active) + foundFormWindow = true; + fwe->updateFormWindowSelectionHandles(active); + } + + m_actionPreview->setEnabled(foundFormWindow); + if (m_actionGenerateCode) + m_actionGenerateCode->setEnabled(foundFormWindow); + m_actionGroupPreviewInStyle->setEnabled(foundFormWindow); +} + +FormWindowEditor *FormEditorW::activeFormWindow() +{ + QDesignerFormWindowInterface *afw = m_fwm->activeFormWindow(); + for (int i = 0; i < m_formWindows.count(); ++i) { + if (FormWindowEditor *fw = m_formWindows[i]) { + QDesignerFormWindowInterface *fwd = fw->formWindow(); + if (fwd == afw) { + return fw; + } + } + } + return 0; +} + +void FormEditorW::activateEditMode(int id) +{ + if (const int count = m_fwm->formWindowCount()) + for (int i = 0; i < count; i++) + m_fwm->formWindow(i)->setCurrentTool(id); +} + +void FormEditorW::activateEditMode(QAction* a) +{ + activateEditMode(a->data().toInt()); +} + +void FormEditorW::toolChanged(int t) +{ + typedef QList<QAction *> ActionList; + if (const QAction *currentAction = m_actionGroupEditMode->checkedAction()) + if (currentAction->data().toInt() == t) + return; + const ActionList actions = m_actionGroupEditMode->actions(); + const ActionList::const_iterator cend = actions.constEnd(); + for (ActionList::const_iterator it = actions.constBegin(); it != cend; ++it) + if ( (*it)->data().toInt() == t) { + (*it)->setChecked(true); + break; + } +} + +void FormEditorW::print() +{ + // Printing code courtesy of designer_actions.cpp + QDesignerFormWindowInterface *fw = m_fwm->activeFormWindow(); + if (!fw) + return; + + const bool oldFullPage = m_core->printer()->fullPage(); + const QPrinter::Orientation oldOrientation = m_core->printer()->orientation (); + m_core->printer()->setFullPage(false); + do { + + // Grab the image to be able to a suggest suitable orientation + QString errorMessage; + const QPixmap pixmap = m_fwm->createPreviewPixmap(&errorMessage); + if (pixmap.isNull()) { + critical(tr("The image could not be create: %1").arg(errorMessage)); + break; + } + + const QSizeF pixmapSize = pixmap.size(); + m_core->printer()->setOrientation( pixmapSize.width() > pixmapSize.height() ? QPrinter::Landscape : QPrinter::Portrait); + + // Printer parameters + QPrintDialog dialog(m_core->printer(), fw); + if (!dialog.exec()) + break; + + const QCursor oldCursor = m_core->mainWindow()->cursor(); + m_core->mainWindow()->setCursor(Qt::WaitCursor); + // Estimate of required scaling to make form look the same on screen and printer. + const double suggestedScaling = static_cast<double>(m_core->printer()->physicalDpiX()) / static_cast<double>(fw->physicalDpiX()); + + QPainter painter(m_core->printer()); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + + // Clamp to page + const QRectF page = painter.viewport(); + const double maxScaling = qMin(page.size().width() / pixmapSize.width(), page.size().height() / pixmapSize.height()); + const double scaling = qMin(suggestedScaling, maxScaling); + + const double xOffset = page.left() + qMax(0.0, (page.size().width() - scaling * pixmapSize.width()) / 2.0); + const double yOffset = page.top() + qMax(0.0, (page.size().height() - scaling * pixmapSize.height()) / 2.0); + + // Draw. + painter.translate(xOffset, yOffset); + painter.scale(scaling, scaling); + painter.drawPixmap(0, 0, pixmap); + m_core->mainWindow()->setCursor(oldCursor); + + m_core->statusBar()->showMessage(tr("Printed %1...").arg(QFileInfo(fw->fileName()).fileName())); + } while (false); + m_core->printer()->setFullPage(oldFullPage); + m_core->printer()->setOrientation(oldOrientation); +} diff --git a/src/plugins/designer/formeditorw.h b/src/plugins/designer/formeditorw.h new file mode 100644 index 00000000000..b8434b2c5d6 --- /dev/null +++ b/src/plugins/designer/formeditorw.h @@ -0,0 +1,175 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FORMEDITORW_H +#define FORMEDITORW_H + +#include <extensionsystem/ExtensionSystemInterfaces> +#include <QtDesigner/QDesignerFormEditorInterface> + +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QtCore/QPointer> + +#include "designerconstants.h" + +QT_BEGIN_NAMESPACE + +class QDesignerIntegrationInterface; +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; + +class QAction; +class QActionGroup; +class QFocusEvent; + +class QWidget; +class QSignalMapper; +class QSettings; +class QToolBar; + +namespace qdesigner_internal { + class QDesignerFormWindowManager; +} + +QT_END_NAMESPACE + +namespace Core { +class ActionManagerInterface; +class IActionContainer; +class ICore; +class IEditor; +} + +namespace Designer { +namespace Internal { + +class FormWindowEditor; +class SettingsPage; + + +/** FormEditorW is a singleton that stores the Designer CoreInterface and + * performs centralized operations. The instance() method will return an + * instance. However, it must be manually deleted when unloading the + * plugin. Since fully initializing Designer at startup is expensive, the + * class has an internal partial initialisation stage "RegisterPlugins" + * which is there to register the Creator plugin objects + * that must be present at startup (settings pages, actions). + * The plugin uses this stage at first by calling ensureInitStage(). + * Requesting an editor via instance() will fully initialize the class. + * This is based on the assumption that the Designer settings work with + * no plugins loaded. If that does not work, full initialization can be + * triggered by connection to the ICore::settingsDialogRequested() signal. + */ +class FormEditorW : public QObject +{ + Q_OBJECT +public: + enum InitializationStage { + // Register Creator plugins (settings pages, actions) + RegisterPlugins, + // Fully initialized for handling editor requests + FullyInitialized + }; + + virtual ~FormEditorW(); + + // Create an instance and initialize up to stage s + static void ensureInitStage(InitializationStage s); + // Returns fully initialized instance + static FormEditorW *instance(); + // Deletes an existing instance if there is one. + static void deleteInstance(); + + inline QDesignerFormEditorInterface *designerEditor() const { return m_formeditor; } + inline QWidget * const*designerSubWindows() const { return m_designerSubWindows; } + QToolBar *createEditorToolBar() const; + + FormWindowEditor *createFormWindowEditor(QWidget* parentWidget); + + FormWindowEditor *activeFormWindow(); + +private slots: + void generateCode(); + void activateEditMode(int id); + void activateEditMode(QAction*); + void activeFormWindowChanged(QDesignerFormWindowInterface *); + void currentEditorChanged(Core::IEditor *editor); + void toolChanged(int); + void print(); + + void editorDestroyed(); + +private: + FormEditorW(); + void fullInit(); + + void saveSettings(QSettings *s); + void restoreSettings(const QSettings *s); + + void initDesignerSubWindows(); + + typedef QList<FormWindowEditor *> EditorList; + + void setupActions(); + Core::IActionContainer *createPreviewStyleMenu(Core::ActionManagerInterface *am, + QActionGroup *actionGroup); + + void critical(const QString &errorMessage); + + static FormEditorW *m_self; + + QDesignerFormEditorInterface *m_formeditor; + QDesignerIntegrationInterface *m_integration; + qdesigner_internal::QDesignerFormWindowManager *m_fwm; + Core::ICore *m_core; + InitializationStage m_initStage; + + QWidget *m_designerSubWindows[Designer::Constants::DesignerSubWindowCount]; + + QList<SettingsPage *> m_settingsPages; + QActionGroup *m_actionGroupEditMode; + QAction *m_actionPrint; + QAction *m_actionGenerateCode; + QAction *m_actionPreview; + QActionGroup *m_actionGroupPreviewInStyle; + + QList<int> m_context; + + EditorList m_formWindows; + QStringList m_toolActionIds; +}; + +} // namespace Internal +} // namespace Designer + +#endif // FORMEDITORW_H diff --git a/src/plugins/designer/formtemplatewizardpage.cpp b/src/plugins/designer/formtemplatewizardpage.cpp new file mode 100644 index 00000000000..a819502b215 --- /dev/null +++ b/src/plugins/designer/formtemplatewizardpage.cpp @@ -0,0 +1,330 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "formtemplatewizardpage.h" +#include "formeditorw.h" + +#include <QtDesigner/private/abstractnewformwidget_p.h> + +#include <QtCore/QDebug> +#include <QtCore/QXmlStreamReader> +#include <QtCore/QXmlStreamAttributes> +#include <QtCore/QByteArray> +#include <QtCore/QBuffer> + +#include <QtGui/QVBoxLayout> +#include <QtGui/QMessageBox> +#include <QtGui/QAbstractButton> + +#ifdef USE_XSLT +# include <QtXmlPatterns/QXmlQuery> +#else +# include <QtXml/QDomDocument> +#endif + +namespace Designer { +namespace Internal { + +// ----------------- FormTemplateWizardPagePage + +FormTemplateWizardPagePage::FormTemplateWizardPagePage(QWidget * parent) : + QWizardPage(parent), + m_newFormWidget(QDesignerNewFormWidgetInterface::createNewFormWidget(FormEditorW::instance()->designerEditor())), + m_templateSelected(m_newFormWidget->hasCurrentTemplate()) +{ + setTitle(tr("Choose a form template")); + QVBoxLayout *layout = new QVBoxLayout; + + connect(m_newFormWidget, SIGNAL(currentTemplateChanged(bool)), + this, SLOT(slotCurrentTemplateChanged(bool))); + connect(m_newFormWidget, SIGNAL(templateActivated()), + this, SIGNAL(templateActivated())); + layout->addWidget(m_newFormWidget); + + setLayout(layout); +} + +bool FormTemplateWizardPagePage::isComplete() const +{ + return m_templateSelected; +} + +void FormTemplateWizardPagePage::slotCurrentTemplateChanged(bool templateSelected) +{ + if (m_templateSelected == templateSelected) + return; + m_templateSelected = templateSelected; + emit completeChanged(); +} + +bool FormTemplateWizardPagePage::validatePage() +{ + QString errorMessage; + m_templateContents = m_newFormWidget->currentTemplate(&errorMessage); + if (m_templateContents.isEmpty()) { + QMessageBox::critical(this, tr("%1 - Error").arg(title()), errorMessage); + return false; + } + return true; +} + +QString FormTemplateWizardPagePage::stripNamespaces(const QString &className) +{ + QString rc = className; + const int namespaceIndex = rc.lastIndexOf(QLatin1String("::")); + if (namespaceIndex != -1) + rc.remove(0, namespaceIndex + 2); + return rc; +} + +bool FormTemplateWizardPagePage::getUIXmlData(const QString &uiXml, + QString *formBaseClass, + QString *uiClassName) +{ + // Parse UI xml to determine + // 1) The ui class name from "<class>Designer::Internal::FormClassWizardPage</class>" + // 2) the base class from: widget class="QWizardPage"... + QXmlStreamReader reader(uiXml); + while (!reader.atEnd()) { + if (reader.readNext() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("class")) { + *uiClassName = reader.readElementText(); + } else { + if (reader.name() == QLatin1String("widget")) { + const QXmlStreamAttributes attrs = reader.attributes(); + *formBaseClass = reader.attributes().value(QLatin1String("class")).toString(); + return !uiClassName->isEmpty() && !formBaseClass->isEmpty(); + } + } + } + } + return false; +} + +#ifdef USE_XSLT + +// Change the UI class name in UI xml: This occurs several times, as contents +// of the <class> element, as name of the first <widget> element, and possibly +// in the signal/slot connections + +static const char *classNameChangingSheetFormatC = +"<?xml version=\"1.0\"?>\n" +"<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"2.0\">\n" +"<xsl:output method=\"xml\" indent=\"yes\" encoding=\"UTF-8\" />\n" +"\n" +"<!-- Grab old class name to be replaced by evaluating <ui><class> -->\n" +"<xsl:param name=\"oldClassName\"><xsl:value-of select=\"/ui/class\"/></xsl:param>\n" +"\n" +"<!-- heavy wizardry to copy nodes and attributes (emulating the default rules) -->\n" +"<xsl:template match=\"@*|node()\">\n" +" <xsl:copy>\n" +" <xsl:apply-templates select=\"@*|node()\"/>\n" +" </xsl:copy>\n" +"</xsl:template>\n" +"\n" +"<!-- Change <ui><class> tag -->\n" +"<xsl:template match=\"class\">\n" +" <xsl:element name=\"class\">\n" +" <xsl:text>%1</xsl:text>\n" +" </xsl:element>\n" +"</xsl:template>\n" +"\n" +"<!-- Change first <widget> tag -->\n" +"<xsl:template match=\"/ui/widget/@name\">\n" +"<xsl:attribute name=\"name\">\n" +"<xsl:text>%1</xsl:text>\n" +"</xsl:attribute>\n" +"</xsl:template>\n" +"\n" +"<!-- Change <sender>/<receiver> elements (for pre-wired QDialog signals) -->\n" +"<xsl:template match=\"receiver[.='Dialog']\">\n" +" <xsl:element name=\"receiver\">\n" +" <xsl:text>%1</xsl:text>\n" +" </xsl:element>\n" +"</xsl:template>\n" +"<xsl:template match=\"sender[.='Dialog']\">\n" +" <xsl:element name=\"sender\">\n" +" <xsl:text>%1</xsl:text>\n" +" </xsl:element>\n" +"</xsl:template>\n" +"</xsl:stylesheet>\n"; + +QString FormTemplateWizardPagePage::changeUiClassName(const QString &uiXml, const QString &newUiClassName) +{ + // Prepare I/O: Sheet + const QString xsltSheet = QString::fromLatin1(classNameChangingSheetFormatC).arg(newUiClassName); + QByteArray xsltSheetBA = xsltSheet.toUtf8(); + QBuffer xsltSheetBuffer(&xsltSheetBA); + xsltSheetBuffer.open(QIODevice::ReadOnly); + // Prepare I/O: Xml + QByteArray xmlBA = uiXml.toUtf8(); + QBuffer xmlBuffer(&xmlBA); + xmlBuffer.open(QIODevice::ReadOnly); + // Prepare I/O: output + QBuffer outputBuffer; + outputBuffer.open(QIODevice::WriteOnly); + + // Run query + QXmlQuery query(QXmlQuery::XSLT20); + query.setFocus(&xmlBuffer); + query.setQuery(&xsltSheetBuffer); + if (!query.evaluateTo(&outputBuffer)) { + qWarning("Unable to change the ui class name in a form template.\n%s\nUsing:\n%s\n", + xmlBA.constData(), xsltSheetBA.constData()); + return uiXml; + } + outputBuffer.close(); + return QString::fromUtf8(outputBuffer.data()); +} +#else + +// Change the contents of a DOM element to a new value if it matches +// a predicate +template <class Predicate> +bool changeDomElementContents(const QDomElement &element, + Predicate p, + const QString &newValue, + QString *ptrToOldValue = 0) +{ + // Find text in "<element>text</element>" + const QDomNodeList children = element.childNodes(); + if (children.size() != 1) + return false; + const QDomNode first = children.at(0); + if (first.nodeType() != QDomNode::TextNode) + return false; + QDomCharacterData data = first.toCharacterData(); + const QString oldValue = data.data(); + + if (p(oldValue)) { + if (ptrToOldValue) + *ptrToOldValue = oldValue; + data.setData(newValue); + return true; + } + return false; +} + +namespace { + bool truePredicate(const QString &) { return true; } + + // Predicate that matches a string value + class MatchPredicate { + public: + MatchPredicate(const QString &m) : m_match(m) {} + bool operator()(const QString &s) const { return s == m_match; } + private: + const QString m_match; + }; + + // Change <sender> and <receiver> in a Dom UI <connections> list + // if they match the class name passed on + void changeDomConnectionList(const QDomElement &connectionsNode, + const QString &oldClassName, + const QString &newClassName) + { + const MatchPredicate oldClassPredicate(oldClassName); + const QString senderTag = QLatin1String("sender"); + const QString receiverTag = QLatin1String("receiver"); + const QDomNodeList connections = connectionsNode.childNodes(); + const int connectionsCount = connections.size(); + // Loop <connection> + for (int c = 0; c < connectionsCount; c++) { + const QDomNodeList connectionElements = connections.at(c).childNodes(); + const int connectionElementCount = connectionElements.count(); + // Loop <sender>, <receiver>, <signal>, <slot> + for (int ce = 0; ce < connectionElementCount; ce++) { + const QDomNode connectionElementNode = connectionElements.at(ce); + if (connectionElementNode.isElement()) { + const QDomElement connectionElement = connectionElementNode.toElement(); + const QString tagName = connectionElement.tagName(); + if (tagName == senderTag || tagName == receiverTag) + changeDomElementContents(connectionElement, oldClassPredicate, newClassName); + } + } + } + } +} + +// Change the UI class name in UI xml: This occurs several times, as contents +// of the <class> element, as name of the first <widget> element, and possibly +// in the signal/slot connections + +QString FormTemplateWizardPagePage::changeUiClassName(const QString &uiXml, const QString &newUiClassName) +{ + QDomDocument domUi; + if (!domUi.setContent(uiXml)) { + qWarning("Failed to parse:\n%s", uiXml.toUtf8().constData()); + return uiXml; + } + + bool firstWidgetElementFound = false; + QString oldClassName; + + // Loop first level children. First child is <ui> + const QDomNodeList children = domUi.firstChildElement().childNodes(); + const QString classTag = QLatin1String("class"); + const QString widgetTag = QLatin1String("widget"); + const QString connectionsTag = QLatin1String("connections"); + const int count = children.size(); + for (int i = 0; i < count; i++) { + const QDomNode node = children.at(i); + if (node.isElement()) { + // Replace <class> element text + QDomElement element = node.toElement(); + const QString name = element.tagName(); + if (name == classTag) { + if (!changeDomElementContents(element, truePredicate, newUiClassName, &oldClassName)) { + qWarning("Unable to change the <class> element:\n%s", uiXml.toUtf8().constData()); + return uiXml; + } + } else { + // Replace first <widget> element name attribute + if (!firstWidgetElementFound && name == widgetTag) { + firstWidgetElementFound = true; + const QString nameAttribute = QLatin1String("name"); + if (element.hasAttribute(nameAttribute)) + element.setAttribute(nameAttribute, newUiClassName); + } else { + // Replace <sender>, <receiver> tags of dialogs. + if (name == connectionsTag) + changeDomConnectionList(element, oldClassName, newUiClassName); + } + } + } + } + const QString rc = domUi.toString(); + return rc; +} +#endif +} // namespace Internal +} // namespace Designer diff --git a/src/plugins/designer/formtemplatewizardpage.h b/src/plugins/designer/formtemplatewizardpage.h new file mode 100644 index 00000000000..2476c9b6e80 --- /dev/null +++ b/src/plugins/designer/formtemplatewizardpage.h @@ -0,0 +1,79 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FORMWIZARDPAGE_H +#define FORMWIZARDPAGE_H + +#include <QtGui/QWizardPage> + +QT_BEGIN_NAMESPACE +class QDesignerNewFormWidgetInterface; +QT_END_NAMESPACE + +namespace Designer { +namespace Internal { + +// A wizard page embedding Qt Designer's QDesignerNewFormWidgetInterface +// widget. + +class FormTemplateWizardPagePage : public QWizardPage +{ + Q_DISABLE_COPY(FormTemplateWizardPagePage) + Q_OBJECT +public: + explicit FormTemplateWizardPagePage(QWidget * parent = 0); + + virtual bool isComplete () const; + virtual bool validatePage(); + + QString templateContents() const { return m_templateContents; } + + static bool getUIXmlData(const QString &uiXml, QString *formBaseClass, QString *uiClassName); + static QString changeUiClassName(const QString &uiXml, const QString &newUiClassName); + static QString stripNamespaces(const QString &className); + +signals: + void templateActivated(); + +private slots: + void slotCurrentTemplateChanged(bool); + +private: + QString m_templateContents; + QDesignerNewFormWidgetInterface *m_newFormWidget; + bool m_templateSelected; +}; + +} // namespace Internal +} // namespace Designer + +#endif //FORMTEMPLATEWIZARDPAGE_H diff --git a/src/plugins/designer/formwindoweditor.cpp b/src/plugins/designer/formwindoweditor.cpp new file mode 100644 index 00000000000..dde574e96f2 --- /dev/null +++ b/src/plugins/designer/formwindoweditor.cpp @@ -0,0 +1,355 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "designerconstants.h" +#include "editorwidget.h" +#include "formeditorw.h" +#include "formwindoweditor.h" +#include "formwindowfile.h" +#include "formwindowhost.h" + +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/projectnodes.h> +#include <projectexplorer/nodesvisitor.h> + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowManagerInterface> +#include <QtDesigner/private/formwindowbase_p.h> +#include <QtDesigner/private/qtresourcemodel_p.h> + +#include <QtCore/QFile> +#include <QtCore/QDir> +#include <QtCore/QByteArray> +#include <QtCore/QFileInfo> +#include <QtCore/QTemporaryFile> +#include <QtCore/QDebug> +#include <QtGui/QToolBar> + +using namespace Designer::Internal; +using namespace Designer::Constants; +using namespace SharedTools; +using ProjectExplorer::NodesVisitor; +using ProjectExplorer::ProjectNode; +using ProjectExplorer::FolderNode; +using ProjectExplorer::FileNode; + +enum { debugFormWindowEditor = 0 }; + +class QrcFilesVisitor : public NodesVisitor { +public: + QStringList qrcFiles() const; + + void visitProjectNode(ProjectNode *node); + void visitFolderNode(FolderNode *node); +private: + QStringList m_qrcFiles; +}; + +QStringList QrcFilesVisitor::qrcFiles() const +{ + return m_qrcFiles; +} + +void QrcFilesVisitor::visitProjectNode(ProjectNode *projectNode) +{ + visitFolderNode(projectNode); +} + +void QrcFilesVisitor::visitFolderNode(FolderNode *folderNode) +{ + foreach (const FileNode *fileNode, folderNode->fileNodes()) { + if (fileNode->fileType() == ProjectExplorer::ResourceType) + m_qrcFiles.append(fileNode->path()); + } +} + + +FormWindowEditor::FormWindowEditor(Core::ICore *core, + const QList<int> &context, + QDesignerFormWindowInterface *form, + QObject *parent) : + Core::IEditor(parent), + m_context(context), + m_formWindow(form), + m_file(new FormWindowFile(core, form, this)), + m_host(new FormWindowHost(form)), + m_editorWidget(new EditorWidget(m_host)), + m_toolBar(0), + m_sessionNode(0), + m_sessionWatcher(0) +{ + if (debugFormWindowEditor) + qDebug() << "FormWindowEditor::FormWindowEditor" << form << parent; + + connect(m_file, SIGNAL(reload(QString)), this, SLOT(slotOpen(QString))); + connect(m_file, SIGNAL(setDisplayName(QString)), this, SLOT(slotSetDisplayName(QString))); + connect(m_file, SIGNAL(changed()), this, SIGNAL(changed())); + connect(m_file, SIGNAL(changed()), this, SLOT(updateResources())); + connect(this, SIGNAL(opened(QString)), m_file, SLOT(setFileName(QString))); + + connect(m_host, SIGNAL(changed()), this, SIGNAL(changed())); + + connect(form, SIGNAL(toolChanged(int)), m_editorWidget, SLOT(toolChanged(int))); + m_editorWidget->activate(); +} + +FormWindowEditor::~FormWindowEditor() +{ + // Close: Delete the Designer form window via embedding widget + delete m_toolBar; + delete m_host; + delete m_editorWidget; + if (debugFormWindowEditor) + qDebug() << "FormWindowEditor::~FormWindowEditor" << m_displayName; + if (m_sessionNode && m_sessionWatcher) { + m_sessionNode->unregisterWatcher(m_sessionWatcher); + delete m_sessionWatcher; + } +} + +bool FormWindowEditor::createNew(const QString &contents) +{ + if (debugFormWindowEditor) + qDebug() << "FormWindowEditor::createNew()" << contents.size() << "chars"; + + if (!m_formWindow) + return false; + + m_formWindow->setContents(contents); + if (!m_formWindow->mainContainer()) + return false; + + if (qdesigner_internal::FormWindowBase *fw = qobject_cast<qdesigner_internal::FormWindowBase *>(m_formWindow)) + fw->setDesignerGrid(qdesigner_internal::FormWindowBase::defaultDesignerGrid()); + return true; +} + +bool FormWindowEditor::open(const QString &fileName /*= QString()*/) +{ + if (debugFormWindowEditor) + qDebug() << "FormWindowEditor::open" << fileName; + + if (fileName.isEmpty()) { + setDisplayName(tr("untitled")); + } else { + const QFileInfo fi(fileName); + const QString fileName = fi.absoluteFilePath(); + + QFile file(fileName); + if (!file.exists()) + return false; + + if (!fi.isReadable()) + return false; + + if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) + return false; + + m_formWindow->setFileName(fileName); + m_formWindow->setContents(&file); + file.close(); + if (!m_formWindow->mainContainer()) + return false; + m_formWindow->setDirty(false); + + ProjectExplorer::ProjectExplorerPlugin *pe = ProjectExplorer::ProjectExplorerPlugin::instance(); + ProjectExplorer::SessionManager *session = pe->session(); + m_sessionNode = session->sessionNode(); + m_sessionWatcher = new ProjectExplorer::NodesWatcher(); + connect(m_sessionWatcher, SIGNAL(filesAdded()), this, SLOT(updateResources())); + connect(m_sessionWatcher, SIGNAL(filesRemoved()), this, SLOT(updateResources())); + connect(m_sessionWatcher, SIGNAL(foldersAdded()), this, SLOT(updateResources())); + connect(m_sessionWatcher, SIGNAL(foldersRemoved()), this, SLOT(updateResources())); + m_sessionNode->registerWatcher(m_sessionWatcher); + + if (qdesigner_internal::FormWindowBase *fw = qobject_cast<qdesigner_internal::FormWindowBase *>(m_formWindow)) { + QtResourceSet *rs = fw->resourceSet(); + m_originalUiQrcPaths = rs->activeQrcPaths(); + } + + emit opened(fileName); + updateResources(); + + QDesignerFormWindowManagerInterface *fwm = FormEditorW::instance()->designerEditor()->formWindowManager(); + fwm->setActiveFormWindow(m_formWindow); + + setDisplayName(fi.fileName()); + } + emit changed(); + return true; +} + +void FormWindowEditor::updateResources() +{ + if (qdesigner_internal::FormWindowBase *fw = qobject_cast<qdesigner_internal::FormWindowBase *>(m_formWindow)) { + ProjectExplorer::ProjectExplorerPlugin *pe = ProjectExplorer::ProjectExplorerPlugin::instance(); + // filename could change in the meantime. + ProjectExplorer::Project *project = pe->session()->projectForFile(m_file->fileName()); + + qdesigner_internal::FormWindowBase::SaveResourcesBehaviour behaviour = qdesigner_internal::FormWindowBase::SaveAll; + QtResourceSet *rs = fw->resourceSet(); + if (project) { + ProjectNode *root = project->rootProjectNode(); + QrcFilesVisitor qrcVisitor; + root->accept(&qrcVisitor); + + rs->activateQrcPaths(qrcVisitor.qrcFiles()); + behaviour = qdesigner_internal::FormWindowBase::SaveOnlyUsedQrcFiles; + } else { + rs->activateQrcPaths(m_originalUiQrcPaths); + } + fw->setSaveResourcesBehaviour(behaviour); + } +} + +void FormWindowEditor::slotOpen(const QString &fileName) +{ + open(fileName); +} + +void FormWindowEditor::slotSetDisplayName(const QString &title) +{ + if (debugFormWindowEditor) + qDebug() << "FormWindowEditor::slotSetDisplayName" << title; + setDisplayName(title); +} + +bool FormWindowEditor::duplicateSupported() const +{ + return false; +} + +Core::IEditor *FormWindowEditor::duplicate(QWidget *) +{ + return 0; +} + +Core::IFile *FormWindowEditor::file() +{ + return m_file; +} + +const char *FormWindowEditor::kind() const +{ + return C_FORMWINDOW; +} + +QString FormWindowEditor::displayName() const +{ + return m_displayName; +} + +void FormWindowEditor::setDisplayName(const QString &title) +{ + m_displayName = title; +} + +QToolBar *FormWindowEditor::toolBar() +{ + if (!m_toolBar) + m_toolBar = FormEditorW::instance()->createEditorToolBar(); + return m_toolBar; +} + +QByteArray FormWindowEditor::saveState() const +{ + return QByteArray(); +} + +bool FormWindowEditor::restoreState(const QByteArray &/*state*/) +{ + return true; +} + +QList<int> FormWindowEditor::context() const +{ + return m_context; +} + +QWidget *FormWindowEditor::widget() +{ + return m_editorWidget; +} + +bool FormWindowEditor::generateCode(QByteArray &header, QString &errorMessage) const +{ + if (debugFormWindowEditor) + qDebug() << "FormWindowEditor::generateCode"; + + QString tempPattern = QDir::tempPath(); + if (!tempPattern.endsWith(QDir::separator())) // platform-dependant + tempPattern += QDir::separator(); + tempPattern += QLatin1String("formXXXXXX.ui"); + QTemporaryFile uiFile(tempPattern); + uiFile.setAutoRemove(true); + if (!uiFile.open()) { + errorMessage = tr("Unable to write to a temporary file."); + return false; + } + if (!m_file->writeFile(uiFile, errorMessage)) { + errorMessage = tr("Unable to write to a temporary file."); + return false; + } + const QString uiFileName = uiFile.fileName(); + uiFile.close(); + + if (!qdesigner_internal::runUIC(uiFileName, qdesigner_internal::UIC_GenerateCode, header, errorMessage)) + return false; + + return true; +} + +QDesignerFormWindowInterface *FormWindowEditor::formWindow() const +{ + return m_formWindow; +} + +QWidget *FormWindowEditor::integrationContainer() +{ + return m_host->integrationContainer(); +} + +void FormWindowEditor::updateFormWindowSelectionHandles(bool state) +{ + m_host->updateFormWindowSelectionHandles(state); +} + +void FormWindowEditor::setSuggestedFileName(const QString &fileName) +{ + m_file->setSuggestedFileName(fileName); +} + +void FormWindowEditor::activate() +{ + m_editorWidget->activate(); +} diff --git a/src/plugins/designer/formwindoweditor.h b/src/plugins/designer/formwindoweditor.h new file mode 100644 index 00000000000..6cffbdf5df3 --- /dev/null +++ b/src/plugins/designer/formwindoweditor.h @@ -0,0 +1,128 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FORMWINDOWEDITOR_H +#define FORMWINDOWEDITOR_H + +#include <coreplugin/editormanager/ieditor.h> + +#include <QtCore/QByteArray> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE +class QDesignerFormWindowInterface; +class QDesignerFormWindowManagerInterface; +class QFile; +QT_END_NAMESPACE + +namespace Core { +class ICore; +} + +namespace ProjectExplorer { +class SessionNode; +class NodesWatcher; +} + +namespace Designer { +namespace Internal { + +class FormWindowFile; +class FormWindowHost; +class EditorWidget; +// Master class maintaining a form window editor, +// containing file and widget host + +class FormWindowEditor : public Core::IEditor +{ + Q_OBJECT + +public: + FormWindowEditor(Core::ICore *core, + const QList<int> &context, + QDesignerFormWindowInterface *form, + QObject *parent = 0); + ~FormWindowEditor(); + + // IEditor + bool createNew(const QString &contents); + bool open(const QString &fileName = QString()); + bool duplicateSupported() const; + Core::IEditor *duplicate(QWidget *); + Core::IFile *file(); + const char *kind() const; + QString displayName() const; + void setDisplayName(const QString &title); + QToolBar *toolBar(); + QByteArray saveState() const; + bool restoreState(const QByteArray &/*state*/); + + // ContextInterface + QList<int> context() const; + QWidget *widget(); + + // Internal + bool generateCode(QByteArray &header, QString &errorMessage) const; + QDesignerFormWindowInterface *formWindow() const; + QWidget *integrationContainer(); + void updateFormWindowSelectionHandles(bool state); + void setSuggestedFileName(const QString &fileName); + +signals: + // Internal + void opened(const QString &fileName); + +public slots: + void activate(); + +private slots: + void slotOpen(const QString &fileName); + void slotSetDisplayName(const QString &title); + void updateResources(); + +private: + QString m_displayName; + const QList<int> m_context; + QDesignerFormWindowInterface *m_formWindow; + FormWindowFile *m_file; + FormWindowHost *m_host; + EditorWidget *m_editorWidget; + QToolBar *m_toolBar; + QStringList m_originalUiQrcPaths; + ProjectExplorer::SessionNode *m_sessionNode; + ProjectExplorer::NodesWatcher *m_sessionWatcher; +}; + +} // namespace Internal +} // namespace Designer + +#endif //FORMWINDOWEDITOR_H diff --git a/src/plugins/designer/formwindowfile.cpp b/src/plugins/designer/formwindowfile.cpp new file mode 100644 index 00000000000..8ee26e4ac69 --- /dev/null +++ b/src/plugins/designer/formwindowfile.cpp @@ -0,0 +1,210 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "formwindowfile.h" +#include "designerconstants.h" + +#include <coreplugin/icore.h> +#include <utils/reloadpromptutils.h> + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormEditorInterface> + +#include <QtGui/QMessageBox> +#include <QtGui/QMainWindow> + +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QByteArray> +#include <QtCore/QDebug> + +using namespace Designer::Internal; +using namespace Designer::Constants; +using namespace SharedTools; + +enum { debugFormWindowFile = 0 }; + + +FormWindowFile::FormWindowFile(Core::ICore *core, + QDesignerFormWindowInterface *form, + QObject *parent) : + Core::IFile(parent), + m_mimeType(QLatin1String(FORM_MIMETYPE)), + m_formWindow(form), + m_core(core) +{ +} + +FormWindowFile::~FormWindowFile() +{ +} + +bool FormWindowFile::save(const QString &name /*= QString()*/) +{ + const QString actualName = name.isEmpty() ? fileName() : name; + + if (debugFormWindowFile) + qDebug() << "FormWindowFile::save" << name << "->" << actualName; + + if (actualName.isEmpty()) + return false; + + const QFileInfo fi(actualName); + const QString oldFormName = m_formWindow->fileName(); + const QString formName = fi.absoluteFilePath(); + m_formWindow->setFileName(formName); + + QString errorString; + if (!writeFile(actualName, errorString)) { + QMessageBox::critical(0, tr("Error saving %1").arg(formName), errorString); + m_formWindow->setFileName(oldFormName); + return false; + } + + m_fileName = fi.absoluteFilePath(); + emit setDisplayName(fi.fileName()); + m_formWindow->setDirty(false); + emit changed(); + + return true; +} + +QString FormWindowFile::fileName() const +{ + return m_fileName; +} + +bool FormWindowFile::isModified() const +{ + return m_formWindow->isDirty(); +} + +bool FormWindowFile::isReadOnly() const +{ + if (m_fileName.isEmpty()) + return false; + const QFileInfo fi(m_fileName); + return !fi.isWritable(); +} + +bool FormWindowFile::isSaveAsAllowed() const +{ + return true; +} + +void FormWindowFile::modified(Core::IFile::ReloadBehavior *behavior) +{ + if (debugFormWindowFile) + qDebug() << "FormWindowFile::modified" << m_fileName << *behavior; + + switch (*behavior) { + case Core::IFile::ReloadNone: + return; + case Core::IFile::ReloadAll: + emit reload(m_fileName); + return; + case Core::IFile::ReloadPermissions: + emit changed(); + return; + case Core::IFile::AskForReload: + break; + } + + switch (Core::Utils::reloadPrompt(m_fileName, m_core->mainWindow())) { + case Core::Utils::ReloadCurrent: + emit reload(m_fileName); + break; + case Core::Utils::ReloadAll: + emit reload(m_fileName); + *behavior = Core::IFile::ReloadAll; + break; + case Core::Utils::ReloadSkipCurrent: + break; + case Core::Utils::ReloadNone: + *behavior = Core::IFile::ReloadNone; + break; + } +} + +QString FormWindowFile::defaultPath() const +{ + return QString(); +} + +void FormWindowFile::setSuggestedFileName(const QString &fileName) +{ + if (debugFormWindowFile) + qDebug() << "FormWindowFile:setSuggestedFileName" << m_fileName << fileName; + + m_suggestedName = fileName; +} + +QString FormWindowFile::suggestedFileName() const +{ + return m_suggestedName; +} + +QString FormWindowFile::mimeType() const +{ + return m_mimeType; +} + +bool FormWindowFile::writeFile(const QString &fileName, QString &errorString) const +{ + if (debugFormWindowFile) + qDebug() << "FormWindowFile::writeFile" << m_fileName << fileName; + + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + errorString = tr("Unable to open %1: %2").arg(fileName, file.errorString()); + return false; + } + const bool rc = writeFile(file, errorString); + file.close(); + return rc; +} + +bool FormWindowFile::writeFile(QFile &file, QString &errorString) const +{ + const QByteArray content = m_formWindow->contents().toUtf8(); + if (!file.write(content)) { + errorString = tr("Unable to write to %1: %2").arg(file.fileName(), file.errorString()); + return false; + } + return true; +} + +void FormWindowFile::setFileName(const QString &fname) +{ + m_fileName = fname; +} diff --git a/src/plugins/designer/formwindowfile.h b/src/plugins/designer/formwindowfile.h new file mode 100644 index 00000000000..fc95b05f86a --- /dev/null +++ b/src/plugins/designer/formwindowfile.h @@ -0,0 +1,105 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FORMWINDOWFILE_H +#define FORMWINDOWFILE_H + +#include <coreplugin/ifile.h> + +#include "widgethost.h" +#include "designerconstants.h" + +QT_BEGIN_NAMESPACE +class QDesignerFormWindowInterface; +class QDesignerFormWindowManagerInterface; +class QFile; +QT_END_NAMESPACE + +namespace Core { +class ICore; +} + +namespace Designer { +namespace Internal { + +class FormWindowSelection; + +class FormWindowFile + : public Core::IFile +{ + Q_OBJECT + +public: + FormWindowFile(Core::ICore *core, + QDesignerFormWindowInterface *form, + QObject *parent = 0); + ~FormWindowFile(); + + //IFile + bool save(const QString &fileName = QString()); + QString fileName() const; + bool isModified() const; + bool isReadOnly() const; + bool isSaveAsAllowed() const; + void modified(Core::IFile::ReloadBehavior *behavior); + QString defaultPath() const; + QString suggestedFileName() const; + virtual QString mimeType() const; + + // Internal + void setSuggestedFileName(const QString &fileName); + bool writeFile(const QString &fileName, QString &errorString) const; + bool writeFile(QFile &file, QString &errorString) const; + +signals: + // IFile + void changed(); + // Internal + void reload(const QString &); + void setDisplayName(const QString &); + +public slots: + void setFileName(const QString &); + +private: + const QString m_mimeType; + QString m_fileName; + QString m_suggestedName; + + QDesignerFormWindowInterface *m_formWindow; + Core::ICore *m_core; +}; + +} // namespace Internal +} // namespace Designer + +#endif //FORMWINDOWFILE_H diff --git a/src/plugins/designer/formwindowhost.cpp b/src/plugins/designer/formwindowhost.cpp new file mode 100644 index 00000000000..c03a17df902 --- /dev/null +++ b/src/plugins/designer/formwindowhost.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "formwindowhost.h" +#include "formeditorw.h" + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerPropertyEditorInterface> + +#include <QtCore/QDebug> +#include <QtCore/QVariant> + +using namespace Designer::Internal; +using namespace SharedTools; + +enum { debugFormWindowHost = 0 }; + +FormWindowHost::FormWindowHost(QDesignerFormWindowInterface *form, + QWidget *parent) : + WidgetHost(parent, form) +{ + connect(formWindow(), SIGNAL(selectionChanged()), this, SIGNAL(changed())); + connect(this, SIGNAL(formWindowSizeChanged(int,int)), this, SLOT(formSizeChanged(int,int))); + connect(formWindow(), SIGNAL(changed()), this, SIGNAL(changed())); +} + +FormWindowHost::~FormWindowHost() +{ + if (debugFormWindowHost) + qDebug() << "FormWindowHost::~FormWindowHost"; +} + +void FormWindowHost::formSizeChanged(int w, int h) +{ + if (debugFormWindowHost) + qDebug() << "FormWindowHost::formSizeChanged" << w << h; + + formWindow()->setDirty(true); + static const QString geometry = QLatin1String("geometry"); + FormEditorW::instance()->designerEditor()->propertyEditor()->setPropertyValue(geometry, QRect(0,0,w,h) ); + emit changed(); +} diff --git a/src/plugins/designer/formwindowhost.h b/src/plugins/designer/formwindowhost.h new file mode 100644 index 00000000000..e74cd12e43b --- /dev/null +++ b/src/plugins/designer/formwindowhost.h @@ -0,0 +1,61 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FORMWINDOWHOST_H +#define FORMWINDOWHOST_H + + +#include "widgethost.h" + +class QDesignerFormWindowInterface; + +namespace Designer { +namespace Internal { + +class FormWindowHost : public SharedTools::WidgetHost +{ + Q_OBJECT +public: + FormWindowHost(QDesignerFormWindowInterface *form, QWidget *parent = 0); + ~FormWindowHost(); + +signals: + void changed(); + +private slots: + void formSizeChanged(int w, int h); +}; + +} // namespace Internal +} // namespace Designer + +#endif //FORMWINDOWHOST_H diff --git a/src/plugins/designer/formwizard.cpp b/src/plugins/designer/formwizard.cpp new file mode 100644 index 00000000000..b17657520bc --- /dev/null +++ b/src/plugins/designer/formwizard.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "formwizard.h" +#include "formwizarddialog.h" +#include "formwindoweditor.h" +#include "designerconstants.h" + +#include <coreplugin/icore.h> + +#include <QtCore/QFile> +#include <QtCore/QDebug> + +enum { debugFormWizard = 0 }; + +using namespace Designer; +using namespace Designer::Internal; + +FormWizard::FormWizard(const BaseFileWizardParameters ¶meters, Core::ICore *core, QObject *parent) : + Core::BaseFileWizard(parameters, core, parent) +{ +} + +QWizard *FormWizard::createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const +{ + FormFileWizardDialog *wizardDialog = new FormFileWizardDialog(core(), extensionPages, parent); + wizardDialog->setPath(defaultPath); + return wizardDialog; +} + +Core::GeneratedFiles FormWizard::generateFiles(const QWizard *w, + QString *errorMessage) const +{ + const FormFileWizardDialog *wizard = qobject_cast<const FormFileWizardDialog *>(w); + const QString fileName = Core::BaseFileWizard::buildFileName(wizard->path(), wizard->name(), + preferredSuffix(QLatin1String(Constants::FORM_MIMETYPE))); + + const QString formTemplate = wizard->templateContents(); + if (formTemplate.isEmpty()) { + *errorMessage = QLatin1String("Internal error: FormWizard::generateFiles: empty template contents"); + return Core::GeneratedFiles(); + } + + Core::GeneratedFile file(fileName); + file.setContents(formTemplate); + file.setEditorKind(QLatin1String(Constants::C_FORMEDITOR)); + return Core::GeneratedFiles() << file; +} diff --git a/src/plugins/designer/formwizard.h b/src/plugins/designer/formwizard.h new file mode 100644 index 00000000000..fe20de2ac6c --- /dev/null +++ b/src/plugins/designer/formwizard.h @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FORMWIZARD_H +#define FORMWIZARD_H + +#include <coreplugin/basefilewizard.h> + +QT_BEGIN_NAMESPACE +class QWizard; +QT_END_NAMESPACE + +namespace Designer { +namespace Internal { + +class FormFileWizardDialog; + +class FormWizard : public Core::BaseFileWizard +{ + Q_DISABLE_COPY(FormWizard) + Q_OBJECT + +public: + typedef Core::BaseFileWizardParameters BaseFileWizardParameters; + + explicit FormWizard(const BaseFileWizardParameters ¶meters, Core::ICore *core, QObject *parent); + +protected: + virtual QWizard *createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const; + + virtual Core::GeneratedFiles generateFiles(const QWizard *w, + QString *errorMessage) const; +}; + +} // namespace Internal +} // namespace Designer + +#endif // FORMWIZARD_H diff --git a/src/plugins/designer/formwizarddialog.cpp b/src/plugins/designer/formwizarddialog.cpp new file mode 100644 index 00000000000..e6733852986 --- /dev/null +++ b/src/plugins/designer/formwizarddialog.cpp @@ -0,0 +1,127 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "formwizarddialog.h" +#include "formtemplatewizardpage.h" +#include "formeditorw.h" + +#include <coreplugin/icore.h> +#include <coreplugin/basefilewizard.h> + +#include <utils/filewizardpage.h> + +#include <QtCore/QDebug> +#include <QtGui/QAbstractButton> + +// Make sure there is a gap before the extension pages +enum { FormPageId, FilePageId, FirstExtensionPageId = 10 }; + +namespace Designer { +namespace Internal { + +// ----------------- FormWizardDialog +FormWizardDialog::FormWizardDialog(Core::ICore *core, + const WizardPageList &extensionPages, + QWidget *parent) : + QWizard(parent), + m_formPage(new FormTemplateWizardPagePage), + m_core(core) +{ + init(extensionPages); +} + +void FormWizardDialog::init(const WizardPageList &extensionPages) +{ + Core::BaseFileWizard::setupWizard(this); + setWindowTitle(tr("Qt Designer Form")); + setPage(FormPageId, m_formPage); + + if (!extensionPages.empty()) { + int id = FirstExtensionPageId; + foreach (QWizardPage *p, extensionPages) + setPage(id++, p); + } +} + +QString FormWizardDialog::templateContents() const +{ + // Template is expensive, cache + if (m_templateContents.isEmpty()) + m_templateContents = m_formPage->templateContents(); + return m_templateContents; +} + +// ----------------- FormFileWizardDialog +FormFileWizardDialog::FormFileWizardDialog(Core::ICore *core, + const WizardPageList &extensionPages, + QWidget *parent) : + FormWizardDialog(core, extensionPages, parent), + m_filePage(new Core::Utils::FileWizardPage) +{ + setPage(FilePageId, m_filePage); + connect(m_filePage, SIGNAL(activated()), + button(QWizard::FinishButton), SLOT(animateClick())); + + connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(slotCurrentIdChanged(int))); +} + +QString FormFileWizardDialog::path() const +{ + return m_filePage->path(); +} + +void FormFileWizardDialog::setPath(const QString &path) +{ + m_filePage->setPath(path); +} + +QString FormFileWizardDialog::name() const +{ + return m_filePage->name(); +} + +void FormFileWizardDialog::slotCurrentIdChanged(int id) +{ + if (id == FilePageId) { + // Change from form to file: Store template and Suggest a name based on + // the ui class + QString formBaseClass; + QString uiClassName; + if (FormTemplateWizardPagePage::getUIXmlData(templateContents(), &formBaseClass, &uiClassName)) { + QString fileName = FormTemplateWizardPagePage::stripNamespaces(uiClassName).toLower(); + fileName += QLatin1String(".ui"); + m_filePage->setName(fileName); + } + } +} +} // namespace Internal +} // namespace Designer diff --git a/src/plugins/designer/formwizarddialog.h b/src/plugins/designer/formwizarddialog.h new file mode 100644 index 00000000000..fd1155e00b5 --- /dev/null +++ b/src/plugins/designer/formwizarddialog.h @@ -0,0 +1,104 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FORMWIZARDDIALOG_H +#define FORMWIZARDDIALOG_H + +#include <QtGui/QWizard> + +namespace Core { + class ICore; + namespace Utils { + class FileWizardPage; + } +} + +namespace Designer { +namespace Internal { + +class FormTemplateWizardPagePage; + +// Single-Page Wizard for new forms offering all types known to Qt Designer. +// To be used for Mode "CreateNewEditor" [not currently used] + +class FormWizardDialog : public QWizard +{ + Q_DISABLE_COPY(FormWizardDialog) + Q_OBJECT + +public: + typedef QList<QWizardPage *> WizardPageList; + explicit FormWizardDialog(Core::ICore *core, + const WizardPageList &extensionPages, + QWidget *parent = 0); + + QString templateContents() const; + +private: + void init(const WizardPageList &extensionPages); + + FormTemplateWizardPagePage *m_formPage; + Core::ICore *m_core; + mutable QString m_templateContents; +}; + +// Two-Page Wizard for new forms for mode "CreateNewFile". Gives +// FormWizardDialog an additional page with file and path fields, +// initially determined from the UI class chosen on page one. + +class FormFileWizardDialog : public FormWizardDialog +{ + Q_DISABLE_COPY(FormFileWizardDialog) + Q_OBJECT + +public: + explicit FormFileWizardDialog(Core::ICore *core, + const WizardPageList &extensionPages, + QWidget *parent = 0); + + QString path() const; + QString name() const; + +public slots: + void setPath(const QString &path); + +private slots: + void slotCurrentIdChanged(int id); + +private: + Core::Utils::FileWizardPage *m_filePage; +}; + +} // namespace Internal +} // namespace Designer + +#endif //FORMWIZARDDIALOG_H diff --git a/src/plugins/designer/images/qt_ui.png b/src/plugins/designer/images/qt_ui.png Binary files differnew file mode 100644 index 00000000000..8fb61ee27ff --- /dev/null +++ b/src/plugins/designer/images/qt_ui.png diff --git a/src/plugins/designer/settingsmanager.cpp b/src/plugins/designer/settingsmanager.cpp new file mode 100644 index 00000000000..9a737e8b0a4 --- /dev/null +++ b/src/plugins/designer/settingsmanager.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <settingsmanager.h> +#include <QtCore/QDebug> + +using namespace Designer::Internal; + +namespace { + bool debug = false; +} + +void SettingsManager::beginGroup(const QString &prefix) +{ + if (debug) + qDebug() << "Designer - beginning group " << addPrefix(prefix); + m_settings.beginGroup(addPrefix(prefix)); +} + +void SettingsManager::endGroup() +{ + if (debug) + qDebug() << "Designer - end group"; + m_settings.endGroup(); +} + +bool SettingsManager::contains(const QString &key) const +{ + return m_settings.contains(addPrefix(key)); +} + +void SettingsManager::setValue(const QString &key, const QVariant &value) +{ + if (debug) + qDebug() << "Designer - storing " << addPrefix(key) << ": " << value; + m_settings.setValue(addPrefix(key), value); +} + +QVariant SettingsManager::value(const QString &key, const QVariant &defaultValue) const +{ + QVariant result = m_settings.value(addPrefix(key), defaultValue); + if (debug) + qDebug() << "Designer - retrieving " << addPrefix(key) << ": " << result; + return result; +} + +void SettingsManager::remove(const QString &key) +{ + m_settings.remove(addPrefix(key)); +} + +QString SettingsManager::addPrefix(const QString &name) const +{ + QString result = name; + if (m_settings.group().isEmpty()) + result.insert(0, QLatin1String("Designer")); + return result; +} diff --git a/src/plugins/designer/settingsmanager.h b/src/plugins/designer/settingsmanager.h new file mode 100644 index 00000000000..92783e45105 --- /dev/null +++ b/src/plugins/designer/settingsmanager.h @@ -0,0 +1,64 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SETTINGSMANAGER_H +#define SETTINGSMANAGER_H + +#include <QtDesigner/private/abstractsettings_p.h> +#include <QtCore/QSettings> + +namespace Designer { +namespace Internal { + +/* Prepends "Designer" to every value stored/retrieved by designer plugins, + to avoid namespace polution. We cannot use a group because groups cannot be nested, + and designer uses groups internally. */ +class SettingsManager : public QDesignerSettingsInterface +{ +public: + virtual void beginGroup(const QString &prefix); + virtual void endGroup(); + + virtual bool contains(const QString &key) const; + virtual void setValue(const QString &key, const QVariant &value); + virtual QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const ; + virtual void remove(const QString &key); + +private: + QString addPrefix(const QString &name) const; + QSettings m_settings; +}; + +} // namespace Internal +} // namespace Designer + +#endif //SETTINGSMANAGER_H diff --git a/src/plugins/designer/settingspage.cpp b/src/plugins/designer/settingspage.cpp new file mode 100644 index 00000000000..c09c3a08494 --- /dev/null +++ b/src/plugins/designer/settingspage.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "settingspage.h" + +#include <extensionsystem/pluginmanager.h> +#include <QtDesigner/private/abstractoptionspage_p.h> + +using namespace Designer::Internal; + +SettingsPage::SettingsPage(QDesignerOptionsPageInterface *designerPage) : + m_designerPage(designerPage) +{ +} + +SettingsPage::~SettingsPage() +{ +} + +QString SettingsPage::name() const +{ + return m_designerPage->name(); +} + +QString SettingsPage::category() const +{ + return QLatin1String("Designer"); +} + +QString SettingsPage::trCategory() const +{ + return tr("Designer"); +} + +QWidget *SettingsPage::createPage(QWidget *parent) +{ + return m_designerPage->createPage(parent); +} + +void SettingsPage::finished(bool accepted) +{ + m_designerPage->finish(accepted); +} diff --git a/src/plugins/designer/settingspage.h b/src/plugins/designer/settingspage.h new file mode 100644 index 00000000000..30da61814b9 --- /dev/null +++ b/src/plugins/designer/settingspage.h @@ -0,0 +1,70 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DESIGNER_SETTINGSPAGE_H +#define DESIGNER_SETTINGSPAGE_H + +#include <coreplugin/icore.h> +#include <coreplugin/dialogs/ioptionspage.h> + +QT_BEGIN_NAMESPACE +class QDesignerOptionsPageInterface; +QT_END_NAMESPACE + +namespace Designer { +namespace Internal { + +class SettingsPageWidget; + +class SettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + explicit SettingsPage(QDesignerOptionsPageInterface *designerPage); + virtual ~SettingsPage(); + + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + +private: + QDesignerOptionsPageInterface *m_designerPage; +}; + +} //namespace Internal +} //namespace QuickOpen + +#endif // DESIGNER_SETTINGSPAGE_H diff --git a/src/plugins/designer/workbenchintegration.cpp b/src/plugins/designer/workbenchintegration.cpp new file mode 100644 index 00000000000..42e55d0265e --- /dev/null +++ b/src/plugins/designer/workbenchintegration.cpp @@ -0,0 +1,352 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "formeditorplugin.h" +#include "workbenchintegration.h" +#include "formeditorw.h" +#include "formwindoweditor.h" + +#include <cpptools/cppmodelmanagerinterface.h> +#include <cplusplus/Symbols.h> +#include <cplusplus/Overview.h> +#include <cplusplus/CoreTypes.h> +#include <cplusplus/Name.h> +#include <cplusplus/Names.h> +#include <cplusplus/Literals.h> +#include <cplusplus/Scope.h> +#include <cplusplus/Control.h> +#include <cplusplus/LookupContext.h> +#include <coreplugin/icore.h> +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/basetexteditor.h> +#include <texteditor/itexteditable.h> + +#include <QtDesigner/QDesignerFormWindowInterface> + +#include <QtCore/QFileInfo> + +#include <QtCore/QDebug> + +using namespace Designer::Internal; +using namespace CPlusPlus; +using namespace TextEditor; + +WorkbenchIntegration::WorkbenchIntegration(QDesignerFormEditorInterface *core, FormEditorW *parent) : + qdesigner_internal::QDesignerIntegration(core, ::qobject_cast<QObject*>(parent)), + m_few(parent) +{ + setResourceFileWatcherBehaviour(QDesignerIntegration::ReloadSilently); + setResourceEditingEnabled(false); + setSlotNavigationEnabled(true); + connect(this, SIGNAL(navigateToSlot(QString, QString)), + this, SLOT(slotNavigateToSlot(QString, QString))); +} + +void WorkbenchIntegration::updateSelection() +{ + if (FormWindowEditor *afww = m_few->activeFormWindow()) + afww->updateFormWindowSelectionHandles(true); + qdesigner_internal::QDesignerIntegration::updateSelection(); +} + +QWidget *WorkbenchIntegration::containerWindow(QWidget * /*widget*/) const +{ + FormWindowEditor *fw = m_few->activeFormWindow(); + if (!fw) + return 0; + return fw->integrationContainer(); +} + +QList<Document::Ptr> WorkbenchIntegration::findDocuments(const QString &uiFileName) const +{ + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + CppTools::CppModelManagerInterface *cppModelManager = + core->pluginManager()->getObject<CppTools::CppModelManagerInterface>(); + + QList<Document::Ptr> docList; + // take all docs + CppTools::CppModelManagerInterface::DocumentTable docTable = cppModelManager->documents(); + foreach (Document::Ptr doc, docTable) { // we go through all documents + QStringList includes = doc->includedFiles(); + foreach (QString include, includes) { + const QFileInfo fi(include); // TODO: we should match an absolute path. Currently uiFileName is a file name only. + if (fi.fileName() == uiFileName) { // we are only interested in docs which includes our ui file + docList.append(doc); + } + } + } + return docList; +} + + + +Class *WorkbenchIntegration::findClass(Namespace *parentNameSpace, const QString &uiClassName) const +{ + for (unsigned i = 0; i < parentNameSpace->memberCount(); i++) { // we go through all namespace members + if (Class *cl = parentNameSpace->memberAt(i)->asClass()) { // we have found a class - we are interested in classes only + Overview o; + QString className = o.prettyName(cl->name()); + for (unsigned j = 0; j < cl->memberCount(); j++) { // we go through class members + const Declaration *decl = cl->memberAt(j)->asDeclaration(); + if (decl) { // we want to know if the class contains a member (so we look into a declaration) of uiClassName type + const QString v1 = QLatin1String("Ui::") + uiClassName; // TODO: handle also the case of namespaced class name + const QString v2 = QLatin1String("Ui_") + uiClassName; + + NamedType *nt = decl->type()->asNamedType(); + + // handle pointers to member variables + if (PointerType *pt = decl->type()->asPointerType()) + nt = pt->elementType()->asNamedType(); + + if (nt) { + Overview typeOverview; + const QString memberClass = typeOverview.prettyName(nt->name()); + if (memberClass == v1 || memberClass == v2) // simple match here + return cl; + } + } + } + } else if (Namespace *ns = parentNameSpace->memberAt(i)->asNamespace()) { + return findClass(ns, uiClassName); + } + } + return 0; +} + +Function *WorkbenchIntegration::findFunction(Class *cl, const QString &functionName) const +{ + // TODO: match properly function name and argument list, for now we match only function name + // (Roberto's suggestion: use QtMethodAST for that, enclosing function with SIGNAL(<fun>) + // then it becames an expression). Robetro's also proposed he can add public methods to parse declarations + + // Quick implementation start + QString funName = functionName.left(functionName.indexOf(QLatin1Char('('))); + // Quick implementation end + for (unsigned j = 0; j < cl->memberCount(); j++) { // go through all members + const Declaration *decl = cl->memberAt(j)->asDeclaration(); + if (decl) { // we are interested only in declarations (can be decl of method or of a field) + Function *fun = decl->type()->asFunction(); + if (fun) { // we are only interested in declarations of methods + Overview typeOverview; + const QString memberFunction = typeOverview.prettyName(fun->name()); + if (memberFunction == funName) // simple match (we match only fun name, we should match also arguments) + return fun; + } + } + } + return 0; +} + +// TODO: remove me, see below +static bool isCompatible(Name *name, Name *otherName) +{ + if (NameId *nameId = name->asNameId()) { + if (TemplateNameId *otherTemplId = otherName->asTemplateNameId()) + return nameId->identifier()->isEqualTo(otherTemplId->identifier()); + } else if (TemplateNameId *templId = name->asTemplateNameId()) { + if (NameId *otherNameId = otherName->asNameId()) + return templId->identifier()->isEqualTo(otherNameId->identifier()); + } + + return name->isEqualTo(otherName); +} + +// TODO: remove me, see below +static bool isCompatible(Function *definition, Symbol *declaration, QualifiedNameId *declarationName) +{ + Function *declTy = declaration->type()->asFunction(); + if (! declTy) + return false; + + Name *definitionName = definition->name(); + if (QualifiedNameId *q = definitionName->asQualifiedNameId()) { + if (! isCompatible(q->unqualifiedNameId(), declaration->name())) + return false; + else if (q->nameCount() > declarationName->nameCount()) + return false; + else if (declTy->argumentCount() != definition->argumentCount()) + return false; + else if (declTy->isConst() != definition->isConst()) + return false; + else if (declTy->isVolatile() != definition->isVolatile()) + return false; + + for (unsigned i = 0; i < definition->argumentCount(); ++i) { + Symbol *arg = definition->argumentAt(i); + Symbol *otherArg = declTy->argumentAt(i); + if (! arg->type().isEqualTo(otherArg->type())) + return false; + } + + for (unsigned i = 0; i != q->nameCount(); ++i) { + Name *n = q->nameAt(q->nameCount() - i - 1); + Name *m = declarationName->nameAt(declarationName->nameCount() - i - 1); + if (! isCompatible(n, m)) + return false; + } + return true; + } else { + // ### TODO: implement isCompatible for unqualified name ids. + } + return false; +} + +// TODO: remove me, this is taken from cppeditor.cpp. Find some common place for this method +Document::Ptr WorkbenchIntegration::findDefinition(Function *functionDeclaration, int *line) const +{ + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + CppTools::CppModelManagerInterface *cppModelManager = + core->pluginManager()->getObject<CppTools::CppModelManagerInterface>(); + if (!cppModelManager) + return Document::Ptr(); + + QVector<Name *> qualifiedName; + Scope *scope = functionDeclaration->scope(); + for (; scope; scope = scope->enclosingScope()) { + if (scope->isClassScope() || scope->isNamespaceScope()) { + if (scope->owner() && scope->owner()->name()) { + Name *scopeOwnerName = scope->owner()->name(); + if (QualifiedNameId *q = scopeOwnerName->asQualifiedNameId()) { + for (unsigned i = 0; i < q->nameCount(); ++i) { + qualifiedName.prepend(q->nameAt(i)); + } + } else { + qualifiedName.prepend(scopeOwnerName); + } + } + } + } + + qualifiedName.append(functionDeclaration->name()); + + Control control; + QualifiedNameId *q = control.qualifiedNameId(&qualifiedName[0], qualifiedName.size()); + LookupContext context(&control); + + const QMap<QString, Document::Ptr> documents = cppModelManager->documents(); + foreach (Document::Ptr doc, documents) { + QList<Scope *> visibleScopes; + visibleScopes.append(doc->globalSymbols()); + visibleScopes = context.expand(visibleScopes); + foreach (Scope *visibleScope, visibleScopes) { + Symbol *symbol = 0; + if (NameId *nameId = q->unqualifiedNameId()->asNameId()) + symbol = visibleScope->lookat(nameId->identifier()); + else if (DestructorNameId *dtorId = q->unqualifiedNameId()->asDestructorNameId()) + symbol = visibleScope->lookat(dtorId->identifier()); + else if (TemplateNameId *templNameId = q->unqualifiedNameId()->asTemplateNameId()) + symbol = visibleScope->lookat(templNameId->identifier()); + else if (OperatorNameId *opId = q->unqualifiedNameId()->asOperatorNameId()) + symbol = visibleScope->lookat(opId->kind()); + // ### cast operators + for (; symbol; symbol = symbol->next()) { + if (! symbol->isFunction()) + continue; + else if (! isCompatible(symbol->asFunction(), functionDeclaration, q)) + continue; + *line = symbol->line(); // TODO: shift the line so that we are inside a function. Maybe just find the nearest '{'? + return doc; + } + } + } + return Document::Ptr(); + +} + +void WorkbenchIntegration::addDeclaration(const QString &docFileName, Class *cl, const QString &functionName) const +{ + // TODO: add argument names (from designer we get only argument types) + for (unsigned j = 0; j < cl->memberCount(); j++) { // go through all members + const Declaration *decl = cl->memberAt(j)->asDeclaration(); + if (decl) { // we want to find any method which is a private slot (then we don't need to add "private slots:" statement) + Function *fun = decl->type()->asFunction(); + if (fun) { // we are only interested in declarations of methods + if (fun->isSlot() && fun->isPrivate()) { + ITextEditable *editable = qobject_cast<ITextEditable *>( + TextEditor::BaseTextEditor::openEditorAt(docFileName, fun->line()/*, fun->column()*/)); // TODO: fun->column() gives me weird number... + if (editable) { + editable->insert(QLatin1String("void ") + functionName + QLatin1String(";\n ")); + } + return; + } + } + } + } + + // TODO: we didn't find "private slots:", let's add it + + return; +} + +void WorkbenchIntegration::slotNavigateToSlot(const QString &objectName, const QString &signalSignature) +{ + const QString currentUiFile = m_few->activeFormWindow()->file()->fileName(); + + // TODO: we should pass to findDocuments an absolute path to generated .h file from ui. + // Currently we are guessing the name of ui_<>.h file and pass the file name only to the findDocuments(). + // The idea is that the .pro file knows if the .ui files is inside, and the .pro file knows it will + // be generating the ui_<>.h file for it, and the .pro file knows what the generated file's name and its absolute path will be. + // So we should somehow get that info from project manager (?) + const QFileInfo fi(currentUiFile); + const QString uicedName = QLatin1String("ui_") + fi.baseName() + QLatin1String(".h"); + + QList<Document::Ptr> docList = findDocuments(uicedName); + if (docList.isEmpty()) + return; + + QDesignerFormWindowInterface *fwi = m_few->activeFormWindow()->formWindow(); + + const QString uiClassName = fwi->mainContainer()->objectName(); + + foreach (Document::Ptr doc, docList) { + Class *cl = findClass(doc->globalNamespace(), uiClassName); + if (cl) { + QString functionName = QLatin1String("on_") + objectName + QLatin1Char('_') + signalSignature; + Function *fun = findFunction(cl, functionName); + int line = 0; + if (!fun) { + // add function declaration to cl + addDeclaration(doc->fileName(), cl, functionName); + // TODO: add function definition to cpp file + + } else { + doc = findDefinition(fun, &line); + } + if (doc) { + // jump to function definition + TextEditor::BaseTextEditor::openEditorAt(doc->fileName(), line); + } + return; + } + } +} + diff --git a/src/plugins/designer/workbenchintegration.h b/src/plugins/designer/workbenchintegration.h new file mode 100644 index 00000000000..21aee510aaa --- /dev/null +++ b/src/plugins/designer/workbenchintegration.h @@ -0,0 +1,71 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef WORKBENCHINTEGRATION_H +#define WORKBENCHINTEGRATION_H + +#include <cpptools/cppmodelmanagerinterface.h> + +#include <QtDesigner/private/qdesigner_integration_p.h> + +namespace Designer { +namespace Internal { + +class FormEditorW; + +class WorkbenchIntegration : public qdesigner_internal::QDesignerIntegration { + Q_OBJECT +public: + WorkbenchIntegration(QDesignerFormEditorInterface *core, FormEditorW *parent = 0); + + QWidget *containerWindow(QWidget *widget) const; + + bool supportsToSlotNavigation() { return true; }; + +public slots: + void updateSelection(); +private slots: + void slotNavigateToSlot(const QString &objectName, const QString &signalSignature); +private: + QList<CPlusPlus::Document::Ptr> findDocuments(const QString &uiFileName) const; + CPlusPlus::Class *findClass(CPlusPlus::Namespace *parentNameSpace, const QString &uiClassName) const; + CPlusPlus::Function *findFunction(CPlusPlus::Class *cl, const QString &functionName) const; + CPlusPlus::Document::Ptr findDefinition(CPlusPlus::Function *functionDeclaration, int *line) const; + void addDeclaration(const QString &docFileName, CPlusPlus::Class *cl, const QString &functionName) const; + + FormEditorW *m_few; +}; + +} // namespace Internal +} // namespace Designer + +#endif // WORKBENCHINTEGRATION_H diff --git a/src/plugins/find/Find.pluginspec b/src/plugins/find/Find.pluginspec new file mode 100644 index 00000000000..13f712235b8 --- /dev/null +++ b/src/plugins/find/Find.pluginspec @@ -0,0 +1,10 @@ +<plugin name="Find" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Provides the find widget and the hooks for find implementations.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/find/basetextfind.cpp b/src/plugins/find/basetextfind.cpp new file mode 100644 index 00000000000..37787596d9e --- /dev/null +++ b/src/plugins/find/basetextfind.cpp @@ -0,0 +1,246 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "basetextfind.h" +#include <QtGui/QTextBlock> + +using namespace Find; + +BaseTextFind::BaseTextFind(QTextEdit *editor) + : m_editor(editor), m_incrementalStartPos(-1) +{ +} + +BaseTextFind::BaseTextFind(QPlainTextEdit *editor) + : m_plaineditor(editor), m_incrementalStartPos(-1) +{ +} + +QTextCursor BaseTextFind::textCursor() const +{ + Q_ASSERT(m_editor || m_plaineditor); + return m_editor ? m_editor->textCursor() : m_plaineditor->textCursor(); + +} + +void BaseTextFind::setTextCursor(const QTextCursor& cursor) +{ + Q_ASSERT(m_editor || m_plaineditor); + m_editor ? m_editor->setTextCursor(cursor) : m_plaineditor->setTextCursor(cursor); +} + +QTextDocument *BaseTextFind::document() const +{ + Q_ASSERT(m_editor || m_plaineditor); + return m_editor ? m_editor->document() : m_plaineditor->document(); +} + +bool BaseTextFind::isReadOnly() const +{ + Q_ASSERT(m_editor || m_plaineditor); + return m_editor ? m_editor->isReadOnly() : m_plaineditor->isReadOnly(); +} + +bool BaseTextFind::supportsReplace() const +{ + return !isReadOnly(); +} + +void BaseTextFind::resetIncrementalSearch() +{ + m_incrementalStartPos = -1; +} + +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(); +} + +bool BaseTextFind::findIncremental(const QString &txt, QTextDocument::FindFlags findFlags) +{ + QTextCursor cursor = textCursor(); + if (m_incrementalStartPos < 0) + m_incrementalStartPos = cursor.selectionStart(); + cursor.setPosition(m_incrementalStartPos); + findFlags &= ~QTextDocument::FindBackward; + bool found = find(txt, findFlags, cursor); + if (found) + emit highlightAll(txt, findFlags); + else + emit highlightAll(QString(), 0); + return found; +} + +bool BaseTextFind::findStep(const QString &txt, QTextDocument::FindFlags findFlags) +{ + bool found = find(txt, findFlags, textCursor()); + if (found) + m_incrementalStartPos = textCursor().selectionStart(); + return found; +} + +bool BaseTextFind::replaceStep(const QString &before, const QString &after, + QTextDocument::FindFlags findFlags) +{ + QTextCursor cursor = textCursor(); + if (cursor.selectedText().compare(before, + ((findFlags&QTextDocument::FindCaseSensitively)!=0) ? Qt::CaseSensitive : Qt::CaseInsensitive) == 0) { + int start = cursor.selectionStart(); + cursor.insertText(after); + if ((findFlags&QTextDocument::FindBackward) != 0) + cursor.setPosition(start); + } + return find(before, findFlags, cursor); +} + +int BaseTextFind::replaceAll(const QString &before, const QString &after, + QTextDocument::FindFlags findFlags) +{ + QTextCursor editCursor = textCursor(); + editCursor.movePosition(QTextCursor::Start); + editCursor.beginEditBlock(); + int count = 0; + QTextCursor found; + found = document()->find(before, editCursor, findFlags); + while (!found.isNull() && inScope(found.selectionStart(), found.selectionEnd())) { + ++count; + editCursor.setPosition(found.selectionStart()); + editCursor.setPosition(found.selectionEnd(), QTextCursor::KeepAnchor); + editCursor.insertText(after); + found = document()->find(before, editCursor, findFlags); + } + editCursor.endEditBlock(); + return count; +} + +bool BaseTextFind::find(const QString &txt, + QTextDocument::FindFlags findFlags, + QTextCursor start) +{ + if (txt.isEmpty()) { + setTextCursor(start); + return true; + } + QTextCursor found = document()->find(txt, start, findFlags); + + if (!m_findScope.isNull()) { + + // scoped + if (found.isNull() || !inScope(found.selectionStart(), found.selectionEnd())) { + if ((findFlags&QTextDocument::FindBackward) == 0) + start.setPosition(m_findScope.selectionStart()); + else + start.setPosition(m_findScope.selectionEnd()); + found = document()->find(txt, start, findFlags); + if (found.isNull() || !inScope(found.selectionStart(), found.selectionEnd())) + return false; + } + } else { + + // entire document + if (found.isNull()) { + if ((findFlags&QTextDocument::FindBackward) == 0) + start.movePosition(QTextCursor::Start); + else + start.movePosition(QTextCursor::End); + found = document()->find(txt, start, findFlags); + if (found.isNull()) { + return false; + } + } + } + if (!found.isNull()) { + setTextCursor(found); + } + return true; +} + +bool BaseTextFind::inScope(int startPosition, int endPosition) const +{ + if (m_findScope.isNull()) + return true; + return (m_findScope.selectionStart() <= startPosition + && m_findScope.selectionEnd() >= endPosition); +} + +void BaseTextFind::defineFindScope() +{ + QTextCursor cursor = textCursor(); + if (cursor.hasSelection() && cursor.block() != cursor.document()->findBlock(cursor.anchor())) { + m_findScope = cursor; + emit findScopeChanged(m_findScope); + cursor.setPosition(cursor.selectionStart()); + setTextCursor(cursor); + } else { + clearFindScope(); + } +} + +void BaseTextFind::clearFindScope() +{ + m_findScope = QTextCursor(); + emit findScopeChanged(m_findScope); +} diff --git a/src/plugins/find/basetextfind.h b/src/plugins/find/basetextfind.h new file mode 100644 index 00000000000..0647c8dccdf --- /dev/null +++ b/src/plugins/find/basetextfind.h @@ -0,0 +1,90 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BASETEXTFIND_H +#define BASETEXTFIND_H + +#include "find_global.h" +#include "ifindsupport.h" + +#include <QtCore/QPointer> +#include <QtGui/QPlainTextEdit> + +namespace Find { + +class FIND_EXPORT BaseTextFind : public Find::IFindSupport +{ + Q_OBJECT + +public: + BaseTextFind(QPlainTextEdit *editor); + BaseTextFind(QTextEdit *editor); + + bool supportsReplace() const; + void resetIncrementalSearch(); + void clearResults(); + QString currentFindString() const; + QString completedFindString() const; + + bool findIncremental(const QString &txt, QTextDocument::FindFlags findFlags); + bool findStep(const QString &txt, QTextDocument::FindFlags findFlags); + bool replaceStep(const QString &before, const QString &after, + QTextDocument::FindFlags findFlags); + int replaceAll(const QString &before, const QString &after, + QTextDocument::FindFlags findFlags); + + void defineFindScope(); + void clearFindScope(); + +signals: + void highlightAll(const QString &txt, QTextDocument::FindFlags findFlags); + void findScopeChanged(const QTextCursor &); + +private: + bool find(const QString &txt, + QTextDocument::FindFlags findFlags, + QTextCursor start); + + QTextCursor textCursor() const; + void setTextCursor(const QTextCursor&); + QTextDocument *document() const; + bool isReadOnly() const; + QPointer<QTextEdit> m_editor; + QPointer<QPlainTextEdit> m_plaineditor; + QTextCursor m_findScope; + bool inScope(int startPosition, int endPosition) const; + int m_incrementalStartPos; +}; + +} // namespace Find + +#endif // BASETEXTFIND_H diff --git a/src/plugins/find/currentdocumentfind.cpp b/src/plugins/find/currentdocumentfind.cpp new file mode 100644 index 00000000000..1a4999cd13f --- /dev/null +++ b/src/plugins/find/currentdocumentfind.cpp @@ -0,0 +1,201 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "currentdocumentfind.h" +#include <aggregation/aggregate.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/modemanager.h> + +#include <QtGui/QApplication> +#include <QtDebug> + +using namespace Core; +using namespace Find; +using namespace Find::Internal; + +CurrentDocumentFind::CurrentDocumentFind(ICore *core) + : m_core(core), m_currentFind(0) +{ + connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), + this, SLOT(updateCurrentFindFilter(QWidget*,QWidget*))); +} + +void CurrentDocumentFind::removeConnections() +{ + disconnect(qApp, 0, this, 0); + removeFindSupportConnections(); +} + +void CurrentDocumentFind::resetIncrementalSearch() +{ + Q_ASSERT(m_currentFind); + if (m_currentFind) + m_currentFind->resetIncrementalSearch(); +} + +void CurrentDocumentFind::clearResults() +{ + Q_ASSERT(m_currentFind); + if (m_currentFind) + m_currentFind->clearResults(); +} + +bool CurrentDocumentFind::isEnabled() const +{ + return m_currentFind && (!m_currentWidget || m_currentWidget->isVisible()); +} + +bool CurrentDocumentFind::supportsReplace() const +{ + Q_ASSERT(m_currentFind); + return m_currentFind ? m_currentFind->supportsReplace() : false; +} + +QString CurrentDocumentFind::currentFindString() const +{ + Q_ASSERT(m_currentFind); + return m_currentFind ? m_currentFind->currentFindString() : QString(); +} + +QString CurrentDocumentFind::completedFindString() const +{ + Q_ASSERT(m_currentFind); + return m_currentFind ? m_currentFind->completedFindString() : QString(); +} + +void CurrentDocumentFind::highlightAll(const QString &txt, QTextDocument::FindFlags findFlags) +{ + Q_ASSERT(m_currentFind); + if (m_currentFind) + m_currentFind->highlightAll(txt, findFlags); +} + +bool CurrentDocumentFind::findIncremental(const QString &txt, QTextDocument::FindFlags findFlags) +{ + Q_ASSERT(m_currentFind); + return (m_currentFind? m_currentFind->findIncremental(txt, findFlags) : false); +} + +bool CurrentDocumentFind::findStep(const QString &txt, QTextDocument::FindFlags findFlags) +{ + Q_ASSERT(m_currentFind); + return (m_currentFind? m_currentFind->findStep(txt, findFlags) : false); +} + +bool CurrentDocumentFind::replaceStep(const QString &before, const QString &after, + QTextDocument::FindFlags findFlags) +{ + Q_ASSERT(m_currentFind); + return (m_currentFind? m_currentFind->replaceStep(before, after, findFlags) : false); +} + +int CurrentDocumentFind::replaceAll(const QString &before, const QString &after, + QTextDocument::FindFlags findFlags) +{ + Q_ASSERT(m_currentFind); + return (m_currentFind? m_currentFind->replaceAll(before, after, findFlags) : 0); +} + +void CurrentDocumentFind::defineFindScope() +{ + Q_ASSERT(m_currentFind); + if (m_currentFind) + m_currentFind->defineFindScope(); +} + +void CurrentDocumentFind::clearFindScope() +{ + Q_ASSERT(m_currentFind); + if (m_currentFind) + m_currentFind->clearFindScope(); +} + +void CurrentDocumentFind::updateCurrentFindFilter(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 (!impl) + return; + removeFindSupportConnections(); + m_currentWidget = candidate; + m_currentFind = impl; + if (m_currentFind) { + connect(m_currentFind, SIGNAL(changed()), this, SIGNAL(changed())); + connect(m_currentFind, SIGNAL(destroyed(QObject*)), SLOT(findSupportDestroyed())); + } + 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(findSupportDestroyed())); + } + if (m_currentWidget) + m_currentWidget->removeEventFilter(this); +} + +void CurrentDocumentFind::findSupportDestroyed() +{ + removeFindSupportConnections(); + m_currentWidget = 0; + m_currentFind = 0; + emit changed(); +} + +bool CurrentDocumentFind::setFocusToCurrentFindSupport() +{ + if (m_currentFind && m_currentWidget) { + m_currentWidget->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); +} diff --git a/src/plugins/find/currentdocumentfind.h b/src/plugins/find/currentdocumentfind.h new file mode 100644 index 00000000000..d818dab0483 --- /dev/null +++ b/src/plugins/find/currentdocumentfind.h @@ -0,0 +1,93 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CURRENTDOCUMENTFIND_H +#define CURRENTDOCUMENTFIND_H + +#include "ifindfilter.h" + +#include <coreplugin/icore.h> + +#include <QtCore/QPointer> +#include <QtGui/QWidget> + +namespace Find { +namespace Internal { + +class CurrentDocumentFind : public QObject +{ + Q_OBJECT + +public: + CurrentDocumentFind(Core::ICore *core); + + void resetIncrementalSearch(); + void clearResults(); + bool supportsReplace() const; + QString currentFindString() const; + QString completedFindString() const; + + bool isEnabled() const; + void highlightAll(const QString &txt, QTextDocument::FindFlags findFlags); + bool findIncremental(const QString &txt, QTextDocument::FindFlags findFlags); + bool findStep(const QString &txt, QTextDocument::FindFlags findFlags); + bool replaceStep(const QString &before, const QString &after, + QTextDocument::FindFlags findFlags); + int replaceAll(const QString &before, const QString &after, + QTextDocument::FindFlags findFlags); + void defineFindScope(); + void clearFindScope(); + + void removeConnections(); + bool setFocusToCurrentFindSupport(); + + bool eventFilter(QObject *obj, QEvent *event); + +signals: + void changed(); + +private slots: + void updateCurrentFindFilter(QWidget *old, QWidget *now); + void findSupportDestroyed(); + +private: + void removeFindSupportConnections(); + + Core::ICore *m_core; + QPointer<IFindSupport> m_currentFind; + QPointer<QWidget> m_currentWidget; +}; + +} // namespace Internal +} // namespace Find + +#endif // CURRENTDOCUMENTFIND_H diff --git a/src/plugins/find/find.pri b/src/plugins/find/find.pri new file mode 100644 index 00000000000..b28aec122f9 --- /dev/null +++ b/src/plugins/find/find.pri @@ -0,0 +1,3 @@ +include(find_dependencies.pri) + +LIBS *= -l$$qtLibraryTarget(Find) diff --git a/src/plugins/find/find.pro b/src/plugins/find/find.pro new file mode 100644 index 00000000000..77a6193801c --- /dev/null +++ b/src/plugins/find/find.pro @@ -0,0 +1,33 @@ +TEMPLATE = lib +TARGET = Find +include(../../qworkbenchplugin.pri) +include(find_dependencies.pri) +DEFINES += FIND_LIBRARY +HEADERS += findtoolwindow.h \ + textfindconstants.h \ + ifindsupport.h \ + ifindfilter.h \ + currentdocumentfind.h \ + basetextfind.h \ + find_global.h \ + findtoolbar.h \ + findplugin.h \ + searchresulttreeitemdelegate.h \ + searchresulttreeitemroles.h \ + searchresulttreeitems.h \ + searchresulttreemodel.h \ + searchresulttreeview.h \ + searchresultwindow.h +SOURCES += findtoolwindow.cpp \ + currentdocumentfind.cpp \ + basetextfind.cpp \ + findtoolbar.cpp \ + findplugin.cpp \ + searchresulttreeitemdelegate.cpp \ + searchresulttreeitems.cpp \ + searchresulttreemodel.cpp \ + searchresulttreeview.cpp \ + searchresultwindow.cpp +FORMS += findwidget.ui \ + finddialog.ui +RESOURCES += find.qrc diff --git a/src/plugins/find/find.qrc b/src/plugins/find/find.qrc new file mode 100644 index 00000000000..db9017c2366 --- /dev/null +++ b/src/plugins/find/find.qrc @@ -0,0 +1,13 @@ +<RCC> + <qresource prefix="/find" > + <file>images/all.png</file> + <file>images/casesensitively.png</file> + <file>images/empty.png</file> + <file>images/expand.png</file> + <file>images/next.png</file> + <file>images/previous.png</file> + <file>images/replace_all.png</file> + <file>images/wholewords.png</file> + <file>images/wordandcase.png</file> + </qresource> +</RCC> diff --git a/src/plugins/find/find_dependencies.pri b/src/plugins/find/find_dependencies.pri new file mode 100644 index 00000000000..a64caedc103 --- /dev/null +++ b/src/plugins/find/find_dependencies.pri @@ -0,0 +1 @@ +include(../../plugins/coreplugin/coreplugin.pri) diff --git a/src/plugins/find/find_global.h b/src/plugins/find/find_global.h new file mode 100644 index 00000000000..b9872a0945f --- /dev/null +++ b/src/plugins/find/find_global.h @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/**************************************************************************** +** +** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef FIND_GLOBAL_H +#define FIND_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(FIND_LIBRARY) +# define FIND_EXPORT Q_DECL_EXPORT +#else +# define FIND_EXPORT Q_DECL_IMPORT +#endif + +#endif // FIND_GLOBAL_H diff --git a/src/plugins/find/finddialog.ui b/src/plugins/find/finddialog.ui new file mode 100644 index 00000000000..72405517720 --- /dev/null +++ b/src/plugins/find/finddialog.ui @@ -0,0 +1,146 @@ +<ui version="4.0" > + <class>Find::Internal::FindDialog</class> + <widget class="QDialog" name="Find::Internal::FindDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>426</width> + <height>168</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Minimum" hsizetype="Minimum" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle" > + <string>Search for...</string> + </property> + <property name="sizeGripEnabled" > + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout" > + <item> + <layout class="QGridLayout" name="gridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>80</width> + <height>0</height> + </size> + </property> + <property name="text" > + <string>Sc&ope:</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="0" column="1" > + <widget class="QComboBox" name="filterList" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="0" column="2" > + <widget class="QPushButton" name="searchButton" > + <property name="text" > + <string>&Search</string> + </property> + <property name="default" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Search &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="1" column="1" > + <widget class="QLineEdit" name="searchTerm" /> + </item> + <item row="1" column="2" > + <widget class="QPushButton" name="closeButton" > + <property name="text" > + <string>&Close</string> + </property> + </widget> + </item> + <item row="4" column="0" colspan="2" > + <widget class="QWidget" native="1" name="configWidget" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>10</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QCheckBox" name="matchCase" > + <property name="text" > + <string>Match &case</string> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QCheckBox" name="wholeWords" > + <property name="text" > + <string>&Whole words only</string> + </property> + </widget> + </item> + </layout> + </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> + </widget> + <tabstops> + <tabstop>filterList</tabstop> + <tabstop>searchTerm</tabstop> + <tabstop>searchButton</tabstop> + <tabstop>closeButton</tabstop> + <tabstop>matchCase</tabstop> + <tabstop>wholeWords</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/find/findplugin.cpp b/src/plugins/find/findplugin.cpp new file mode 100644 index 00000000000..92c52212b62 --- /dev/null +++ b/src/plugins/find/findplugin.cpp @@ -0,0 +1,272 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "findplugin.h" + +#include "textfindconstants.h" +#include "currentdocumentfind.h" +#include "findtoolwindow.h" +#include "searchresultwindow.h" + +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/actionmanager/iactioncontainer.h> +#include <coreplugin/actionmanager/icommand.h> +#include <coreplugin/coreconstants.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QSettings> + +Q_DECLARE_METATYPE(Find::IFindFilter*) + +namespace { + const int MAX_COMPLETIONS = 50; +} + +using namespace Find; +using namespace Find::Internal; + +FindPlugin::FindPlugin() + : m_core(0), + m_currentDocumentFind(0), + m_findToolBar(0), + m_findDialog(0), + m_findCompletionModel(new QStringListModel(this)), + m_replaceCompletionModel(new QStringListModel(this)) +{ +} + +FindPlugin::~FindPlugin() +{ + delete m_currentDocumentFind; + delete m_findToolBar; + delete m_findDialog; +} + +bool FindPlugin::initialize(const QStringList &, QString *) +{ + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + setupMenu(); + + m_currentDocumentFind = new CurrentDocumentFind(m_core); + + m_findToolBar = new FindToolBar(this, m_currentDocumentFind); + m_findDialog = new FindToolWindow(this); + SearchResultWindow *searchResultWindow = new SearchResultWindow(m_core); + addAutoReleasedObject(searchResultWindow); + return true; +} + +void FindPlugin::extensionsInitialized() +{ + setupFilterMenuItems(); + readSettings(); +} + +void FindPlugin::shutdown() +{ + m_findToolBar->setParent(0); + m_currentDocumentFind->removeConnections(); + writeSettings(); +} + +void FindPlugin::filterChanged() +{ + IFindFilter *changedFilter = qobject_cast<IFindFilter *>(sender()); + QAction *action = m_filterActions.value(changedFilter); + Q_ASSERT(changedFilter); + Q_ASSERT(action); + if (!changedFilter || !action) + return; + action->setEnabled(changedFilter->isEnabled()); +} + +void FindPlugin::openFindFilter() +{ + QAction *action = qobject_cast<QAction*>(sender()); + Q_ASSERT(action); + if (!action) + return; + IFindFilter *filter = action->data().value<IFindFilter *>(); + Q_ASSERT(filter); + Q_ASSERT(filter->isEnabled()); + if (!filter || !filter->isEnabled()) + return; + QString currentFindString = (m_currentDocumentFind->isEnabled() ? m_currentDocumentFind->currentFindString() : ""); + if (!currentFindString.isEmpty()) + m_findDialog->setFindText(currentFindString); + m_findDialog->open(filter); +} + + +void FindPlugin::setupMenu() +{ + Core::ActionManagerInterface *am = m_core->actionManager(); + Core::IActionContainer *medit = am->actionContainer(Core::Constants::M_EDIT); + Core::IActionContainer *mfind = am->createMenu(Constants::M_FIND); + medit->addMenu(mfind, Core::Constants::G_EDIT_FIND); + mfind->menu()->setTitle(tr("&Find/Replace")); + mfind->appendGroup(Constants::G_FIND_FILTERS); + mfind->appendGroup(Constants::G_FIND_FLAGS); + mfind->appendGroup(Constants::G_FIND_ACTIONS); + QList<int> globalcontext = QList<int>() << Core::Constants::C_GLOBAL_ID; + Core::ICommand *cmd; + QAction *separator; + separator = new QAction(this); + separator->setSeparator(true); + cmd = am->registerAction(separator, QLatin1String("Find.Sep.Flags"), globalcontext); + mfind->addAction(cmd, Constants::G_FIND_FLAGS); + separator = new QAction(this); + separator->setSeparator(true); + cmd = am->registerAction(separator, QLatin1String("Find.Sep.Actions"), globalcontext); + mfind->addAction(cmd, Constants::G_FIND_ACTIONS); +} + +void FindPlugin::setupFilterMenuItems() +{ + Core::ActionManagerInterface *am = m_core->actionManager(); + QList<IFindFilter*> findInterfaces = + ExtensionSystem::PluginManager::instance()->getObjects<IFindFilter>(); + Core::ICommand *cmd; + QList<int> globalcontext = QList<int>() << Core::Constants::C_GLOBAL_ID; + + Core::IActionContainer *mfind = am->actionContainer(Constants::M_FIND); + m_filterActions.clear(); + foreach (IFindFilter *filter, findInterfaces) { + QAction *action = new QAction(filter->name(), this); + action->setEnabled(filter->isEnabled()); + action->setData(qVariantFromValue(filter)); + cmd = am->registerAction(action, QLatin1String("FindFilter.")+filter->name(), globalcontext); + cmd->setDefaultKeySequence(filter->defaultShortcut()); + mfind->addAction(cmd, Constants::G_FIND_FILTERS); + m_filterActions.insert(filter, action); + connect(action, SIGNAL(triggered(bool)), this, SLOT(openFindFilter())); + connect(filter, SIGNAL(changed()), this, SLOT(filterChanged())); + } + m_findDialog->setFindFilters(findInterfaces); +} + +Core::ICore *FindPlugin::core() +{ + return m_core; +} + +QTextDocument::FindFlags FindPlugin::findFlags() const +{ + return m_findFlags; +} + +void FindPlugin::setCaseSensitive(bool sensitive) +{ + setFindFlag(QTextDocument::FindCaseSensitively, sensitive); +} + +void FindPlugin::setWholeWord(bool wholeOnly) +{ + setFindFlag(QTextDocument::FindWholeWords, wholeOnly); +} + +void FindPlugin::setBackward(bool backward) +{ + setFindFlag(QTextDocument::FindBackward, backward); +} + +void FindPlugin::setFindFlag(QTextDocument::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 != QTextDocument::FindBackward) + emit findFlagsChanged(); +} + +bool FindPlugin::hasFindFlag(QTextDocument::FindFlag flag) +{ + return m_findFlags & flag; +} + +void FindPlugin::writeSettings() +{ + QSettings *settings = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()->settings(); + settings->beginGroup("Find"); + settings->setValue("Backward", QVariant((m_findFlags & QTextDocument::FindBackward) != 0)); + settings->setValue("CaseSensitively", QVariant((m_findFlags & QTextDocument::FindCaseSensitively) != 0)); + settings->setValue("WholeWords", QVariant((m_findFlags & QTextDocument::FindWholeWords) != 0)); + settings->setValue("FindStrings", m_findCompletions); + settings->setValue("ReplaceStrings", m_replaceCompletions); + settings->endGroup(); + m_findDialog->writeSettings(); +} + +void FindPlugin::readSettings() +{ + QSettings *settings = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()->settings(); + settings->beginGroup("Find"); + bool block = blockSignals(true); + setBackward(settings->value("Backward", false).toBool()); + setCaseSensitive(settings->value("CaseSensitively", false).toBool()); + setWholeWord(settings->value("WholeWords", false).toBool()); + blockSignals(block); + m_findCompletions = settings->value("FindStrings").toStringList(); + m_replaceCompletions = settings->value("ReplaceStrings").toStringList(); + m_findCompletionModel->setStringList(m_findCompletions); + m_replaceCompletionModel->setStringList(m_replaceCompletions); + settings->endGroup(); + m_findDialog->readSettings(); + emit findFlagsChanged(); // would have been done in the setXXX methods above +} + +void FindPlugin::updateFindCompletion(const QString &text) +{ + updateCompletion(text, m_findCompletions, m_findCompletionModel); +} + +void FindPlugin::updateReplaceCompletion(const QString &text) +{ + updateCompletion(text, m_replaceCompletions, 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); +} + +Q_EXPORT_PLUGIN(FindPlugin) diff --git a/src/plugins/find/findplugin.h b/src/plugins/find/findplugin.h new file mode 100644 index 00000000000..98659f00030 --- /dev/null +++ b/src/plugins/find/findplugin.h @@ -0,0 +1,111 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FINDPLUGIN_H +#define FINDPLUGIN_H + +#include "ui_findwidget.h" +#include "ifindfilter.h" +#include "findtoolbar.h" + +#include <coreplugin/icore.h> +#include <extensionsystem/iplugin.h> + +#include <QtCore/QHash> +#include <QtCore/QStringList> +#include <QtGui/QAction> +#include <QtGui/QTextDocument> + +namespace Find { +namespace Internal { + +class FindToolWindow; + +class FindPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + FindPlugin(); + virtual ~FindPlugin(); + + // IPlugin + bool initialize(const QStringList &arguments, QString *error_message); + void extensionsInitialized(); + void shutdown(); + + Core::ICore *core(); + QTextDocument::FindFlags findFlags() const; + void updateFindCompletion(const QString &text); + void updateReplaceCompletion(const QString &text); + QStringListModel *findCompletionModel() { return m_findCompletionModel; } + QStringListModel *replaceCompletionModel() { return m_replaceCompletionModel; } + +public slots: + void setCaseSensitive(bool sensitive); + void setWholeWord(bool wholeOnly); + void setBackward(bool backward); + +signals: + void findFlagsChanged(); + +private slots: + void filterChanged(); + void openFindFilter(); + +private: + void setFindFlag(QTextDocument::FindFlag flag, bool enabled); + bool hasFindFlag(QTextDocument::FindFlag flag); + void updateCompletion(const QString &text, QStringList &completions, QStringListModel *model); + void setupMenu(); + void setupFilterMenuItems(); + void writeSettings(); + void readSettings(); + + //variables + Core::ICore *m_core; + QHash<IFindFilter *, QAction *> m_filterActions; + + CurrentDocumentFind *m_currentDocumentFind; + FindToolBar *m_findToolBar; + FindToolWindow *m_findDialog; + QTextDocument::FindFlags m_findFlags; + QStringListModel *m_findCompletionModel; + QStringListModel *m_replaceCompletionModel; + QStringList m_findCompletions; + QStringList m_replaceCompletions; +}; + +} // namespace Internal +} // namespace Find + +#endif // FINDPLUGIN_H diff --git a/src/plugins/find/findtoolbar.cpp b/src/plugins/find/findtoolbar.cpp new file mode 100644 index 00000000000..a6afa56d043 --- /dev/null +++ b/src/plugins/find/findtoolbar.cpp @@ -0,0 +1,482 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "findtoolbar.h" +#include "findplugin.h" +#include "textfindconstants.h" + +#include <coreplugin/coreconstants.h> +#include <coreplugin/findplaceholder.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/actionmanager/iactioncontainer.h> +#include <coreplugin/actionmanager/icommand.h> + +#include <QtCore/QSettings> +#include <QtGui/QPushButton> +#include <QtGui/QMenu> +#include <QtGui/QToolButton> +#include <QtGui/QLineEdit> +#include <QtGui/QKeyEvent> +#include <QtGui/QClipboard> +#include <QtGui/QPainter> +#include <QtGui/QCompleter> +#include <QDebug> + +Q_DECLARE_METATYPE(QStringList) +Q_DECLARE_METATYPE(Find::IFindFilter*) + +using namespace Find; +using namespace Find::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_replaceNextAction(0), + m_widget(new QWidget) +{ + //setup ui + m_ui.setupUi(m_widget); + addWidget(m_widget); + setFocusProxy(m_ui.findEdit); + + connect(m_ui.findEdit, SIGNAL(editingFinished()), this, SLOT(invokeResetIncrementalSearch())); + + QWidget *spacerItem = new QWidget; + spacerItem->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + addWidget(spacerItem); + QToolButton *close = new QToolButton; + close->setProperty("type", QLatin1String("dockbutton")); + close->setIcon(QIcon(":/qworkbench/images/closebutton.png")); + connect(close, SIGNAL(clicked()), this, SLOT(hideAndResetFocus())); + addWidget(close); + + m_ui.findPreviousButton->setProperty("type", QLatin1String("dockbutton")); + m_ui.findNextButton->setProperty("type", QLatin1String("dockbutton")); + m_ui.replacePreviousButton->setProperty("type", QLatin1String("dockbutton")); + m_ui.replaceNextButton->setProperty("type", QLatin1String("dockbutton")); + m_ui.replaceAllButton->setProperty("type", QLatin1String("dockbutton")); + + m_findCompleter->setModel(m_plugin->findCompletionModel()); + m_replaceCompleter->setModel(m_plugin->replaceCompletionModel()); + m_ui.findEdit->setCompleter(m_findCompleter); + m_findCompleter->popup()->installEventFilter(this); + m_ui.replaceEdit->setCompleter(m_replaceCompleter); + + m_ui.findEdit->setSide(qApp->layoutDirection() == Qt::LeftToRight ? Core::Utils::FancyLineEdit::Right : Core::Utils::FancyLineEdit::Left); + QMenu *lineEditMenu = new QMenu(m_ui.findEdit); + m_ui.findEdit->setMenu(lineEditMenu); + + m_ui.findEdit->installEventFilter(this); + m_ui.replaceEdit->installEventFilter(this); + m_widget->installEventFilter(this); + + connect(m_ui.findEdit, SIGNAL(textChanged(const QString&)), this, SLOT(invokeFindIncremental())); + connect(m_ui.findEdit, SIGNAL(returnPressed()), this, SLOT(invokeFindEnter())); + connect(m_ui.replaceEdit, SIGNAL(returnPressed()), this, SLOT(invokeReplaceEnter())); + + QAction *shiftEnterAction = new QAction(m_ui.findEdit); + shiftEnterAction->setShortcut(QKeySequence("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("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("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("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 + QList<int> globalcontext; + globalcontext << Core::Constants::C_GLOBAL_ID; + + Core::ActionManagerInterface *am = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()->actionManager(); + Core::IActionContainer *mfind = am->actionContainer(Constants::M_FIND); + Core::ICommand *cmd; + + m_findInDocumentAction = new QAction(tr("Current Document"), this); + cmd = am->registerAction(m_findInDocumentAction, Constants::FIND_IN_DOCUMENT, globalcontext); + cmd->setDefaultKeySequence(QKeySequence::Find); + mfind->addAction(cmd, Constants::G_FIND_FILTERS); + connect(m_findInDocumentAction, SIGNAL(triggered()), this, SLOT(openFind())); + + if (QApplication::clipboard()->supportsFindBuffer()) { + m_enterFindStringAction = new QAction(tr("Enter Find String"), this); + cmd = am->registerAction(m_enterFindStringAction, tr("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 = am->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 = am->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_replaceNextAction = new QAction(tr("Replace && Find Next"), this); + cmd = am->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 = am->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_ui.replacePreviousButton->setDefaultAction(cmd->action()); + + m_replaceAllAction = new QAction(tr("Replace All"), this); + cmd = am->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(":/find/images/casesensitively.png")); + m_caseSensitiveAction->setCheckable(true); + m_caseSensitiveAction->setChecked(false); + cmd = am->registerAction(m_caseSensitiveAction, Constants::CASE_SENSITIVE, globalcontext); + mfind->addAction(cmd, Constants::G_FIND_FLAGS); + connect(m_caseSensitiveAction, SIGNAL(triggered(bool)), m_plugin, SLOT(setCaseSensitive(bool))); + lineEditMenu->addAction(m_caseSensitiveAction); + + m_wholeWordAction = new QAction(tr("Whole Words Only"), this); + m_wholeWordAction->setIcon(QIcon(":/find/images/wholewords.png")); + m_wholeWordAction->setCheckable(true); + m_wholeWordAction->setChecked(false); + cmd = am->registerAction(m_wholeWordAction, Constants::WHOLE_WORDS, globalcontext); + mfind->addAction(cmd, Constants::G_FIND_FLAGS); + connect(m_wholeWordAction, SIGNAL(triggered(bool)), m_plugin, SLOT(setWholeWord(bool))); + lineEditMenu->addAction(m_wholeWordAction); + + connect(m_currentDocumentFind, SIGNAL(changed()), this, SLOT(updateActions())); + updateActions(); + updateIcons(); + connect(m_plugin, SIGNAL(findFlagsChanged()), this, SLOT(findFlagsChanged())); +} + +FindToolBar::~FindToolBar() +{ +} + +bool FindToolBar::eventFilter(QObject *obj, QEvent *event) +{ + if ((obj == m_ui.findEdit || obj == m_findCompleter->popup()) + && event->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast<QKeyEvent *>(event); +#ifdef Q_OS_MAC + if (ke->key() == Qt::Key_Space && (ke->modifiers() & Qt::MetaModifier)) { +#else + if (ke->key() == Qt::Key_Space && (ke->modifiers() & Qt::ControlModifier)) { +#endif + QString completedText = m_currentDocumentFind->completedFindString(); + if (!completedText.isEmpty()) { + setFindText(completedText); + ke->accept(); + return true; + } + } + } else if (obj == m_widget && event->type() == QEvent::ShortcutOverride) { + QKeyEvent *ke = static_cast<QKeyEvent *>(event); + if (ke->key() == Qt::Key_Escape && !ke->modifiers() + && !m_findCompleter->popup()->isVisible() + && !m_replaceCompleter->popup()->isVisible()) { + if (setFocusToCurrentFindSupport()) { + event->accept(); + return true; + } +#ifdef Q_OS_MAC + } else if (ke->key() == Qt::Key_Space && (ke->modifiers() & Qt::MetaModifier)) { +#else + } else if (ke->key() == Qt::Key_Space && (ke->modifiers() & Qt::ControlModifier)) { +#endif + event->accept(); + return true; + } + } else if (obj == m_widget && event->type() == QEvent::Hide) { + invokeClearResults(); + if (m_currentDocumentFind->isEnabled()) { + m_currentDocumentFind->clearFindScope(); + } + } + return QToolBar::eventFilter(obj, event); +} + +void FindToolBar::updateActions() +{ + bool enabled = m_currentDocumentFind->isEnabled(); + bool replaceEnabled = enabled && m_currentDocumentFind->supportsReplace(); + m_findInDocumentAction->setEnabled(enabled); + m_findNextAction->setEnabled(enabled); + m_findPreviousAction->setEnabled(enabled); + m_replaceNextAction->setEnabled(replaceEnabled); + m_replacePreviousAction->setEnabled(replaceEnabled); + m_replaceAllAction->setEnabled(replaceEnabled); + m_caseSensitiveAction->setEnabled(enabled); + m_wholeWordAction->setEnabled(enabled); + 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); + if (!replaceEnabled && enabled && replaceFocus) + m_ui.findEdit->setFocus(); +} + +void FindToolBar::invokeFindEnter() +{ + if (m_currentDocumentFind->isEnabled()) { + 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() +{ + m_plugin->setBackward(false); + invokeFindStep(); +} + +void FindToolBar::invokeFindPrevious() +{ + m_plugin->setBackward(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(const QString&)), this, SLOT(invokeFindIncremental())); + m_ui.findEdit->setText(text); + connect(m_ui.findEdit, SIGNAL(textChanged(const QString&)), this, SLOT(invokeFindIncremental())); +} + +void FindToolBar::selectFindText() +{ + m_ui.findEdit->selectAll(); +} + +void FindToolBar::invokeFindStep() +{ + if (m_currentDocumentFind->isEnabled()) { + m_plugin->updateFindCompletion(getFindText()); + m_currentDocumentFind->findStep(getFindText(), m_plugin->findFlags()); + } +} + +void FindToolBar::invokeFindIncremental() +{ + if (m_currentDocumentFind->isEnabled()) { + QString text = getFindText(); + m_currentDocumentFind->findIncremental(text, m_plugin->findFlags()); + if (text.isEmpty()) + m_currentDocumentFind->clearResults(); + } +} + +void FindToolBar::invokeReplaceNext() +{ + m_plugin->setBackward(false); + invokeReplaceStep(); +} + +void FindToolBar::invokeReplacePrevious() +{ + m_plugin->setBackward(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(), m_plugin->findFlags()); + } +} + +void FindToolBar::invokeReplaceAll() +{ + m_plugin->updateFindCompletion(getFindText()); + m_plugin->updateReplaceCompletion(getReplaceText()); + if (m_currentDocumentFind->isEnabled() && m_currentDocumentFind->supportsReplace()) { + m_currentDocumentFind->replaceAll(getFindText(), getReplaceText(), m_plugin->findFlags()); + } +} + +void FindToolBar::invokeResetIncrementalSearch() +{ + 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(); +} + +void FindToolBar::updateIcons() +{ + bool casesensitive = m_plugin->findFlags() & QTextDocument::FindCaseSensitively; + bool wholewords = m_plugin->findFlags() & QTextDocument::FindWholeWords; + + if (casesensitive && wholewords) { + QPixmap image = QPixmap(":/find/images/wordandcase.png"); + m_ui.findEdit->setPixmap(image); + } else if (casesensitive) { + QPixmap image = QPixmap(":/find/images/casesensitively.png"); + m_ui.findEdit->setPixmap(image); + } else if (wholewords) { + QPixmap image = QPixmap(":/find/images/wholewords.png"); + m_ui.findEdit->setPixmap(image); + } else { + m_ui.findEdit->setPixmap(QPixmap(Core::Constants::ICON_MAGNIFIER)); + } +} + +void FindToolBar::updateFlagMenus() +{ + bool wholeOnly = ((m_plugin->findFlags() & QTextDocument::FindWholeWords)); + bool sensitive = ((m_plugin->findFlags() & QTextDocument::FindCaseSensitively)); + if (m_wholeWordAction->isChecked() != wholeOnly) + m_wholeWordAction->setChecked(wholeOnly); + if (m_caseSensitiveAction->isChecked() != sensitive) + m_caseSensitiveAction->setChecked(sensitive); +} + +bool FindToolBar::setFocusToCurrentFindSupport() +{ + return m_currentDocumentFind->setFocusToCurrentFindSupport(); +} + +void FindToolBar::hideAndResetFocus() +{ + m_currentDocumentFind->setFocusToCurrentFindSupport(); + hide(); +} + +void FindToolBar::openFind() +{ + if (!m_currentDocumentFind->isEnabled()) + return; + Core::FindToolBarPlaceHolder *holder = Core::FindToolBarPlaceHolder::getCurrent(); + QLayout *findContainerLayout = holder ? holder->layout() : 0; + + if (findContainerLayout) { + findContainerLayout->addWidget(this); + holder->setVisible(true); + setVisible(true); + setFocus(); + } + QString text = m_currentDocumentFind->currentFindString(); + if (!text.isEmpty()) + setFindText(text); + m_currentDocumentFind->defineFindScope(); + m_currentDocumentFind->highlightAll(getFindText(), m_plugin->findFlags()); + selectFindText(); +} diff --git a/src/plugins/find/findtoolbar.h b/src/plugins/find/findtoolbar.h new file mode 100644 index 00000000000..cb85470f410 --- /dev/null +++ b/src/plugins/find/findtoolbar.h @@ -0,0 +1,112 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FINDTOOLBAR_H +#define FINDTOOLBAR_H + +#include "ui_findwidget.h" +#include "ifindfilter.h" +#include "currentdocumentfind.h" + +#include <QtGui/QStringListModel> +#include <QtGui/QWidget> +#include <QtGui/QToolBar> +#include <QtGui/QLabel> + +namespace Find { +namespace Internal { + +class FindPlugin; + +class FindToolBar : public QToolBar +{ + Q_OBJECT + +public: + FindToolBar(FindPlugin *plugin, CurrentDocumentFind *currentDocumentFind); + ~FindToolBar(); + + void invokeClearResults(); + +private slots: + void invokeFindNext(); + void invokeFindPrevious(); + void invokeFindStep(); + void invokeReplaceNext(); + void invokeReplacePrevious(); + void invokeReplaceStep(); + void invokeReplaceAll(); + void invokeResetIncrementalSearch(); + + void invokeFindIncremental(); + void invokeFindEnter(); + void invokeReplaceEnter(); + void putSelectionToFindClipboard(); + void updateFromFindClipboard(); + + void hideAndResetFocus(); + void openFind(); + void updateActions(); + void findFlagsChanged(); + +private: + bool setFocusToCurrentFindSupport(); + + bool eventFilter(QObject *obj, QEvent *event); + void setFindText(const QString &text); + QString getFindText(); + QString getReplaceText(); + void selectFindText(); + void updateIcons(); + void updateFlagMenus(); + + FindPlugin *m_plugin; + CurrentDocumentFind *m_currentDocumentFind; + Ui::FindWidget m_ui; + QCompleter *m_findCompleter; + QCompleter *m_replaceCompleter; + QAction *m_findInDocumentAction; + QAction *m_enterFindStringAction; + QAction *m_findNextAction; + QAction *m_findPreviousAction; + QAction *m_replaceNextAction; + QAction *m_replacePreviousAction; + QAction *m_replaceAllAction; + QAction *m_caseSensitiveAction; + QAction *m_wholeWordAction; + QWidget *m_widget; +}; + +} // namespace Internal +} // namespace Find + +#endif // FINDTOOLBAR_H diff --git a/src/plugins/find/findtoolwindow.cpp b/src/plugins/find/findtoolwindow.cpp new file mode 100644 index 00000000000..40c476dcffb --- /dev/null +++ b/src/plugins/find/findtoolwindow.cpp @@ -0,0 +1,140 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "findtoolwindow.h" +#include "findplugin.h" + +#include <coreplugin/icore.h> + +#include <QtGui/QMainWindow> + +using namespace Find; +using namespace Find::Internal; + +FindToolWindow::FindToolWindow(FindPlugin *plugin) + : QDialog(plugin->core()->mainWindow()), + m_plugin(plugin), + m_findCompleter(new QCompleter(this)) +{ + m_ui.setupUi(this); + connect(m_ui.closeButton, SIGNAL(clicked()), this, SLOT(reject())); + connect(m_ui.searchButton, SIGNAL(clicked()), this, SLOT(accept())); + 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.filterList, SIGNAL(currentIndexChanged(int)), this, SLOT(setCurrentFilter(int))); + connect(this, SIGNAL(accepted()), this, SLOT(search())); + m_findCompleter->setModel(m_plugin->findCompletionModel()); + m_ui.searchTerm->setCompleter(m_findCompleter); + QVBoxLayout *layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + m_ui.configWidget->setLayout(layout); +} + +FindToolWindow::~FindToolWindow() +{ + qDeleteAll(m_configWidgets); +} + +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->name(); + m_configWidgets.append(filter->createConfigWidget()); + } + m_ui.filterList->addItems(names); +} + +void FindToolWindow::setFindText(const QString &text) +{ + m_ui.searchTerm->setText(text); +} + +void FindToolWindow::open(IFindFilter *filter) +{ + int index = m_filters.indexOf(filter); + if (index >= 0) { + m_ui.filterList->setCurrentIndex(index); + } + m_ui.matchCase->setChecked(m_plugin->findFlags() & QTextDocument::FindCaseSensitively); + m_ui.wholeWords->setChecked(m_plugin->findFlags() & QTextDocument::FindWholeWords); + m_ui.searchTerm->setFocus(); + m_ui.searchTerm->selectAll(); + exec(); +} + +void FindToolWindow::setCurrentFilter(int index) +{ + for (int i = 0; i < m_configWidgets.size(); ++i) { + QWidget *configWidget = m_configWidgets.at(i); + if (!configWidget) + continue; + if (i == index) + m_ui.configWidget->layout()->addWidget(configWidget); + else + configWidget->setParent(0); + } +} + +void FindToolWindow::search() +{ + m_plugin->updateFindCompletion(m_ui.searchTerm->text()); + int index = m_ui.filterList->currentIndex(); + QString term = m_ui.searchTerm->text(); + if (term.isEmpty() || index < 0) + return; + IFindFilter *filter = m_filters.at(index); + filter->findAll(term, m_plugin->findFlags()); +} + +void FindToolWindow::writeSettings() +{ + QSettings *settings = m_plugin->core()->settings(); + settings->beginGroup("Find"); + foreach (IFindFilter *filter, m_filters) + filter->writeSettings(settings); + settings->endGroup(); +} + +void FindToolWindow::readSettings() +{ + QSettings *settings = m_plugin->core()->settings(); + settings->beginGroup("Find"); + foreach (IFindFilter *filter, m_filters) + filter->readSettings(settings); + settings->endGroup(); +} diff --git a/src/plugins/find/findtoolwindow.h b/src/plugins/find/findtoolwindow.h new file mode 100644 index 00000000000..558b8198ab5 --- /dev/null +++ b/src/plugins/find/findtoolwindow.h @@ -0,0 +1,78 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FINDTOOLWINDOW_H +#define FINDTOOLWINDOW_H + +#include "ui_finddialog.h" +#include "ifindfilter.h" + +#include <QtCore/QList> +#include <QtGui/QCompleter> +#include <QtGui/QWidget> + +namespace Find { +namespace Internal { + +class FindPlugin; + +class FindToolWindow : public QDialog +{ + Q_OBJECT + +public: + FindToolWindow(FindPlugin *plugin); + ~FindToolWindow(); + + void setFindFilters(const QList<IFindFilter *> &filters); + + void setFindText(const QString &text); + void open(IFindFilter *filter); + void readSettings(); + void writeSettings(); + +private slots: + void search(); + void setCurrentFilter(int index); + +private: + Ui::FindDialog m_ui; + FindPlugin *m_plugin; + QList<IFindFilter *> m_filters; + QCompleter *m_findCompleter; + QList<QWidget *> m_configWidgets; +}; + +} // namespace Internal +} // namespace Find + +#endif // FINDTOOLWINDOW_H diff --git a/src/plugins/find/findwidget.ui b/src/plugins/find/findwidget.ui new file mode 100644 index 00000000000..c85f3362997 --- /dev/null +++ b/src/plugins/find/findwidget.ui @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Find::Internal::FindWidget</class> + <widget class="QWidget" name="Find::Internal::FindWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>71</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>600</width> + <height>0</height> + </size> + </property> + <property name="windowTitle"> + <string>Find</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <property name="spacing"> + <number>15</number> + </property> + <property name="margin"> + <number>1</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="spacing"> + <number>2</number> + </property> + <item> + <widget class="QLabel" name="findLabel"> + <property name="text"> + <string>Find:</string> + </property> + </widget> + </item> + <item> + <widget class="Core::Utils::FancyLineEdit" name="findEdit"> + <property name="minimumSize"> + <size> + <width>160</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>160</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="findPreviousButton"> + <property name="arrowType"> + <enum>Qt::LeftArrow</enum> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="findNextButton"> + <property name="font"> + <font/> + </property> + <property name="arrowType"> + <enum>Qt::RightArrow</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>2</number> + </property> + <item> + <widget class="QLabel" name="replaceLabel"> + <property name="text"> + <string>Replace with:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="replaceEdit"> + <property name="minimumSize"> + <size> + <width>150</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>150</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="replacePreviousButton"> + <property name="arrowType"> + <enum>Qt::LeftArrow</enum> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="replaceNextButton"> + <property name="font"> + <font/> + </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>All</string> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextOnly</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Core::Utils::FancyLineEdit</class> + <extends>QLineEdit</extends> + <header location="global">utils/fancylineedit.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/find/ifindfilter.h b/src/plugins/find/ifindfilter.h new file mode 100644 index 00000000000..356c293b289 --- /dev/null +++ b/src/plugins/find/ifindfilter.h @@ -0,0 +1,66 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IFINDFILTER_H +#define IFINDFILTER_H + +#include "ifindsupport.h" +#include <QtCore/QSettings> +#include <QtGui/QIcon> +#include <QtGui/QKeySequence> +#include <QtGui/QWidget> + +namespace Find { + +class FIND_EXPORT IFindFilter : public QObject +{ + Q_OBJECT +public: + + virtual ~IFindFilter() {} + + virtual QString name() const = 0; + virtual bool isEnabled() const = 0; + virtual QKeySequence defaultShortcut() const = 0; + virtual void findAll(const QString &txt, QTextDocument::FindFlags findFlags) = 0; + + virtual QWidget *createConfigWidget() { return 0; } + virtual void writeSettings(QSettings *settings) { Q_UNUSED(settings); } + virtual void readSettings(QSettings *settings) { Q_UNUSED(settings); } + +signals: + void changed(); +}; + +} // namespace Find + +#endif // IFINDFILTER_H diff --git a/src/plugins/find/ifindsupport.h b/src/plugins/find/ifindsupport.h new file mode 100644 index 00000000000..14786ed922d --- /dev/null +++ b/src/plugins/find/ifindsupport.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IFINDSUPPORT_H +#define IFINDSUPPORT_H + +#include "find_global.h" +#include <QtGui/QTextDocument> + +namespace Find { + +class FIND_EXPORT IFindSupport : public QObject +{ + Q_OBJECT + +public: + + IFindSupport() : QObject(0) {} + virtual ~IFindSupport() {} + + virtual bool supportsReplace() 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, QTextDocument::FindFlags findFlags); + virtual bool findIncremental(const QString &txt, QTextDocument::FindFlags findFlags) = 0; + virtual bool findStep(const QString &txt, QTextDocument::FindFlags findFlags) = 0; + virtual bool replaceStep(const QString &before, const QString &after, + QTextDocument::FindFlags findFlags) = 0; + virtual int replaceAll(const QString &before, const QString &after, + QTextDocument::FindFlags findFlags) = 0; + + virtual void defineFindScope(){} + virtual void clearFindScope(){} + +signals: + void changed(); +}; + + +inline void IFindSupport::highlightAll(const QString &, QTextDocument::FindFlags){} + +} // namespace Find + +#endif // IFINDSUPPORT_H diff --git a/src/plugins/find/images/all.png b/src/plugins/find/images/all.png Binary files differnew file mode 100644 index 00000000000..f5c1c1f767d --- /dev/null +++ b/src/plugins/find/images/all.png diff --git a/src/plugins/find/images/casesensitively.png b/src/plugins/find/images/casesensitively.png Binary files differnew file mode 100644 index 00000000000..93038c0523c --- /dev/null +++ b/src/plugins/find/images/casesensitively.png diff --git a/src/plugins/find/images/empty.png b/src/plugins/find/images/empty.png Binary files differnew file mode 100644 index 00000000000..f02154247c2 --- /dev/null +++ b/src/plugins/find/images/empty.png diff --git a/src/plugins/find/images/expand.png b/src/plugins/find/images/expand.png Binary files differnew file mode 100644 index 00000000000..48fcb9b703b --- /dev/null +++ b/src/plugins/find/images/expand.png diff --git a/src/plugins/find/images/next.png b/src/plugins/find/images/next.png Binary files differnew file mode 100644 index 00000000000..1844929119b --- /dev/null +++ b/src/plugins/find/images/next.png diff --git a/src/plugins/find/images/previous.png b/src/plugins/find/images/previous.png Binary files differnew file mode 100644 index 00000000000..4fe50af9a87 --- /dev/null +++ b/src/plugins/find/images/previous.png diff --git a/src/plugins/find/images/replace_all.png b/src/plugins/find/images/replace_all.png Binary files differnew file mode 100644 index 00000000000..d2298a8aade --- /dev/null +++ b/src/plugins/find/images/replace_all.png diff --git a/src/plugins/find/images/wholewords.png b/src/plugins/find/images/wholewords.png Binary files differnew file mode 100644 index 00000000000..0187023ada3 --- /dev/null +++ b/src/plugins/find/images/wholewords.png diff --git a/src/plugins/find/images/wordandcase.png b/src/plugins/find/images/wordandcase.png Binary files differnew file mode 100644 index 00000000000..3a34cbea83b --- /dev/null +++ b/src/plugins/find/images/wordandcase.png diff --git a/src/plugins/find/searchresulttreeitemdelegate.cpp b/src/plugins/find/searchresulttreeitemdelegate.cpp new file mode 100644 index 00000000000..a69d4357fee --- /dev/null +++ b/src/plugins/find/searchresulttreeitemdelegate.cpp @@ -0,0 +1,121 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "searchresulttreeitemdelegate.h" +#include "searchresulttreeitemroles.h" + +#include <math.h> + +#include <QModelIndex> +#include <QTextDocument> +#include <QPainter> +#include <QAbstractTextDocumentLayout> +#include <QApplication> + +using namespace Find::Internal; + +SearchResultTreeItemDelegate::SearchResultTreeItemDelegate(QObject *parent) +: QItemDelegate(parent) +{ +} + +void SearchResultTreeItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + if (index.model()->data(index, ItemDataRoles::TypeRole).toString().compare("file") == 0) + { + QItemDelegate::paint(painter, option, index); + } + else + { + painter->save(); + + QStyleOptionViewItemV3 opt = setOptions(index, option); + painter->setFont(opt.font); + + QItemDelegate::drawBackground(painter, opt, index); + + int lineNumberAreaWidth = drawLineNumber(painter, opt, index); + + QRect resultRowRect(opt.rect.adjusted(lineNumberAreaWidth, 0, 0, 0)); + QString displayString = index.model()->data(index, Qt::DisplayRole).toString(); + drawMarker(painter, index, displayString, resultRowRect); + + // Draw the text and focus/selection + QItemDelegate::drawDisplay(painter, opt, resultRowRect, displayString); + QItemDelegate::drawFocus(painter, opt, opt.rect); + + painter->restore(); + } +} + +int SearchResultTreeItemDelegate::drawLineNumber(QPainter *painter, const QStyleOptionViewItemV3 &option, + const QModelIndex &index) const +{ + static const int lineNumberAreaHorizontalPadding = 4; + const bool isSelected = option.state & QStyle::State_Selected; + int lineNumber = index.model()->data(index, ItemDataRoles::ResultLineNumberRole).toInt(); + int lineNumberDigits = (int)floor(log10((double)lineNumber)) + 1; + int minimumLineNumberDigits = qMax((int)m_minimumLineNumberDigits, lineNumberDigits); + int fontWidth = painter->fontMetrics().width(QString(minimumLineNumberDigits, '0')); + int lineNumberAreaWidth = lineNumberAreaHorizontalPadding + fontWidth + lineNumberAreaHorizontalPadding; + QRect lineNumberAreaRect(option.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):QBrush(qRgb(230, 230, 230)))); + painter->setPen(isSelected? + option.palette.color(cg, QPalette::HighlightedText):Qt::darkGray); + painter->drawText(lineNumberAreaRect.adjusted(0, 0, -lineNumberAreaHorizontalPadding, 0), + Qt::AlignRight, QString::number(lineNumber)); + + return lineNumberAreaWidth; +} + +void SearchResultTreeItemDelegate::drawMarker(QPainter *painter, const QModelIndex &index, const QString text, + const QRect &rect) const +{ + const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; + int searchTermStart = index.model()->data(index, ItemDataRoles::SearchTermStartRole).toInt(); + int searchTermStartPixels = painter->fontMetrics().width(text.left(searchTermStart)); + int searchTermLength = index.model()->data(index, ItemDataRoles::SearchTermLengthRole).toInt(); + int searchTermLengthPixels = painter->fontMetrics().width(text.mid(searchTermStart, searchTermLength)); + QRect resultHighlightRect(rect); + resultHighlightRect.setLeft(resultHighlightRect.left() + searchTermStartPixels + textMargin - 1); // -1: Cosmetics + resultHighlightRect.setRight(resultHighlightRect.left() + searchTermLengthPixels + 1); // +1: Cosmetics + painter->fillRect(resultHighlightRect, QBrush(qRgb(255, 240, 120))); +} diff --git a/src/plugins/find/searchresulttreeitemdelegate.h b/src/plugins/find/searchresulttreeitemdelegate.h new file mode 100644 index 00000000000..ede85741df9 --- /dev/null +++ b/src/plugins/find/searchresulttreeitemdelegate.h @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SEARCHRESULTTREEITEMDELEGATE_H +#define SEARCHRESULTTREEITEMDELEGATE_H + +#include <QtGui/QItemDelegate> + +namespace Find { +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 QModelIndex &index) const; + void drawMarker(QPainter *painter, const QModelIndex &index, const QString text, const QRect &rect) const; + + static const int m_minimumLineNumberDigits = 6; +}; + +} //Internal +} //Find + +#endif diff --git a/src/plugins/find/searchresulttreeitemroles.h b/src/plugins/find/searchresulttreeitemroles.h new file mode 100644 index 00000000000..2b838a44ec0 --- /dev/null +++ b/src/plugins/find/searchresulttreeitemroles.h @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SEARCHRESULTTREEITEMROLES_H +#define SEARCHRESULTTREEITEMROLES_H + +namespace Find { +namespace Internal { +namespace ItemDataRoles { + +enum roles +{ + TypeRole = Qt::UserRole, + FileNameRole, + ResultLinesCountRole, + ResultIndexRole, + ResultLineRole, + ResultLineNumberRole, + SearchTermStartRole, + SearchTermLengthRole, + RowOfItem // The ?-th child of its parent is this this item +}; + +} //Internal +} //Find +} //itemDataRoles + +#endif diff --git a/src/plugins/find/searchresulttreeitems.cpp b/src/plugins/find/searchresulttreeitems.cpp new file mode 100644 index 00000000000..46200165595 --- /dev/null +++ b/src/plugins/find/searchresulttreeitems.cpp @@ -0,0 +1,136 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "searchresulttreeitems.h" + +using namespace Find::Internal; + +SearchResultTreeItem::SearchResultTreeItem(SearchResultTreeItem::itemType type, const SearchResultTreeItem *parent) +: m_type(type) +, m_parent(parent) +{ +} + +SearchResultTreeItem::~SearchResultTreeItem() +{ + clearChildren(); +} + +void SearchResultTreeItem::clearChildren(void) +{ + qDeleteAll(m_children); + m_children.clear(); +} + +SearchResultTreeItem::itemType SearchResultTreeItem::getItemType(void) const +{ + return m_type; +} + +int SearchResultTreeItem::getChildrenCount(void) const +{ + return m_children.count(); +} + +int SearchResultTreeItem::getRowOfItem(void) const +{ + return (m_parent?m_parent->m_children.indexOf(const_cast<SearchResultTreeItem*>(this)):0); +} + +const SearchResultTreeItem* SearchResultTreeItem::getChild(int index) const +{ + return m_children.at(index); +} + +const SearchResultTreeItem *SearchResultTreeItem::getParent(void) const +{ + return m_parent; +} + +void SearchResultTreeItem::appendChild(SearchResultTreeItem *child) +{ + m_children.append(child); +} + +SearchResultTextRow::SearchResultTextRow(int index, int lineNumber, + const QString &rowText, int searchTermStart, int searchTermLength, const SearchResultTreeItem *parent) +: SearchResultTreeItem(resultRow, parent), + m_index(index), + m_lineNumber(lineNumber), + m_rowText(rowText), + m_searchTermStart(searchTermStart), + m_searchTermLength(searchTermLength) +{ +} + +int SearchResultTextRow::index() const +{ + return m_index; +} + +QString SearchResultTextRow::rowText() const +{ + return m_rowText; +} + +int SearchResultTextRow::lineNumber() const +{ + return m_lineNumber; +} + +int SearchResultTextRow::searchTermStart() const +{ + return m_searchTermStart; +} + +int SearchResultTextRow::searchTermLength() const +{ + return m_searchTermLength; +} + +SearchResultFile::SearchResultFile(const QString &fileName, const SearchResultTreeItem *parent) +: SearchResultTreeItem(resultFile, parent) +, m_fileName(fileName) +{ +} + +QString SearchResultFile::getFileName(void) const +{ + return m_fileName; +} + +void SearchResultFile::appendResultLine(int index, int lineNumber, const QString &rowText, int searchTermStart, + int searchTermLength) +{ + SearchResultTreeItem *child = new SearchResultTextRow(index, lineNumber, rowText, searchTermStart, searchTermLength, this); + appendChild(child); +} diff --git a/src/plugins/find/searchresulttreeitems.h b/src/plugins/find/searchresulttreeitems.h new file mode 100644 index 00000000000..62be73942bc --- /dev/null +++ b/src/plugins/find/searchresulttreeitems.h @@ -0,0 +1,106 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SEARCHRESULTTREEITEMS_H +#define SEARCHRESULTTREEITEMS_H + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtCore/QList> + +namespace Find { +namespace Internal { + +class SearchResultTreeItem; + +class SearchResultTreeItem +{ +public: + enum itemType + { + root, + resultRow, + resultFile + }; + + SearchResultTreeItem(itemType type = root, const SearchResultTreeItem *parent = NULL); + virtual ~SearchResultTreeItem(); + + itemType getItemType(void) const; + const SearchResultTreeItem *getParent(void) const; + const SearchResultTreeItem *getChild(int index) const; + void appendChild(SearchResultTreeItem *child); + int getChildrenCount(void) const; + int getRowOfItem(void) const; + void clearChildren(void); + +private: + itemType m_type; + const SearchResultTreeItem *m_parent; + QList<SearchResultTreeItem *> m_children; +}; + +class SearchResultTextRow: public SearchResultTreeItem +{ +public: + SearchResultTextRow(int index, int lineNumber, const QString &rowText, int searchTermStart, + int searchTermLength, const SearchResultTreeItem *parent); + int index() const; + QString rowText() const; + int lineNumber() const; + int searchTermStart() const; + int searchTermLength() const; + +private: + int m_index; + int m_lineNumber; + QString m_rowText; + int m_searchTermStart; + int m_searchTermLength; +}; + +class SearchResultFile: public SearchResultTreeItem +{ +public: + SearchResultFile(const QString &fileName, const SearchResultTreeItem *parent); + QString getFileName(void) const; + void appendResultLine(int index, int lineNumber, const QString &rowText, int searchTermStart, + int searchTermLength); + +private: + QString m_fileName; +}; + +} //Internal +} //Find + +#endif diff --git a/src/plugins/find/searchresulttreemodel.cpp b/src/plugins/find/searchresulttreemodel.cpp new file mode 100644 index 00000000000..17a81e852d3 --- /dev/null +++ b/src/plugins/find/searchresulttreemodel.cpp @@ -0,0 +1,260 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "searchresulttreemodel.h" +#include "searchresulttreeitems.h" +#include "searchresulttreeitemroles.h" +#include <QtGui/QFont> +#include <QtGui/QColor> + +using namespace Find::Internal; + +SearchResultTreeModel::SearchResultTreeModel(QObject *parent) +: QAbstractItemModel(parent) +, m_lastAppendedResultFile(NULL) +{ + m_rootItem = new SearchResultTreeItem(); +} + +SearchResultTreeModel::~SearchResultTreeModel() +{ + delete m_rootItem; +} + +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 = static_cast<const SearchResultTreeItem*>(parent.internalPointer()); + + const SearchResultTreeItem *childItem = parentItem->getChild(row); + if (childItem) + return createIndex(row, column, (void *)childItem); + else + return QModelIndex(); +} + +QModelIndex SearchResultTreeModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + const SearchResultTreeItem *childItem = static_cast<const SearchResultTreeItem*>(index.internalPointer()); + const SearchResultTreeItem *parentItem = childItem->getParent(); + + if (parentItem == m_rootItem) + return QModelIndex(); + + return createIndex(parentItem->getRowOfItem(), 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 = static_cast<const SearchResultTreeItem*>(parent.internalPointer()); + + return parentItem->getChildrenCount(); +} + +int SearchResultTreeModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 1; +} + +QVariant SearchResultTreeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + const SearchResultTreeItem *item = static_cast<const SearchResultTreeItem*>(index.internalPointer()); + + QVariant result; + + if (item->getItemType() == SearchResultTreeItem::resultRow) + { + const SearchResultTextRow *row = static_cast<const SearchResultTextRow *>(item); + result = data(row, role); + } + else if (item->getItemType() == SearchResultTreeItem::resultFile) + { + const SearchResultFile *file = static_cast<const SearchResultFile *>(item); + result = data(file, role); + } + + return result; +} + +QVariant SearchResultTreeModel::data(const SearchResultTextRow *row, int role) const +{ + QVariant result; + + switch (role) + { + case Qt::ToolTipRole: + result = row->rowText().trimmed(); + break; + case Qt::FontRole: + result = QFont("courier"); + break; + case ItemDataRoles::ResultLineRole: + case Qt::DisplayRole: + result = row->rowText(); + break; + case ItemDataRoles::ResultIndexRole: + result = row->index(); + break; + case ItemDataRoles::ResultLineNumberRole: + result = row->lineNumber(); + break; + case ItemDataRoles::SearchTermStartRole: + result = row->searchTermStart(); + break; + case ItemDataRoles::SearchTermLengthRole: + result = row->searchTermLength(); + break; + case ItemDataRoles::TypeRole: + result = "row"; + break; + case ItemDataRoles::FileNameRole: + { + const SearchResultFile *file = dynamic_cast<const SearchResultFile *>(row->getParent()); + result = file->getFileName(); + break; + } + default: + result = QVariant(); + break; + } + + return result; +} + +QVariant SearchResultTreeModel::data(const SearchResultFile *file, int role) const +{ + QVariant result; + + switch (role) + { + case Qt::BackgroundRole: + result = QColor(qRgb(245, 245, 245)); + break; + case Qt::FontRole: + { + QFont font; + font.setPointSize(font.pointSize() + 1); + result = font; + break; + } + case Qt::DisplayRole: + result = file->getFileName() + " (" + QString::number(file->getChildrenCount()) + ")"; + break; + case ItemDataRoles::FileNameRole: + case Qt::ToolTipRole: + result = file->getFileName(); + break; + case ItemDataRoles::ResultLinesCountRole: + result = file->getChildrenCount(); + break; + case ItemDataRoles::TypeRole: + result = "file"; + 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(); +} + +void SearchResultTreeModel::appendResultFile(const QString &fileName) +{ + m_lastAppendedResultFile = new SearchResultFile(fileName, m_rootItem); + + beginInsertRows(QModelIndex(), m_rootItem->getChildrenCount(), m_rootItem->getChildrenCount()); + m_rootItem->appendChild(m_lastAppendedResultFile); + endInsertRows(); +} + +void SearchResultTreeModel::appendResultLine(int index, int lineNumber, const QString &rowText, int searchTermStart, + int searchTermLength) +{ + if (!m_lastAppendedResultFile) + return; + + QModelIndex lastFile(createIndex(m_lastAppendedResultFile->getRowOfItem(), 0, m_lastAppendedResultFile)); + + beginInsertRows(lastFile, m_lastAppendedResultFile->getChildrenCount(), m_lastAppendedResultFile->getChildrenCount()); + m_lastAppendedResultFile->appendResultLine(index, lineNumber, rowText, searchTermStart, searchTermLength); + endInsertRows(); + + dataChanged(lastFile, lastFile); // Make sure that the number after the file name gets updated +} + +void SearchResultTreeModel::appendResultLine(int index, const QString &fileName, int lineNumber, const QString &rowText, + int searchTermStart, int searchTermLength) +{ + if (!m_lastAppendedResultFile || (m_lastAppendedResultFile->getFileName() != fileName)) + appendResultFile(fileName); + + appendResultLine(index, lineNumber, rowText, searchTermStart, searchTermLength); +} + +void SearchResultTreeModel::clear(void) +{ + m_lastAppendedResultFile = NULL; + m_rootItem->clearChildren(); + reset(); +} diff --git a/src/plugins/find/searchresulttreemodel.h b/src/plugins/find/searchresulttreemodel.h new file mode 100644 index 00000000000..5a55920fa46 --- /dev/null +++ b/src/plugins/find/searchresulttreemodel.h @@ -0,0 +1,87 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SEARCHRESULTTREEMODEL_H +#define SEARCHRESULTTREEMODEL_H + +#include <QtCore/QAbstractItemModel> + +namespace Find{ +namespace Internal { + +class SearchResultTreeItem; +class SearchResultTextRow; +class SearchResultFile; + +class SearchResultTreeModel: public QAbstractItemModel +{ + Q_OBJECT + +public: + SearchResultTreeModel(QObject *parent = 0); + ~SearchResultTreeModel(); + + 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; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + +signals: + void jumpToSearchResult(const QString &fileName, int lineNumber, + int searchTermStart, int searchTermLength); + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + +public slots: + void clear(); + void appendResultLine(int index, int lineNumber, const QString &rowText, int searchTermStart, + int searchTermLength); + void appendResultLine(int index, const QString &fileName, int lineNumber, const QString &rowText, int searchTermStart, + int searchTermLength); + +private: + void appendResultFile(const QString &fileName); + QVariant data(const SearchResultTextRow *row, int role) const; + QVariant data(const SearchResultFile *file, int role) const; + void initializeData(void); + void disposeData(void); + + SearchResultTreeItem *m_rootItem; + SearchResultFile *m_lastAppendedResultFile; +}; + +} //Internal +} //Find + +#endif diff --git a/src/plugins/find/searchresulttreeview.cpp b/src/plugins/find/searchresulttreeview.cpp new file mode 100644 index 00000000000..c7bee6febbf --- /dev/null +++ b/src/plugins/find/searchresulttreeview.cpp @@ -0,0 +1,99 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "searchresulttreeview.h" +#include "searchresulttreeitemroles.h" +#include "searchresulttreemodel.h" +#include "searchresulttreeitemdelegate.h" + +#include <QtGui/QHeaderView> + +using namespace Find::Internal; + +SearchResultTreeView::SearchResultTreeView(QWidget *parent) +: QTreeView(parent) +, m_autoExpandResults(false) +{ + m_model = new SearchResultTreeModel(this); + setModel(m_model); + setItemDelegate(new SearchResultTreeItemDelegate(this)); + + setIndentation(14); + header()->hide(); + + connect (this, SIGNAL(activated(const QModelIndex&)), this, SLOT(emitJumpToSearchResult(const QModelIndex&))); +} + +void SearchResultTreeView::setAutoExpandResults(bool expand) +{ + m_autoExpandResults = expand; +} + +void SearchResultTreeView::clear(void) +{ + m_model->clear(); +} + +void SearchResultTreeView::appendResultLine(int index, const QString &fileName, int lineNumber, const QString &rowText, + int searchTermStart, int searchTermLength) +{ + int rowsBefore = m_model->rowCount(); + m_model->appendResultLine(index, fileName, lineNumber, rowText, searchTermStart, searchTermLength); + int rowsAfter = m_model->rowCount(); + + if (m_autoExpandResults && (rowsAfter > rowsBefore)) + setExpanded(model()->index(model()->rowCount()-1, 0), true); +} + +void SearchResultTreeView::emitJumpToSearchResult(const QModelIndex &index) +{ + if (model()->data(index, ItemDataRoles::TypeRole).toString().compare("row") != 0) + return; + + QString fileName(model()->data(index, ItemDataRoles::FileNameRole).toString()); + int position = model()->data(index, ItemDataRoles::ResultIndexRole).toInt(); + int lineNumber = model()->data(index, ItemDataRoles::ResultLineNumberRole).toInt(); + int searchTermStart = model()->data(index, ItemDataRoles::SearchTermStartRole).toInt(); + int searchTermLength = model()->data(index, ItemDataRoles::SearchTermLengthRole).toInt(); + + emit jumpToSearchResult(position, fileName, lineNumber, searchTermStart, searchTermLength); +} + +void SearchResultTreeView::keyPressEvent(QKeyEvent *e) +{ + if (!e->modifiers() && e->key() == Qt::Key_Return) { + emit activated(currentIndex()); + e->accept(); + return; + } + QTreeView::keyPressEvent(e); +} diff --git a/src/plugins/find/searchresulttreeview.h b/src/plugins/find/searchresulttreeview.h new file mode 100644 index 00000000000..e5491cc0ceb --- /dev/null +++ b/src/plugins/find/searchresulttreeview.h @@ -0,0 +1,74 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SEARCHRESULTTREEVIEW_H +#define SEARCHRESULTTREEVIEW_H + +#include <QtGui/QTreeView> +#include <QtGui/QKeyEvent> + +namespace Find { +namespace Internal { + +class SearchResultTreeModel; + +class SearchResultTreeView: public QTreeView +{ + Q_OBJECT + +public: + SearchResultTreeView(QWidget *parent = 0); + void setAutoExpandResults(bool expand); + +signals: + void jumpToSearchResult(int index, const QString &fileName, int lineNumber, + int searchTermStart, int searchTermLength); + +public slots: + void clear(); + void appendResultLine(int index, const QString &fileName, int lineNumber, const QString &lineText, + int searchTermStart, int searchTermLength); + +private slots: + void emitJumpToSearchResult(const QModelIndex &index); + +protected: + void keyPressEvent(QKeyEvent *e); + + SearchResultTreeModel *m_model; + bool m_autoExpandResults; +}; + +} // namespace Internal +} // namespace Find + +#endif // SEARCHRESULTTREEVIEW_H diff --git a/src/plugins/find/searchresultwindow.cpp b/src/plugins/find/searchresultwindow.cpp new file mode 100644 index 00000000000..a113d439f84 --- /dev/null +++ b/src/plugins/find/searchresultwindow.cpp @@ -0,0 +1,196 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "searchresultwindow.h" +#include "searchresulttreemodel.h" + +#include <QtCore/QFile> +#include <QtCore/QTextStream> +#include <QtCore/QSettings> +#include <QtGui/QListWidget> +#include <QtGui/QToolButton> + +using namespace Find; +using namespace Find::Internal; + +static const QString SETTINGSKEYSECTIONNAME("SearchResults"); +static const QString SETTINGSKEYEXPANDRESULTS("ExpandResults"); + +SearchResultWindow::SearchResultWindow(Core::ICore *core): + m_core(core), + m_widget(new QStackedWidget()) +{ + m_widget->setWindowTitle(name()); + + m_searchResultTreeView = new SearchResultTreeView(m_widget); + m_searchResultTreeView->setUniformRowHeights(true); + m_searchResultTreeView->setFrameStyle(QFrame::NoFrame); + m_widget->addWidget(m_searchResultTreeView); + + m_noMatchesFoundDisplay = new QListWidget(m_widget); + m_noMatchesFoundDisplay->addItem(tr("No matches found!")); + m_noMatchesFoundDisplay->setFrameStyle(QFrame::NoFrame); + m_widget->addWidget(m_noMatchesFoundDisplay); + + m_expandCollapseToolButton = new QToolButton(m_widget); + m_expandCollapseToolButton->setAutoRaise(true); + m_expandCollapseToolButton->setCheckable(true); + m_expandCollapseToolButton->setIcon(QIcon(":/find/images/expand.png")); + m_expandCollapseToolButton->setToolTip(tr("Expand All")); + + connect(m_searchResultTreeView, SIGNAL(jumpToSearchResult(int,const QString&,int,int,int)), + this, SLOT(handleJumpToSearchResult(int,const QString&,int,int,int))); + connect(m_expandCollapseToolButton, SIGNAL(toggled(bool)), this, SLOT(handleExpandCollapseToolButton(bool))); + + readSettings(); +} + +SearchResultWindow::~SearchResultWindow() +{ + writeSettings(); + delete m_widget; + m_widget = 0; + qDeleteAll(m_items); + m_items.clear(); +} + +bool SearchResultWindow::hasFocus() +{ + return m_searchResultTreeView->hasFocus(); +} + +bool SearchResultWindow::canFocus() +{ + return !m_items.isEmpty(); +} + +void SearchResultWindow::setFocus() +{ + if (!m_items.isEmpty()) + m_searchResultTreeView->setFocus(); +} + +void SearchResultWindow::visibilityChanged(bool /*visible*/) +{ +} + +QWidget *SearchResultWindow::outputWidget(QWidget *) +{ + return m_widget; +} + +QList<QWidget*> SearchResultWindow::toolBarWidgets(void) const +{ + return QList<QWidget*>() << m_expandCollapseToolButton; +} + +void SearchResultWindow::clearContents() +{ + m_widget->setCurrentWidget(m_searchResultTreeView); + m_searchResultTreeView->clear(); + qDeleteAll(m_items); + m_items.clear(); +} + +void SearchResultWindow::showNoMatchesFound(void) +{ + m_widget->setCurrentWidget(m_noMatchesFoundDisplay); +} + +bool SearchResultWindow::isEmpty() const +{ + return (m_searchResultTreeView->model()->rowCount() < 1); +} + +int SearchResultWindow::numberOfResults() const +{ + return m_searchResultTreeView->model()->rowCount(); +} + +void SearchResultWindow::handleJumpToSearchResult(int index, const QString &fileName, int lineNumber, + int searchTermStart, int searchTermLength) +{ + Q_UNUSED(searchTermLength); + ResultWindowItem *item = m_items.at(index); + emit item->activated(fileName, lineNumber, searchTermStart); +} + +ResultWindowItem *SearchResultWindow::addResult(const QString &fileName, int lineNumber, const QString &rowText, + int searchTermStart, int searchTermLength) +{ + m_widget->setCurrentWidget(m_searchResultTreeView); + int index = m_items.size(); + ResultWindowItem *item = new ResultWindowItem; + m_items.append(item); + m_searchResultTreeView->appendResultLine(index, fileName, lineNumber, rowText, searchTermStart, searchTermLength); + if (index == 0) { + // We didn't have an item before, set the focus to the m_searchResultTreeView + m_searchResultTreeView->setFocus(); + m_searchResultTreeView->selectionModel()->select(m_searchResultTreeView->model()->index(0, 0, QModelIndex()), QItemSelectionModel::Select); + } + + return item; +} + +void SearchResultWindow::handleExpandCollapseToolButton(bool checked) +{ + m_searchResultTreeView->setAutoExpandResults(checked); + if (checked) + m_searchResultTreeView->expandAll(); + else + m_searchResultTreeView->collapseAll(); +} + +void SearchResultWindow::readSettings(void) +{ + if (m_core && m_core->settings()) { + QSettings *s = m_core->settings(); + s->beginGroup(SETTINGSKEYSECTIONNAME); + m_expandCollapseToolButton->setChecked(s->value(SETTINGSKEYEXPANDRESULTS, m_initiallyExpand).toBool()); + s->endGroup(); + } +} + +void SearchResultWindow::writeSettings(void) +{ + if (m_core && m_core->settings()) { + QSettings *s = m_core->settings(); + s->beginGroup(SETTINGSKEYSECTIONNAME); + s->setValue(SETTINGSKEYEXPANDRESULTS, m_expandCollapseToolButton->isChecked()); + s->endGroup(); + } +} + +int SearchResultWindow::priorityInStatusBar() const +{ + return 80; +} diff --git a/src/plugins/find/searchresultwindow.h b/src/plugins/find/searchresultwindow.h new file mode 100644 index 00000000000..463b66a106c --- /dev/null +++ b/src/plugins/find/searchresultwindow.h @@ -0,0 +1,108 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SEARCHRESULTWINDOW_H +#define SEARCHRESULTWINDOW_H + +#include "find_global.h" +#include "searchresulttreeview.h" + +#include <coreplugin/ioutputpane.h> +#include <coreplugin/icore.h> + +#include <QtCore/QThread> +#include <QtCore/QStringList> +#include <QtGui/QStackedWidget> +#include <QtGui/QListWidget> +#include <QtGui/QToolButton> + +namespace Find { + +class SearchResultWindow; + +class FIND_EXPORT ResultWindowItem : public QObject +{ + Q_OBJECT + +signals: + void activated(const QString &fileName, int lineNumber, int column); + + friend class SearchResultWindow; +}; + +class FIND_EXPORT SearchResultWindow : public Core::IOutputPane +{ + Q_OBJECT + +public: + SearchResultWindow(Core::ICore *core); + ~SearchResultWindow(); + + QWidget *outputWidget(QWidget *); + QList<QWidget*> toolBarWidgets(void) const; + + QString name() const { return tr("Search Results"); } + int priorityInStatusBar() const; + void visibilityChanged(bool visible); + bool isEmpty() const; + int numberOfResults() const; + bool hasFocus(); + bool canFocus(); + void setFocus(); + +public slots: + void clearContents(); + void showNoMatchesFound(); + ResultWindowItem *addResult(const QString &fileName, int lineNumber, const QString &lineText, + int searchTermStart, int searchTermLength); + +private slots: + void handleExpandCollapseToolButton(bool checked); + void handleJumpToSearchResult(int index, const QString &fileName, int lineNumber, + int searchTermStart, int searchTermLength); + +private: + void readSettings(void); + void writeSettings(void); + + Internal::SearchResultTreeView *m_searchResultTreeView; + QListWidget *m_noMatchesFoundDisplay; + Core::ICore *m_core; + QToolButton *m_expandCollapseToolButton; + static const bool m_initiallyExpand = false; + QStackedWidget *m_widget; + QList<ResultWindowItem *> m_items; +}; + +} // namespace Find + +#endif // SEARCHRESULTWINDOW_H diff --git a/src/plugins/find/textfindconstants.h b/src/plugins/find/textfindconstants.h new file mode 100644 index 00000000000..73c92ed441c --- /dev/null +++ b/src/plugins/find/textfindconstants.h @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef TEXTFINDCONSTANTS_H +#define TEXTFINDCONSTANTS_H + + namespace Find { + namespace Constants { + const char * const M_FIND = "Find.FindMenu"; + const char * const G_FIND_FILTERS = "Find.FindMenu.Filters"; + const char * const G_FIND_FLAGS = "Find.FindMenu.Flags"; + const char * const G_FIND_ACTIONS = "Find.FindMenu.Actions"; + + const char * const FIND = "Find.FindReplace"; + const char * const FIND_IN_DOCUMENT = "Find.FindInCurrentDocument"; + const char * const FIND_NEXT = "Find.FindNext"; + const char * const FIND_PREVIOUS = "Find.FindPrevious"; + const char * const FIND_ALL = "Find.FindAll"; + const char * const REPLACE_NEXT = "Find.ReplaceNext"; + const char * const REPLACE_PREVIOUS = "Find.ReplacePrevious"; + const char * const REPLACE_ALL = "Find.ReplaceAll"; + const char * const CASE_SENSITIVE = "Find.CaseSensitive"; + const char * const WHOLE_WORDS = "Find.WholeWords"; + const char * const TASK_SEARCH = "Find.Task.Search"; + } + } //Find + +#endif //TEXTFINDCONSTANTS_H diff --git a/src/plugins/git/ScmGit.pluginspec b/src/plugins/git/ScmGit.pluginspec new file mode 100644 index 00000000000..689cc30a579 --- /dev/null +++ b/src/plugins/git/ScmGit.pluginspec @@ -0,0 +1,13 @@ +<plugin name="ScmGit" version="0.1" compatVersion="0.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Git integration.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="TextEditor" version="0.9.1"/> + <dependency name="ProjectExplorer" version="0.9.1"/> + <dependency name="Core" version="0.9.1"/> + <dependency name="VCSBase" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/git/TODO.txt b/src/plugins/git/TODO.txt new file mode 100644 index 00000000000..2913ba5f46f --- /dev/null +++ b/src/plugins/git/TODO.txt @@ -0,0 +1,26 @@ +- Make texts translateable +- Do not use QErrorMessage, Creator standard error instead? +Commands: + - P2: + - branch [list, create, delete] + - checkout [with/without creation] + - combine both above to a single dialog? + - P3: + - stash [creating, listing, applying] + - allow to use external viewer instead of greenhouse one + as these have more functionality usually + +GUI: + - Better diff view + - Commit view View (reuse diff view?) + - Commit action View + - Able to add further files to commit (list of modified/untracked files) + - use List for Log (and allow 10+ entries) + - Have commits clickable for 'git show' +Backend: + - Don't use forked processes, instead find a library connection like libgit-thin + - http://repo.or.cz/w/git/libgit-gsoc.git + - apply to SCM Manager in Greenhouse, currently it's mostly independent + +Suggestions: + - Bjorn: Use a "Summary" Lineedit in the commit dialog to make commits look nicer on gitweb or such. diff --git a/src/plugins/git/annotationhighlighter.cpp b/src/plugins/git/annotationhighlighter.cpp new file mode 100644 index 00000000000..534c45395b0 --- /dev/null +++ b/src/plugins/git/annotationhighlighter.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "annotationhighlighter.h" +#include <QtCore/QDebug> + +namespace Git { +namespace Internal { + +GitAnnotationHighlighter::GitAnnotationHighlighter(const ChangeNumbers &changeNumbers, + QTextDocument *document) : + VCSBase::BaseAnnotationHighlighter(changeNumbers, document), + m_blank(QLatin1Char(' ')) +{ +} + +QString GitAnnotationHighlighter::changeNumber(const QString &block) const +{ + const int pos = block.indexOf(m_blank, 4); + return pos > 1 ? block.left(pos) : QString(); +} + +} +} diff --git a/src/plugins/git/annotationhighlighter.h b/src/plugins/git/annotationhighlighter.h new file mode 100644 index 00000000000..3368e4dc9ff --- /dev/null +++ b/src/plugins/git/annotationhighlighter.h @@ -0,0 +1,58 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ANNOTATIONHIGHLIGHTER_H +#define ANNOTATIONHIGHLIGHTER_H + +#include <vcsbase/baseannotationhighlighter.h> + +namespace Git { +namespace Internal { + +// Annotation highlighter for p4 triggering on 'changenumber:' +class GitAnnotationHighlighter : public VCSBase::BaseAnnotationHighlighter +{ + Q_OBJECT +public: + explicit GitAnnotationHighlighter(const ChangeNumbers &changeNumbers, + QTextDocument *document = 0); + +private: + virtual QString changeNumber(const QString &block) const; + + const QChar m_blank; +}; + +} //namespace Git +} //namespace Internal + +#endif diff --git a/src/plugins/git/changeselectiondialog.cpp b/src/plugins/git/changeselectiondialog.cpp new file mode 100644 index 00000000000..6491614b5f7 --- /dev/null +++ b/src/plugins/git/changeselectiondialog.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "changeselectiondialog.h" + +#include <QtGui/QFileDialog> +#include <QtGui/QMessageBox> + +using namespace Git::Internal; + +ChangeSelectionDialog::ChangeSelectionDialog(QWidget *parent) + : QDialog(parent) +{ + m_ui.setupUi(this); + connect(m_ui.repositoryButton, SIGNAL(clicked()), this, SLOT(selectWorkingDirectory())); +} + +void ChangeSelectionDialog::selectWorkingDirectory() +{ + static QString location = QString(); + location = QFileDialog::getExistingDirectory(this, + QLatin1String("Select Git repository"), + location); + if (location.isEmpty()) + return; + + // Verify that the location is a repository + // We are polite, we also allow to specify a directory, which is not + // the head directory of the repository. + QDir repository(location); + do { + if (repository.entryList(QDir::AllDirs).contains(QLatin1String(".git"))) { + m_ui.repositoryEdit->setText(repository.absolutePath()); + return; + } + } while (repository.cdUp()); + + // Did not find a repo + QMessageBox::critical(this, QLatin1String("Error"), + QLatin1String("Selected directory is not a Git repository")); + +} diff --git a/src/plugins/git/changeselectiondialog.h b/src/plugins/git/changeselectiondialog.h new file mode 100644 index 00000000000..02d0aa76d36 --- /dev/null +++ b/src/plugins/git/changeselectiondialog.h @@ -0,0 +1,63 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CHANGESELECTIONDIALOG_H +#define CHANGESELECTIONDIALOG_H + +#include <QtGui/QDialog> + +#include "ui_changeselectiondialog.h" + +namespace Git { +namespace Internal { + + class GitPlugin; + +class ChangeSelectionDialog : public QDialog +{ + Q_OBJECT +public: + ChangeSelectionDialog(QWidget *parent = 0); + +public slots: + void selectWorkingDirectory(); + +private: + friend class GitPlugin; + Ui_ChangeSelectionDialog m_ui; + +}; + +} //namespace Internal +} //namespace Git + +#endif // CHANGESELECTIONDIALOG_H diff --git a/src/plugins/git/changeselectiondialog.ui b/src/plugins/git/changeselectiondialog.ui new file mode 100644 index 00000000000..34f2718ec8f --- /dev/null +++ b/src/plugins/git/changeselectiondialog.ui @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ChangeSelectionDialog</class> + <widget class="QDialog" name="ChangeSelectionDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>401</width> + <height>142</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Repository Location:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="repositoryEdit"/> + </item> + <item row="0" column="2"> + <widget class="QPushButton" name="repositoryButton"> + <property name="text"> + <string>Select</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Change:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="changeNumberEdit"/> + </item> + <item row="2" column="0" colspan="3"> + <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> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ChangeSelectionDialog</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>ChangeSelectionDialog</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/git/commitdata.cpp b/src/plugins/git/commitdata.cpp new file mode 100644 index 00000000000..aafafe1566f --- /dev/null +++ b/src/plugins/git/commitdata.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "commitdata.h" + +#include <QtCore/QDebug> + +namespace Git { +namespace Internal { + +void GitSubmitEditorPanelInfo::clear() +{ + repository.clear(); + description.clear(); + branch.clear(); +} + +QDebug operator<<(QDebug d, const GitSubmitEditorPanelInfo &data) +{ + d.nospace() << "Rep: " << data.repository << " Descr: " << data.description + << " branch: " << data.branch; + return d; +} + +void GitSubmitEditorPanelData::clear() +{ + author.clear(); + email.clear(); +} + +QString GitSubmitEditorPanelData::authorString() const +{ + QString rc; + rc += QLatin1Char('"'); + rc += author; + rc += QLatin1String(" <"); + rc += email; + rc += QLatin1String(">\""); + return rc; +} + +QDebug operator<<(QDebug d, const GitSubmitEditorPanelData &data) +{ + d.nospace() << " author:" << data.author << " email: " << data.email; + return d; +} + +void CommitData::clear() +{ + panelInfo.clear(); + panelData.clear(); + + commitFiles.clear(); + notUpdatedFiles.clear(); + untrackedFiles.clear(); +} + +QDebug operator<<(QDebug d, const CommitData &data) +{ + d << data.panelInfo << data.panelData; + d.nospace() << "Commit: " << data.commitFiles << " Not updated: " + << data.notUpdatedFiles << " Untracked: " << data.untrackedFiles; + return d; +} + +} +} diff --git a/src/plugins/git/commitdata.h b/src/plugins/git/commitdata.h new file mode 100644 index 00000000000..94a82005aea --- /dev/null +++ b/src/plugins/git/commitdata.h @@ -0,0 +1,81 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef COMMITDATA_H +#define COMMITDATA_H + +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE +class QDebug; +QT_END_NAMESPACE + +namespace Git { +namespace Internal { + + // Read-only + struct GitSubmitEditorPanelInfo { + void clear(); + QString repository; + QString description; + QString branch; + }; + + QDebug operator<<(QDebug d, const GitSubmitEditorPanelInfo &); + + struct GitSubmitEditorPanelData { + void clear(); + // Format as "John Doe <[email protected]>" + QString authorString() const; + + QString author; + QString email; + }; + + QDebug operator<<(QDebug d, const GitSubmitEditorPanelData &); + + struct CommitData { + void clear(); + GitSubmitEditorPanelInfo panelInfo; + GitSubmitEditorPanelData panelData; + QStringList commitFiles; + QStringList notUpdatedFiles; + QStringList untrackedFiles; + }; + + QDebug operator<<(QDebug d, const CommitData &); + + +} +} + +#endif diff --git a/src/plugins/git/git.pro b/src/plugins/git/git.pro new file mode 100644 index 00000000000..258639dcbe3 --- /dev/null +++ b/src/plugins/git/git.pro @@ -0,0 +1,35 @@ +TEMPLATE = lib +TARGET = ScmGit +include(../../qworkbenchplugin.pri) +include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/texteditor/texteditor.pri) +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/vcsbase/vcsbase.pri) +include(../../libs/utils/utils.pri) + +HEADERS += gitplugin.h \ + gitconstants.h \ + gitoutputwindow.h \ + gitclient.h \ + changeselectiondialog.h \ + commitdata.h \ + settingspage.h \ + giteditor.h \ + annotationhighlighter.h \ + gitsubmiteditorwidget.h \ + gitsubmiteditor.h + +SOURCES += gitplugin.cpp \ + gitoutputwindow.cpp \ + gitclient.cpp \ + changeselectiondialog.cpp \ + commitdata.cpp \ + settingspage.cpp \ + giteditor.cpp \ + annotationhighlighter.cpp \ + gitsubmiteditorwidget.cpp \ + gitsubmiteditor.cpp + +FORMS += changeselectiondialog.ui \ + settingspage.ui \ + gitsubmitpanel.ui diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp new file mode 100644 index 00000000000..b3f71078613 --- /dev/null +++ b/src/plugins/git/gitclient.cpp @@ -0,0 +1,635 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gitclient.h" +#include "gitplugin.h" +#include "gitconstants.h" +#include "commitdata.h" + +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/messagemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/progressmanager/progressmanagerinterface.h> +#include <vcsbase/vcsbaseeditor.h> +#include <texteditor/itexteditor.h> + +#include <QtCore/QRegExp> +#include <QtCore/QTemporaryFile> +#include <QtCore/QFuture> + +#include <QtGui/QErrorMessage> + +using namespace Git; +using namespace Git::Internal; + +const char* const kGitCommand = "git"; +const char* const kGitDirectoryC = ".git"; +const char* const kBranchIndicatorC = "# On branch"; + +static inline QString msgServerFailure() +{ + return GitClient::tr( +"Note that the git plugin for QtCreator is not able to interact with the server " +"so far. Thus, manual ssh-identification etc. will not work."); +} + +inline Core::IEditor* locateEditor(const Core::ICore *core, const char *property, const QString &entry) +{ + foreach (Core::IEditor *ed, core->editorManager()->openedEditors()) + if (ed->property(property).toString() == entry) + return ed; + return 0; +} + +GitClient::GitClient(GitPlugin* plugin, Core::ICore *core) : + m_msgWait(tr("Waiting for data...")), + m_plugin(plugin), + m_core(core) +{ +} + +GitClient::~GitClient() +{ +} + +bool GitClient::vcsOpen(const QString &fileName) +{ + return m_plugin->vcsOpen(fileName); +} + +QString GitClient::findRepositoryForFile(const QString &fileName) +{ + const QString gitDirectory = QLatin1String(kGitDirectoryC); + const QFileInfo info(fileName); + QDir dir = info.absoluteDir(); + do { + if (dir.entryList(QDir::AllDirs|QDir::Hidden).contains(gitDirectory)) + return dir.absolutePath(); + } while (dir.cdUp()); + + return QString(); +} + +QString GitClient::findRepositoryForDirectory(const QString &dir) +{ + const QString gitDirectory = QLatin1String(kGitDirectoryC); + QDir directory(dir); + do { + if (directory.entryList(QDir::AllDirs|QDir::Hidden).contains(gitDirectory)) + return directory.absolutePath(); + } while (directory.cdUp()); + + return QString(); +} + +// Return source file or directory string depending on parameters +// ('git diff XX' -> 'XX' , 'git diff XX file' -> 'XX/file'). +static QString source(const QString &workingDirectory, const QString &fileName) +{ + if (fileName.isEmpty()) + return workingDirectory; + QString rc = workingDirectory; + if (!rc.isEmpty() && !rc.endsWith(QDir::separator())) + rc += QDir::separator(); + rc += fileName; + return rc; +} + +/* Create an editor associated to VCS output of a source file/directory + * (using the file's codec). Makes use of a dynamic property to find an + * existing instance and to reuse it (in case, say, 'git diff foo' is + * already open). */ +VCSBase::VCSBaseEditor + *GitClient::createVCSEditor(const QString &kind, + QString title, + // Source file or directory + const QString &source, + bool setSourceCodec, + // Dynamic property and value to identify that editor + const char *registerDynamicProperty, + const QString &dynamicPropertyValue) const +{ + VCSBase::VCSBaseEditor *rc = 0; + Core::IEditor* outputEditor = locateEditor(m_core, registerDynamicProperty, dynamicPropertyValue); + if (outputEditor) { + // Exists already + outputEditor->createNew(m_msgWait); + rc = VCSBase::VCSBaseEditor::getVcsBaseEditor(outputEditor); + Q_ASSERT(rc); + m_core->editorManager()->setCurrentEditor(outputEditor); + } else { + // Create new, set wait message, set up with source and codec + outputEditor = m_core->editorManager()->newFile(kind, &title, m_msgWait); + outputEditor->setProperty(registerDynamicProperty, dynamicPropertyValue); + rc = VCSBase::VCSBaseEditor::getVcsBaseEditor(outputEditor); + Q_ASSERT(rc); + rc->setSource(source); + if (setSourceCodec) + rc->setCodec(VCSBase::VCSBaseEditor::getCodec(m_core, source)); + } + return rc; +} + +void GitClient::diff(const QString &workingDirectory, const QStringList &fileNames) +{ + if (Git::Constants::debug) + qDebug() << "diff" << workingDirectory << fileNames; + QStringList arguments; + arguments << QLatin1String("diff") << fileNames; + + const QString kind = QLatin1String(Git::Constants::GIT_DIFF_EDITOR_KIND); + const QString title = tr("Git Diff"); + + VCSBase::VCSBaseEditor *editor = createVCSEditor(kind, title, workingDirectory, true, "originalFileName", workingDirectory); + executeGit(workingDirectory, arguments, m_plugin->m_outputWindow, editor); + +} + +void GitClient::diff(const QString &workingDirectory, const QString &fileName) +{ + if (Git::Constants::debug) + qDebug() << "diff" << workingDirectory << fileName; + QStringList arguments; + arguments << QLatin1String("diff"); + if (!fileName.isEmpty()) + arguments << fileName; + + const QString kind = QLatin1String(Git::Constants::GIT_DIFF_EDITOR_KIND); + const QString title = tr("Git Diff %1").arg(fileName); + const QString sourceFile = source(workingDirectory, fileName); + + VCSBase::VCSBaseEditor *editor = createVCSEditor(kind, title, sourceFile, true, "originalFileName", sourceFile); + executeGit(workingDirectory, arguments, m_plugin->m_outputWindow, editor); +} + +void GitClient::status(const QString &workingDirectory) +{ + executeGit(workingDirectory, QStringList(QLatin1String("status")), m_plugin->m_outputWindow, 0,true); +} + +void GitClient::log(const QString &workingDirectory, const QString &fileName) +{ + if (Git::Constants::debug) + qDebug() << "log" << workingDirectory << fileName; + QStringList arguments; + int logCount = 10; + if (m_plugin->m_settingsPage && m_plugin->m_settingsPage->logCount() > 0) + logCount = m_plugin->m_settingsPage->logCount(); + + arguments << QLatin1String("log") << QLatin1String("-n") + << QString::number(logCount); + if (!fileName.isEmpty()) + arguments << fileName; + + const QString title = tr("Git Log %1").arg(fileName); + const QString kind = QLatin1String(Git::Constants::GIT_LOG_EDITOR_KIND); + const QString sourceFile = source(workingDirectory, fileName); + VCSBase::VCSBaseEditor *editor = createVCSEditor(kind, title, sourceFile, false, "logFileName", sourceFile); + executeGit(workingDirectory, arguments, m_plugin->m_outputWindow, editor); +} + +void GitClient::show(const QString &source, const QString &id) +{ + if (Git::Constants::debug) + qDebug() << "show" << source << id; + QStringList arguments(QLatin1String("show")); + arguments << id; + + const QString title = tr("Git Show %1").arg(id); + const QString kind = QLatin1String(Git::Constants::GIT_DIFF_EDITOR_KIND); + VCSBase::VCSBaseEditor *editor = createVCSEditor(kind, title, source, true, "show", id); + + const QFileInfo sourceFi(source); + const QString workDir = sourceFi.isDir() ? sourceFi.absoluteFilePath() : sourceFi.absolutePath(); + executeGit(workDir, arguments, m_plugin->m_outputWindow, editor); +} + +void GitClient::blame(const QString &workingDirectory, const QString &fileName) +{ + if (Git::Constants::debug) + qDebug() << "blame" << workingDirectory << fileName; + QStringList arguments(QLatin1String("blame")); + arguments << fileName; + + const QString kind = QLatin1String(Git::Constants::GIT_BLAME_EDITOR_KIND); + const QString title = tr("Git Blame %1").arg(fileName); + const QString sourceFile = source(workingDirectory, fileName); + + VCSBase::VCSBaseEditor *editor = createVCSEditor(kind, title, sourceFile, true, "blameFileName", sourceFile); + executeGit(workingDirectory, arguments, m_plugin->m_outputWindow, editor); +} + +void GitClient::checkout(const QString &workingDirectory, const QString &fileName) +{ + // Passing an empty argument as the file name is very dangereous, since this makes + // git checkout apply to all files. Almost looks like a bug in git. + if (fileName.isEmpty()) + return; + + QStringList arguments; + arguments << QLatin1String("checkout") << QLatin1String("HEAD") << QLatin1String("--") + << fileName; + + executeGit(workingDirectory, arguments, m_plugin->m_outputWindow, 0,true); +} + +void GitClient::hardReset(const QString &workingDirectory, const QString &commit) +{ + QStringList arguments; + arguments << QLatin1String("reset") << QLatin1String("--hard"); + if (!commit.isEmpty()) + arguments << commit; + + executeGit(workingDirectory, arguments, m_plugin->m_outputWindow, 0,true); +} + +void GitClient::addFile(const QString &workingDirectory, const QString &fileName) +{ + QStringList arguments; + arguments << QLatin1String("add") << fileName; + + executeGit(workingDirectory, arguments, m_plugin->m_outputWindow, 0,true); +} + +bool GitClient::synchronousAdd(const QString &workingDirectory, const QStringList &files) +{ + QByteArray outputText; + QByteArray errorText; + QStringList arguments; + arguments << "add" << files; + const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText); + if (!rc) { + const QString errorMessage = tr("Unable to add %n file(s) to %1: %2", 0, files.size()). + arg(workingDirectory, QString::fromLocal8Bit(errorText)); + m_plugin->m_outputWindow->append(errorMessage); + m_plugin->m_outputWindow->popup(false); + } + return rc; +} + +void GitClient::executeGit(const QString &workingDirectory, const QStringList &arguments, + GitOutputWindow *outputWindow, VCSBase::VCSBaseEditor* editor, + bool outputToWindow) +{ + if (Git::Constants::debug) + qDebug() << "executeGit" << workingDirectory << arguments << editor; + outputWindow->clearContents(); + + QProcess process; + ProjectExplorer::Environment environment = ProjectExplorer::Environment::systemEnvironment(); + + if (m_plugin->m_settingsPage && !m_plugin->m_settingsPage->adoptEnvironment()) + environment.set(QLatin1String("PATH"), m_plugin->m_settingsPage->path()); + + GitCommand* command = new GitCommand(); + if (outputToWindow) { + Q_ASSERT(outputWindow); + connect(command, SIGNAL(outputText(QString)), outputWindow, SLOT(append(QString))); + connect(command, SIGNAL(outputData(QByteArray)), outputWindow, SLOT(appendData(QByteArray))); + } else { + Q_ASSERT(editor); + connect(command, SIGNAL(outputText(QString)), editor, SLOT(setPlainText(QString))); + connect(command, SIGNAL(outputData(QByteArray)), editor, SLOT(setPlainTextData(QByteArray))); + } + + if (outputWindow) + connect(command, SIGNAL(errorText(QString)), outputWindow, SLOT(append(QString))); + + command->execute(arguments, workingDirectory, environment); +} + +bool GitClient::synchronousGit(const QString &workingDirectory + , const QStringList &arguments + , QByteArray* outputText + , QByteArray* errorText) +{ + if (Git::Constants::debug) + qDebug() << "synchronousGit" << workingDirectory << arguments; + QProcess process; + + process.setWorkingDirectory(workingDirectory); + + ProjectExplorer::Environment environment = ProjectExplorer::Environment::systemEnvironment(); + if (m_plugin->m_settingsPage && !m_plugin->m_settingsPage->adoptEnvironment()) + environment.set(QLatin1String("PATH"), m_plugin->m_settingsPage->path()); + process.setEnvironment(environment.toStringList()); + + process.start(QLatin1String(kGitCommand), arguments); + if (!process.waitForFinished()) { + if (errorText) + *errorText = "Error: Git timed out"; + return false; + } + + if (outputText) + *outputText = process.readAllStandardOutput(); + + if (errorText) + *errorText = process.readAllStandardError(); + + if (Git::Constants::debug) + qDebug() << "synchronousGit ex=" << process.exitCode(); + return process.exitCode() == 0; +} + +/* Parse a git status file list: + * \code + # Changes to be committed: + #<tab>modified:<blanks>git.pro + # Changed but not updated: + #<tab>modified:<blanks>git.pro + # Untracked files: + #<tab>modified:<blanks>git.pro + \endcode +*/ +static bool parseFiles(const QStringList &lines, CommitData *d) +{ + enum State { None, CommitFiles, NotUpdatedFiles, UntrackedFiles }; + + const QString branchIndicator = QLatin1String(kBranchIndicatorC); + const QString commitIndicator = QLatin1String("# Changes to be committed:"); + const QString notUpdatedIndicator = QLatin1String("# Changed but not updated:"); + const QString untrackedIndicator = QLatin1String("# Untracked files:"); + + State s = None; + + const QRegExp filesPattern(QLatin1String("#\\t[^:]+:\\s+[^ ]+")); + Q_ASSERT(filesPattern.isValid()); + + const QStringList::const_iterator cend = lines.constEnd(); + for (QStringList::const_iterator it = lines.constBegin(); it != cend; ++it) { + const QString line = *it; + if (line.startsWith(branchIndicator)) { + d->panelInfo.branch = line.mid(branchIndicator.size() + 1); + } else { + if (line.startsWith(commitIndicator)) { + s = CommitFiles; + } else { + if (line.startsWith(notUpdatedIndicator)) { + s = NotUpdatedFiles; + } else { + if (line.startsWith(untrackedIndicator)) { + s = UntrackedFiles; + } else { + if (filesPattern.exactMatch(line)) { + const QString fileSpec = line.mid(2).simplified(); + switch (s) { + case CommitFiles: + d->commitFiles.push_back(fileSpec); + break; + case NotUpdatedFiles: + d->notUpdatedFiles.push_back(fileSpec); + break; + case UntrackedFiles: + d->untrackedFiles.push_back(fileSpec); + break; + case None: + break; + } + } + } + } + } + } + } + return !d->commitFiles.empty() || !d->notUpdatedFiles.empty() || !d->untrackedFiles.empty(); +} + +bool GitClient::getCommitData(const QString &workingDirectory, + QString *commitTemplate, + CommitData *d, + QString *errorMessage) +{ + d->clear(); + + // Find repo + const QString repoDirectory = GitClient::findRepositoryForDirectory(workingDirectory); + if (repoDirectory.isEmpty()) { + *errorMessage = tr("Unable to determine the repository for %1.").arg(workingDirectory); + return false; + } + + d->panelInfo.repository = repoDirectory; + + QDir gitDir(repoDirectory); + if (!gitDir.cd(QLatin1String(kGitDirectoryC))) { + *errorMessage = tr("The repository %1 is not initialized yet.").arg(repoDirectory); + return false; + } + + // Read description + const QString descriptionFile = gitDir.absoluteFilePath(QLatin1String("description")); + if (QFileInfo(descriptionFile).isFile()) { + QFile file(descriptionFile); + if (file.open(QIODevice::ReadOnly|QIODevice::Text)) + d->panelInfo.description = QString::fromLocal8Bit(file.readAll()).trimmed(); + } + + // Run status. Note that it has exitcode 1 if there are no added files. + QByteArray outputText; + QByteArray errorText; + const bool statusRc = synchronousGit(workingDirectory, QStringList(QLatin1String("status")), &outputText, &errorText); + if (!statusRc) { + // Something fatal + if (!outputText.contains(kBranchIndicatorC)) { + *errorMessage = tr("Unable to obtain the project status: %1").arg(QString::fromLocal8Bit(errorText)); + return false; + } + // All unchanged + if (outputText.contains("nothing to commit")) { + *errorMessage = tr("There are no modified files."); + return false; + } + } + + // Output looks like: + // # On branch [branchname] + // # Changes to be committed: + // # (use "git reset HEAD <file>..." to unstage) + // # + // # modified: somefile.cpp + // # new File: somenew.h + // # + // # Changed but not updated: + // # (use "git add <file>..." to update what will be committed) + // # + // # modified: someother.cpp + // # + // # Untracked files: + // # (use "git add <file>..." to include in what will be committed) + // # + // # list of files... + + const QStringList lines = QString::fromLocal8Bit(outputText).remove(QLatin1Char('\r')).split(QLatin1Char('\n')); + if (!parseFiles(lines, d)) { + *errorMessage = tr("Unable to parse the file output."); + return false; + } + + d->panelData.author = readConfigValue(workingDirectory, QLatin1String("user.name")); + d->panelData.email = readConfigValue(workingDirectory, QLatin1String("user.email")); + + // Get the commit template + const QString templateFilename = readConfigValue(workingDirectory, QLatin1String("commit.template")); + if (!templateFilename.isEmpty()) { + QFile templateFile(templateFilename); + if (templateFile.open(QIODevice::ReadOnly|QIODevice::Text)) { + *commitTemplate = QString::fromLocal8Bit(templateFile.readAll()); + } else { + qWarning("Unable to read commit template %s: %s", + qPrintable(templateFilename), + qPrintable(templateFile.errorString())); + } + } + return true; +} + +bool GitClient::addAndCommit(const QString &workingDirectory, + const GitSubmitEditorPanelData &data, + const QString &messageFile, + const QStringList &files) +{ + // Re-add all to make sure we have the latest changes + if (!synchronousAdd(workingDirectory, files)) + return false; + + // Do the final commit + QStringList args; + args << QLatin1String("commit") + << QLatin1String("-F") << QDir::toNativeSeparators(messageFile) + << QLatin1String("--author") << data.authorString(); + + QByteArray outputText; + QByteArray errorText; + const bool rc = synchronousGit(workingDirectory, args, &outputText, &errorText); + const QString message = rc ? + tr("Committed %n file(s).", 0, files.size()) : + tr("Unable to commit %n file(s): %1", 0, files.size()).arg(QString::fromLocal8Bit(errorText)); + + m_plugin->m_outputWindow->append(message); + m_plugin->m_outputWindow->popup(false); + return rc; +} + +void GitClient::pull(const QString &workingDirectory) +{ + executeGit(workingDirectory, QStringList(QLatin1String("pull")), m_plugin->m_outputWindow, 0,true); +} + +void GitClient::push(const QString &workingDirectory) +{ + executeGit(workingDirectory, QStringList(QLatin1String("push")), m_plugin->m_outputWindow, 0,true); +} + +QString GitClient::readConfig(const QString &workingDirectory, const QStringList &configVar) +{ + QStringList arguments; + arguments << QLatin1String("config") << configVar; + + QByteArray outputText; + if (synchronousGit(workingDirectory, arguments, &outputText)) + return outputText; + return QString(); +} + +// Read a single-line config value, return trimmed +QString GitClient::readConfigValue(const QString &workingDirectory, const QString &configVar) +{ + return readConfig(workingDirectory, QStringList(configVar)).remove(QLatin1Char('\n')); +} + +GitCommand::GitCommand() +{ +} + +GitCommand::~GitCommand() +{ +} + +void GitCommand::execute(const QStringList &arguments + , const QString &workingDirectory + , const ProjectExplorer::Environment &environment) +{ + if (Git::Constants::debug) + qDebug() << "GitCommand::execute" << workingDirectory << arguments; + + // For some reason QtConcurrent::run() only works on this + QFuture<void> task = QtConcurrent::run(this, &GitCommand::run + , arguments + , workingDirectory + , environment); + QString taskName = QLatin1String("Git ") + arguments[0]; + + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + core->progressManager()->addTask(task, taskName + , QLatin1String("Git.action") + , Core::ProgressManagerInterface::CloseOnSuccess); +} + +void GitCommand::run(const QStringList &arguments + , const QString &workingDirectory + , const ProjectExplorer::Environment &environment) +{ + if (Git::Constants::debug) + qDebug() << "GitCommand::run" << workingDirectory << arguments; + QProcess process; + if (!workingDirectory.isEmpty()) + process.setWorkingDirectory(workingDirectory); + + ProjectExplorer::Environment env = environment; + if (env.toStringList().isEmpty()) + env = ProjectExplorer::Environment::systemEnvironment(); + process.setEnvironment(env.toStringList()); + + process.start(QLatin1String(kGitCommand), arguments); + if (!process.waitForFinished()) { + emit errorText(QLatin1String("Error: Git timed out")); + return; + } + + const QByteArray output = process.readAllStandardOutput(); + if (output.isEmpty()) { + if (arguments.at(0) == QLatin1String("diff")) + emit outputText(tr("The file does not differ from HEAD")); + } else { + emit outputData(output); + } + const QByteArray error = process.readAllStandardError(); + if (!error.isEmpty()) + emit errorText(QString::fromLocal8Bit(error)); + + // As it is used asynchronously, we need to delete ourselves + this->deleteLater(); +} diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h new file mode 100644 index 00000000000..f91eefda662 --- /dev/null +++ b/src/plugins/git/gitclient.h @@ -0,0 +1,168 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GITCLIENT_H +#define GITCLIENT_H + +#include <coreplugin/iversioncontrol.h> +#include <coreplugin/editormanager/ieditor.h> +#include <projectexplorer/environment.h> + +#include <QtCore/QString> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE +class QErrorMessage; +QT_END_NAMESPACE + +namespace Core { + class ICore; +} + +namespace VCSBase { + class VCSBaseEditor; +} + +namespace Git { +namespace Internal { + +class GitPlugin; +class GitOutputWindow; +class GitCommand; +struct CommitData; +struct GitSubmitEditorPanelData; + +class GitClient : public Core::IVersionControl +{ + Q_OBJECT +public: + explicit GitClient(GitPlugin *plugin, Core::ICore *core); + ~GitClient(); + bool vcsOpen(const QString &fileName); + bool vcsAdd(const QString&) {return false;} + bool vcsDelete(const QString&) {return false;} + bool managesDirectory(const QString&) const {return false;} + QString findTopLevelForDirectory(const QString&) const {return QString();} + + static QString findRepositoryForFile(const QString &fileName); + static QString findRepositoryForDirectory(const QString &dir); + + void diff(const QString &workingDirectory, + const QString &fileName); + void diff(const QString &workingDirectory, + const QStringList &fileNames); + + void status(const QString &workingDirectory); + void log(const QString &workingDirectory + , const QString &fileName); + void blame(const QString &workingDirectory + , const QString &fileName); + void showCommit(const QString &workingDirectory + , const QString &commit); + void checkout(const QString &workingDirectory + , const QString &file); + void hardReset(const QString &workingDirectory + , const QString &commit); + void addFile(const QString &workingDirectory + , const QString &fileName); + bool synchronousAdd(const QString &workingDirectory, + const QStringList &files); + void pull(const QString &workingDirectory); + void push(const QString &workingDirectory); + + QString readConfig(const QString &workingDirectory + , const QStringList &configVar); + + QString readConfigValue(const QString &workingDirectory, + const QString &configVar); + + bool getCommitData(const QString &workingDirectory, + QString *commitTemplate, + CommitData *d, + QString *errorMessage); + + bool addAndCommit(const QString &workingDirectory, + const GitSubmitEditorPanelData &data, + const QString &messageFile, + const QStringList &files); + +public slots: + void show(const QString &source, const QString &id); + +private: + VCSBase::VCSBaseEditor *createVCSEditor(const QString &kind, + QString title, + const QString &source, + bool setSourceCodec, + const char *registerDynamicProperty, + const QString &dynamicPropertyValue) const; + + + void executeGit(const QString &workingDirectory + , const QStringList &arguments + , GitOutputWindow *outputWindow + , VCSBase::VCSBaseEditor* editor = 0 + , bool outputToWindow = false); + + bool synchronousGit(const QString &workingDirectory + , const QStringList &arguments + , QByteArray* outputText = 0 + , QByteArray* errorText = 0); + + const QString m_msgWait; + GitPlugin *m_plugin; + Core::ICore *m_core; +}; + +class GitCommand : public QObject +{ + Q_OBJECT +public: + GitCommand(); + ~GitCommand(); + void execute(const QStringList &arguments + , const QString &workingDirectory + , const ProjectExplorer::Environment &environment); + void run(const QStringList &arguments + , const QString &workingDirectory + , const ProjectExplorer::Environment &environment); + +Q_SIGNALS: + void outputData(const QByteArray&); + void outputText(const QString&); + void errorText(const QString&); +}; + + } +} + +#endif // GITCLIENT_H diff --git a/src/plugins/git/gitconstants.h b/src/plugins/git/gitconstants.h new file mode 100644 index 00000000000..a74c9030317 --- /dev/null +++ b/src/plugins/git/gitconstants.h @@ -0,0 +1,58 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GIT_CONSTANTS_H +#define GIT_CONSTANTS_H + +namespace Git { + namespace Constants { + const char * const GIT_COMMAND_LOG_EDITOR_KIND = "Git Command Log Editor"; + const char * const GIT_LOG_EDITOR_KIND = "Git File Log Editor"; + const char * const GIT_BLAME_EDITOR_KIND = "Git Annotation Editor"; + const char * const GIT_DIFF_EDITOR_KIND = "Git Diff Editor"; + + const char * const C_GITSUBMITEDITOR = "Git Submit Editor"; + const char * const GITSUBMITEDITOR_KIND = "Git Submit Editor"; + const char * const SUBMIT_CURRENT = "Nokia.Git.SubmitCurrentLog"; + const char * const DIFF_SELECTED = "Nokia.Git.DiffSelectedFilesInLog"; + const char * const SUBMIT_MIMETYPE = "application/vnd.nokia.text.git.submit"; + + // TODO: For the moment, trust p4 is loaded... + const char * const ICON_SUBMIT = ":/trolltech.perforce/images/submit.png"; + const char * const ICON_DIFF = ":/trolltech.perforce/images/diff.png"; + + const char * const DIFF_FILE_INDICATOR = "--- "; + enum { debug = 0 }; + } +} + +#endif // GIT_CONSTANTS_H diff --git a/src/plugins/git/giteditor.cpp b/src/plugins/git/giteditor.cpp new file mode 100644 index 00000000000..3bf31da6984 --- /dev/null +++ b/src/plugins/git/giteditor.cpp @@ -0,0 +1,145 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "giteditor.h" +#include "annotationhighlighter.h" +#include "gitconstants.h" +#include "gitplugin.h" +#include "gitclient.h" + +#include <vcsbase/diffhighlighter.h> +#include <coreplugin/editormanager/editormanager.h> + +#include <QtCore/QFileInfo> +#include <QtCore/QTextStream> +#include <QtCore/QSet> +#include <QtCore/QRegExp> +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtGui/QTextEdit> +#include <QtGui/QTextCursor> + +#define CHANGE_PATTERN_8C "[a-f0-9]{8,8}" +#define CHANGE_PATTERN_40C "[a-f0-9]{40,40}" + +namespace Git { +namespace Internal { + +// ------------ GitEditor +GitEditor::GitEditor(const VCSBase::VCSBaseEditorParameters *type, + QWidget *parent) : + VCSBase::VCSBaseEditor(type, parent), + m_changeNumberPattern8(QLatin1String(CHANGE_PATTERN_8C)), + m_changeNumberPattern40(QLatin1String(CHANGE_PATTERN_40C)) +{ + Q_ASSERT(m_changeNumberPattern8.isValid()); + Q_ASSERT(m_changeNumberPattern40.isValid()); + if (Git::Constants::debug) + qDebug() << "GitEditor::GitEditor" << type->type << type->kind; +} + +QSet<QString> GitEditor::annotationChanges() const +{ + QSet<QString> changes; + const QString txt = toPlainText(); + if (txt.isEmpty()) + return changes; + // Hunt for first change number in annotation: "<change>:" + QRegExp r(QLatin1String("^("CHANGE_PATTERN_8C") ")); + Q_ASSERT(r.isValid()); + if (r.indexIn(txt) != -1) { + changes.insert(r.cap(1)); + r.setPattern(QLatin1String("\n("CHANGE_PATTERN_8C") ")); + Q_ASSERT(r.isValid()); + int pos = 0; + while ((pos = r.indexIn(txt, pos)) != -1) { + pos += r.matchedLength(); + changes.insert(r.cap(1)); + } + } + if (Git::Constants::debug) + qDebug() << "GitEditor::annotationChanges() returns #" << changes.size(); + return changes; +} + +QString GitEditor::changeUnderCursor(const QTextCursor &c) const +{ + QTextCursor cursor = c; + // Any number is regarded as change number. + cursor.select(QTextCursor::WordUnderCursor); + if (!cursor.hasSelection()) + return QString(); + const QString change = cursor.selectedText(); + if (Git::Constants::debug > 1) + qDebug() << "GitEditor:::changeUnderCursor:" << change; + if (m_changeNumberPattern8.exactMatch(change)) + return change; + if (m_changeNumberPattern40.exactMatch(change)) + return change; + return QString(); +} + +VCSBase::DiffHighlighter *GitEditor::createDiffHighlighter() const +{ + const QRegExp filePattern(QLatin1String("^[-+][-+][-+] [ab].*")); + return new VCSBase::DiffHighlighter(filePattern); +} + +VCSBase::BaseAnnotationHighlighter *GitEditor::createAnnotationHighlighter(const QSet<QString> &changes) const +{ + return new GitAnnotationHighlighter(changes); +} + +QString GitEditor::fileNameFromDiffSpecification(const QTextBlock &inBlock) const +{ + QString errorMessage; + // Check for "+++ b/src/plugins/git/giteditor.cpp" (blame and diff) + // Go back chunks. + const QString newFileIndicator = QLatin1String("+++ b/"); + for (QTextBlock block = inBlock; block.isValid(); block = block.previous()) { + QString diffFileName = block.text(); + if (diffFileName.startsWith(newFileIndicator)) { + diffFileName.remove(0, newFileIndicator.size()); + const QString fileOrDir = source(); + const QString repo = QFileInfo(fileOrDir).isDir() ? + GitClient::findRepositoryForDirectory(fileOrDir) : GitClient::findRepositoryForFile(fileOrDir); + const QString absPath = QDir(repo).absoluteFilePath(diffFileName); + if (Git::Constants::debug) + qDebug() << "fileNameFromDiffSpecification" << repo << diffFileName << absPath; + return absPath; + } + } + return QString(); +} + +} +} diff --git a/src/plugins/git/giteditor.h b/src/plugins/git/giteditor.h new file mode 100644 index 00000000000..87e71597dd4 --- /dev/null +++ b/src/plugins/git/giteditor.h @@ -0,0 +1,68 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GITEDITOR_H +#define GITEDITOR_H + +#include <vcsbase/vcsbaseeditor.h> + +#include <QtCore/QRegExp> + +namespace Git { +namespace Internal { + +class GitPlugin; + +class GitEditor : public VCSBase::VCSBaseEditor +{ + Q_OBJECT + +public: + explicit GitEditor(const VCSBase::VCSBaseEditorParameters *type, + QWidget *parent); + +private: + virtual QSet<QString> annotationChanges() const; + virtual QString changeUnderCursor(const QTextCursor &) const; + virtual VCSBase::DiffHighlighter *createDiffHighlighter() const; + virtual VCSBase::BaseAnnotationHighlighter *createAnnotationHighlighter(const QSet<QString> &changes) const; + virtual QString fileNameFromDiffSpecification(const QTextBlock &diffFileName) const; + + const QRegExp m_changeNumberPattern8; + const QRegExp m_changeNumberPattern40; + GitPlugin *m_plugin; +}; + +} // namespace Git +} // namespace Internal + +#endif // GITEDITOR_H diff --git a/src/plugins/git/gitoutputwindow.cpp b/src/plugins/git/gitoutputwindow.cpp new file mode 100644 index 00000000000..e8210ed0358 --- /dev/null +++ b/src/plugins/git/gitoutputwindow.cpp @@ -0,0 +1,122 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gitoutputwindow.h" + +#include <QtCore/QTextCodec> +#include <QtGui/QKeyEvent> +#include <QtGui/QMouseEvent> +#include <QtGui/QMenu> +#include <QtGui/QAction> +#include <QtGui/QListWidget> + +using namespace Git::Internal; + +GitOutputWindow::GitOutputWindow() + : Core::IOutputPane() +{ + m_outputListWidget = new QListWidget; + m_outputListWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_outputListWidget->setFrameStyle(QFrame::NoFrame); + + m_outputListWidget->setWindowTitle(tr("Git Output")); +} + +GitOutputWindow::~GitOutputWindow() +{ + delete m_outputListWidget; +} + +QWidget *GitOutputWindow::outputWidget(QWidget *parent) +{ + m_outputListWidget->setParent(parent); + return m_outputListWidget; +} + +QString GitOutputWindow::name() const +{ + return tr("Git"); +} + +void GitOutputWindow::clearContents() +{ + m_outputListWidget->clear(); +} + +void GitOutputWindow::visibilityChanged(bool b) +{ + if (b) + m_outputListWidget->setFocus(); +} + +bool GitOutputWindow::hasFocus() +{ + return m_outputListWidget->hasFocus(); +} + +bool GitOutputWindow::canFocus() +{ + return false; +} + +void GitOutputWindow::setFocus() +{ +} + +void GitOutputWindow::setText(const QString &text) +{ + clearContents(); + append(text); +} + +void GitOutputWindow::append(const QString &text) +{ + const QStringList lines = text.split(QLatin1Char('\n')); + foreach (const QString &s, lines) + m_outputListWidget->addItem(s); + popup(); +} + +void GitOutputWindow::setData(const QByteArray &data) +{ + setText(QTextCodec::codecForLocale()->toUnicode(data)); +} + +void GitOutputWindow::appendData(const QByteArray &data) +{ + append(QTextCodec::codecForLocale()->toUnicode(data)); +} + +int GitOutputWindow::priorityInStatusBar() const +{ + return -1; +} diff --git a/src/plugins/git/gitoutputwindow.h b/src/plugins/git/gitoutputwindow.h new file mode 100644 index 00000000000..ed776c5df4a --- /dev/null +++ b/src/plugins/git/gitoutputwindow.h @@ -0,0 +1,79 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GITOUTPUTWINDOW_H +#define GITOUTPUTWINDOW_H + +#include <coreplugin/ioutputpane.h> + +#include <QtGui/QAction> +#include <QtGui/QListWidget> +#include <QtGui/QListWidgetItem> + +namespace Git { +namespace Internal { + + +class GitOutputWindow : public Core::IOutputPane +{ + Q_OBJECT + +public: + GitOutputWindow(); + ~GitOutputWindow(); + + QWidget *outputWidget(QWidget *parent); + QList<QWidget*> toolBarWidgets(void) const { return QList<QWidget *>(); } + + QString name() const; + int priorityInStatusBar() const; + void clearContents(); + void visibilityChanged(bool visible); + + bool canFocus(); + bool hasFocus(); + void setFocus(); + +public slots: + void setText(const QString &text); + void append(const QString &text); + void setData(const QByteArray &data); + void appendData(const QByteArray &data); + +private: + QListWidget *m_outputListWidget; +}; + +} // namespace Internal +} // namespace Git + +#endif diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp new file mode 100644 index 00000000000..cbb08cc05ad --- /dev/null +++ b/src/plugins/git/gitplugin.cpp @@ -0,0 +1,717 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gitplugin.h" +#include "gitclient.h" +#include "giteditor.h" +#include "gitconstants.h" +#include "changeselectiondialog.h" +#include "gitsubmiteditor.h" +#include "commitdata.h" + +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/filemanager.h> +#include <coreplugin/messagemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> +#include <vcsbase/basevcseditorfactory.h> +#include <vcsbase/vcsbaseeditor.h> +#include <vcsbase/basevcssubmiteditorfactory.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtCore/QTemporaryFile> +#include <QtCore/QDir> +#include <QtGui/QAction> +#include <QtGui/QMenu> +#include <QtGui/QMessageBox> +#include <QtGui/QMainWindow> +#include <QtGui/QFileDialog> + +static const VCSBase::VCSBaseEditorParameters editorParameters[] = { +{ + VCSBase::RegularCommandOutput, + Git::Constants::GIT_COMMAND_LOG_EDITOR_KIND, + Core::Constants::C_GLOBAL, + "application/vnd.nokia.text.scs_git_commandlog", + "gitlog"}, +{ VCSBase::LogOutput, + Git::Constants::GIT_LOG_EDITOR_KIND, + Core::Constants::C_GLOBAL, + "application/vnd.nokia.text.scs_git_filelog", + "gitfilelog"}, +{ VCSBase::AnnotateOutput, + Git::Constants::GIT_BLAME_EDITOR_KIND, + Core::Constants::C_GLOBAL, + "application/vnd.nokia.text.scs_git_annotation", + "gitsannotate"}, +{ VCSBase::DiffOutput, + Git::Constants::GIT_DIFF_EDITOR_KIND, + Core::Constants::C_GLOBAL, + "text/x-patch","diff"} +}; + +// Utility to find a parameter set by type +static inline const VCSBase::VCSBaseEditorParameters *findType(int ie) +{ + const VCSBase::EditorContentType et = static_cast<VCSBase::EditorContentType>(ie); + return VCSBase::VCSBaseEditor::findType(editorParameters, sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters), et); +} + +using namespace Git; +using namespace Git::Internal; + +// CoreListener + +bool CoreListener::editorAboutToClose(Core::IEditor *editor) +{ + return m_plugin->editorAboutToClose(editor); +} + +// GitPlugin + +GitPlugin *GitPlugin::m_instance = 0; + +GitPlugin::GitPlugin() : + m_core(0), + m_diffAction(0), + m_diffProjectAction(0), + m_statusAction(0), + m_statusProjectAction(0), + m_logAction(0), + m_blameAction(0), + m_logProjectAction(0), + m_undoFileAction(0), + m_undoProjectAction(0), + m_showAction(0), + m_addAction(0), + m_commitAction(0), + m_pullAction(0), + m_pushAction(0), + m_submitCurrentAction(0), + m_diffSelectedFilesAction(0), + m_undoAction(0), + m_redoAction(0), + m_projectExplorer(0), + m_gitClient(0), + m_outputWindow(0), + m_changeSelectionDialog(0), + m_settingsPage(0), + m_coreListener(0), + m_submitEditorFactory(0), + m_changeTmpFile(0) +{ + Q_ASSERT(m_instance == 0); + m_instance = this; +} + +GitPlugin::~GitPlugin() +{ + if (m_outputWindow) { + removeObject(m_outputWindow); + delete m_outputWindow; + m_outputWindow = 0; + } + + if (m_settingsPage) { + removeObject(m_settingsPage); + delete m_settingsPage; + m_settingsPage = 0; + } + + if (!m_editorFactories.empty()) { + foreach(Core::IEditorFactory* pf, m_editorFactories) + removeObject(pf); + qDeleteAll(m_editorFactories); + } + + if (m_coreListener) { + removeObject(m_coreListener); + delete m_coreListener; + m_coreListener = 0; + } + + if (m_submitEditorFactory) { + removeObject(m_submitEditorFactory); + m_submitEditorFactory = 0; + } + + cleanChangeTmpFile(); + delete m_gitClient; + m_instance = 0; +} + +void GitPlugin::cleanChangeTmpFile() +{ + if (m_changeTmpFile) { + delete m_changeTmpFile; + m_changeTmpFile = 0; + } +} + +GitPlugin *GitPlugin::instance() +{ + return m_instance; +} + +static const VCSBase::VCSBaseSubmitEditorParameters submitParameters = { + Git::Constants::SUBMIT_MIMETYPE, + Git::Constants::GITSUBMITEDITOR_KIND, + Git::Constants::C_GITSUBMITEDITOR, + Core::Constants::UNDO, + Core::Constants::REDO, + Git::Constants::SUBMIT_CURRENT, + Git::Constants::DIFF_SELECTED +}; + + +bool GitPlugin::initialize(const QStringList &arguments, QString *error_message) +{ + typedef VCSBase::VCSEditorFactory<GitEditor> GitEditorFactory; + typedef VCSBase::VCSSubmitEditorFactory<GitSubmitEditor> GitSubmitEditorFactory; + + Q_UNUSED(arguments); + Q_UNUSED(error_message); + + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + m_gitClient = new GitClient(this, m_core); + // Create the globalcontext list to register actions accordingly + QList<int> globalcontext; + globalcontext << m_core->uniqueIDManager()-> + uniqueIdentifier(Core::Constants::C_GLOBAL); + + // Create the output Window + m_outputWindow = new GitOutputWindow(); + addObject(m_outputWindow); + + // Create the settings Page + m_settingsPage = new SettingsPage(); + addObject(m_settingsPage); + + static const char *describeSlot = SLOT(show(QString,QString)); + const int editorCount = sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters); + for (int i = 0; i < editorCount; i++) { + m_editorFactories.push_back(new GitEditorFactory(editorParameters + i, m_core, m_gitClient, describeSlot)); + addObject(m_editorFactories.back()); + } + + m_coreListener = new CoreListener(this); + addObject(m_coreListener); + + m_submitEditorFactory = new GitSubmitEditorFactory(&submitParameters); + addObject(m_submitEditorFactory); + + //register actions + Core::ActionManagerInterface *actionManager = m_core->actionManager(); + + Core::IActionContainer *toolsContainer = + actionManager->actionContainer(Core::Constants::M_TOOLS); + + Core::IActionContainer *gitContainer = + actionManager->createMenu(QLatin1String("Git")); + gitContainer->menu()->setTitle(tr("&Git")); + toolsContainer->addMenu(gitContainer); + + Core::ICommand *command; + QAction *tmpaction; + + m_diffAction = new QAction(tr("Diff current file"), this); + command = actionManager->registerAction(m_diffAction, "Git.Diff", globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+D"))); + connect(m_diffAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile())); + gitContainer->addAction(command); + + m_statusAction = new QAction(tr("File Status"), this); + command = actionManager->registerAction(m_statusAction, "Git.Status", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+S"))); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_statusAction, SIGNAL(triggered()), this, SLOT(statusFile())); + gitContainer->addAction(command); + + m_logAction = new QAction(tr("Log File"), this); + command = actionManager->registerAction(m_logAction, "Git.Log", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+L"))); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_logAction, SIGNAL(triggered()), this, SLOT(logFile())); + gitContainer->addAction(command); + + m_blameAction = new QAction(tr("Blame"), this); + command = actionManager->registerAction(m_blameAction, "Git.Blame", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+B"))); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_blameAction, SIGNAL(triggered()), this, SLOT(blameFile())); + gitContainer->addAction(command); + + m_undoFileAction = new QAction(tr("Undo Changes"), this); + command = actionManager->registerAction(m_undoFileAction, "Git.Undo", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+U"))); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_undoFileAction, SIGNAL(triggered()), this, SLOT(undoFileChanges())); + gitContainer->addAction(command); + + m_addAction = new QAction(tr("Add File"), this); + command = actionManager->registerAction(m_addAction, "Git.Add", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+A"))); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_addAction, SIGNAL(triggered()), this, SLOT(addFile())); + gitContainer->addAction(command); + + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + command = actionManager->registerAction(tmpaction, QLatin1String("Git.Sep.Project"), globalcontext); + gitContainer->addAction(command); + + m_diffProjectAction = new QAction(tr("Diff current project"), this); + command = actionManager->registerAction(m_diffProjectAction, "Git.DiffProject", globalcontext); + command->setDefaultKeySequence(QKeySequence("Alt+G,Alt+Shift+D")); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_diffProjectAction, SIGNAL(triggered()), this, SLOT(diffCurrentProject())); + gitContainer->addAction(command); + + m_statusProjectAction = new QAction(tr("Project status"), this); + command = actionManager->registerAction(m_statusProjectAction, "Git.StatusProject", globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_statusProjectAction, SIGNAL(triggered()), this, SLOT(statusProject())); + gitContainer->addAction(command); + + m_logProjectAction = new QAction(tr("Log project"), this); + command = actionManager->registerAction(m_logProjectAction, "Git.LogProject", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+K"))); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_logProjectAction, SIGNAL(triggered()), this, SLOT(logProject())); + gitContainer->addAction(command); + + m_undoProjectAction = new QAction(tr("Undo Project Changes"), this); + command = actionManager->registerAction(m_undoProjectAction, "Git.UndoProject", globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_undoProjectAction, SIGNAL(triggered()), this, SLOT(undoProjectChanges())); + gitContainer->addAction(command); + + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + command = actionManager->registerAction(tmpaction, QLatin1String("Git.Sep.Global"), globalcontext); + gitContainer->addAction(command); + + m_showAction = new QAction(tr("Show commit..."), this); + command = actionManager->registerAction(m_showAction, "Git.ShowCommit", globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_showAction, SIGNAL(triggered()), this, SLOT(showCommit())); + gitContainer->addAction(command); + + m_commitAction = new QAction(tr("Commit..."), this); + command = actionManager->registerAction(m_commitAction, "Git.Commit", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+C"))); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_commitAction, SIGNAL(triggered()), this, SLOT(startCommit())); + gitContainer->addAction(command); + + m_pullAction = new QAction(tr("Pull"), this); + command = actionManager->registerAction(m_pullAction, "Git.Pull", globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_pullAction, SIGNAL(triggered()), this, SLOT(pull())); + gitContainer->addAction(command); + + m_pushAction = new QAction(tr("Push"), this); + command = actionManager->registerAction(m_pushAction, "Git.Push", globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_pushAction, SIGNAL(triggered()), this, SLOT(push())); + gitContainer->addAction(command); + + // Submit editor + QList<int> submitContext; + submitContext.push_back(m_core->uniqueIDManager()->uniqueIdentifier(QLatin1String(Constants::C_GITSUBMITEDITOR))); + m_submitCurrentAction = new QAction(QIcon(Constants::ICON_SUBMIT), tr("Commit"), this); + command = actionManager->registerAction(m_submitCurrentAction, Constants::SUBMIT_CURRENT, submitContext); + // TODO + connect(m_submitCurrentAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog())); + + m_diffSelectedFilesAction = new QAction(QIcon(Constants::ICON_DIFF), tr("Diff Selected Files"), this); + command = actionManager->registerAction(m_diffSelectedFilesAction, Constants::DIFF_SELECTED, submitContext); + + m_undoAction = new QAction(tr("&Undo"), this); + command = actionManager->registerAction(m_undoAction, Core::Constants::UNDO, submitContext); + + m_redoAction = new QAction(tr("&Redo"), this); + command = actionManager->registerAction(m_redoAction, Core::Constants::REDO, submitContext); + + // Ask for updates of our actions, in case context switches + connect(m_core, SIGNAL(contextChanged(Core::IContext *)), + this, SLOT(updateActions())); + connect(m_core->fileManager(), SIGNAL(currentFileChanged(const QString &)), + this, SLOT(updateActions())); + + return true; +} + +void GitPlugin::extensionsInitialized() +{ + m_projectExplorer = ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::ProjectExplorerPlugin>(); +} + +bool GitPlugin::vcsOpen(const QString &fileName) +{ + Q_UNUSED(fileName); + return false; +} + +void GitPlugin::submitEditorDiff(const QStringList &files) +{ + if (files.empty()) + return; + m_gitClient->diff(m_submitRepository, files); +} + +void GitPlugin::diffCurrentFile() +{ + QFileInfo fileInfo = currentFile(); + QString fileName = fileInfo.fileName(); + QString workingDirectory = fileInfo.absolutePath(); + m_gitClient->diff(workingDirectory, fileName); +} + +void GitPlugin::diffCurrentProject() +{ + QString workingDirectory = getWorkingDirectory(); + if (workingDirectory.isEmpty()) + return; + m_gitClient->diff(workingDirectory, QString()); +} + +QFileInfo GitPlugin::currentFile() +{ + QString fileName = m_core->fileManager()->currentFile(); + QFileInfo fileInfo(fileName); + return fileInfo; +} + +QString GitPlugin::getWorkingDirectory() +{ + QString workingDirectory; + if (m_projectExplorer && m_projectExplorer->currentNode()) { + workingDirectory = QFileInfo(m_projectExplorer->currentNode()->path()).absolutePath(); + } + if (Git::Constants::debug > 1) + qDebug() << Q_FUNC_INFO << "Project" << workingDirectory; + + if (workingDirectory.isEmpty()) + workingDirectory = QFileInfo(m_core->fileManager()->currentFile()).absolutePath(); + if (Git::Constants::debug > 1) + qDebug() << Q_FUNC_INFO << "file" << workingDirectory; + + if (workingDirectory.isEmpty()) { + m_outputWindow->clearContents(); + m_outputWindow->append(tr("Could not find working directory")); + m_outputWindow->popup(); + return QString(); + } + return workingDirectory; +} + +void GitPlugin::statusProject() +{ + QString workingDirectory = getWorkingDirectory(); + if (workingDirectory.isEmpty()) + return; + m_gitClient->status(workingDirectory); +} + +void GitPlugin::statusFile() +{ + m_gitClient->status(currentFile().absolutePath()); +} + +void GitPlugin::logFile() +{ + const QFileInfo fileInfo = currentFile(); + const QString fileName = fileInfo.fileName(); + const QString workingDirectory = fileInfo.absolutePath(); + m_gitClient->log(workingDirectory, fileName); +} + +void GitPlugin::blameFile() +{ + const QFileInfo fileInfo = currentFile(); + const QString fileName = fileInfo.fileName(); + const QString workingDirectory = fileInfo.absolutePath(); + m_gitClient->blame(workingDirectory, fileName); +} + +void GitPlugin::logProject() +{ + QString workingDirectory = getWorkingDirectory(); + if (workingDirectory.isEmpty()) + return; + m_gitClient->log(workingDirectory, QString()); +} + +void GitPlugin::undoFileChanges() +{ + QFileInfo fileInfo = currentFile(); + QString fileName = fileInfo.fileName(); + QString workingDirectory = fileInfo.absolutePath(); + m_gitClient->checkout(workingDirectory, fileName); +} + +void GitPlugin::undoProjectChanges() +{ + QString workingDirectory = getWorkingDirectory(); + if (workingDirectory.isEmpty()) + return; + m_gitClient->hardReset(workingDirectory, QString()); +} + +void GitPlugin::addFile() +{ + QFileInfo fileInfo = currentFile(); + QString fileName = fileInfo.fileName(); + QString workingDirectory = fileInfo.absolutePath(); + m_gitClient->addFile(workingDirectory, fileName); +} + +void GitPlugin::startCommit() +{ + if (m_changeTmpFile) { + m_outputWindow->append(tr("Another submit is currently beeing executed.")); + m_outputWindow->popup(false); + return; + } + + // Find repository and get commit data + const QFileInfo currentFileInfo = currentFile(); + if (!currentFileInfo.exists()) + return; + + const QString workingDirectory = currentFileInfo.absolutePath(); + QString errorMessage, commitTemplate; + CommitData data; + if (!m_gitClient->getCommitData(workingDirectory, &commitTemplate, &data, &errorMessage)) { + m_outputWindow->append(errorMessage); + m_outputWindow->popup(false); + return; + } + + m_submitRepository = data.panelInfo.repository; + + if (Git::Constants::debug) + qDebug() << Q_FUNC_INFO << data << commitTemplate; + + // Start new temp file with message template + QTemporaryFile *changeTmpFile = new QTemporaryFile(this); + changeTmpFile->setAutoRemove(true); + if (!changeTmpFile->open()) { + m_outputWindow->append(tr("Cannot create temporary file: %1").arg(changeTmpFile->errorString())); + delete changeTmpFile; + return; + } + m_changeTmpFile = changeTmpFile; + m_changeTmpFile->write(commitTemplate.toLocal8Bit()); + m_changeTmpFile->flush(); + // Keep the file alive, else it removes self and forgets + // its name + m_changeTmpFile->seek(0); + openSubmitEditor(m_changeTmpFile->fileName(), data); +} + +Core::IEditor *GitPlugin::openSubmitEditor(const QString &fileName, const CommitData &cd) +{ + Core::IEditor *editor = m_core->editorManager()->openEditor(fileName, QLatin1String(Constants::GITSUBMITEDITOR_KIND)); + if (Git::Constants::debug) + qDebug() << Q_FUNC_INFO << fileName << editor; + m_core->editorManager()->ensureEditorManagerVisible(); + GitSubmitEditor *submitEditor = qobject_cast<GitSubmitEditor*>(editor); + Q_ASSERT(submitEditor); + // The actions are for some reason enabled by the context switching + // mechanism. Disable them correctly. + m_submitCurrentAction->setEnabled(!cd.commitFiles.empty()); + m_diffSelectedFilesAction->setEnabled(false); + m_undoAction->setEnabled(false); + m_redoAction->setEnabled(false); + submitEditor->setCommitData(cd); + connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(submitEditorDiff(QStringList))); + return editor; +} + +void GitPlugin::submitCurrentLog() +{ + // Close the submit editor + QList<Core::IEditor*> editors; + editors.push_back(m_core->editorManager()->currentEditor()); + m_core->editorManager()->closeEditors(editors); +} + +bool GitPlugin::editorAboutToClose(Core::IEditor *iEditor) +{ + // Closing a submit editor? + if (!m_changeTmpFile || !iEditor || qstrcmp(iEditor->kind(), Constants::GITSUBMITEDITOR_KIND)) + return true; + Core::IFile *fileIFace = iEditor->file(); + const GitSubmitEditor *editor = qobject_cast<GitSubmitEditor *>(iEditor); + if (!fileIFace || !editor) + return true; + // Submit editor closing. Make it write out the commit message + // and retrieve files + const QFileInfo editorFile(fileIFace->fileName()); + const QFileInfo changeFile(m_changeTmpFile->fileName()); + // Paranoia! + if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath()) + return true; + // Prompt user. + const QMessageBox::StandardButton answer = QMessageBox::question(m_core->mainWindow(), tr("Closing git editor"), tr("Do you want to commit the change?"), + QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes); + switch (answer) { + case QMessageBox::Cancel: + return false; // Keep editing and change file + case QMessageBox::No: + cleanChangeTmpFile(); + return true; // Cancel all + default: + break; + } + // Go ahead! + const QStringList fileList = editor->checkedFiles(); + if (Git::Constants::debug) + qDebug() << Q_FUNC_INFO << fileList; + if (!fileList.empty()) { + // get message & commit + m_core->fileManager()->blockFileChange(fileIFace); + fileIFace->save(); + m_core->fileManager()->unblockFileChange(fileIFace); + + m_gitClient->addAndCommit(m_submitRepository, + editor->panelData(), + m_changeTmpFile->fileName(), + fileList); + } + cleanChangeTmpFile(); + return true; +} + +void GitPlugin::pull() +{ + QString workingDirectory = getWorkingDirectory(); + if (workingDirectory.isEmpty()) + return; + m_gitClient->pull(workingDirectory); +} + +void GitPlugin::push() +{ + QString workingDirectory = getWorkingDirectory(); + if (workingDirectory.isEmpty()) + return; + m_gitClient->push(workingDirectory); +} + +void GitPlugin::updateActions() +{ + QFileInfo current = currentFile(); + const QString fileName = current.fileName(); + const QString currentDirectory = getWorkingDirectory(); + QString repository = m_gitClient->findRepositoryForFile(current.absoluteFilePath()); + // First check for file commands and if the current file is inside + // a Git-repository + m_diffAction->setText(tr("Diff %1").arg(fileName)); + m_statusAction->setText(tr("Status related to %1").arg(fileName)); + m_logAction->setText(tr("Log %1").arg(fileName)); + m_blameAction->setText(tr("Blame %1").arg(fileName)); + m_undoFileAction->setText(tr("Undo changes for %1").arg(fileName)); + m_addAction->setText(tr("Add %1").arg(fileName)); + if (repository.isEmpty()) { + // If the file is not in a repository, the corresponding project will + // be neither and we can disable everything and return + m_diffAction->setEnabled(false); + m_statusAction->setEnabled(false); + m_logAction->setEnabled(false); + m_blameAction->setEnabled(false); + m_undoFileAction->setEnabled(false); + m_addAction->setEnabled(false); + m_diffProjectAction->setEnabled(false); + m_diffProjectAction->setText(tr("Diff Project")); + m_statusProjectAction->setText(tr("Status Project")); + m_statusProjectAction->setEnabled(false); + m_logProjectAction->setText(tr("Log Project")); + m_logProjectAction->setEnabled(false); + return; + } else { + // We only know the file is in some repository, we do not know + // anything about any project so far. + m_diffAction->setEnabled(true); + m_statusAction->setEnabled(true); + m_logAction->setEnabled(true); + m_blameAction->setEnabled(true); + m_undoFileAction->setEnabled(true); + m_addAction->setEnabled(true); + } + + if (m_projectExplorer && m_projectExplorer->currentNode() + && m_projectExplorer->currentNode()->projectNode()) { + QString name = QFileInfo(m_projectExplorer->currentNode()->projectNode()->path()).baseName(); + m_diffProjectAction->setEnabled(true); + m_diffProjectAction->setText(tr("Diff Project %1").arg(name)); + m_statusProjectAction->setEnabled(true); + m_statusProjectAction->setText(tr("Status Project %1").arg(name)); + m_logProjectAction->setEnabled(true); + m_logProjectAction->setText(tr("Log Project %1").arg(name)); + } else { + m_diffProjectAction->setEnabled(false); + m_diffProjectAction->setText(tr("Diff Project")); + m_statusProjectAction->setEnabled(false); + m_statusProjectAction->setText(tr("Status Project")); + m_logProjectAction->setEnabled(false); + m_logProjectAction->setText(tr("Log Project")); + } +} + +void GitPlugin::showCommit() +{ + if (!m_changeSelectionDialog) + m_changeSelectionDialog = new ChangeSelectionDialog(); + + const QFileInfo currentInfo = currentFile(); + QString repositoryLocation = m_gitClient->findRepositoryForFile(currentInfo.absoluteFilePath()); + if (!repositoryLocation.isEmpty()) + m_changeSelectionDialog->m_ui.repositoryEdit->setText(repositoryLocation); + + if (m_changeSelectionDialog->exec() != QDialog::Accepted) + return; + const QString change = m_changeSelectionDialog->m_ui.changeNumberEdit->text(); + if (change .isEmpty()) + return; + + m_gitClient->show(m_changeSelectionDialog->m_ui.repositoryEdit->text(), change); +} + +Q_EXPORT_PLUGIN(GitPlugin) diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h new file mode 100644 index 00000000000..c57c380fcf6 --- /dev/null +++ b/src/plugins/git/gitplugin.h @@ -0,0 +1,163 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GITPLUGIN_H +#define GITPLUGIN_H + +#include "gitoutputwindow.h" +#include "settingspage.h" + +#include <coreplugin/editormanager/ieditorfactory.h> +#include <coreplugin/icorelistener.h> +#include <projectexplorer/ProjectExplorerInterfaces> +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> +#include <QtCore/QProcess> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE +class QFile; +class QAction; +class QTemporaryFile; +QT_END_NAMESPACE + +namespace Core { + class IEditorFactory; + class ICore; +} + +namespace Git { +namespace Internal { + + class GitPlugin; + class GitClient; + class ChangeSelectionDialog; + class GitSubmitEditor; + struct CommitData; + +// Just a proxy for GitPlugin +class CoreListener : public Core::ICoreListener +{ + Q_OBJECT +public: + CoreListener(GitPlugin *plugin) : m_plugin(plugin) { } + bool editorAboutToClose(Core::IEditor *editor); + bool coreAboutToClose() { return true; } +private: + GitPlugin *m_plugin; +}; + +class GitPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + GitPlugin(); + ~GitPlugin(); + static GitPlugin *instance(); + + bool vcsOpen(const QString &fileName); + + bool initialize(const QStringList &arguments + , QString *error_message); + void extensionsInitialized(); + + QString getWorkingDirectory(); + +public slots: + void updateActions(); + bool editorAboutToClose(Core::IEditor *editor); + +private slots: + void diffCurrentFile(); + void diffCurrentProject(); + void submitEditorDiff(const QStringList &); + void submitCurrentLog(); + void statusFile(); + void statusProject(); + void logFile(); + void blameFile(); + void logProject(); + void undoFileChanges(); + void undoProjectChanges(); + void addFile(); + + void showCommit(); + void startCommit(); + void pull(); + void push(); + +private: + friend class GitClient; + QFileInfo currentFile(); + Core::IEditor *openSubmitEditor(const QString &fileName, const CommitData &cd); + void cleanChangeTmpFile(); + + static GitPlugin *m_instance; + Core::ICore *m_core; + QAction *m_diffAction; + QAction *m_diffProjectAction; + QAction *m_statusAction; + QAction *m_statusProjectAction; + QAction *m_logAction; + QAction *m_blameAction; + QAction *m_logProjectAction; + QAction *m_undoFileAction; + QAction *m_undoProjectAction; + QAction *m_showAction; + QAction *m_addAction; + QAction *m_commitAction; + QAction *m_pullAction; + QAction *m_pushAction; + + QAction *m_submitCurrentAction; + QAction *m_diffSelectedFilesAction; + QAction *m_undoAction; + QAction *m_redoAction; + + ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer; + GitClient *m_gitClient; + GitOutputWindow *m_outputWindow; + ChangeSelectionDialog *m_changeSelectionDialog; + SettingsPage *m_settingsPage; + QList<Core::IEditorFactory*> m_editorFactories; + CoreListener *m_coreListener; + Core::IEditorFactory *m_submitEditorFactory; + QString m_submitRepository; + QTemporaryFile *m_changeTmpFile; +}; + +} // namespace Git +} // namespace Internal + +#endif diff --git a/src/plugins/git/gitsubmiteditor.cpp b/src/plugins/git/gitsubmiteditor.cpp new file mode 100644 index 00000000000..bf0d78af57b --- /dev/null +++ b/src/plugins/git/gitsubmiteditor.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gitsubmiteditor.h" +#include "gitsubmiteditorwidget.h" +#include "gitconstants.h" +#include "commitdata.h" + +#include <QtCore/QDebug> + +namespace Git { +namespace Internal { + +GitSubmitEditor::GitSubmitEditor(const VCSBase::VCSBaseSubmitEditorParameters *parameters, QWidget *parent) : + VCSBaseSubmitEditor(parameters, new GitSubmitEditorWidget(parent)) +{ + setDisplayName(tr("Git Commit")); +} + +GitSubmitEditorWidget *GitSubmitEditor::submitEditorWidget() +{ + return static_cast<GitSubmitEditorWidget *>(widget()); +} + +QStringList GitSubmitEditor::vcsFileListToFileList(const QStringList &rawList) const +{ + QStringList rc; + foreach (const QString &rf, rawList) + rc.push_back(fileFromChangeLine(rf)); + return rc; +} + +void GitSubmitEditor::setCommitData(const CommitData &d) +{ + submitEditorWidget()->setPanelData(d.panelData); + submitEditorWidget()->setPanelInfo(d.panelInfo); + + // Commited: Checked, user cannot uncheck + addFiles(d.commitFiles, true, false); + // Not Updated: User can check + addFiles(d.notUpdatedFiles, false, true); + addFiles(d.untrackedFiles, false, true); +} + +GitSubmitEditorPanelData GitSubmitEditor::panelData() const +{ + return const_cast<GitSubmitEditor*>(this)->submitEditorWidget()->panelData(); +} + +QString GitSubmitEditor::fileFromChangeLine(const QString &line) +{ + QString rc = line; + // "modified: mainwindow.cpp" + const int index = rc.indexOf(QLatin1Char(':')); + if (index != -1) + rc.remove(0, index + 1); + const QChar blank(' '); + while (rc.startsWith(blank)) + rc.remove(0, 1); + return rc; +} +} +} diff --git a/src/plugins/git/gitsubmiteditor.h b/src/plugins/git/gitsubmiteditor.h new file mode 100644 index 00000000000..b424361a6a3 --- /dev/null +++ b/src/plugins/git/gitsubmiteditor.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GITSUBMITEDITOR_H +#define GITSUBMITEDITOR_H + +#include <vcsbase/vcsbasesubmiteditor.h> + +namespace Git { +namespace Internal { + +class GitSubmitEditorWidget; +struct CommitData; +struct GitSubmitEditorPanelData; + +/* */ +class GitSubmitEditor : public VCSBase::VCSBaseSubmitEditor +{ + Q_OBJECT +public: + explicit GitSubmitEditor(const VCSBase::VCSBaseSubmitEditorParameters *parameters, QWidget *parent); + + void setCommitData(const CommitData &); + GitSubmitEditorPanelData panelData() const; + + static QString fileFromChangeLine(const QString &line); + +protected: + virtual QStringList vcsFileListToFileList(const QStringList &) const; + +private: + inline GitSubmitEditorWidget *submitEditorWidget(); +}; + +} // namespace Internal +} // namespace Git + +#endif // GITSUBMITEDITOR_H diff --git a/src/plugins/git/gitsubmiteditorwidget.cpp b/src/plugins/git/gitsubmiteditorwidget.cpp new file mode 100644 index 00000000000..aede10acd86 --- /dev/null +++ b/src/plugins/git/gitsubmiteditorwidget.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gitsubmiteditorwidget.h" +#include "commitdata.h" + +namespace Git { +namespace Internal { + +GitSubmitEditorWidget::GitSubmitEditorWidget(QWidget *parent) : + Core::Utils::SubmitEditorWidget(parent), + m_gitSubmitPanel(new QWidget) +{ + m_gitSubmitPanelUi.setupUi(m_gitSubmitPanel); + insertTopWidget(m_gitSubmitPanel); +} + +void GitSubmitEditorWidget::setPanelInfo(const GitSubmitEditorPanelInfo &info) +{ + m_gitSubmitPanelUi.repositoryLabel->setText(info.repository); + m_gitSubmitPanelUi.descriptionLabel->setText(info.description); + m_gitSubmitPanelUi.branchLabel->setText(info.branch); +} + +GitSubmitEditorPanelData GitSubmitEditorWidget::panelData() const +{ + GitSubmitEditorPanelData rc; + rc.author = m_gitSubmitPanelUi.authorLineEdit->text(); + rc.email = m_gitSubmitPanelUi.emailLineEdit->text(); + return rc; +}; + +void GitSubmitEditorWidget::setPanelData(const GitSubmitEditorPanelData &data) +{ + m_gitSubmitPanelUi.authorLineEdit->setText(data.author); + m_gitSubmitPanelUi.emailLineEdit->setText(data.email); +} + +} +} diff --git a/src/plugins/git/gitsubmiteditorwidget.h b/src/plugins/git/gitsubmiteditorwidget.h new file mode 100644 index 00000000000..7a842f50eff --- /dev/null +++ b/src/plugins/git/gitsubmiteditorwidget.h @@ -0,0 +1,73 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GITSUBMITEDITORWIDGET_H +#define GITSUBMITEDITORWIDGET_H + +#include "ui_gitsubmitpanel.h" +#include <utils/submiteditorwidget.h> + +namespace Git { +namespace Internal { + +struct GitSubmitEditorPanelInfo; +struct GitSubmitEditorPanelData; + +/* Submit editor widget with 2 additional panes: + * 1) Info with branch, description, etc + * 2) Data, with author and email to edit. + * The file contents is the submit message. + * The previously added files will be added 'checked' to the file list, the + * remaining un-added and untracked files will be added 'unchecked' for the + * user to click. */ + +class GitSubmitEditorWidget : public Core::Utils::SubmitEditorWidget +{ + +public: + explicit GitSubmitEditorWidget(QWidget *parent = 0); + + + GitSubmitEditorPanelData panelData() const; + void setPanelData(const GitSubmitEditorPanelData &data); + + void setPanelInfo(const GitSubmitEditorPanelInfo &info); + +private: + QWidget *m_gitSubmitPanel; + Ui::GitSubmitPanel m_gitSubmitPanelUi; +}; + +} // namespace Internal +} // namespace Perforce + +#endif // GITSUBMITEDITORWIDGET_H diff --git a/src/plugins/git/gitsubmitpanel.ui b/src/plugins/git/gitsubmitpanel.ui new file mode 100644 index 00000000000..8a42052a995 --- /dev/null +++ b/src/plugins/git/gitsubmitpanel.ui @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Git::Internal::GitSubmitPanel</class> + <widget class="QWidget" name="Git::Internal::GitSubmitPanel"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>201</width> + <height>210</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="infoGroup"> + <property name="title"> + <string>General Information</string> + </property> + <layout class="QFormLayout" name="infoFormLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="repositoryLabelLabel"> + <property name="text"> + <string>Repository:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="repositoryLabel"> + <property name="text"> + <string>repository</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="descriptionLabelLabel"> + <property name="text"> + <string>Description:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="descriptionLabel"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>description</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="branchLabelLabel"> + <property name="text"> + <string>Branch:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="branchLabel"> + <property name="text"> + <string>branch</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="editGroup"> + <property name="title"> + <string>Commit Information</string> + </property> + <layout class="QFormLayout" name="editFormLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="authorLabel"> + <property name="text"> + <string>Author:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="authorLineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="emailLabel"> + <property name="text"> + <string>Email:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="emailLineEdit"/> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/git/settingspage.cpp b/src/plugins/git/settingspage.cpp new file mode 100644 index 00000000000..90f6371c0af --- /dev/null +++ b/src/plugins/git/settingspage.cpp @@ -0,0 +1,115 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "settingspage.h" + +#include <coreplugin/icore.h> +#include <extensionsystem/pluginmanager.h> + +#include <QtCore/QSettings> +#include <QtGui/QLineEdit> +#include <QtGui/QFileDialog> +#include <QtCore/QDebug> + +using namespace Git::Internal; + +static const char *groupC = "Git"; +static const char *sysEnvKeyC = "SysEnv"; +static const char *pathKeyC = "Path"; +static const char *logCountKeyC = "LogCount"; + +SettingsPage::SettingsPage() +{ + Core::ICore *coreIFace = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + if (coreIFace) + m_settings = coreIFace->settings(); + + if (m_settings) { + m_settings->beginGroup(QLatin1String(groupC)); + m_adopt = m_settings->value(QLatin1String(sysEnvKeyC), true).toBool(); + m_path = m_settings->value(QLatin1String(pathKeyC), QString()).toString(); + m_logCount = m_settings->value(QLatin1String(logCountKeyC), 10).toInt(); + m_settings->endGroup(); + } +} + +QString SettingsPage::name() const +{ + return tr("General"); +} + +QString SettingsPage::category() const +{ + return QLatin1String("Git"); +} + +QString SettingsPage::trCategory() const +{ + return tr("Git"); +} + +QWidget *SettingsPage::createPage(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + m_ui.setupUi(w); + m_ui.adoptCheckBox->setChecked(m_adopt); + m_ui.pathLineEdit->setText(m_path); + m_ui.logLineEdit->setText(QString::number(m_logCount)); + + connect(m_ui.adoptButton, SIGNAL(clicked()), this, SLOT(setSystemPath())); + return w; +} + +void SettingsPage::finished(bool accepted) +{ + if (!accepted) + return; + + m_adopt = m_ui.adoptCheckBox->isChecked(); + m_path = m_ui.pathLineEdit->text(); + m_logCount = m_ui.logLineEdit->text().toInt(); + + if (!m_settings) + return; + + m_settings->beginGroup(QLatin1String(groupC)); + m_settings->setValue(QLatin1String(sysEnvKeyC), m_adopt); + m_settings->setValue(QLatin1String(pathKeyC), m_path); + m_settings->setValue(QLatin1String(logCountKeyC), m_logCount); + m_settings->endGroup(); +} + +void SettingsPage::setSystemPath() +{ + m_path = qgetenv("PATH"); + m_ui.pathLineEdit->setText(m_path); +} diff --git a/src/plugins/git/settingspage.h b/src/plugins/git/settingspage.h new file mode 100644 index 00000000000..de627bd9cea --- /dev/null +++ b/src/plugins/git/settingspage.h @@ -0,0 +1,82 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SETTINGSPAGE_H +#define SETTINGSPAGE_H + +#include <QtGui/QWidget> + +#include <coreplugin/dialogs/ioptionspage.h> + +#include "ui_settingspage.h" + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace Git { +namespace Internal { + +class SettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + SettingsPage(); + + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + + bool adoptEnvironment() const { return m_adopt; } + int logCount() const { return m_logCount; } + QString path() const { return m_path; } + +private slots: + void setSystemPath(); + +private: + Ui_SettingsPage m_ui; + QSettings *m_settings; + + bool m_adopt; + QString m_path; + int m_logCount; +}; + +} //namespace Internal +} //namespace Git + +#endif diff --git a/src/plugins/git/settingspage.ui b/src/plugins/git/settingspage.ui new file mode 100644 index 00000000000..738413e676f --- /dev/null +++ b/src/plugins/git/settingspage.ui @@ -0,0 +1,135 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Git::Internal::SettingsPage</class> + <widget class="QWidget" name="Git::Internal::SettingsPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>345</width> + <height>177</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QCheckBox" name="adoptCheckBox"> + <property name="text"> + <string>Use System Environment</string> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Environment variables</string> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>PATH:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="pathLineEdit"/> + </item> + <item row="0" column="2"> + <widget class="QPushButton" name="adoptButton"> + <property name="text"> + <string>Adopt</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>52</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" colspan="2"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Note</span><span style=" font-size:8pt;"> that Git needs Perl in the environment as well</span></p></body></html></string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Commit display count:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="logLineEdit"> + <property name="toolTip"> + <string>Note that huge amount of commits might take some time.</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>141</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <tabstops> + <tabstop>pathLineEdit</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>adoptCheckBox</sender> + <signal>toggled(bool)</signal> + <receiver>groupBox</receiver> + <slot>setDisabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>144</x> + <y>33</y> + </hint> + <hint type="destinationlabel"> + <x>139</x> + <y>65</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/helloworld/HelloWorld.pluginspec b/src/plugins/helloworld/HelloWorld.pluginspec new file mode 100644 index 00000000000..9cd262ca4d4 --- /dev/null +++ b/src/plugins/helloworld/HelloWorld.pluginspec @@ -0,0 +1,10 @@ +<plugin name="HelloWorld" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Hello World sample plugin.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/helloworld/helloworld.pro b/src/plugins/helloworld/helloworld.pro new file mode 100644 index 00000000000..19be467b0f6 --- /dev/null +++ b/src/plugins/helloworld/helloworld.pro @@ -0,0 +1,12 @@ +TEMPLATE = lib +TARGET = HelloWorld + +include(../../qworkbenchplugin.pri) +include(../../plugins/coreplugin/coreplugin.pri) + +HEADERS += helloworldplugin.h \ + helloworldwindow.h + +SOURCES += helloworldplugin.cpp \ + helloworldwindow.cpp + diff --git a/src/plugins/helloworld/helloworldplugin.cpp b/src/plugins/helloworld/helloworldplugin.cpp new file mode 100644 index 00000000000..ad7bf48fee7 --- /dev/null +++ b/src/plugins/helloworld/helloworldplugin.cpp @@ -0,0 +1,166 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "helloworldplugin.h" + +#include "helloworldwindow.h" + +#include <coreplugin/modemanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/uniqueidmanager.h> + +#include <QtCore/QDebug> +#include <QtCore/qplugin.h> +#include <QtGui/QAction> +#include <QtGui/QMenu> +#include <QtGui/QMessageBox> +#include <QtGui/QPushButton> + +#include <coreplugin/CoreTools> + +#include "helloworldplugin.h" + +using namespace HelloWorld::Internal; + +/*! Constructs the Hello World plugin. Normally plugins don't do anything in + their constructor except for initializing their member variables. The + actual work is done later, in the initialize() and extensionsInitialized() + methods. +*/ +HelloWorldPlugin::HelloWorldPlugin() +{ +} + +/*! Plugins are responsible for deleting objects they created on the heap, and + to unregister objects from the plugin manager that they registered there. +*/ +HelloWorldPlugin::~HelloWorldPlugin() +{ +} + +/*! Initializes the plugin. Returns true on success. + Plugins want to register objects with the plugin manager here. + + \a error_message can be used to pass an error message to the plugin system, + if there was any. +*/ +bool HelloWorldPlugin::initialize(const QStringList & /*arguments*/, QString *error_message) +{ + Q_UNUSED(error_message) + + // Get the primary access point to the workbench. + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + + // Create our own widget that we want to show in a view in the IDE. + HelloWorldWindow *window = new HelloWorldWindow; + + // Create a unique context id for our own view, that will be used for the + // menu entry later. + QList<int> context = QList<int>() + << core->uniqueIDManager()->uniqueIdentifier( + QLatin1String("HelloWorld.MainView")); + + // Create a new view that contains our widget and register it with the + // plugin manager. The view will have the id "HelloWorld.HelloWorldWindow", + // and if it has focus it provides 'context' to the context list in + // Qt Creator. It will be put into the right dock widget area. + addAutoReleasedObject(new Core::BaseView("HelloWorld.HelloWorldWindow", + window, context, + Qt::RightDockWidgetArea)); + + // Create an action to be triggered by a menu entry + QAction *helloWorldAction = new QAction("Say \"&Hello World!\"", this); + connect(helloWorldAction, SIGNAL(triggered()), SLOT(sayHelloWorld())); + + // Register the action with the action manager + Core::ActionManagerInterface *actionManager = core->actionManager(); + Core::ICommand *command = + actionManager->registerAction( + helloWorldAction, "HelloWorld.HelloWorldAction", context); + + // Create our own menu to place in the Tools menu + Core::IActionContainer *helloWorldMenu = + actionManager->createMenu("HelloWorld.HelloWorldMenu"); + QMenu *menu = helloWorldMenu->menu(); + menu->setTitle(tr("&Hello World")); + menu->setEnabled(true); + + // Add the Hello World action command to the menu + helloWorldMenu->addAction(command); + + // Request the Tools menu and add the Hello World menu to it + Core::IActionContainer *toolsMenu = + actionManager->actionContainer(Core::Constants::M_TOOLS); + toolsMenu->addMenu(helloWorldMenu); + + // Add a mode with a push button based on BaseMode. Like the BaseView, it will unregister + // itself from the plugin manager when it is deleted. + addAutoReleasedObject(new Core::BaseMode(tr("Hello world!"), + "HelloWorld.HelloWorldMode", + QIcon(), + 0, // priority + new QPushButton(tr("Hello World PushButton!")))); + + // Add the Hello World action command to the mode manager (with 0 priority) + Core::ModeManager *modeManager = core->modeManager(); + modeManager->addAction(command, 0); + + return true; +} + +/*! Notification that all extensions that this plugin depends on have been + initialized. The dependencies are defined in the plugins .qwp file. + + Normally this method is used for things that rely on other plugins to have + added objects to the plugin manager, that implement interfaces that we're + interested in. These objects can now be requested through the + PluginManagerInterface. + + The HelloWorldPlugin doesn't need things from other plugins, so it does + nothing here. +*/ +void HelloWorldPlugin::extensionsInitialized() +{ +} + +void HelloWorldPlugin::sayHelloWorld() +{ + // When passing 0 for the parent, the message box becomes an + // application-global modal dialog box + QMessageBox::information( + 0, "Hello World!", "Hello World! Beautiful day today, isn't it?"); +} + +Q_EXPORT_PLUGIN(HelloWorldPlugin) diff --git a/src/plugins/helloworld/helloworldplugin.h b/src/plugins/helloworld/helloworldplugin.h new file mode 100644 index 00000000000..3e73e6ae8fd --- /dev/null +++ b/src/plugins/helloworld/helloworldplugin.h @@ -0,0 +1,65 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef HELLOWORLDPLUGIN_H +#define HELLOWORLDPLUGIN_H + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> + +namespace HelloWorld { +namespace Internal { + +class HelloWorldPlugin + : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + HelloWorldPlugin(); + ~HelloWorldPlugin(); + + bool initialize(const QStringList &arguments, QString *error_message); + + void extensionsInitialized(); + +private slots: + void sayHelloWorld(); + +private: +}; + +} // namespace Internal +} // namespace HelloWorld + +#endif //HELLOWORLDPLUGIN_H diff --git a/src/plugins/helloworld/helloworldwindow.cpp b/src/plugins/helloworld/helloworldwindow.cpp new file mode 100644 index 00000000000..66a1b3d224c --- /dev/null +++ b/src/plugins/helloworld/helloworldwindow.cpp @@ -0,0 +1,46 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtGui/QTextEdit> +#include <QtGui/QVBoxLayout> + +#include "helloworldwindow.h" + +using namespace HelloWorld::Internal; + +HelloWorldWindow::HelloWorldWindow(QWidget *parent) + :QWidget(parent) +{ + QBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(new QTextEdit("Focus me to activate my context!")); + setWindowTitle(tr("Hello, world!")); +} diff --git a/src/plugins/helloworld/helloworldwindow.h b/src/plugins/helloworld/helloworldwindow.h new file mode 100644 index 00000000000..8d02923a2eb --- /dev/null +++ b/src/plugins/helloworld/helloworldwindow.h @@ -0,0 +1,54 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef HELLOWORLDWINDOW_H +#define HELLOWORLDWINDOW_H + +#include <QtGui/QWidget> + +QT_FORWARD_DECLARE_CLASS(QLabel); + +namespace HelloWorld { +namespace Internal { + +class HelloWorldWindow : public QWidget +{ + Q_OBJECT + +public: + HelloWorldWindow(QWidget *parent = 0); +}; + +} // namespace Internal +} // namespace HelloWorld + +#endif // HELLOWORLDWINDOW_H diff --git a/src/plugins/help/Help.pluginspec b/src/plugins/help/Help.pluginspec new file mode 100644 index 00000000000..b60b320827e --- /dev/null +++ b/src/plugins/help/Help.pluginspec @@ -0,0 +1,12 @@ +<plugin name="Help" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Help system.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> + <dependency name="Find" version="0.9.1"/> + <dependency name="QuickOpen" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/help/centralwidget.cpp b/src/plugins/help/centralwidget.cpp new file mode 100644 index 00000000000..6c63c28b1ea --- /dev/null +++ b/src/plugins/help/centralwidget.cpp @@ -0,0 +1,687 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "centralwidget.h" +#include "helpviewer.h" +#include "topicchooser.h" + +#include <QtCore/QDir> +#include <QtCore/QEvent> +#include <QtCore/QTimer> + +#include <QtGui/QMenu> +#include <QtGui/QLabel> +#include <QtGui/QLayout> +#include <QtGui/QPrinter> +#include <QtGui/QLineEdit> +#include <QtGui/QCheckBox> +#include <QtGui/QTabBar> +#include <QtGui/QTabWidget> +#include <QtGui/QToolButton> +#include <QtGui/QMouseEvent> +#include <QtGui/QFocusEvent> +#include <QtGui/QMainWindow> +#include <QtGui/QSpacerItem> +#include <QtGui/QTextCursor> +#include <QtGui/QPrintDialog> +#include <QtGui/QApplication> +#include <QtGui/QTextDocumentFragment> +#include <QtGui/QPrintPreviewDialog> +#include <QtGui/QPageSetupDialog> + +#include <QtHelp/QHelpEngine> + +using namespace Help::Internal; + +namespace { + HelpViewer* helpViewerFromTabPosition(const QTabWidget *widget, const QPoint &point) + { + QTabBar *tabBar = qFindChild<QTabBar*>(widget); + for (int i = 0; i < tabBar->count(); ++i) { + if (tabBar->tabRect(i).contains(point)) + return qobject_cast<HelpViewer*>(widget->widget(i)); + } + return 0; + } + CentralWidget *staticCentralWidget = 0; +} + +CentralWidget::CentralWidget(QHelpEngine *engine, QWidget *parent) + : QWidget(parent) + , findBar(0) + , tabWidget(0) + , helpEngine(engine) + , printer(0) +{ + lastTabPage = 0; + globalActionList.clear(); + collectionFile = helpEngine->collectionFile(); + + QString system = QLatin1String("win"); + +#ifdef Q_OS_MAC + system = QLatin1String("mac"); +#endif + + tabWidget = new QTabWidget; + tabWidget->setDocumentMode(true); + tabWidget->setMovable(true); + connect(tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); + connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(currentPageChanged(int))); + + QToolButton *newTabButton = new QToolButton(this); + newTabButton->setAutoRaise(true); + newTabButton->setToolTip(tr("Add new page")); + newTabButton->setIcon(QIcon(QString::fromUtf8(":/trolltech/assistant/images/%1/addtab.png").arg(system))); + + tabWidget->setCornerWidget(newTabButton, Qt::TopLeftCorner); + connect(newTabButton, SIGNAL(clicked()), this, SLOT(newTab())); + + QVBoxLayout *vboxLayout = new QVBoxLayout(this); + vboxLayout->setMargin(0); + vboxLayout->addWidget(tabWidget); + + QTabBar *tabBar = qFindChild<QTabBar*>(tabWidget); + if (tabBar) { + tabBar->installEventFilter(this); + tabBar->setContextMenuPolicy(Qt::CustomContextMenu); + connect(tabBar, SIGNAL(customContextMenuRequested(const QPoint&)), + this, SLOT(showTabBarContextMenu(const QPoint&))); + } + + staticCentralWidget = this; +} + +CentralWidget::~CentralWidget() +{ + QDir dir; + QString currentPages; + QHelpEngineCore engine(collectionFile, 0); + + if (engine.setupData()) { + for (int i = 0; i < tabWidget->count(); ++i) { + HelpViewer *viewer = qobject_cast<HelpViewer*>(tabWidget->widget(i)); + if (viewer && viewer->source().isValid()) + currentPages.append(viewer->source().toString()).append(QLatin1Char('|')); + } + engine.setCustomValue(QLatin1String("LastTabPage"), lastTabPage); + engine.setCustomValue(QLatin1String("LastShownPages"), currentPages); + } +} + +CentralWidget *CentralWidget::instance() +{ + return staticCentralWidget; +} + +void CentralWidget::newTab() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + setSourceInNewTab(viewer->source()); +} + +void CentralWidget::zoomIn() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->zoomIn(); +} + +void CentralWidget::zoomOut() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->zoomOut(); +} + +void CentralWidget::nextPage() +{ + if (tabWidget->currentIndex() < tabWidget->count() -1) + tabWidget->setCurrentIndex(tabWidget->currentIndex() +1); + else + tabWidget->setCurrentIndex(0); +} + +void CentralWidget::resetZoom() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->resetZoom(); +} + +void CentralWidget::previousPage() +{ + int index = tabWidget->currentIndex() -1; + if (index >= 0) + tabWidget->setCurrentIndex(index); + else + tabWidget->setCurrentIndex(tabWidget->count() -1); +} + +void CentralWidget::closeTab() +{ + closeTab(tabWidget->currentIndex()); +} + +void CentralWidget::closeTab(int index) +{ + HelpViewer* viewer = helpViewerAtIndex(index); + if (!viewer || tabWidget->count() == 1) + return; + + tabWidget->removeTab(index); + QTimer::singleShot(0, viewer, SLOT(deleteLater())); +} + +void CentralWidget::setSource(const QUrl &url) +{ + HelpViewer* viewer = currentHelpViewer(); + HelpViewer* lastViewer = qobject_cast<HelpViewer*>(tabWidget->widget(lastTabPage)); + + if (!viewer && !lastViewer) { + viewer = new HelpViewer(helpEngine, this); + viewer->installEventFilter(this); + lastTabPage = tabWidget->addTab(viewer, QString()); + tabWidget->setCurrentIndex(lastTabPage); + connectSignals(); + qApp->processEvents(); + } else + viewer = lastViewer; + + viewer->setSource(url); + currentPageChanged(lastTabPage); + viewer->setFocus(Qt::OtherFocusReason); + tabWidget->setCurrentIndex(lastTabPage); + tabWidget->setTabText(lastTabPage, quoteTabTitle(viewer->documentTitle())); +} + +void CentralWidget::setLastShownPages() +{ + const QStringList lastShownPageList = helpEngine->customValue(QLatin1String("LastShownPages")). + toString().split(QLatin1Char('|'), QString::SkipEmptyParts); + + if (!lastShownPageList.isEmpty()) { + foreach (const QString page, lastShownPageList) + setSourceInNewTab(page); + + tabWidget->setCurrentIndex(helpEngine->customValue(QLatin1String("LastTabPage"), 0).toInt()); + } else { + QUrl url = helpEngine->findFile(QUrl("qthelp://com.trolltech.qt.440/qdoc/index.html")); + if (url.isValid()) + setSource(url); + else + setSource(QUrl("qthelp://com.trolltech.qt.440/qdoc/index.html")); + } + + updateBrowserFont(); +} + +bool CentralWidget::hasSelection() const +{ + const HelpViewer* viewer = currentHelpViewer(); + return viewer ? viewer->hasSelection() : false; +} + +QUrl CentralWidget::currentSource() const +{ + const HelpViewer* viewer = currentHelpViewer(); + if (viewer) + return viewer->source(); + + return QUrl(); +} + +QString CentralWidget::currentTitle() const +{ + const HelpViewer* viewer = currentHelpViewer(); + if (viewer) + return viewer->documentTitle(); + + return QString(); +} + +void CentralWidget::copySelection() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->copy(); +} + +void CentralWidget::initPrinter() +{ +#ifndef QT_NO_PRINTER + if (!printer) + printer = new QPrinter(QPrinter::HighResolution); +#endif +} + +void CentralWidget::print() +{ +#ifndef QT_NO_PRINTER + HelpViewer* viewer = currentHelpViewer(); + if (!viewer) + return; + + initPrinter(); + + QPrintDialog *dlg = new QPrintDialog(printer, this); +#if !defined(USE_WEBKIT) + if (viewer->textCursor().hasSelection()) + dlg->addEnabledOption(QAbstractPrintDialog::PrintSelection); +#endif + dlg->addEnabledOption(QAbstractPrintDialog::PrintPageRange); + dlg->addEnabledOption(QAbstractPrintDialog::PrintCollateCopies); + dlg->setWindowTitle(tr("Print Document")); + if (dlg->exec() == QDialog::Accepted) { + viewer->print(printer); + } + delete dlg; +#endif +} + +void CentralWidget::printPreview() +{ +#ifndef QT_NO_PRINTER + initPrinter(); + QPrintPreviewDialog preview(printer, this); + connect(&preview, SIGNAL(paintRequested(QPrinter *)), SLOT(printPreview(QPrinter *))); + preview.exec(); +#endif +} + +void CentralWidget::printPreview(QPrinter *p) +{ +#ifndef QT_NO_PRINTER + HelpViewer *viewer = currentHelpViewer(); + if (viewer) + viewer->print(p); +#endif +} + +void CentralWidget::pageSetup() +{ +#ifndef QT_NO_PRINTER + initPrinter(); + QPageSetupDialog dlg(printer); + dlg.exec(); +#endif +} + +bool CentralWidget::isHomeAvailable() const +{ + return currentHelpViewer() ? true : false; +} + +void CentralWidget::home() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->home(); +} + +bool CentralWidget::isForwardAvailable() const +{ + const HelpViewer* viewer = currentHelpViewer(); + if (viewer) + return viewer->isForwardAvailable(); + + return false; +} + +void CentralWidget::forward() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->forward(); +} + +bool CentralWidget::isBackwardAvailable() const +{ + const HelpViewer* viewer = currentHelpViewer(); + if (viewer) + return viewer->isBackwardAvailable(); + + return false; +} + +void CentralWidget::backward() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->backward(); +} + + +QList<QAction*> CentralWidget::globalActions() const +{ + return globalActionList; +} + +void CentralWidget::setGlobalActions(const QList<QAction*> &actions) +{ + globalActionList = actions; +} +void CentralWidget::setSourceInNewTab(const QUrl &url) +{ + HelpViewer* viewer = new HelpViewer(helpEngine, this); + viewer->installEventFilter(this); + viewer->setSource(url); + viewer->setFocus(Qt::OtherFocusReason); + tabWidget->setCurrentIndex(tabWidget->addTab(viewer, viewer->documentTitle())); + + QFont font = qApp->font(); + if (helpEngine->customValue(QLatin1String("useBrowserFont")).toBool()) + font = qVariantValue<QFont>(helpEngine->customValue(QLatin1String("browserFont"))); + + viewer->setFont(font); + + connectSignals(); +} + +HelpViewer *CentralWidget::newEmptyTab() +{ + HelpViewer* viewer = new HelpViewer(helpEngine, this); + viewer->installEventFilter(this); + viewer->setFocus(Qt::OtherFocusReason); +#if !defined(USE_WEBKIT) + viewer->setDocumentTitle(tr("unknown")); +#endif + tabWidget->setCurrentIndex(tabWidget->addTab(viewer, tr("unknown"))); + + connectSignals(); + return viewer; +} + +void CentralWidget::connectSignals() +{ + const HelpViewer* viewer = currentHelpViewer(); + if (viewer) { + connect(viewer, SIGNAL(copyAvailable(bool)), this, SIGNAL(copyAvailable(bool))); + connect(viewer, SIGNAL(forwardAvailable(bool)), this, SIGNAL(forwardAvailable(bool))); + connect(viewer, SIGNAL(backwardAvailable(bool)), this, SIGNAL(backwardAvailable(bool))); + connect(viewer, SIGNAL(sourceChanged(const QUrl&)), this, SIGNAL(sourceChanged(const QUrl&))); + connect(viewer, SIGNAL(highlighted(const QString&)), this, SIGNAL(highlighted(const QString&))); + + connect(viewer, SIGNAL(sourceChanged(const QUrl&)), this, SLOT(setTabTitle(const QUrl&))); + } +} + +HelpViewer *CentralWidget::helpViewerAtIndex(int index) const +{ + return qobject_cast<HelpViewer*>(tabWidget->widget(index)); +} + +HelpViewer *CentralWidget::currentHelpViewer() const +{ + return qobject_cast<HelpViewer*>(tabWidget->currentWidget()); +} + +void CentralWidget::activateTab(bool onlyHelpViewer) +{ + if (currentHelpViewer()) { + currentHelpViewer()->setFocus(); + } else { + int idx = 0; + if (onlyHelpViewer) + idx = lastTabPage; + tabWidget->setCurrentIndex(idx); + tabWidget->currentWidget()->setFocus(); + } +} + +void CentralWidget::setTabTitle(const QUrl& url) +{ + int tab = lastTabPage; + HelpViewer* viewer = currentHelpViewer(); + +#if defined(USE_WEBKIT) + if (!viewer || viewer->source() != url) { + QTabBar *tabBar = qFindChild<QTabBar*>(tabWidget); + for (tab = 0; tab < tabBar->count(); ++tab) { + viewer = qobject_cast<HelpViewer*>(tabWidget->widget(tab)); + if (viewer && viewer->source() == url) + break; + } + } +#endif + + if (viewer) { + tabWidget->setTabText(tab, + quoteTabTitle(viewer->documentTitle().trimmed())); + } +} + +void CentralWidget::currentPageChanged(int index) +{ + const HelpViewer *viewer = currentHelpViewer(); + + if (viewer || tabWidget->count() == 1) + lastTabPage = index; + + bool enabled = false; + if (viewer) + enabled = tabWidget->count() > 1; + + tabWidget->setTabsClosable(enabled); + tabWidget->cornerWidget(Qt::TopLeftCorner)->setEnabled(true); + + emit currentViewerChanged(); +} + +void CentralWidget::showTabBarContextMenu(const QPoint &point) +{ + HelpViewer* viewer = helpViewerFromTabPosition(tabWidget, point); + if (!viewer) + return; + + QTabBar *tabBar = qFindChild<QTabBar*>(tabWidget); + + QMenu menu(QLatin1String(""), tabBar); + QAction *new_page = menu.addAction(tr("Add New Page")); + QAction *close_page = menu.addAction(tr("Close This Page")); + QAction *close_pages = menu.addAction(tr("Close Other Pages")); + menu.addSeparator(); + QAction *newBookmark = menu.addAction(tr("Add Bookmark for this Page...")); + + if (tabBar->count() == 1) { + close_page->setEnabled(false); + close_pages->setEnabled(false); + } + + QAction *picked_action = menu.exec(tabBar->mapToGlobal(point)); + if (!picked_action) + return; + + if (picked_action == new_page) + setSourceInNewTab(viewer->source()); + + if (picked_action == close_page) { + tabWidget->removeTab(tabWidget->indexOf(viewer)); + QTimer::singleShot(0, viewer, SLOT(deleteLater())); + } + + if (picked_action == close_pages) { + int currentPage = tabWidget->indexOf(viewer); + for (int i = tabBar->count() -1; i >= 0; --i) { + viewer = qobject_cast<HelpViewer*>(tabWidget->widget(i)); + if (i != currentPage && viewer) { + tabWidget->removeTab(i); + QTimer::singleShot(0, viewer, SLOT(deleteLater())); + + if (i < currentPage) + --currentPage; + } + } + } + + if (picked_action == newBookmark) + emit addNewBookmark(viewer->documentTitle(), viewer->source().toString()); +} + +// if we have a current help viewer then this is the 'focus proxy', otherwise +// it's the tab widget itself +// this is needed, so an embedding program can just set the focus to the central widget +// and it does TheRightThing +void CentralWidget::focusInEvent(QFocusEvent * /* event */) +{ + if (currentHelpViewer()) + QTimer::singleShot(1, currentHelpViewer(), SLOT(setFocus())); + else + QTimer::singleShot(1, tabWidget, SLOT(setFocus())); +} + +bool CentralWidget::eventFilter(QObject *object, QEvent *e) +{ + if (currentHelpViewer() == object && e->type() == QEvent::KeyPress){ + QKeyEvent *ke = static_cast<QKeyEvent*>(e); + if (ke->key() == Qt::Key_Backspace) { + HelpViewer *viewer = currentHelpViewer(); + if (viewer && viewer->isBackwardAvailable()) + viewer->backward(); + return true; + } + } + + QTabBar *tabBar = qobject_cast<QTabBar*>(object); + bool mousRel = e->type() == QEvent::MouseButtonRelease; + bool dblClick = e->type() == QEvent::MouseButtonDblClick; + + if (tabBar && (mousRel || dblClick)) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(e); + HelpViewer *viewer = helpViewerFromTabPosition(tabWidget, mouseEvent->pos()); + if (tabWidget->count() <= 1) + return QWidget::eventFilter(object, e); + + if (viewer && (mouseEvent->button() == Qt::MidButton || dblClick)) { + tabWidget->removeTab(tabWidget->indexOf(viewer)); + QTimer::singleShot(0, viewer, SLOT(deleteLater())); + currentPageChanged(tabWidget->currentIndex()); + return true; + } + } + return QWidget::eventFilter(object, e); +} + +void CentralWidget::updateBrowserFont() +{ + QFont font = qApp->font(); + if (helpEngine->customValue(QLatin1String("useBrowserFont")).toBool()) + font = qVariantValue<QFont>(helpEngine->customValue(QLatin1String("browserFont"))); + + QWidget* widget = 0; + for (int i = 0; i < tabWidget->count(); ++i) { + widget = tabWidget->widget(i); + if (widget->font() != font) + widget->setFont(font); + } +} + +bool CentralWidget::find(const QString &txt, QTextDocument::FindFlags findFlags, bool incremental) +{ + HelpViewer* viewer = currentHelpViewer(); + +#if defined(USE_WEBKIT) + Q_UNUSED(incremental); + if (viewer) { + QWebPage::FindFlags options = QWebPage::FindWrapsAroundDocument; + if (findFlags & QTextDocument::FindBackward) + options |= QWebPage::FindBackward; + if (findFlags & QTextDocument::FindCaseSensitively) + options |= QWebPage::FindCaseSensitively; + + return viewer->findText(txt, options); + } + return false; +#else + QTextCursor cursor; + QTextDocument *doc = 0; + QTextBrowser *browser = 0; + + if (viewer) { + doc = viewer->document(); + cursor = viewer->textCursor(); + browser = qobject_cast<QTextBrowser*>(viewer); + } + /* + if (tabWidget->currentWidget() == m_searchWidget) { + QTextBrowser* browser = qFindChild<QTextBrowser*>(m_searchWidget); + if (browser) { + doc = browser->document(); + cursor = browser->textCursor(); + } + } + */ + if (!browser || !doc || cursor.isNull()) + return false; + if (incremental) + cursor.setPosition(cursor.selectionStart()); + + QTextCursor found = doc->find(txt, cursor, findFlags); + if (found.isNull()) { + if ((findFlags&QTextDocument::FindBackward) == 0) + cursor.movePosition(QTextCursor::Start); + else + cursor.movePosition(QTextCursor::End); + found = doc->find(txt, cursor, findFlags); + if (found.isNull()) { + return false; + } + } + if (!found.isNull()) { + viewer->setTextCursor(found); + } + return true; +#endif +} + +void CentralWidget::showTopicChooser(const QMap<QString, QUrl> &links, + const QString &keyword) +{ + TopicChooser tc(this, keyword, links); + if (tc.exec() == QDialog::Accepted) + setSource(tc.link()); +} + +void CentralWidget::copy() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->copy(); +} + +QString CentralWidget::quoteTabTitle(const QString &title) const +{ + QString s = title; + return s.replace(QLatin1Char('&'), QLatin1String("&&")); +} diff --git a/src/plugins/help/centralwidget.h b/src/plugins/help/centralwidget.h new file mode 100644 index 00000000000..a75e999b07c --- /dev/null +++ b/src/plugins/help/centralwidget.h @@ -0,0 +1,157 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#ifndef CENTRALWIDGET_H +#define CENTRALWIDGET_H + +#include <QtCore/QUrl> +#include <QtCore/QPoint> +#include <QtCore/QObject> + +#include <QtGui/QWidget> +#include <QtGui/QTextDocument> + +QT_BEGIN_NAMESPACE + +class QEvent; +class QLabel; +class QAction; +class QCheckBox; +class QLineEdit; +class QToolButton; +class QTabWidget; +class QHelpEngine; +class QFocusEvent; +class CentralWidget; +class HelpViewer; + +QT_END_NAMESPACE + +namespace Help { +namespace Internal { + +class PrintHelper; + +} +} + +QT_BEGIN_NAMESPACE + +class CentralWidget : public QWidget +{ + Q_OBJECT + +public: + CentralWidget(QHelpEngine *engine, QWidget *parent = 0); + ~CentralWidget(); + + bool hasSelection() const; + QUrl currentSource() const; + QString currentTitle() const; + bool isHomeAvailable() const; + bool isForwardAvailable() const; + bool isBackwardAvailable() const; + QList<QAction*> globalActions() const; + void setGlobalActions(const QList<QAction*> &actions); + HelpViewer *currentHelpViewer() const; + void activateTab(bool onlyHelpViewer = false); + bool find(const QString &txt, QTextDocument::FindFlags findFlags, bool incremental); + void setLastShownPages(); + + static CentralWidget *instance(); + +public slots: + void zoomIn(); + void zoomOut(); + void nextPage(); + void resetZoom(); + void previousPage(); + void copySelection(); + void print(); + void pageSetup(); + void printPreview(); + void updateBrowserFont(); + void setSource(const QUrl &url); + void setSourceInNewTab(const QUrl &url); + HelpViewer *newEmptyTab(); + void home(); + void forward(); + void backward(); + void showTopicChooser(const QMap<QString, QUrl> &links, + const QString &keyword); + void copy(); + +protected: + void focusInEvent(QFocusEvent *event); + +signals: + void currentViewerChanged(); + void copyAvailable(bool yes); + void sourceChanged(const QUrl &url); + void highlighted(const QString &link); + void forwardAvailable(bool available); + void backwardAvailable(bool available); + void addNewBookmark(const QString &title, const QString &url); + +private slots: + void newTab(); + void closeTab(); + void closeTab(int index); + void setTabTitle(const QUrl& url); + void currentPageChanged(int index); + void showTabBarContextMenu(const QPoint &point); + void printPreview(QPrinter *printer); + +private: + void connectSignals(); + bool eventFilter(QObject *object, QEvent *e); + void initPrinter(); + HelpViewer *helpViewerAtIndex(int index) const; + QString quoteTabTitle(const QString &title) const; + +private: + int lastTabPage; + QString collectionFile; + QList<QAction*> globalActionList; + + QWidget *findBar; + QTabWidget* tabWidget; + QHelpEngine *helpEngine; + QPrinter *printer; +}; + +QT_END_NAMESPACE +//} // namespace Internal +//} // namespace Help + +#endif // CENTRALWIDGET_H diff --git a/src/plugins/help/contentstoolwindow.cpp b/src/plugins/help/contentstoolwindow.cpp new file mode 100644 index 00000000000..1af3aff3b15 --- /dev/null +++ b/src/plugins/help/contentstoolwindow.cpp @@ -0,0 +1,180 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtCore/QDebug> +#include <QtCore/QStack> +#include <QtGui/QFocusEvent> +#include <QtGui/QKeyEvent> + +#include "contentstoolwindow.h" +#include "helpengine.h" + +using namespace Help::Internal; + +ContentsToolWidget::ContentsToolWidget() +{ + wasInitialized = false; + + setRootIsDecorated(true); + setItemHidden(headerItem(), true); + setUniformRowHeights(true); + setColumnCount(1); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + setWindowTitle(tr("Contents")); + setWindowIcon(QIcon(":/help/images/book.png")); +} + +void ContentsToolWidget::focusInEvent(QFocusEvent *e) +{ + if (wasInitialized) { + if (e && e->reason() != Qt::MouseFocusReason + && !currentItem() && topLevelItemCount()) + setCurrentItem(topLevelItem(0)); + return; + } + wasInitialized = true; + setCursor(QCursor(Qt::WaitCursor)); + emit buildRequested(); +} + +void ContentsToolWidget::showEvent(QShowEvent *) +{ + if (wasInitialized) + return; + wasInitialized = true; + setCursor(QCursor(Qt::WaitCursor)); + emit buildRequested(); +} + +void ContentsToolWidget::keyPressEvent(QKeyEvent *e) +{ + if (e && e->key() == Qt::Key_Escape) { + emit escapePressed(); + e->accept(); + return; + } + QTreeWidget::keyPressEvent(e); +} + + +enum +{ + LinkRole = Qt::UserRole + 1000 +}; + +ContentsToolWindow::ContentsToolWindow(const QList<int> &context, HelpEngine *help) +{ + m_widget = new ContentsToolWidget; + helpEngine = help; + connect(helpEngine, SIGNAL(contentsInitialized()), this, SLOT(contentsDone())); + connect(m_widget, SIGNAL(buildRequested()), helpEngine, SLOT(buildContents())); + + m_context = context; + m_context << 0; + + connect(m_widget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), this, SLOT(indexRequested())); + connect(m_widget, SIGNAL(escapePressed()), this, SIGNAL(escapePressed())); +} + +ContentsToolWindow::~ContentsToolWindow() +{ + delete m_widget; +} + +const QList<int> &ContentsToolWindow::context() const +{ + return m_context; +} + +QWidget *ContentsToolWindow::widget() +{ + return m_widget; +} + +void ContentsToolWindow::contentsDone() +{ + m_widget->setCursor(QCursor(Qt::WaitCursor)); + QList<QPair<QString, ContentList> > contentList = helpEngine->contents(); + for(QList<QPair<QString, ContentList> >::Iterator it = contentList.begin(); it != contentList.end(); ++it) { + QTreeWidgetItem *newEntry; + QTreeWidgetItem *contentEntry; + QStack<QTreeWidgetItem*> stack; + stack.clear(); + int depth = 0; + bool root = false; + + QTreeWidgetItem *lastItem[64]; + for(int j = 0; j < 64; ++j) + lastItem[j] = 0; + + ContentList lst = (*it).second; + for (ContentList::ConstIterator it = lst.begin(); it != lst.end(); ++it) { + ContentItem item = *it; + if (item.depth == 0) { + newEntry = new QTreeWidgetItem(m_widget, 0); + newEntry->setIcon(0, QIcon(QString::fromUtf8(":/help/images/book.png"))); + newEntry->setText(0, item.title); + newEntry->setData(0, LinkRole, item.reference); + stack.push(newEntry); + depth = 1; + root = true; + } + else{ + if((item.depth > depth) && root) { + depth = item.depth; + stack.push(contentEntry); + } + if(item.depth == depth) { + contentEntry = new QTreeWidgetItem(stack.top(), lastItem[ depth ]); + lastItem[ depth ] = contentEntry; + contentEntry->setText(0, item.title); + contentEntry->setData(0, LinkRole, item.reference); + } + else if(item.depth < depth) { + stack.pop(); + depth--; + item = *(--it); + } + } + } + } + m_widget->setCursor(QCursor(Qt::ArrowCursor)); +} + +void ContentsToolWindow::indexRequested() +{ + QTreeWidgetItem *itm = m_widget->currentItem(); + if (!itm) + return; + emit showLinkRequested(itm->data(0, LinkRole).toString(), false); +} diff --git a/src/plugins/help/contentstoolwindow.h b/src/plugins/help/contentstoolwindow.h new file mode 100644 index 00000000000..51163c1ea1e --- /dev/null +++ b/src/plugins/help/contentstoolwindow.h @@ -0,0 +1,102 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CONTENTSTOOLWINDOW_H +#define CONTENTSTOOLWINDOW_H + +#include <QtGui/QTreeWidget> + +#include <coreplugin/iview.h> + +namespace Help { +namespace Internal { + +class HelpEngine; +class ContentsToolWindow; + +class ContentsToolWidget : public QTreeWidget +{ + Q_OBJECT +public: + ContentsToolWidget(); + +signals: + void buildRequested(); + void escapePressed(); + +private: + friend class ContentsToolWindow; + void showEvent(QShowEvent *e); + void focusInEvent(QFocusEvent *e); + void keyPressEvent(QKeyEvent *e); + + bool wasInitialized; +}; + +class ContentsToolWindow : public Core::IView +{ + Q_OBJECT + +public: + ContentsToolWindow(const QList<int> &context, HelpEngine *help); + ~ContentsToolWindow(); + + const QList<int> &context() const; + QWidget *widget(); + + QList<QWidget*> dockToolBarWidgets() const { return QList<QWidget*>(); } + + const char *uniqueViewName() const { return "Help.ContentsToolWindow"; } + const char *globalMenuGroup() const { return "Help.Group"; } + inline QKeySequence defaultShortcut() const { return QKeySequence(); } + Qt::DockWidgetArea defaultArea() const { return Qt::RightDockWidgetArea; } + IView::ViewPosition defaultPosition() const { return IView::First; } + +signals: + void showLinkRequested(const QString &link, bool newWindow); + void escapePressed(); + +private slots: + void contentsDone(); + void indexRequested(); + +private: + HelpEngine *helpEngine; + + QList<int> m_context; + ContentsToolWidget *m_widget; +}; + +} //namespace Internal +} //namespace Help + +#endif diff --git a/src/plugins/help/docsettingspage.cpp b/src/plugins/help/docsettingspage.cpp new file mode 100644 index 00000000000..763d134c236 --- /dev/null +++ b/src/plugins/help/docsettingspage.cpp @@ -0,0 +1,143 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "docsettingspage.h" + +#include <QtGui/QFileDialog> +#include <QtGui/QMessageBox> +#include <QtHelp/QHelpEngine> + +using namespace Help::Internal; + +DocSettingsPage::DocSettingsPage(QHelpEngine *helpEngine) + : m_helpEngine(helpEngine), + m_registeredDocs(false) +{ + +} + +QString DocSettingsPage::name() const +{ + return "Documentation"; +} + +QString DocSettingsPage::category() const +{ + return "Help"; +} + +QString DocSettingsPage::trCategory() const +{ + return tr("Help"); +} + +QWidget *DocSettingsPage::createPage(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + m_ui.setupUi(w); + + connect(m_ui.addButton, SIGNAL(clicked()), + this, SLOT(addDocumentation())); + connect(m_ui.removeButton, SIGNAL(clicked()), + this, SLOT(removeDocumentation())); + + m_ui.docsListWidget->addItems(m_helpEngine->registeredDocumentations()); + m_registeredDocs = false; + m_removeDocs.clear(); + + return w; +} + +void DocSettingsPage::addDocumentation() +{ + QStringList files = QFileDialog::getOpenFileNames(m_ui.addButton->parentWidget(), + tr("Add Documentation"), + QString(), tr("Qt Help Files (*.qch)")); + + if (files.isEmpty()) + return; + + foreach (const QString &file, files) { + QString nsName = QHelpEngineCore::namespaceName(file); + if (nsName.isEmpty()) { + QMessageBox::warning(m_ui.addButton->parentWidget(), + tr("Add Documentation"), + tr("The file %1 is not a valid Qt Help file!") + .arg(file)); + continue; + } + m_helpEngine->registerDocumentation(file); + m_ui.docsListWidget->addItem(nsName); + } + m_registeredDocs = true; + emit documentationAdded(); +} + +void DocSettingsPage::removeDocumentation() +{ + QListWidgetItem *item = m_ui.docsListWidget->currentItem(); + if (!item) + return; + + m_removeDocs.append(item->text()); + int row = m_ui.docsListWidget->currentRow(); + m_ui.docsListWidget->takeItem(row); + if (row > 0) + --row; + if (m_ui.docsListWidget->count()) + m_ui.docsListWidget->setCurrentRow(row); + + delete item; +} + +void DocSettingsPage::finished(bool accepted) +{ + if (!accepted) + return; + + emit dialogAccepted(); +} + +bool DocSettingsPage::applyChanges() +{ + QStringList::const_iterator it = m_removeDocs.constBegin(); + while (it != m_removeDocs.constEnd()) { + if (!m_helpEngine->unregisterDocumentation((*it))) { + QMessageBox::warning(m_ui.addButton->parentWidget(), + tr("Documentation"), + tr("Cannot unregister documentation file %1!") + .arg((*it))); + } + ++it; + } + return m_registeredDocs || m_removeDocs.count(); +} diff --git a/src/plugins/help/docsettingspage.h b/src/plugins/help/docsettingspage.h new file mode 100644 index 00000000000..efafb940348 --- /dev/null +++ b/src/plugins/help/docsettingspage.h @@ -0,0 +1,81 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#ifndef DOCSETTINGSPAGE_H +#define DOCSETTINGSPAGE_H + +#include <QtGui/QWidget> +#include <coreplugin/dialogs/ioptionspage.h> + +#include "ui_docsettingspage.h" + +QT_FORWARD_DECLARE_CLASS(QHelpEngine) + +namespace Help { +namespace Internal { + +class DocSettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + DocSettingsPage(QHelpEngine *helpEngine); + + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + + bool applyChanges(); + +signals: + void documentationAdded(); + void dialogAccepted(); + +private slots: + void addDocumentation(); + void removeDocumentation(); + +private: + QHelpEngine *m_helpEngine; + bool m_registeredDocs; + QStringList m_removeDocs; + Ui::DocSettingsPage m_ui; +}; + +} // namespace Help +} // namespace Internal + +#endif // DOCSETTINGSPAGE_H diff --git a/src/plugins/help/docsettingspage.ui b/src/plugins/help/docsettingspage.ui new file mode 100644 index 00000000000..311be060c76 --- /dev/null +++ b/src/plugins/help/docsettingspage.ui @@ -0,0 +1,77 @@ +<ui version="4.0" > + <class>DocSettingsPage</class> + <widget class="QWidget" name="DocSettingsPage" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>364</width> + <height>240</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout" > + <item> + <widget class="QLabel" name="label" > + <property name="text" > + <string>Registered Documentation:</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="_3" > + <property name="spacing" > + <number>6</number> + </property> + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QListWidget" name="docsListWidget" /> + </item> + <item> + <layout class="QVBoxLayout" name="_4" > + <property name="spacing" > + <number>6</number> + </property> + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="addButton" > + <property name="text" > + <string>Add...</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="removeButton" > + <property name="text" > + <string>Remove</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> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/help/filtersettingspage.cpp b/src/plugins/help/filtersettingspage.cpp new file mode 100644 index 00000000000..6087a94084e --- /dev/null +++ b/src/plugins/help/filtersettingspage.cpp @@ -0,0 +1,220 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "filtersettingspage.h" +#include "filternamedialog.h" + +#include <QtGui/QFileDialog> +#include <QtGui/QMessageBox> +#include <QtHelp/QHelpEngine> + +using namespace Help::Internal; + +FilterSettingsPage::FilterSettingsPage(QHelpEngine *helpEngine) +{ + m_helpEngine = helpEngine; +} + +QString FilterSettingsPage::name() const +{ + return "Filters"; +} + +QString FilterSettingsPage::category() const +{ + return "Help"; +} + +QString FilterSettingsPage::trCategory() const +{ + return tr("Help"); +} + +QWidget *FilterSettingsPage::createPage(QWidget *parent) +{ + m_currentPage = new QWidget(parent); + m_ui.setupUi(m_currentPage); + m_ui.attributeWidget->header()->hide(); + m_ui.attributeWidget->setRootIsDecorated(false); + + connect(m_ui.attributeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), + this, SLOT(updateFilterMap())); + connect(m_ui.filterWidget, + SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), + this, SLOT(updateAttributes(QListWidgetItem*))); + connect(m_ui.filterAddButton, SIGNAL(clicked()), + this, SLOT(addFilter())); + connect(m_ui.filterRemoveButton, SIGNAL(clicked()), + this, SLOT(removeFilter())); + updateFilterPage(); + + return m_currentPage; +} + +void FilterSettingsPage::updateFilterPage() +{ + if (!m_helpEngine) + return; + + m_ui.filterWidget->clear(); + m_ui.attributeWidget->clear(); + + QHelpEngineCore help(m_helpEngine->collectionFile(), 0); + help.setupData(); + m_filterMapBackup.clear(); + const QStringList filters = help.customFilters(); + foreach (const QString filter, filters) { + QStringList atts = help.filterAttributes(filter); + m_filterMapBackup.insert(filter, atts); + if (!m_filterMap.contains(filter)) + m_filterMap.insert(filter, atts); + } + + m_ui.filterWidget->addItems(m_filterMap.keys()); + + foreach (const QString a, help.filterAttributes()) + new QTreeWidgetItem(m_ui.attributeWidget, QStringList() << a); + + if (m_filterMap.keys().count()) + m_ui.filterWidget->setCurrentRow(0); +} + +void FilterSettingsPage::updateAttributes(QListWidgetItem *item) +{ + QStringList checkedList; + if (item) + checkedList = m_filterMap.value(item->text()); + QTreeWidgetItem *itm; + for (int i=0; i<m_ui.attributeWidget->topLevelItemCount(); ++i) { + itm = m_ui.attributeWidget->topLevelItem(i); + if (checkedList.contains(itm->text(0))) + itm->setCheckState(0, Qt::Checked); + else + itm->setCheckState(0, Qt::Unchecked); + } +} + +void FilterSettingsPage::updateFilterMap() +{ + if (!m_ui.filterWidget->currentItem()) + return; + QString filter = m_ui.filterWidget->currentItem()->text(); + if (!m_filterMap.contains(filter)) + return; + + QStringList newAtts; + QTreeWidgetItem *itm = 0; + for (int i=0; i<m_ui.attributeWidget->topLevelItemCount(); ++i) { + itm = m_ui.attributeWidget->topLevelItem(i); + if (itm->checkState(0) == Qt::Checked) + newAtts.append(itm->text(0)); + } + m_filterMap[filter] = newAtts; +} + +void FilterSettingsPage::addFilter() +{ + FilterNameDialog dia(m_currentPage); + if (dia.exec() == QDialog::Rejected) + return; + + QString filterName = dia.filterName(); + if (!m_filterMap.contains(filterName)) { + m_filterMap.insert(filterName, QStringList()); + m_ui.filterWidget->addItem(filterName); + } + + QList<QListWidgetItem*> lst = m_ui.filterWidget + ->findItems(filterName, Qt::MatchCaseSensitive); + m_ui.filterWidget->setCurrentItem(lst.first()); +} + +void FilterSettingsPage::removeFilter() +{ + QListWidgetItem *item = m_ui.filterWidget + ->takeItem(m_ui.filterWidget->currentRow()); + if (!item) + return; + + m_filterMap.remove(item->text()); + m_removedFilters.append(item->text()); + delete item; + if (m_ui.filterWidget->count()) + m_ui.filterWidget->setCurrentRow(0); +} + +void FilterSettingsPage::finished(bool) +{ +} + +bool FilterSettingsPage::applyChanges() +{ + bool changed = false; + if (m_filterMap.count() != m_filterMapBackup.count()) { + changed = true; + } else { + QMapIterator<QString, QStringList> it(m_filterMapBackup); + while (it.hasNext() && !changed) { + it.next(); + if (!m_filterMap.contains(it.key())) { + changed = true; + } else { + QStringList a = it.value(); + QStringList b = m_filterMap.value(it.key()); + if (a.count() != b.count()) { + changed = true; + } else { + QStringList::const_iterator i(a.constBegin()); + while (i != a.constEnd()) { + if (!b.contains(*i)) { + changed = true; + break; + } + ++i; + } + } + } + } + } + if (changed) { + foreach (QString filter, m_removedFilters) + m_helpEngine->removeCustomFilter(filter); + QMapIterator<QString, QStringList> it(m_filterMap); + while (it.hasNext()) { + it.next(); + m_helpEngine->addCustomFilter(it.key(), it.value()); + } + return true; + } + return false; +} diff --git a/src/plugins/help/filtersettingspage.h b/src/plugins/help/filtersettingspage.h new file mode 100644 index 00000000000..5cf83619a21 --- /dev/null +++ b/src/plugins/help/filtersettingspage.h @@ -0,0 +1,82 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#ifndef FILTERSETTINGSPAGE_H +#define FILTERSETTINGSPAGE_H + +#include <QtGui/QWidget> +#include <coreplugin/dialogs/ioptionspage.h> + +#include "ui_filtersettingspage.h" + +QT_FORWARD_DECLARE_CLASS(QHelpEngine) + +namespace Help { +namespace Internal { + +class FilterSettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + FilterSettingsPage(QHelpEngine *helpEngine); + + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + + bool applyChanges(); + +private slots: + void updateAttributes(QListWidgetItem *item); + void updateFilterMap(); + void updateFilterPage(); + void addFilter(); + void removeFilter(); + +private: + QHelpEngine *m_helpEngine; + Ui::FilterSettingsPage m_ui; + QMap<QString, QStringList> m_filterMapBackup; + QMap<QString, QStringList> m_filterMap; + QStringList m_removedFilters; + QWidget *m_currentPage; +}; + +} // namespace Help +} // namespace Internal + +#endif // DOCSETTINGSPAGE_H diff --git a/src/plugins/help/filtersettingspage.ui b/src/plugins/help/filtersettingspage.ui new file mode 100644 index 00000000000..367e7ce0a9e --- /dev/null +++ b/src/plugins/help/filtersettingspage.ui @@ -0,0 +1,72 @@ +<ui version="4.0" > + <class>FilterSettingsPage</class> + <widget class="QWidget" name="FilterSettingsPage" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout" > + <item row="0" column="0" colspan="2" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Filter:</string> + </property> + </widget> + </item> + <item row="0" column="2" > + <widget class="QLabel" name="label_2" > + <property name="frameShape" > + <enum>QFrame::NoFrame</enum> + </property> + <property name="text" > + <string>Attributes:</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2" > + <widget class="QListWidget" name="filterWidget" /> + </item> + <item rowspan="2" row="1" column="2" > + <widget class="QTreeWidget" name="attributeWidget" > + <property name="showDropIndicator" stdset="0" > + <bool>false</bool> + </property> + <property name="rootIsDecorated" > + <bool>false</bool> + </property> + <property name="uniformRowHeights" > + <bool>true</bool> + </property> + <column> + <property name="text" > + <string>1</string> + </property> + </column> + </widget> + </item> + <item row="2" column="0" > + <widget class="QPushButton" name="filterAddButton" > + <property name="text" > + <string>Add</string> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QPushButton" name="filterRemoveButton" > + <property name="text" > + <string>Remove</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/help/help.pri b/src/plugins/help/help.pri new file mode 100644 index 00000000000..cea3ed71f9d --- /dev/null +++ b/src/plugins/help/help.pri @@ -0,0 +1,3 @@ +include(help_dependencies.pri) + +LIBS *= -l$$qtLibraryTarget(Help) diff --git a/src/plugins/help/help.pro b/src/plugins/help/help.pro new file mode 100644 index 00000000000..a7de200f743 --- /dev/null +++ b/src/plugins/help/help.pro @@ -0,0 +1,36 @@ +TEMPLATE = lib +TARGET = Help +include(../../qworkbenchplugin.pri) +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/find/find.pri) +include(../../plugins/quickopen/quickopen.pri) +QT += network +CONFIG += help +DEFINES += QT_CLUCENE_SUPPORT \ + HELP_LIBRARY +HEADERS += helpplugin.h \ + docsettingspage.h \ + filtersettingspage.h \ + helpmode.h \ + centralwidget.h \ + searchwidget.h \ + helpfindsupport.h \ + help_global.h \ + helpindexfilter.h \ + indexwindow.h +SOURCES += helpplugin.cpp \ + docsettingspage.cpp \ + filtersettingspage.cpp \ + helpmode.cpp \ + centralwidget.cpp \ + searchwidget.cpp \ + helpfindsupport.cpp \ + helpindexfilter.cpp +FORMS += docsettingspage.ui \ + filtersettingspage.ui +RESOURCES += help.qrc +include(../../../shared/help/help.pri) +contains(QT_CONFIG, webkit) { + DEFINES += USE_WEBKIT + QT += webkit +} diff --git a/src/plugins/help/help.qrc b/src/plugins/help/help.qrc new file mode 100644 index 00000000000..0b529633f80 --- /dev/null +++ b/src/plugins/help/help.qrc @@ -0,0 +1,16 @@ +<RCC> + <qresource prefix="/help" > + <file>images/find.png</file> + <file>images/book.png</file> + <file>images/previous.png</file> + <file>images/next.png</file> + <file>images/home.png</file> + <file>images/bookmark.png</file> + </qresource> + <qresource prefix="/trolltech/assistant" > + <file>images/mac/addtab.png</file> + <file>images/mac/closetab.png</file> + <file>images/win/addtab.png</file> + <file>images/win/closetab.png</file> + </qresource> +</RCC> diff --git a/src/plugins/help/help_global.h b/src/plugins/help/help_global.h new file mode 100644 index 00000000000..104511ac880 --- /dev/null +++ b/src/plugins/help/help_global.h @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/**************************************************************************** +** +** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef HELP_GLOBAL_H +#define HELP_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(HELP_LIBRARY) +# define HELP_EXPORT Q_DECL_EXPORT +#else +# define HELP_EXPORT Q_DECL_IMPORT +#endif + +#endif // HELP_GLOBAL_H diff --git a/src/plugins/help/helpengine.cpp b/src/plugins/help/helpengine.cpp new file mode 100644 index 00000000000..790e4bf54ad --- /dev/null +++ b/src/plugins/help/helpengine.cpp @@ -0,0 +1,606 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QDateTime> +#include <QtCore/QCoreApplication> + +#include "helpengine.h" +#include "config.h" + +using namespace Help::Internal; + +static bool verifyDirectory(const QString &str) +{ + QFileInfo dirInfo(str); + if (!dirInfo.exists()) + return QDir().mkdir(str); + if (!dirInfo.isDir()) { + qWarning("'%s' exists but is not a directory", str.toLatin1().constData()); + return false; + } + return true; +} + +struct IndexKeyword { + IndexKeyword(const QString &kw, const QString &l) + : keyword(kw), link(l) {} + IndexKeyword() : keyword(QString()), link(QString()) {} + bool operator<(const IndexKeyword &ik) const { + return keyword.toLower() < ik.keyword.toLower(); + } + bool operator<=(const IndexKeyword &ik) const { + return keyword.toLower() <= ik.keyword.toLower(); + } + bool operator>(const IndexKeyword &ik) const { + return keyword.toLower() > ik.keyword.toLower(); + } + Q_DUMMY_COMPARISON_OPERATOR(IndexKeyword) + QString keyword; + QString link; +}; + +QDataStream &operator>>(QDataStream &s, IndexKeyword &ik) +{ + s >> ik.keyword; + s >> ik.link; + return s; +} + +QDataStream &operator<<(QDataStream &s, const IndexKeyword &ik) +{ + s << ik.keyword; + s << ik.link; + return s; +} + + +/** + * Compare in a human-preferred alphanumeric way, + * e.g. 'Qt tutorial 2' will be less than 'Qt tutorial 11'. + */ +bool caseInsensitiveLessThan(const QString &as, const QString &bs) +{ + const QChar *a = as.unicode(); + const QChar *b = bs.unicode(); + int result = 0; + while (result == 0) + { + ushort aa = a->unicode(); + ushort bb = b->unicode(); + + if (aa == 0 || bb == 0) { + result = aa - bb; + break; + } + else if (a->isDigit() && b->isDigit()) + { + const QChar *a_begin = a; + const QChar *b_begin = b; + bool loop = true; + do { + if (a->isDigit()) ++a; + else if (b->isDigit()) ++b; + else loop = false; + } while (loop); + + // optimization: comparing the length of the two numbers is more efficient than constructing two qstrings. + result = (a - a_begin) - (b - b_begin); + if (result == 0) { + QString astr(a_begin, a - a_begin); + QString bstr(b_begin, b - b_begin); + long la = astr.toLong(); + long lb = bstr.toLong(); + result = la - lb; + } + } else { + aa = QChar(aa).toLower().unicode(); + bb = QChar(bb).toLower().unicode(); + result = aa - bb; + ++a; + ++b; + } + } + + return result < 0 ? true : false; +} + +/** + * \a real is kinda a hack for the smart search, need a way to match a regexp to an item + * How would you say the best match for Q.*Wiget is QWidget? + */ +QModelIndex IndexListModel::filter(const QString &s, const QString &real) +{ + QStringList list; + + int goodMatch = -1; + int perfectMatch = -1; + if (s.isEmpty()) + perfectMatch = 0; + + const QRegExp regExp(s); + QMultiMap<QString, QString>::iterator it = contents.begin(); + QString lastKey; + for (; it != contents.end(); ++it) { + if (it.key() == lastKey) + continue; + lastKey = it.key(); + const QString key = it.key(); + if (key.contains(regExp) || key.contains(s, Qt::CaseInsensitive)) { + list.append(key); + //qDebug() << regExp << regExp.indexIn(s) << s << key << regExp.matchedLength(); + if (perfectMatch == -1 && (key.startsWith(real, Qt::CaseInsensitive))) { + if (goodMatch == -1) + goodMatch = list.count() - 1; + if (s.length() == key.length()) + perfectMatch = list.count() - 1; + } else if (perfectMatch > -1 && s == key) { + perfectMatch = list.count() - 1; + } + } + } + + int bestMatch = perfectMatch; + if (bestMatch == -1) + bestMatch = goodMatch; + + bestMatch = qMax(0, bestMatch); + + // sort the new list + QString match; + if (bestMatch >= 0 && list.count() > bestMatch) + match = list[bestMatch]; + qSort(list.begin(), list.end(), caseInsensitiveLessThan); + setStringList(list); + for (int i = 0; i < list.size(); ++i) { + if (list.at(i) == match){ + bestMatch = i; + break; + } + } + return index(bestMatch, 0, QModelIndex()); +} + + + + + + + + + +HelpEngine::HelpEngine(QObject *parent, const QString &defaultQtVersionPath) + : QObject(parent) +{ + titleMapThread = new TitleMapThread(this); + connect(titleMapThread, SIGNAL(errorOccured(const QString&)), + this, SIGNAL(errorOccured(const QString&))); + connect(titleMapThread, SIGNAL(finished()), this, SLOT(titleMapFinished())); + indexThread = new IndexThread(this); + connect(indexThread, SIGNAL(errorOccured(const QString&)), + this, SIGNAL(errorOccured(const QString&))); + connect(indexThread, SIGNAL(finished()), this, SLOT(indexFinished())); + + indexModel = new IndexListModel(this); + + Config::loadConfig(defaultQtVersionPath); + cacheFilesPath = QDir::homePath() + QLatin1String("/.assistant"); +} + +HelpEngine::~HelpEngine() +{ + Config::configuration()->save(); +} + +void HelpEngine::init() +{ +} + +QString HelpEngine::cacheFilePath() const +{ + return cacheFilesPath; +} + +IndexListModel *HelpEngine::indices() +{ + return indexModel; +} + +void HelpEngine::buildContents() +{ + contentsOnly = true; + if (!titleMapThread->isRunning()) { + titleMapThread->start(QThread::NormalPriority); + } +} + +void HelpEngine::buildIndex() +{ + if (!titleMapThread->isRunning()) { + contentsOnly = false; + titleMapThread->start(QThread::NormalPriority); + } + if (!indexThread->isRunning()) + indexThread->start(QThread::NormalPriority); +} + +void HelpEngine::titleMapFinished() +{ + contentList = titleMapThread->contents(); + titleMap = titleMapThread->documentTitleMap(); + if (contentsOnly) { + contentsOnly = false; + emit contentsInitialized(); + } +} + +void HelpEngine::indexFinished() +{ + indexModel = indexThread->model(); + emit indexInitialized(); +} + +void HelpEngine::removeOldCacheFiles(bool onlyFulltextSearchIndex) +{ + if (!verifyDirectory(cacheFilesPath)) { + qWarning("Failed to created assistant directory"); + return; + } + QString pname = QLatin1String(".") + Config::configuration()->profileName(); + + QStringList fileList; + fileList << QLatin1String("indexdb40.dict") + << QLatin1String("indexdb40.doc"); + + if (!onlyFulltextSearchIndex) + fileList << QLatin1String("indexdb40") << QLatin1String("contentdb40"); + + QStringList::iterator it = fileList.begin(); + for (; it != fileList.end(); ++it) { + if (QFile::exists(cacheFilesPath + QDir::separator() + *it + pname)) { + QFile f(cacheFilesPath + QDir::separator() + *it + pname); + f.remove(); + } + } +} + +quint32 HelpEngine::getFileAges() +{ + QStringList addDocuFiles = Config::configuration()->docFiles(); + QStringList::const_iterator i = addDocuFiles.begin(); + + quint32 fileAges = 0; + for(; i != addDocuFiles.end(); ++i) { + QFileInfo fi(*i); + if (fi.exists()) + fileAges += fi.lastModified().toTime_t(); + } + + return fileAges; +} + +QString HelpEngine::removeAnchorFromLink(const QString &link) +{ + int i = link.length(); + int j = link.lastIndexOf('/'); + int l = link.lastIndexOf(QDir::separator()); + if (l > j) + j = l; + if (j > -1) { + QString fileName = link.mid(j+1); + int k = fileName.lastIndexOf('#'); + if (k > -1) + i = j + k + 1; + } + return link.left(i); +} + +QString HelpEngine::titleOfLink(const QString &link) +{ + QString s = HelpEngine::removeAnchorFromLink(link); + s = titleMap[s]; + if (s.isEmpty()) + return link; + return s; +} + +QString HelpEngine::home() const +{ + QString link = Config::configuration()->homePage(); + if (!link.startsWith(QLatin1String("file:"))) + link.prepend("file:"); + return link; +} + + + + + + + + + + +TitleMapThread::TitleMapThread(HelpEngine *he) + : QThread(he) +{ + engine = he; + done = false; +} + +TitleMapThread::~TitleMapThread() +{ + +} + +void TitleMapThread::run() +{ + if (done) { + engine->mutex.lock(); + engine->titleMapDoneCondition.wakeAll(); + engine->mutex.unlock(); + return; + } + + bool needRebuild = false; + if (Config::configuration()->profileName() == QLatin1String("default")) { + const QStringList docuFiles = Config::configuration()->docFiles(); + for(QStringList::ConstIterator it = docuFiles.begin(); it != docuFiles.end(); it++) { + if (!QFile::exists(*it)) { + Config::configuration()->saveProfile(Profile::createDefaultProfile()); + Config::configuration()->loadDefaultProfile(); + needRebuild = true; + break; + } + } + } + + if (Config::configuration()->docRebuild() || needRebuild) { + engine->removeOldCacheFiles(); + Config::configuration()->setDocRebuild(false); + Config::configuration()->save(); + } + if (contentList.isEmpty()) + getAllContents(); + + titleMap.clear(); + for(QList<QPair<QString, ContentList> >::Iterator it = contentList.begin(); it != contentList.end(); ++it) { + ContentList lst = (*it).second; + foreach (ContentItem item, lst) { + titleMap[item.reference] = item.title.trimmed(); + } + } + done = true; + engine->mutex.lock(); + engine->titleMapDoneCondition.wakeAll(); + engine->mutex.unlock(); +} + +void TitleMapThread::getAllContents() +{ + QFile contentFile(engine->cacheFilePath() + QDir::separator() + QLatin1String("contentdb40.") + + Config::configuration()->profileName()); + contentList.clear(); + if (!contentFile.open(QFile::ReadOnly)) { + buildContentDict(); + return; + } + + QDataStream ds(&contentFile); + quint32 fileAges; + ds >> fileAges; + if (fileAges != engine->getFileAges()) { + contentFile.close(); + engine->removeOldCacheFiles(true); + buildContentDict(); + return; + } + QString key; + QList<ContentItem> lst; + while (!ds.atEnd()) { + ds >> key; + ds >> lst; + contentList += qMakePair(key, QList<ContentItem>(lst)); + } + contentFile.close(); + +} + +void TitleMapThread::buildContentDict() +{ + QStringList docuFiles = Config::configuration()->docFiles(); + + quint32 fileAges = 0; + for(QStringList::iterator it = docuFiles.begin(); it != docuFiles.end(); it++) { + QFile file(*it); + if (!file.exists()) { +#ifdef _SHOW_ERRORS_ + emit errorOccured(tr("Documentation file %1 does not exist!\n" + "Skipping file.").arg(QFileInfo(file).absoluteFilePath())); +#endif + continue; + } + fileAges += QFileInfo(file).lastModified().toTime_t(); + DocuParser *handler = DocuParser::createParser(*it); + if(!handler) { +#ifdef _SHOW_ERRORS_ + emit errorOccured(tr("Documentation file %1 is not compatible!\n" + "Skipping file.").arg(QFileInfo(file).absoluteFilePath())); +#endif + continue; + } + bool ok = handler->parse(&file); + file.close(); + if(ok) { + contentList += qMakePair(*it, QList<ContentItem>(handler->getContentItems())); + delete handler; + } else { +#ifdef _SHOW_ERRORS_ + QString msg = QString::fromLatin1("In file %1:\n%2") + .arg(QFileInfo(file).absoluteFilePath()) + .arg(handler->errorProtocol()); + emit errorOccured(msg); +#endif + continue; + } + } + + QFile contentOut(engine->cacheFilePath() + QDir::separator() + QLatin1String("contentdb40.") + + Config::configuration()->profileName()); + if (contentOut.open(QFile::WriteOnly)) { + QDataStream s(&contentOut); + s << fileAges; + for(QList<QPair<QString, ContentList> >::Iterator it = contentList.begin(); it != contentList.end(); ++it) { + s << *it; + } + contentOut.close(); + } +} + + + + + +IndexThread::IndexThread(HelpEngine *he) + : QThread(he) +{ + engine = he; + indexModel = new IndexListModel(this); + indexDone = false; +} + +void IndexThread::run() +{ + if (indexDone) + return; + engine->mutex.lock(); + if (engine->titleMapThread->isRunning()) + engine->titleMapDoneCondition.wait(&(engine->mutex)); + engine->mutex.unlock(); + + keywordDocuments.clear(); + QList<IndexKeyword> lst; + QFile indexFile(engine->cacheFilePath() + QDir::separator() + QLatin1String("indexdb40.") + + Config::configuration()->profileName()); + if (!indexFile.open(QFile::ReadOnly)) { + buildKeywordDB(); + if (!indexFile.open(QFile::ReadOnly)) { +#ifdef _SHOW_ERRORS_ + emit errorOccured(tr("Failed to load keyword index file!")); +#endif + return; + } + } + + QDataStream ds(&indexFile); + quint32 fileAges; + ds >> fileAges; + if (fileAges != engine->getFileAges()) { + indexFile.close(); + buildKeywordDB(); + if (!indexFile.open(QFile::ReadOnly)) { +#ifdef _SHOW_ERRORS_ + emit errorOccured(tr("Cannot open the index file %1") + .arg(QFileInfo(indexFile).absoluteFilePath())); +#endif + return; + } + ds.setDevice(&indexFile); + ds >> fileAges; + } + ds >> lst; + indexFile.close(); + + for (int i=0; i<lst.count(); ++i) { + const IndexKeyword &idx = lst.at(i); + indexModel->addLink(idx.keyword, idx.link); + keywordDocuments << HelpEngine::removeAnchorFromLink(idx.link); + } + indexModel->publish(); + indexDone = true; +} + +void IndexThread::buildKeywordDB() +{ + QStringList addDocuFiles = Config::configuration()->docFiles(); + QStringList::iterator i = addDocuFiles.begin(); + + int steps = 0; + for(; i != addDocuFiles.end(); i++) + steps += QFileInfo(*i).size(); + + QList<IndexKeyword> lst; + quint32 fileAges = 0; + for(i = addDocuFiles.begin(); i != addDocuFiles.end(); i++){ + QFile file(*i); + if (!file.exists()) { +#ifdef _SHOW_ERRORS_ + emit errorOccured(tr("Documentation file %1 does not exist!\n" + "Skipping file.").arg(QFileInfo(file).absoluteFilePath())); +#endif + continue; + } + fileAges += QFileInfo(file).lastModified().toTime_t(); + DocuParser *handler = DocuParser::createParser(*i); + bool ok = handler->parse(&file); + file.close(); + if(!ok){ +#ifdef _SHOW_ERRORS_ + QString msg = QString::fromLatin1("In file %1:\n%2") + .arg(QFileInfo(file).absoluteFilePath()) + .arg(handler->errorProtocol()); + emit errorOccured(msg); +#endif + delete handler; + continue; + } + + QList<IndexItem*> indLst = handler->getIndexItems(); + foreach (IndexItem *indItem, indLst) { + QFileInfo fi(indItem->reference); + lst.append(IndexKeyword(indItem->keyword, indItem->reference)); + } + delete handler; + } + if (!lst.isEmpty()) + qSort(lst); + + QFile indexout(engine->cacheFilePath() + QDir::separator() + QLatin1String("indexdb40.") + + Config::configuration()->profileName()); + if (verifyDirectory(engine->cacheFilePath()) && indexout.open(QFile::WriteOnly)) { + QDataStream s(&indexout); + s << fileAges; + s << lst; + indexout.close(); + } +} diff --git a/src/plugins/help/helpengine.h b/src/plugins/help/helpengine.h new file mode 100644 index 00000000000..9aa79f4e528 --- /dev/null +++ b/src/plugins/help/helpengine.h @@ -0,0 +1,182 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef HELPENGINE_H +#define HELPENGINE_H + +#include <QtCore/QThread> +#include <QtCore/QPair> +#include <QtCore/QMap> +#include <QtCore/QWaitCondition> +#include <QtCore/QMutex> +#include <QtGui/QStringListModel> + +#include "docuparser.h" + +namespace Help { +namespace Internal { + +class HelpEngine; + +typedef QList<ContentItem> ContentList; + +class IndexListModel: public QStringListModel +{ +public: + IndexListModel(QObject *parent = 0) + : QStringListModel(parent) {} + + void clear() { contents.clear(); setStringList(QStringList()); } + + QString description(int index) const { return stringList().at(index); } + QStringList links(int index) const { return contents.values(stringList().at(index)); } + void addLink(const QString &description, const QString &link) { contents.insert(description, link); } + + void publish() { filter(QString(), QString()); } + + QModelIndex filter(const QString &s, const QString &real); + + virtual Qt::ItemFlags flags(const QModelIndex &index) const + { return QStringListModel::flags(index) & ~Qt::ItemIsEditable; } + +private: + QMultiMap<QString, QString> contents; +}; + +class TitleMapThread : public QThread +{ + Q_OBJECT + +public: + TitleMapThread(HelpEngine *he); + ~TitleMapThread(); + void setup(); + QList<QPair<QString, ContentList> > contents() const { return contentList; } + QMap<QString, QString> documentTitleMap() const { return titleMap; } + +signals: + void errorOccured(const QString &errMsg); + +protected: + void run(); + +private: + void getAllContents(); + void buildContentDict(); + + QList<QPair<QString, ContentList> > contentList; + QMap<QString, QString> titleMap; + + HelpEngine *engine; + bool done; +}; + +class IndexThread : public QThread +{ + Q_OBJECT + +public: + IndexThread(HelpEngine *he); + void setup(); + IndexListModel *model() const { return indexModel; } + +protected: + void run(); + +signals: + void errorOccured(const QString &errMsg); + +private: + void buildKeywordDB(); + + HelpEngine *engine; + QStringList keywordDocuments; + IndexListModel *indexModel; + bool indexDone; +}; + +class HelpEngine : public QObject +{ + Q_OBJECT + +public: + HelpEngine(QObject *parent, const QString& defaultQtVersionPath); + ~HelpEngine(); + void init(); + QList<QPair<QString, ContentList> > contents() const { return contentList; } + IndexListModel *indices(); + + QString titleOfLink(const QString &link); + static QString removeAnchorFromLink(const QString &link); + + QString home() const; + +signals: + void indexInitialized(); + void contentsInitialized(); + void errorOccured(const QString &errMsg); + +public slots: + void buildContents(); + void buildIndex(); + +private slots: + void titleMapFinished(); + void indexFinished(); + +private: + friend class TitleMapThread; + friend class IndexThread; + + void removeOldCacheFiles(bool onlyFulltextSearchIndex = false); + QString cacheFilePath() const; + quint32 getFileAges(); + + QList<QPair<QString, ContentList> > contentList; + QMap<QString, QString> titleMap; + + QString cacheFilesPath; + IndexListModel *indexModel; + + QWaitCondition titleMapDoneCondition; + QMutex mutex; + + TitleMapThread *titleMapThread; + IndexThread *indexThread; + + bool contentsOnly; +}; + +} //namespace Internal +} //namespace Help + +#endif diff --git a/src/plugins/help/helpfindsupport.cpp b/src/plugins/help/helpfindsupport.cpp new file mode 100644 index 00000000000..306a1d5f40b --- /dev/null +++ b/src/plugins/help/helpfindsupport.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "helpfindsupport.h" +#include "helpviewer.h" + +using namespace Help::Internal; + +HelpFindSupport::HelpFindSupport(CentralWidget *centralWidget) + : m_centralWidget(centralWidget) +{ +} + +HelpFindSupport::~HelpFindSupport() +{ +} + +bool HelpFindSupport::isEnabled() const +{ + return true; +} +QString HelpFindSupport::currentFindString() const +{ + Q_ASSERT(m_centralWidget); + HelpViewer* viewer = m_centralWidget->currentHelpViewer(); + if (!viewer) + return QString(); +#if defined(USE_WEBKIT) + return viewer->selectedText(); +#else + return viewer->textCursor().selectedText(); +#endif +} + + +QString HelpFindSupport::completedFindString() const { return QString(); } + +bool HelpFindSupport::findIncremental(const QString &txt, QTextDocument::FindFlags findFlags) +{ + Q_ASSERT(m_centralWidget); + findFlags &= ~QTextDocument::FindBackward; + return m_centralWidget->find(txt, findFlags, true); +} + +bool HelpFindSupport::findStep(const QString &txt, QTextDocument::FindFlags findFlags) +{ + Q_ASSERT(m_centralWidget); + return m_centralWidget->find(txt, findFlags, false); +} diff --git a/src/plugins/help/helpfindsupport.h b/src/plugins/help/helpfindsupport.h new file mode 100644 index 00000000000..082e908c75d --- /dev/null +++ b/src/plugins/help/helpfindsupport.h @@ -0,0 +1,74 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef HELPFINDSUPPORT_H +#define HELPFINDSUPPORT_H + +#include "centralwidget.h" + +#include <find/ifindsupport.h> + +namespace Help { +namespace Internal { + +class HelpFindSupport : public Find::IFindSupport +{ + Q_OBJECT + +public: + HelpFindSupport(CentralWidget *centralWidget); + ~HelpFindSupport(); + + bool isEnabled() const; + bool supportsReplace() const { return false; } + void resetIncrementalSearch() {} + void clearResults() {} + QString currentFindString() const; + QString completedFindString() const; + + bool findIncremental(const QString &txt, QTextDocument::FindFlags findFlags); + bool findStep(const QString &txt, QTextDocument::FindFlags findFlags); + bool replaceStep(const QString &, const QString &, + QTextDocument::FindFlags ) { return false; } + int replaceAll(const QString &, const QString &, + QTextDocument::FindFlags ) { return 0; } + +private: + bool find(const QString &ttf, QTextDocument::FindFlags findFlags, bool incremental); + + CentralWidget *m_centralWidget; +}; + +} // namespace Internal +} // namespace Help + +#endif // HELPFINDSUPPORT_H diff --git a/src/plugins/help/helpindexfilter.cpp b/src/plugins/help/helpindexfilter.cpp new file mode 100644 index 00000000000..b8aa2edfba5 --- /dev/null +++ b/src/plugins/help/helpindexfilter.cpp @@ -0,0 +1,114 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "helpindexfilter.h" +#include "helpplugin.h" + +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/modemanager.h> + +#include <QtHelp/QHelpEngine> +#include <QtHelp/QHelpIndexModel> + +using namespace QuickOpen; +using namespace Help; +using namespace Help::Internal; + +Q_DECLARE_METATYPE(IQuickOpenFilter*); + +HelpIndexFilter::HelpIndexFilter(HelpPlugin *plugin, QHelpEngine *helpEngine): + m_plugin(plugin), + m_helpEngine(helpEngine), + m_icon(QIcon()) // TODO: Put an icon next to the results +{ + setIncludedByDefault(false); + setShortcutString("?"); + + connect(m_helpEngine->indexModel(), SIGNAL(indexCreated()), + this, SLOT(updateIndices())); +} + +void HelpIndexFilter::updateIndices() +{ + const QString currentFilter = m_plugin->indexFilter(); + if (!currentFilter.isEmpty()) + m_plugin->setIndexFilter(QString()); + + m_helpIndex = m_helpEngine->indexModel()->stringList(); + + if (!currentFilter.isEmpty()) + m_plugin->setIndexFilter(currentFilter); +} + +QString HelpIndexFilter::trName() const +{ + return tr("Help index"); +} + +QString HelpIndexFilter::name() const +{ + return QLatin1String("HelpIndexFilter"); +} + +IQuickOpenFilter::Priority HelpIndexFilter::priority() const +{ + return Medium; +} + +QList<FilterEntry> HelpIndexFilter::matchesFor(const QString &entry) +{ + QList<FilterEntry> entries; + foreach (const QString &string, m_helpIndex) { + if (string.contains(entry, Qt::CaseInsensitive)) { + FilterEntry entry(this, string, QVariant(), m_icon); + entries.append(entry); + } + } + return entries; +} + +void HelpIndexFilter::accept(FilterEntry selection) const +{ + QMap<QString, QUrl> links = m_helpEngine->indexModel()->linksForKeyword(selection.displayName); + if (links.size() == 1) { + emit linkActivated(links.begin().value()); + } else if (!links.isEmpty()) { + emit linksActivated(links, selection.displayName); + } +} + +void HelpIndexFilter::refresh(QFutureInterface<void> &future) +{ + Q_UNUSED(future); + // Nothing to refresh +} diff --git a/src/plugins/help/helpindexfilter.h b/src/plugins/help/helpindexfilter.h new file mode 100644 index 00000000000..0d62a9b5e13 --- /dev/null +++ b/src/plugins/help/helpindexfilter.h @@ -0,0 +1,82 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef HELPINDEXFILTER_H +#define HELPINDEXFILTER_H + +#include <quickopen/iquickopenfilter.h> + +#include <QtGui/QIcon> + +QT_BEGIN_NAMESPACE +class QHelpEngine; +class QUrl; +QT_END_NAMESPACE + +namespace Help { +namespace Internal { + +class HelpPlugin; + +class HelpIndexFilter : public QuickOpen::IQuickOpenFilter +{ + Q_OBJECT + +public: + HelpIndexFilter(HelpPlugin *plugin, QHelpEngine *helpEngine); + + // IQuickOpenFilter + QString trName() const; + QString name() const; + Priority priority() const; + QList<QuickOpen::FilterEntry> matchesFor(const QString &entry); + void accept(QuickOpen::FilterEntry selection) const; + void refresh(QFutureInterface<void> &future); + +signals: + void linkActivated(const QUrl &link) const; + void linksActivated(const QMap<QString, QUrl> &urls, const QString &keyword) const; + +private slots: + void updateIndices(); + +private: + HelpPlugin *m_plugin; + QHelpEngine *m_helpEngine; + QStringList m_helpIndex; + QIcon m_icon; +}; + +} // namespace Internal +} // namespace Help + +#endif // HELPINDEXFILTER_H diff --git a/src/plugins/help/helpmode.cpp b/src/plugins/help/helpmode.cpp new file mode 100644 index 00000000000..b57da2da22f --- /dev/null +++ b/src/plugins/help/helpmode.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "helpmode.h" +#include "helpplugin.h" + +#include <QtCore/QLatin1String> +#include <QtGui/QWidget> +#include <coreplugin/findplaceholder.h> + +using namespace Help; +using namespace Help::Internal; + +HelpMode::HelpMode(QWidget *widget, QWidget *centralWidget, QObject *parent): + BaseMode(tr("Help"), + Constants::ID_MODE_HELP, + QIcon((QLatin1String(":/fancyactionbar/images/mode_Reference.png"))), Constants::P_MODE_HELP, widget, parent), + m_centralWidget(centralWidget) +{ + m_centralWidget->layout()->setSpacing(0); + m_centralWidget->layout()->addWidget(new Core::FindToolBarPlaceHolder(this)); +} + + diff --git a/src/plugins/help/helpmode.h b/src/plugins/help/helpmode.h new file mode 100644 index 00000000000..19b5c73a29e --- /dev/null +++ b/src/plugins/help/helpmode.h @@ -0,0 +1,61 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef HELPMODE_H +#define HELPMODE_H + +#include <QtCore/QObject> + +#include <coreplugin/basemode.h> + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +namespace Help { +namespace Internal { + +class HelpMode : public Core::BaseMode +{ + Q_OBJECT + +public: + HelpMode(QWidget *widget, QWidget *centralWidget, QObject *parent = 0); + +private: + QWidget *m_centralWidget; +}; + +} // namespace Internal +} // namespace Help + +#endif // HELPMODE_H diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp new file mode 100644 index 00000000000..17712cd576a --- /dev/null +++ b/src/plugins/help/helpplugin.cpp @@ -0,0 +1,684 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "helpplugin.h" +#include "docsettingspage.h" +#include "filtersettingspage.h" +#include "helpindexfilter.h" +#include "helpmode.h" +#include "helpviewer.h" +#include "contentwindow.h" +#include "indexwindow.h" +#include "bookmarkmanager.h" +#include "centralwidget.h" +#include "helpfindsupport.h" +#include "searchwidget.h" + +#include <QtCore/QDebug> +#include <QtCore/qplugin.h> +#include <QtCore/QFileInfo> +#include <QtCore/QSettings> +#include <QtCore/QDir> +#include <QtCore/QResource> +#include <QtGui/QAction> +#include <QtGui/QShortcut> +#include <QtGui/QSplitter> +#include <QtGui/QStyle> +#include <QtGui/QToolBar> +#include <QtGui/QComboBox> +#include <QtHelp/QHelpEngine> + +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/minisplitter.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/rightpane.h> +#include <coreplugin/sidebar.h> +#include <coreplugin/welcomemode.h> + +using namespace Help; +using namespace Help::Internal; + +HelpManager::HelpManager(QHelpEngine *helpEngine) + : m_helpEngine(helpEngine) +{ +} + +void HelpManager::registerDocumentation(const QStringList &fileNames) +{ + bool needsSetup = false; + { + QHelpEngineCore hc(m_helpEngine->collectionFile()); + hc.setupData(); + foreach (const QString &fileName, fileNames) { + if (!QFile::exists(fileName)) + continue; + QString fileNamespace = QHelpEngineCore::namespaceName(fileName); + if (!fileNamespace.isEmpty() && !hc.registeredDocumentations().contains(fileNamespace)) { + if (hc.registerDocumentation(fileName)) + needsSetup = true; + else + qDebug() << "error registering" << fileName << hc.error(); + } + } + } + if (needsSetup) + qDebug() << m_helpEngine->setupData(); +} + +HelpPlugin::HelpPlugin() : + m_core(0), + m_helpEngine(0), + m_contextHelpEngine(0), + m_contentWidget(0), + m_indexWidget(0), + m_centralWidget(0), + m_helpViewerForSideBar(0), + m_mode(0), + m_shownLastPages(false), + m_contentItem(0), + m_indexItem(0), + m_searchItem(0), + m_bookmarkItem(0), + m_rightPaneSideBar(0) +{ +} + +HelpPlugin::~HelpPlugin() +{ +} + +bool HelpPlugin::initialize(const QStringList & /*arguments*/, QString *) +{ + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + QList<int> globalcontext; + globalcontext << Core::Constants::C_GLOBAL_ID; + QList<int> modecontext; + modecontext << m_core->uniqueIDManager()->uniqueIdentifier(Constants::C_MODE_HELP); + + // FIXME shouldn't the help engine create the directory if it doesn't exist? + QFileInfo fi(m_core->settings()->fileName()); + QDir directory(fi.absolutePath()); + if (!directory.exists()) + directory.mkpath(directory.absolutePath()); + m_helpEngine = new QHelpEngine(directory.absolutePath() + + QLatin1String("/helpcollection.qhc"), this); + connect(m_helpEngine, SIGNAL(setupFinished()), + this, SLOT(updateFilterComboBox())); + + addAutoReleasedObject(new HelpManager(m_helpEngine)); + + m_docSettingsPage = new DocSettingsPage(m_helpEngine); + addAutoReleasedObject(m_docSettingsPage); + + m_filterSettingsPage = new FilterSettingsPage(m_helpEngine); + addAutoReleasedObject(m_filterSettingsPage); + connect(m_docSettingsPage, SIGNAL(documentationAdded()), + m_filterSettingsPage, SLOT(updateFilterPage())); + connect(m_docSettingsPage, SIGNAL(dialogAccepted()), + this, SLOT(checkForHelpChanges())); + + m_contentWidget = new ContentWindow(m_helpEngine); + m_contentWidget->setWindowTitle(tr("Contents")); + m_indexWidget = new IndexWindow(m_helpEngine); + m_indexWidget->setWindowTitle(tr("Index")); + m_searchWidget = new SearchWidget(m_helpEngine->searchEngine()); + m_searchWidget->setWindowTitle(tr("Search")); + m_bookmarkManager = new BookmarkManager(m_helpEngine); + m_bookmarkWidget = new BookmarkWidget(m_bookmarkManager, 0, false); + m_bookmarkWidget->setWindowTitle(tr("Bookmarks")); + connect(m_bookmarkWidget, SIGNAL(addBookmark()), + this, SLOT(addBookmark())); + + Core::ActionManagerInterface *am = m_core->actionManager(); + Core::ICommand *cmd; + + // Add Home, Previous and Next actions (used in the toolbar) + QAction *homeAction = new QAction(QIcon(QLatin1String(":/help/images/home.png")), tr("Home"), this); + cmd = am->registerAction(homeAction, QLatin1String("Help.Home"), globalcontext); + + QAction *previousAction = new QAction(QIcon(QLatin1String(":/help/images/previous.png")), + tr("Previous"), this); + cmd = am->registerAction(previousAction, QLatin1String("Help.Previous"), modecontext); + cmd->setDefaultKeySequence(QKeySequence(Qt::Key_Backspace)); + + QAction *nextAction = new QAction(QIcon(QLatin1String(":/help/images/next.png")), tr("Next"), this); + cmd = am->registerAction(nextAction, QLatin1String("Help.Next"), modecontext); + + QAction *addBookmarkAction = new QAction(QIcon(QLatin1String(":/help/images/bookmark.png")), + tr("Add Bookmark"), this); + cmd = am->registerAction(addBookmarkAction, QLatin1String("Help.AddBookmark"), modecontext); + cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::Key_M)); + + // Add Index, Contents, and Context menu items and a separator to the Help menu + QAction *indexAction = new QAction(tr("Index"), this); + cmd = am->registerAction(indexAction, QLatin1String("Help.Index"), globalcontext); + am->actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP); + + QAction *contentsAction = new QAction(tr("Contents"), this); + cmd = am->registerAction(contentsAction, QLatin1String("Help.Contents"), globalcontext); + am->actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP); + + QAction *searchAction = new QAction(tr("Search"), this); + cmd = am->registerAction(searchAction, QLatin1String("Help.Search"), globalcontext); + am->actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP); + + QAction *contextAction = new QAction(tr("Context Help"), this); + cmd = am->registerAction(contextAction, QLatin1String("Help.Context"), globalcontext); + cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F1)); + am->actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP); + +#ifndef Q_OS_MAC + QAction *sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("Help.Separator"), globalcontext); + am->actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP); +#endif + + m_centralWidget = new CentralWidget(m_helpEngine); + Aggregation::Aggregate *agg = new Aggregation::Aggregate; + agg->add(m_centralWidget); + agg->add(new HelpFindSupport(m_centralWidget)); + QWidget *mainWidget = new QWidget; + QVBoxLayout *mainWidgetLayout = new QVBoxLayout(mainWidget); + mainWidgetLayout->setMargin(0); + mainWidgetLayout->setSpacing(0); + mainWidgetLayout->addWidget(createToolBar()); + mainWidgetLayout->addWidget(m_centralWidget); + + m_contentItem = new Core::SideBarItem(m_contentWidget); + m_indexItem = new Core::SideBarItem(m_indexWidget); + m_searchItem = new Core::SideBarItem(m_searchWidget); + m_bookmarkItem = new Core::SideBarItem(m_bookmarkWidget); + QList<Core::SideBarItem*> itemList; + itemList << m_contentItem << m_indexItem << m_searchItem << m_bookmarkItem; + m_sideBar = new Core::SideBar(itemList, QList<Core::SideBarItem*>() << m_indexItem); + + QSplitter *splitter = new Core::MiniSplitter; + splitter->setOpaqueResize(false); + splitter->addWidget(m_sideBar); + splitter->addWidget(mainWidget); + splitter->setStretchFactor(0, 0); + splitter->setStretchFactor(1, 1); + splitter->setSizes(QList<int>() << 300 << 300); + + m_mode = new HelpMode(splitter, m_centralWidget); + m_mode->setContext(QList<int>() << modecontext); + addAutoReleasedObject(m_mode); + + QAction *printAction = new QAction(this); + am->registerAction(printAction, Core::Constants::PRINT, modecontext); + connect(printAction, SIGNAL(triggered()), m_centralWidget, SLOT(print())); + + QAction *copyAction = new QAction(this); + cmd = am->registerAction(copyAction, Core::Constants::COPY, modecontext); + connect(copyAction, SIGNAL(triggered()), m_centralWidget, SLOT(copy())); + copyAction->setText(cmd->action()->text()); + copyAction->setIcon(cmd->action()->icon()); + + QMap<QString, Core::ICommand*> shortcutMap; + QShortcut *shortcut = new QShortcut(splitter); + shortcut->setWhatsThis(tr("Activate Index in Help mode")); + cmd = am->registerShortcut(shortcut, QLatin1String("Help.IndexShortcut"), modecontext); + cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::Key_I)); + connect(shortcut, SIGNAL(activated()), this, SLOT(activateIndex())); + shortcutMap.insert(m_indexWidget->windowTitle(), cmd); + + shortcut = new QShortcut(splitter); + shortcut->setWhatsThis(tr("Activate Contents in Help mode")); + cmd = am->registerShortcut(shortcut, QLatin1String("Help.ContentsShortcut"), modecontext); + cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::Key_T)); + connect(shortcut, SIGNAL(activated()), this, SLOT(activateContents())); + shortcutMap.insert(m_contentWidget->windowTitle(), cmd); + + shortcut = new QShortcut(splitter); + shortcut->setWhatsThis(tr("Activate Search in Help mode")); + cmd = am->registerShortcut(shortcut, QLatin1String("Help.SearchShortcut"), modecontext); + cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::Key_S)); + connect(shortcut, SIGNAL(activated()), this, SLOT(activateSearch())); + shortcutMap.insert(m_searchWidget->windowTitle(), cmd); + shortcutMap.insert(m_bookmarkWidget->windowTitle(), 0); + + m_sideBar->setShortcutMap(shortcutMap); + + connect(homeAction, SIGNAL(triggered()), m_centralWidget, SLOT(home())); + connect(previousAction, SIGNAL(triggered()), m_centralWidget, SLOT(backward())); + connect(nextAction, SIGNAL(triggered()), m_centralWidget, SLOT(forward())); + connect(addBookmarkAction, SIGNAL(triggered()), this, SLOT(addBookmark())); + connect(m_contentWidget, SIGNAL(linkActivated(const QUrl&)), + m_centralWidget, SLOT(setSource(const QUrl&))); + connect(m_indexWidget, SIGNAL(linkActivated(const QUrl&)), + m_centralWidget, SLOT(setSource(const QUrl&))); + connect(m_searchWidget, SIGNAL(requestShowLink(const QUrl&)), + m_centralWidget, SLOT(setSource(const QUrl&))); + connect(m_searchWidget, SIGNAL(requestShowLinkInNewTab(const QUrl&)), + m_centralWidget, SLOT(setSourceInNewTab(const QUrl&))); + connect(m_bookmarkWidget, SIGNAL(requestShowLink(const QUrl&)), + m_centralWidget, SLOT(setSource(const QUrl&))); + + connect(m_centralWidget, SIGNAL(backwardAvailable(bool)), + previousAction, SLOT(setEnabled(bool))); + connect(m_centralWidget, SIGNAL(forwardAvailable(bool)), + nextAction, SLOT(setEnabled(bool))); + connect(m_centralWidget, SIGNAL(addNewBookmark(const QString&, const QString&)), + this, SLOT(addNewBookmark(const QString&, const QString&))); + + QList<QAction*> actionList; + actionList << previousAction + << nextAction + << homeAction +#ifndef Q_OS_MAC + << sep +#endif + << copyAction; + m_centralWidget->setGlobalActions(actionList); + + connect(contextAction, SIGNAL(triggered()), this, SLOT(activateContext())); + connect(indexAction, SIGNAL(triggered()), this, SLOT(activateIndex())); + connect(contentsAction, SIGNAL(triggered()), this, SLOT(activateContents())); + connect(searchAction, SIGNAL(triggered()), this, SLOT(activateSearch())); + + connect(m_core->modeManager(), SIGNAL(currentModeChanged(Core::IMode*)), + this, SLOT(modeChanged(Core::IMode*))); + + connect(m_contentWidget, SIGNAL(linkActivated(const QUrl&)), + m_centralWidget, SLOT(setSource(const QUrl&))); + connect(m_indexWidget, SIGNAL(linkActivated(const QUrl&)), + m_centralWidget, SLOT(setSource(const QUrl&))); + connect(m_indexWidget, SIGNAL(linksActivated(const QMap<QString, QUrl>&, const QString&)), + m_centralWidget, SLOT(showTopicChooser(const QMap<QString, QUrl>&, const QString&))); + + HelpIndexFilter *helpIndexFilter = new HelpIndexFilter(this, m_helpEngine); + addAutoReleasedObject(helpIndexFilter); + connect(helpIndexFilter, SIGNAL(linkActivated(QUrl)), + this, SLOT(switchToHelpMode(QUrl))); + connect(helpIndexFilter, SIGNAL(linksActivated(const QMap<QString, QUrl>&, const QString&)), + this, SLOT(switchToHelpMode(const QMap<QString, QUrl>&, const QString&))); + + previousAction->setEnabled(m_centralWidget->isBackwardAvailable()); + nextAction->setEnabled(m_centralWidget->isForwardAvailable()); + + createRightPaneSideBar(); + + return true; +} + +void HelpPlugin::createRightPaneSideBar() +{ + QAction *switchToHelpMode = new QAction("Go to Help Mode", this); + m_rightPaneBackwardAction = new QAction(QIcon(QLatin1String(":/help/images/previous.png")), tr("Previous"), this); + m_rightPaneForwardAction = new QAction(QIcon(QLatin1String(":/help/images/next.png")), tr("Next"), this); + + QToolBar *rightPaneToolBar = new QToolBar(); + rightPaneToolBar->addAction(switchToHelpMode); + rightPaneToolBar->addAction(m_rightPaneBackwardAction); + rightPaneToolBar->addAction(m_rightPaneForwardAction); + + connect(switchToHelpMode, SIGNAL(triggered()), this, SLOT(switchToHelpMode())); + connect(m_rightPaneBackwardAction, SIGNAL(triggered()), this, SLOT(rightPaneBackward())); + connect(m_rightPaneForwardAction, SIGNAL(triggered()), this, SLOT(rightPaneForward())); + + QToolButton *closeButton = new QToolButton(); + closeButton->setProperty("type", QLatin1String("dockbutton")); + closeButton->setIcon(QIcon(":/qworkbench/images/closebutton.png")); + + // Dummy layout to align the close button to the right + QHBoxLayout *hboxLayout = new QHBoxLayout(); + hboxLayout->setSpacing(0); + hboxLayout->setMargin(0); + hboxLayout->addStretch(5); + hboxLayout->addWidget(closeButton); + + QWidget *w = new QWidget(rightPaneToolBar); + w->setLayout(hboxLayout); + rightPaneToolBar->addWidget(w); + connect(closeButton, SIGNAL(clicked()), this, SLOT(slotHideRightPane())); + + QVBoxLayout *rightPaneLayout = new QVBoxLayout; + rightPaneLayout->setMargin(0); + rightPaneLayout->setSpacing(0); + rightPaneLayout->addWidget(rightPaneToolBar); + + m_helpViewerForSideBar = new HelpViewer(m_helpEngine, 0); + rightPaneLayout->addWidget(m_helpViewerForSideBar); + + m_rightPaneSideBar = new QWidget; + m_rightPaneSideBar->setLayout(rightPaneLayout); + m_rightPaneSideBar->setFocusProxy(m_helpViewerForSideBar); + addAutoReleasedObject(new Core::BaseRightPaneWidget(m_rightPaneSideBar)); +} + +void HelpPlugin::rightPaneBackward() +{ + m_helpViewerForSideBar->backward(); +} + +void HelpPlugin::rightPaneForward() +{ + m_helpViewerForSideBar->forward(); +} + +void HelpPlugin::switchToHelpMode() +{ + switchToHelpMode(m_helpViewerForSideBar->source()); + Core::RightPaneWidget::instance()->setShown(false); +} + +void HelpPlugin::switchToHelpMode(const QUrl &source) +{ + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + m_centralWidget->setSource(source); + m_centralWidget->setFocus(); +} + +void HelpPlugin::switchToHelpMode(const QMap<QString, QUrl> &urls, const QString &keyword) +{ + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + m_centralWidget->showTopicChooser(urls, keyword); +} + +void HelpPlugin::slotHideRightPane() +{ + Core::RightPaneWidget::instance()->setShown(false); +} + +void HelpPlugin::extensionsInitialized() +{ + m_sideBar->readSettings(m_core->settings()); + if (!m_helpEngine->setupData()) { + qWarning() << "Could not initialize help engine: " << m_helpEngine->error(); + return; + } + + bool needsSetup = false; + bool assistantInternalDocRegistered = false; + + foreach (QString ns, m_helpEngine->registeredDocumentations()) { + if (ns == QString("com.nokia.qtcreator.%1%2") + .arg(IDE_VERSION_MAJOR).arg(IDE_VERSION_MINOR)) { + assistantInternalDocRegistered = true; + break; + } + } + + if (!assistantInternalDocRegistered) { + QFileInfo fi(m_helpEngine->collectionFile()); + + const QString qchFileName = + QDir::cleanPath(QCoreApplication::applicationDirPath() +#if defined(Q_OS_MAC) + + QLatin1String("/../Resources/doc/qtcreator.qch")); +#else + + QLatin1String("/../doc/qtcreator.qch")); +#endif + QHelpEngineCore hc(fi.absoluteFilePath()); + hc.setupData(); + if (!hc.registerDocumentation(qchFileName)) + qDebug() << hc.error(); + needsSetup = true; + } + + int i = m_helpEngine->customValue( + QLatin1String("UnfilteredFilterInserted")).toInt(); + if (i != 1) { + { + QHelpEngineCore hc(m_helpEngine->collectionFile()); + hc.setupData(); + hc.addCustomFilter(tr("Unfiltered"), QStringList()); + hc.setCustomValue(QLatin1String("UnfilteredFilterInserted"), 1); + } + m_helpEngine->blockSignals(true); + m_helpEngine->setCurrentFilter(tr("Unfiltered")); + m_helpEngine->blockSignals(false); + needsSetup = true; + } + + if (needsSetup) + m_helpEngine->setupData(); + + updateFilterComboBox(); + m_bookmarkManager->setupBookmarkModels(); + + if (Core::Internal::WelcomeMode *welcomeMode = qobject_cast<Core::Internal::WelcomeMode*>(m_core->modeManager()->mode(Core::Constants::MODE_WELCOME))) { + connect(welcomeMode, SIGNAL(requestHelp(QString)), this, SLOT(openGettingStarted())); + } +} + +void HelpPlugin::shutdown() +{ + m_sideBar->saveSettings(m_core->settings()); + m_bookmarkManager->saveBookmarks(); + delete m_bookmarkManager; +} + +void HelpPlugin::setIndexFilter(const QString &filter) +{ + m_indexWidget->setSearchLineEditText(filter); +} + +QString HelpPlugin::indexFilter() const +{ + return m_indexWidget->searchLineEditText(); +} + +void HelpPlugin::modeChanged(Core::IMode *mode) +{ + if (mode == m_mode && !m_shownLastPages) { + m_shownLastPages = true; + qApp->processEvents(); + qApp->setOverrideCursor(Qt::WaitCursor); + m_centralWidget->setLastShownPages(); + qApp->restoreOverrideCursor(); + } +} + +void HelpPlugin::activateContext() +{ + using namespace Core; + // case 1 sidebar shown and has focus, we show whatever we have in the + // sidebar in big + RightPanePlaceHolder* placeHolder = RightPanePlaceHolder::current(); + if(placeHolder && Core::RightPaneWidget::instance()->hasFocus()) { + switchToHelpMode(); + return; + } + + bool useSideBar = false; + if (placeHolder && !Core::RightPaneWidget::instance()->hasFocus()) + useSideBar = true; + + // Find out what to show + HelpViewer *viewer = 0; + if (IContext *context = m_core->currentContextObject()) { + if (!m_contextHelpEngine) { + m_contextHelpEngine = new QHelpEngineCore(m_helpEngine->collectionFile(), this); + //m_contextHelpEngine->setAutoSaveFilter(false); + m_contextHelpEngine->setupData(); + m_contextHelpEngine->setCurrentFilter(tr("Unfiltered")); + } + + const QString &id = context->contextHelpId(); + QMap<QString, QUrl> links = m_contextHelpEngine->linksForIdentifier(id); + if (!links.isEmpty()) { + if (useSideBar) { + Core::RightPaneWidget::instance()->setShown(true); + viewer = m_helpViewerForSideBar; + } else { + viewer = m_centralWidget->currentHelpViewer(); + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + } + + if (viewer) { + QUrl source = *links.begin(); + if (viewer->source() != source) + viewer->setSource(source); + viewer->setFocus(); + } + } else { + // No link found + if (useSideBar) { + Core::RightPaneWidget::instance()->setShown(true); + viewer = m_helpViewerForSideBar; + } else { + viewer = m_centralWidget->currentHelpViewer(); + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + } + + if (viewer) { + viewer->setHtml(tr("<title>No Documentation</title><br><br>" + "<center><b>%1</b><br><br>No documentation available."). + arg(id)); + viewer->setSource(QUrl()); + //activateIndex(); + } + } + } else { + // No context object + if (useSideBar) { + Core::RightPaneWidget::instance()->setShown(true); + viewer = m_helpViewerForSideBar; + } else { + viewer = m_centralWidget->currentHelpViewer(); + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + } + + if (viewer) { + viewer->setSource(QUrl()); + viewer->setHtml("<title>No Documentation</title><br><br><center>No" + " documentation available."); + //activateIndex(); + } + } +} + +void HelpPlugin::activateIndex() +{ + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + m_sideBar->activateItem(m_indexItem); +} + +void HelpPlugin::activateContents() +{ + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + m_sideBar->activateItem(m_contentItem); +} + +void HelpPlugin::activateSearch() +{ + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + m_sideBar->activateItem(m_searchItem); +} + +QToolBar *HelpPlugin::createToolBar() +{ + QToolBar *toolWidget = new QToolBar; + Core::ActionManagerInterface *am = m_core->actionManager(); + toolWidget->addAction(am->command(QLatin1String("Help.Home"))->action()); + toolWidget->addAction(am->command(QLatin1String("Help.Previous"))->action()); + toolWidget->addAction(am->command(QLatin1String("Help.Next"))->action()); + toolWidget->addSeparator(); + toolWidget->addAction(am->command(QLatin1String("Help.AddBookmark"))->action()); + //int size = toolWidget->style()->pixelMetric(QStyle::PM_SmallIconSize); + //toolWidget->setIconSize(QSize(size, size)); + toolWidget->setMovable(false); + + toolWidget->addSeparator(); + + QWidget *w = new QWidget; + QHBoxLayout *layout = new QHBoxLayout(w); + layout->setMargin(0); + layout->addSpacing(10); + layout->addWidget(new QLabel(tr("Filtered by:"))); + m_filterComboBox = new QComboBox; + m_filterComboBox->setMinimumContentsLength(20); + connect(m_filterComboBox, SIGNAL(activated(const QString&)), + this, SLOT(filterDocumentation(const QString&))); + layout->addWidget(m_filterComboBox); + toolWidget->addWidget(w); + + return toolWidget; +} + +void HelpPlugin::updateFilterComboBox() +{ + QString curFilter = m_filterComboBox->currentText(); + if (curFilter.isEmpty()) + curFilter = m_helpEngine->currentFilter(); + m_filterComboBox->clear(); + m_filterComboBox->addItems(m_helpEngine->customFilters()); + int idx = m_filterComboBox->findText(curFilter); + if (idx < 0) + idx = 0; + m_filterComboBox->setCurrentIndex(idx); +} + +void HelpPlugin::checkForHelpChanges() +{ + bool changed = m_docSettingsPage->applyChanges(); + changed |= m_filterSettingsPage->applyChanges(); + if (changed) + m_helpEngine->setupData(); +} + +void HelpPlugin::filterDocumentation(const QString &customFilter) +{ + m_helpEngine->setCurrentFilter(customFilter); +} + +void HelpPlugin::addBookmark() +{ + addNewBookmark(m_centralWidget->currentTitle(), m_centralWidget->currentSource().toString()); +} + +void HelpPlugin::addNewBookmark(const QString &title, const QString &url) +{ + if (url.isEmpty()) + return; + + m_bookmarkManager->showBookmarkDialog(m_centralWidget, title, url); +} + +void HelpPlugin::openGettingStarted() +{ + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + m_centralWidget->setSource( + QString("qthelp://com.nokia.qtcreator.%1%2/doc/index.html") + .arg(IDE_VERSION_MAJOR).arg(IDE_VERSION_MINOR)); +} + + +Q_EXPORT_PLUGIN(HelpPlugin) diff --git a/src/plugins/help/helpplugin.h b/src/plugins/help/helpplugin.h new file mode 100644 index 00000000000..21dab96dc98 --- /dev/null +++ b/src/plugins/help/helpplugin.h @@ -0,0 +1,169 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#ifndef HELPPLUGIN_H +#define HELPPLUGIN_H + +#include "help_global.h" +#include <extensionsystem/iplugin.h> + +#include <QtCore/QMap> + +QT_BEGIN_NAMESPACE +class QAction; +class QComboBox; +class QHelpEngineCore; +class QHelpEngine; +class QShortcut; +class QToolBar; +class QUrl; + +class IndexWindow; +class ContentWindow; +class BookmarkManager; +class BookmarkWidget; +class CentralWidget; +class HelpViewer; +QT_END_NAMESPACE + + +namespace Core { +class ICore; +class IMode; +class SideBar; +class SideBarItem; +} + +namespace Help { + +namespace Constants { + const char * const HELPVIEWER_KIND = "Qt Help Viewer"; + const char * const C_MODE_HELP = "Help Mode"; + const int P_MODE_HELP = 70; + const char * const ID_MODE_HELP = "Help.HelpMode"; +} + +class HELP_EXPORT HelpManager : public QObject +{ + Q_OBJECT +public: + HelpManager(QHelpEngine *helpEngine); + + void registerDocumentation(const QStringList &fileNames); + +private: + QHelpEngine *m_helpEngine; +}; + +namespace Internal { + +class HelpMode; +class HelpPluginEditorFactory; +class DocSettingsPage; +class FilterSettingsPage; +class SearchWidget; + +class HelpPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + HelpPlugin(); + virtual ~HelpPlugin(); + + bool initialize(const QStringList &arguments, QString *error_message); + void extensionsInitialized(); + void shutdown(); + + // Necessary to get the unfiltered list in the help index filter + void setIndexFilter(const QString &filter); + QString indexFilter() const; + +private slots: + void modeChanged(Core::IMode *mode); + void activateContext(); + void activateIndex(); + void activateContents(); + void activateSearch(); + void checkForHelpChanges(); + void updateFilterComboBox(); + void filterDocumentation(const QString &customFilter); + void addBookmark(); + void addNewBookmark(const QString &title, const QString &url); + + void rightPaneBackward(); + void rightPaneForward(); + void switchToHelpMode(); + void switchToHelpMode(const QUrl &source); + void switchToHelpMode(const QMap<QString, QUrl> &urls, const QString &keyword); + void slotHideRightPane(); + + void openGettingStarted(); + +private: + QToolBar *createToolBar(); + void createRightPaneSideBar(); + + Core::ICore *m_core; + QHelpEngine *m_helpEngine; + QHelpEngineCore *m_contextHelpEngine; + ContentWindow *m_contentWidget; + IndexWindow *m_indexWidget; + BookmarkWidget *m_bookmarkWidget; + BookmarkManager *m_bookmarkManager; + SearchWidget *m_searchWidget; + CentralWidget *m_centralWidget; + HelpViewer *m_helpViewerForSideBar; + HelpMode *m_mode; + bool m_shownLastPages; + + Core::SideBarItem *m_contentItem; + Core::SideBarItem *m_indexItem; + Core::SideBarItem *m_searchItem; + Core::SideBarItem *m_bookmarkItem; + + DocSettingsPage *m_docSettingsPage; + FilterSettingsPage *m_filterSettingsPage; + + QComboBox *m_filterComboBox; + Core::SideBar *m_sideBar; + QWidget *m_rightPaneSideBar; + + QAction *m_rightPaneBackwardAction; + QAction *m_rightPaneForwardAction; +}; + +} // namespace Internal +} // namespace Help + +#endif // HELPPLUGIN_H diff --git a/src/plugins/help/images/book.png b/src/plugins/help/images/book.png Binary files differnew file mode 100644 index 00000000000..ecd311b31f5 --- /dev/null +++ b/src/plugins/help/images/book.png diff --git a/src/plugins/help/images/bookmark.png b/src/plugins/help/images/bookmark.png Binary files differnew file mode 100644 index 00000000000..7b2e5fd0cef --- /dev/null +++ b/src/plugins/help/images/bookmark.png diff --git a/src/plugins/help/images/find.png b/src/plugins/help/images/find.png Binary files differnew file mode 100644 index 00000000000..fafcd3bb210 --- /dev/null +++ b/src/plugins/help/images/find.png diff --git a/src/plugins/help/images/home.png b/src/plugins/help/images/home.png Binary files differnew file mode 100644 index 00000000000..9cee3025d07 --- /dev/null +++ b/src/plugins/help/images/home.png diff --git a/src/plugins/help/images/mac/addtab.png b/src/plugins/help/images/mac/addtab.png Binary files differnew file mode 100644 index 00000000000..20928fb402a --- /dev/null +++ b/src/plugins/help/images/mac/addtab.png diff --git a/src/plugins/help/images/mac/closetab.png b/src/plugins/help/images/mac/closetab.png Binary files differnew file mode 100644 index 00000000000..ab9d669eeeb --- /dev/null +++ b/src/plugins/help/images/mac/closetab.png diff --git a/src/plugins/help/images/next.png b/src/plugins/help/images/next.png Binary files differnew file mode 100644 index 00000000000..7700d6fce6b --- /dev/null +++ b/src/plugins/help/images/next.png diff --git a/src/plugins/help/images/previous.png b/src/plugins/help/images/previous.png Binary files differnew file mode 100644 index 00000000000..99dc8733c73 --- /dev/null +++ b/src/plugins/help/images/previous.png diff --git a/src/plugins/help/images/win/addtab.png b/src/plugins/help/images/win/addtab.png Binary files differnew file mode 100644 index 00000000000..4bb0feb92de --- /dev/null +++ b/src/plugins/help/images/win/addtab.png diff --git a/src/plugins/help/images/win/closetab.png b/src/plugins/help/images/win/closetab.png Binary files differnew file mode 100644 index 00000000000..ef9e02086c6 --- /dev/null +++ b/src/plugins/help/images/win/closetab.png diff --git a/src/plugins/help/indextoolwindow.cpp b/src/plugins/help/indextoolwindow.cpp new file mode 100644 index 00000000000..f80bd453cb7 --- /dev/null +++ b/src/plugins/help/indextoolwindow.cpp @@ -0,0 +1,215 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtCore/QDebug> +#include <QtGui/QKeyEvent> +#include <QtGui/QFocusEvent> +#include <QtGui/QLayout> +#include <QtGui/QLabel> +#include <QtGui/QLineEdit> +#include <QtGui/QListView> +#include <QtGui/QApplication> + +#include "indextoolwindow.h" +#include "helpengine.h" +#include "topicchooser.h" + +using namespace Help::Internal; + +IndexToolWidget::IndexToolWidget() +{ + wasInitialized = false; + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + + QLabel *l = new QLabel(tr("Look for:"), this); + layout->addWidget(l); + + findLineEdit = new QLineEdit(this); + findLineEdit->installEventFilter(this); + layout->addWidget(findLineEdit); + + indicesView = new QListView(this); + indicesView->setLayoutMode(QListView::Batched); + indicesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + layout->addWidget(indicesView); + + setWindowTitle(tr("Index")); + setWindowIcon(QIcon(":/help/images/find.png")); +} + +void IndexToolWidget::focusInEvent(QFocusEvent *e) +{ + showEvent(0); + if (e && e->reason() != Qt::MouseFocusReason) { + findLineEdit->selectAll(); + findLineEdit->setFocus(); + } +} + +void IndexToolWidget::showEvent(QShowEvent *) +{ + if (!wasInitialized) { + wasInitialized = true; + setCursor(QCursor(Qt::WaitCursor)); + emit buildRequested(); + } +} + +bool IndexToolWidget::eventFilter(QObject * o, QEvent * e) +{ + if (o == findLineEdit && e->type() == QEvent::KeyPress) { + switch (static_cast<QKeyEvent*>(e)->key()) { + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_PageDown: + case Qt::Key_PageUp: + QApplication::sendEvent(indicesView, e); + break; + case Qt::Key_Escape: + emit escapePressed(); + break; + default: + break; + } + } + return QWidget::eventFilter(o, e); +} + + +IndexToolWindow::IndexToolWindow(const QList<int> &context, HelpEngine *help) +{ + m_context = context; + m_context << 0; + + m_widget = new IndexToolWidget; + + helpEngine = help; + connect(helpEngine, SIGNAL(indexInitialized()), this, SLOT(indexDone())); + model = 0; + + connect(m_widget->findLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(searchInIndex(const QString &))); + connect(m_widget->findLineEdit, SIGNAL(returnPressed()), this, SLOT(indexRequested())); + connect(m_widget, SIGNAL(buildRequested()), helpEngine, SLOT(buildIndex())); + + connect(m_widget->indicesView, SIGNAL(activated(const QModelIndex&)), + this, SLOT(indexRequested())); + connect(m_widget, SIGNAL(escapePressed()), this, SIGNAL(escapePressed())); + +} + +IndexToolWindow::~IndexToolWindow() +{ + delete m_widget; +} + +const QList<int> &IndexToolWindow::context() const +{ + return m_context; +} + +QWidget *IndexToolWindow::widget() +{ + return m_widget; +} + +void IndexToolWindow::indexDone() +{ + model = helpEngine->indices(); + m_widget->indicesView->setModel(model); + m_widget->setCursor(QCursor(Qt::ArrowCursor)); +} + +void IndexToolWindow::searchInIndex(const QString &str) +{ + if (!model) + return; + QRegExp atoz("[A-Z]"); + int matches = str.count(atoz); + if (matches > 0 && !str.contains(".*")) + { + int start = 0; + QString newSearch; + for (; matches > 0; --matches) { + int match = str.indexOf(atoz, start+1); + if (match <= start) + continue; + newSearch += str.mid(start, match-start); + newSearch += ".*"; + start = match; + } + newSearch += str.mid(start); + m_widget->indicesView->setCurrentIndex(model->filter(newSearch, str)); + } + else + m_widget->indicesView->setCurrentIndex(model->filter(str, str)); +} + +void IndexToolWindow::indexRequested() +{ + if (!model) + return; + int row = m_widget->indicesView->currentIndex().row(); + if (row == -1 || row >= model->rowCount()) + return; + + QString description = model->description(row); + QStringList links = model->links(row); + + bool blocked = m_widget->findLineEdit->blockSignals(true); + m_widget->findLineEdit->setText(description); + m_widget->findLineEdit->blockSignals(blocked); + + if (links.count() == 1) { + emit showLinkRequested(links.first(), false); + } else { + qSort(links); + QStringList::Iterator it = links.begin(); + QStringList linkList; + QStringList linkNames; + for (; it != links.end(); ++it) { + linkList << *it; + linkNames << helpEngine->titleOfLink(*it); + } + QString link = TopicChooser::getLink(m_widget, linkNames, linkList, description); + if (!link.isEmpty()) + emit showLinkRequested(link, false); + } + + model->publish(); + m_widget->indicesView->setCurrentIndex(model->index(model->stringList().indexOf(description))); + m_widget->indicesView->scrollTo(m_widget->indicesView->currentIndex(), QAbstractItemView::PositionAtTop); +} + diff --git a/src/plugins/help/indextoolwindow.h b/src/plugins/help/indextoolwindow.h new file mode 100644 index 00000000000..4d4eb8f6b25 --- /dev/null +++ b/src/plugins/help/indextoolwindow.h @@ -0,0 +1,113 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef INDEXTOOLWINDOW_H +#define INDEXTOOLWINDOW_H + +#include <QtCore/QModelIndex> +#include <QtGui/QWidget> + +#include <coreplugin/iview.h> + +class QListView; +class QLineEdit; + +namespace Help { +namespace Internal { + +class HelpEngine; +class IndexListModel; +class IndexToolWindow; + +class IndexToolWidget : public QWidget +{ + Q_OBJECT +public: + IndexToolWidget(); + +signals: + void buildRequested(); + void escapePressed(); + +private: + friend class IndexToolWindow; + + bool eventFilter(QObject * o, QEvent * e); + void showEvent(QShowEvent *e); + void focusInEvent(QFocusEvent *e); + + bool wasInitialized; + QLineEdit *findLineEdit; + QListView *indicesView; +}; + + +class IndexToolWindow : public Core::IView +{ + Q_OBJECT + +public: + IndexToolWindow(const QList<int> &context, HelpEngine *help); + ~IndexToolWindow(); + + const QList<int> &context() const; + QWidget *widget(); + + QList<QWidget*> dockToolBarWidgets() const { return QList<QWidget*>(); } + + const char *uniqueViewName() const { return "Help.IndexToolWindow"; } + const char *globalMenuGroup() const { return "Help.Group"; } + inline QKeySequence defaultShortcut() const { return QKeySequence(); } + Qt::DockWidgetArea defaultArea() const { return Qt::RightDockWidgetArea; } + IView::ViewPosition defaultPosition() const { return IView::Second; } + +signals: + void showLinkRequested(const QString &link, bool newWindow); + void escapePressed(); + +private slots: + void indexDone(); + void searchInIndex(const QString &str); + void indexRequested(); + +private: + HelpEngine *helpEngine; + IndexListModel *model; + + QList<int> m_context; + IndexToolWidget *m_widget; +}; + +} // namespace Internal +} // namespace Help + +#endif // INDEXTOOLWINDOW_H diff --git a/src/plugins/help/indexwindow.h b/src/plugins/help/indexwindow.h new file mode 100644 index 00000000000..a0e43a3f2ac --- /dev/null +++ b/src/plugins/help/indexwindow.h @@ -0,0 +1,82 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#ifndef INDEXWINDOW +#define INDEXWINDOW + +#include <QtCore/QUrl> +#include <QtGui/QWidget> +#include <QtGui/QLineEdit> + +QT_BEGIN_NAMESPACE + +class QHelpIndexWidget; +class QHelpEngine; + +class IndexWindow : public QWidget +{ + Q_OBJECT + +public: + IndexWindow(QHelpEngine *helpEngine, QWidget *parent = 0); + ~IndexWindow(); + + void setSearchLineEditText(const QString &text); + QString searchLineEditText() const + { + return m_searchLineEdit->text(); + } + +signals: + void linkActivated(const QUrl &link); + void linksActivated(const QMap<QString, QUrl> &links, + const QString &keyword); + void escapePressed(); + +private slots: + void filterIndices(const QString &filter); + void enableSearchLineEdit(); + void disableSearchLineEdit(); + +private: + bool eventFilter(QObject *obj, QEvent *e); + void focusInEvent(QFocusEvent *e); + + QLineEdit *m_searchLineEdit; + QHelpIndexWidget *m_indexWidget; + QHelpEngine *m_helpEngine; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/help/searchwidget.cpp b/src/plugins/help/searchwidget.cpp new file mode 100644 index 00000000000..620ca7ef766 --- /dev/null +++ b/src/plugins/help/searchwidget.cpp @@ -0,0 +1,213 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "searchwidget.h" + +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStringList> + +#include <QtGui/QMenu> +#include <QtGui/QLayout> +#include <QtGui/QKeyEvent> +#include <QtGui/QClipboard> +#include <QtGui/QApplication> +#include <QtGui/QTextBrowser> + +#include <QtHelp/QHelpSearchEngine> +#include <QtHelp/QHelpSearchQueryWidget> +#include <QtHelp/QHelpSearchResultWidget> + +using namespace Help::Internal; + +SearchWidget::SearchWidget(QHelpSearchEngine *engine, QWidget *parent) + : QWidget(parent) + , zoomCount(0) + , searchEngine(engine) +{ + QVBoxLayout *vLayout = new QVBoxLayout(this); + + resultWidget = searchEngine->resultWidget(); + QHelpSearchQueryWidget *queryWidget = searchEngine->queryWidget(); + + vLayout->addWidget(queryWidget); + vLayout->addWidget(resultWidget); + + setFocusProxy(queryWidget); + + connect(queryWidget, SIGNAL(search()), this, SLOT(search())); + connect(resultWidget, SIGNAL(requestShowLink(const QUrl&)), + this, SIGNAL(requestShowLink(const QUrl&))); + + connect(searchEngine, SIGNAL(searchingStarted()), this, SLOT(searchingStarted())); + connect(searchEngine, SIGNAL(searchingFinished(int)), this, SLOT(searchingFinished(int))); +} + +SearchWidget::~SearchWidget() +{ + // nothing todo +} + +void SearchWidget::zoomIn() +{ +#ifndef QT_CLUCENE_SUPPORT + return; +#endif + + QTextBrowser* browser = qFindChild<QTextBrowser*>(resultWidget); + if (browser && zoomCount != 10) { + zoomCount++; + browser->zoomIn(); + } +} + +void SearchWidget::zoomOut() +{ +#ifndef QT_CLUCENE_SUPPORT + return; +#endif + + QTextBrowser* browser = qFindChild<QTextBrowser*>(resultWidget); + if (browser && zoomCount != -5) { + zoomCount--; + browser->zoomOut(); + } +} + +void SearchWidget::resetZoom() +{ +#ifndef QT_CLUCENE_SUPPORT + return; +#endif + + if (zoomCount == 0) + return; + + QTextBrowser* browser = qFindChild<QTextBrowser*>(resultWidget); + if (browser) { + browser->zoomOut(zoomCount); + zoomCount = 0; + } +} + +void SearchWidget::search() const +{ + QList<QHelpSearchQuery> query = searchEngine->queryWidget()->query(); + searchEngine->search(query); +} + +void SearchWidget::searchingStarted() +{ + qApp->setOverrideCursor(QCursor(Qt::WaitCursor)); +} + +void SearchWidget::searchingFinished(int hits) +{ + Q_UNUSED(hits) + qApp->restoreOverrideCursor(); +} + +void SearchWidget::keyPressEvent(QKeyEvent *keyEvent) +{ + if (keyEvent->key() == Qt::Key_Escape) + emit escapePressed(); +} + +void SearchWidget::contextMenuEvent(QContextMenuEvent *contextMenuEvent) +{ + QMenu menu; + QPoint point = contextMenuEvent->globalPos(); + +#ifdef QT_CLUCENE_SUPPORT + QTextBrowser* browser = qFindChild<QTextBrowser*>(resultWidget); + if (!browser) + return; + + point = browser->mapFromGlobal(point); + if (!browser->rect().contains(point, true)) + return; + + QUrl link = browser->anchorAt(point); + + QAction *copyAction = menu.addAction(tr("&Copy") + + QString(QLatin1String("\t") + QString(QKeySequence(Qt::CTRL | Qt::Key_C)))); + copyAction->setEnabled(QTextCursor(browser->textCursor()).hasSelection()); + + QAction *copyAnchorAction = menu.addAction(tr("Copy &Link Location")); + copyAnchorAction->setEnabled(!link.isEmpty() && link.isValid()); + + QAction *newTabAction = menu.addAction(tr("Open Link in New Tab") + + QString(QLatin1String("\t") + QString(QKeySequence(Qt::CTRL))) + + QLatin1String("LMB")); + newTabAction->setEnabled(!link.isEmpty() && link.isValid()); + + menu.addSeparator(); + + QAction *selectAllAction = menu.addAction(tr("Select All") + + QString(QLatin1String("\t") + QString(QKeySequence(Qt::CTRL | Qt::Key_A)))); + + QAction *usedAction = menu.exec(mapToGlobal(contextMenuEvent->pos())); + if (usedAction == copyAction) { + QTextCursor cursor = browser->textCursor(); + if (!cursor.isNull() && cursor.hasSelection()) { + QString selectedText = cursor.selectedText(); + QMimeData *data = new QMimeData(); + data->setText(selectedText); + QApplication::clipboard()->setMimeData(data); + } + } + else if (usedAction == copyAnchorAction) { + QApplication::clipboard()->setText(link.toString()); + } + else if (usedAction == newTabAction) { + emit requestShowLinkInNewTab(link); + } + else if (usedAction == selectAllAction) { + browser->selectAll(); + } +#else + point = resultWidget->mapFromGlobal(point); + QUrl link = resultWidget->linkAt(point); + if (link.isEmpty() || !link.isValid()) + return; + + QAction *curTab = menu.addAction(tr("Open Link")); + QAction *newTab = menu.addAction(tr("Open Link in New Tab")); + + QAction *action = menu.exec(mapToGlobal(contextMenuEvent->pos())); + if (curTab == action) + emit requestShowLink(link); + else if (newTab == action) + emit requestShowLinkInNewTab(link); +#endif +} diff --git a/src/plugins/help/searchwidget.h b/src/plugins/help/searchwidget.h new file mode 100644 index 00000000000..64143fafbfe --- /dev/null +++ b/src/plugins/help/searchwidget.h @@ -0,0 +1,88 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#ifndef SEARCHWIDGET_H +#define SEARCHWIDGET_H + +#include <QtCore/QUrl> +#include <QtCore/QPoint> + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +class QMouseEvent; +class QHelpSearchEngine; +class QHelpSearchResultWidget; + +QT_END_NAMESPACE + +namespace Help { +namespace Internal { + +class SearchWidget : public QWidget +{ + Q_OBJECT + +public: + SearchWidget(QHelpSearchEngine *engine, QWidget *parent = 0); + ~SearchWidget(); + + void zoomIn(); + void zoomOut(); + void resetZoom(); + +signals: + void requestShowLink(const QUrl &url); + void requestShowLinkInNewTab(const QUrl &url); + void escapePressed(); + +private slots: + void search() const; + void searchingStarted(); + void searchingFinished(int hits); + +private: + void keyPressEvent(QKeyEvent *keyEvent); + void contextMenuEvent(QContextMenuEvent *contextMenuEvent); + +private: + int zoomCount; + QHelpSearchEngine *searchEngine; + QHelpSearchResultWidget *resultWidget; +}; + +} // namespace Internal +} // namespace Help + +#endif // SEARCHWIDGET_H diff --git a/src/plugins/perforce/Perforce.mimetypes.xml b/src/plugins/perforce/Perforce.mimetypes.xml new file mode 100644 index 00000000000..3c2d3327710 --- /dev/null +++ b/src/plugins/perforce/Perforce.mimetypes.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="application/vnd.nokia.text.p4.submit"> + <comment>Perforce submit template</comment> + <sub-class-of type="text/plain"/> + <magic priority="50"> + <match value="# A Perforce Change Specification." type="string" offset="0"/> + </magic> + </mime-type> +</mime-info> diff --git a/src/plugins/perforce/Perforce.pluginspec b/src/plugins/perforce/Perforce.pluginspec new file mode 100644 index 00000000000..367b3afd79c --- /dev/null +++ b/src/plugins/perforce/Perforce.pluginspec @@ -0,0 +1,13 @@ +<plugin name="Perforce" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Perforce integration.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="TextEditor" version="0.9.1"/> + <dependency name="ProjectExplorer" version="0.9.1"/> + <dependency name="Core" version="0.9.1"/> + <dependency name="VCSBase" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/perforce/annotationhighlighter.cpp b/src/plugins/perforce/annotationhighlighter.cpp new file mode 100644 index 00000000000..ab4b855c4b1 --- /dev/null +++ b/src/plugins/perforce/annotationhighlighter.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "annotationhighlighter.h" + +namespace Perforce { +namespace Internal { + +PerforceAnnotationHighlighter::PerforceAnnotationHighlighter(const ChangeNumbers &changeNumbers, + QTextDocument *document) : + VCSBase::BaseAnnotationHighlighter(changeNumbers, document), + m_colon(QLatin1Char(':')) +{ +} + +QString PerforceAnnotationHighlighter::changeNumber(const QString &block) const +{ + const int pos = block.indexOf(m_colon); + return pos > 1 ? block.left(pos) : QString(); +} + +} +} diff --git a/src/plugins/perforce/annotationhighlighter.h b/src/plugins/perforce/annotationhighlighter.h new file mode 100644 index 00000000000..e162753c4b3 --- /dev/null +++ b/src/plugins/perforce/annotationhighlighter.h @@ -0,0 +1,58 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ANNOTATIONHIGHLIGHTER_H +#define ANNOTATIONHIGHLIGHTER_H + +#include <vcsbase/baseannotationhighlighter.h> + +namespace Perforce { +namespace Internal { + +// Annotation highlighter for p4 triggering on 'changenumber:' +class PerforceAnnotationHighlighter : public VCSBase::BaseAnnotationHighlighter +{ + Q_OBJECT +public: + explicit PerforceAnnotationHighlighter(const ChangeNumbers &changeNumbers, + QTextDocument *document = 0); + +private: + virtual QString changeNumber(const QString &block) const; + + const QChar m_colon; +}; + +} //namespace Perforce +} //namespace Internal + +#endif diff --git a/src/plugins/perforce/changenumberdialog.cpp b/src/plugins/perforce/changenumberdialog.cpp new file mode 100644 index 00000000000..8bf5827900d --- /dev/null +++ b/src/plugins/perforce/changenumberdialog.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtGui/QIntValidator> + +#include "changenumberdialog.h" + +using namespace Perforce::Internal; + +ChangeNumberDialog::ChangeNumberDialog(QWidget *parent) + : QDialog(parent) +{ + m_ui.setupUi(this); + m_ui.numberLineEdit->setValidator(new QIntValidator(0, 1000000, this)); +} + +int ChangeNumberDialog::number() const +{ + if (m_ui.numberLineEdit->text().isEmpty()) + return -1; + bool ok; + return m_ui.numberLineEdit->text().toInt(&ok); +} diff --git a/src/plugins/perforce/changenumberdialog.h b/src/plugins/perforce/changenumberdialog.h new file mode 100644 index 00000000000..cc2429f297a --- /dev/null +++ b/src/plugins/perforce/changenumberdialog.h @@ -0,0 +1,61 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CHANGENUMBERDIALOG_H +#define CHANGENUMBERDIALOG_H + +#include <QtGui/QDialog> + +#include "ui_changenumberdialog.h" + +namespace Perforce { +namespace Internal { + +// Input a change number for pending changes. +class ChangeNumberDialog : public QDialog +{ + Q_OBJECT +public: + ChangeNumberDialog(QWidget *parent = 0); + int number() const; + +private: + Ui::ChangeNumberDialog m_ui; + +}; + +} //namespace Perforce +} //namespace Internal + +#endif + + diff --git a/src/plugins/perforce/changenumberdialog.ui b/src/plugins/perforce/changenumberdialog.ui new file mode 100644 index 00000000000..6fbcbc32e3e --- /dev/null +++ b/src/plugins/perforce/changenumberdialog.ui @@ -0,0 +1,79 @@ +<ui version="4.0" > + <class>Perforce::Internal::ChangeNumberDialog</class> + <widget class="QDialog" name="Perforce::Internal::ChangeNumberDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>319</width> + <height>76</height> + </rect> + </property> + <property name="windowTitle" > + <string>Change Number</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="0" column="1" > + <widget class="QLineEdit" name="numberLineEdit" /> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Change Number:</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2" > + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Perforce::Internal::ChangeNumberDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>59</x> + <y>24</y> + </hint> + <hint type="destinationlabel" > + <x>160</x> + <y>38</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Perforce::Internal::ChangeNumberDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>59</x> + <y>24</y> + </hint> + <hint type="destinationlabel" > + <x>160</x> + <y>38</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/perforce/images/diff.png b/src/plugins/perforce/images/diff.png Binary files differnew file mode 100644 index 00000000000..b3597f9ff85 --- /dev/null +++ b/src/plugins/perforce/images/diff.png diff --git a/src/plugins/perforce/images/submit.png b/src/plugins/perforce/images/submit.png Binary files differnew file mode 100644 index 00000000000..4f302302b9e --- /dev/null +++ b/src/plugins/perforce/images/submit.png diff --git a/src/plugins/perforce/p4.h b/src/plugins/perforce/p4.h new file mode 100644 index 00000000000..70c18384910 --- /dev/null +++ b/src/plugins/perforce/p4.h @@ -0,0 +1,48 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef P4_API_INCL +#define P4_API_INCL + +#include <qconfig.h> + +#ifdef USE_P4_API +# +# if defined(Q_OS_WIN) && defined(SetPort) +# undef SetPort +# endif +# +# include <clientapi.h> +# include <diff.h> +#endif + +#endif // P4_API_INCL diff --git a/src/plugins/perforce/pendingchangesdialog.cpp b/src/plugins/perforce/pendingchangesdialog.cpp new file mode 100644 index 00000000000..971153ba978 --- /dev/null +++ b/src/plugins/perforce/pendingchangesdialog.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtCore/QRegExp> + +#include "pendingchangesdialog.h" + +using namespace Perforce::Internal; + +PendingChangesDialog::PendingChangesDialog(const QString &data, QWidget *parent) + : QDialog(parent) +{ + m_ui.setupUi(this); + if (!data.isEmpty()) { + QRegExp r("Change\\s(\\d+).*\\s\\*pending\\*\\s(.+)\n"); + r.setMinimal(true); + int pos = 0; + QListWidgetItem *item; + while ((pos = r.indexIn(data, pos)) != -1) { + item = new QListWidgetItem(tr("Change %1: %2").arg(r.cap(1)) + .arg(r.cap(2).trimmed()), m_ui.listWidget); + item->setData(234, r.cap(1).trimmed()); + ++pos; + } + } + m_ui.listWidget->setSelectionMode(QListWidget::SingleSelection); + if (m_ui.listWidget->count()) { + m_ui.listWidget->setCurrentRow(0); + m_ui.submitButton->setEnabled(true); + } else { + m_ui.submitButton->setEnabled(false); + } +} + +int PendingChangesDialog::changeNumber() const +{ + QListWidgetItem *item = m_ui.listWidget->item(m_ui.listWidget->currentRow()); + if (!item) + return -1; + bool ok = true; + int i = item->data(234).toInt(&ok); + return ok ? i : -1; +} diff --git a/src/plugins/perforce/pendingchangesdialog.h b/src/plugins/perforce/pendingchangesdialog.h new file mode 100644 index 00000000000..b31acf367a5 --- /dev/null +++ b/src/plugins/perforce/pendingchangesdialog.h @@ -0,0 +1,59 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PENDINGCHANGESDIALOG_H +#define PENDINGCHANGESDIALOG_H + +#include <QtGui/QDialog> + +#include "ui_pendingchangesdialog.h" + +namespace Perforce { +namespace Internal { + +class PendingChangesDialog : public QDialog +{ + Q_OBJECT + +public: + PendingChangesDialog(const QString &data, QWidget *parent = 0); + int changeNumber() const; + +private: + Ui::PendingChangesDialog m_ui; +}; + +} //namespace Perforce +} //namespace Internal + +#endif + diff --git a/src/plugins/perforce/pendingchangesdialog.ui b/src/plugins/perforce/pendingchangesdialog.ui new file mode 100644 index 00000000000..bcee662a027 --- /dev/null +++ b/src/plugins/perforce/pendingchangesdialog.ui @@ -0,0 +1,99 @@ +<ui version="4.0" > + <class>Perforce::Internal::PendingChangesDialog</class> + <widget class="QDialog" name="Perforce::Internal::PendingChangesDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>333</width> + <height>126</height> + </rect> + </property> + <property name="windowTitle" > + <string>P4 Pending Changes</string> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QListWidget" name="listWidget" /> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>131</width> + <height>31</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="submitButton" > + <property name="text" > + <string>Submit</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cancelButton" > + <property name="text" > + <string>Cancel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>submitButton</sender> + <signal>clicked()</signal> + <receiver>Perforce::Internal::PendingChangesDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>278</x> + <y>253</y> + </hint> + <hint type="destinationlabel" > + <x>96</x> + <y>254</y> + </hint> + </hints> + </connection> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>Perforce::Internal::PendingChangesDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>369</x> + <y>253</y> + </hint> + <hint type="destinationlabel" > + <x>179</x> + <y>282</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/perforce/perforce.pro b/src/plugins/perforce/perforce.pro new file mode 100644 index 00000000000..992777f1723 --- /dev/null +++ b/src/plugins/perforce/perforce.pro @@ -0,0 +1,38 @@ +TEMPLATE = lib +TARGET = Perforce + +include(../../qworkbenchplugin.pri) +include(perforce_dependencies.pri) + +HEADERS += p4.h \ + perforceplugin.h \ + perforceoutputwindow.h \ + settingspage.h \ + perforceeditor.h \ + changenumberdialog.h \ + perforcesubmiteditor.h \ + pendingchangesdialog.h \ + perforceconstants.h \ + perforceversioncontrol.h \ + perforcesettings.h \ + annotationhighlighter.h \ + perforcesubmiteditorwidget.h + +SOURCES += perforceplugin.cpp \ + perforceoutputwindow.cpp \ + settingspage.cpp \ + perforceeditor.cpp \ + changenumberdialog.cpp \ + perforcesubmiteditor.cpp \ + pendingchangesdialog.cpp \ + perforceversioncontrol.cpp \ + perforcesettings.cpp \ + annotationhighlighter.cpp \ + perforcesubmiteditorwidget.cpp + +FORMS += settingspage.ui \ + changenumberdialog.ui \ + pendingchangesdialog.ui \ + submitpanel.ui + +RESOURCES += perforce.qrc diff --git a/src/plugins/perforce/perforce.qrc b/src/plugins/perforce/perforce.qrc new file mode 100644 index 00000000000..de0db02a3d9 --- /dev/null +++ b/src/plugins/perforce/perforce.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/trolltech.perforce" > + <file>images/diff.png</file> + <file>images/submit.png</file> + <file>Perforce.mimetypes.xml</file> + </qresource> +</RCC> diff --git a/src/plugins/perforce/perforce_dependencies.pri b/src/plugins/perforce/perforce_dependencies.pri new file mode 100644 index 00000000000..9e7c28e9e1a --- /dev/null +++ b/src/plugins/perforce/perforce_dependencies.pri @@ -0,0 +1,5 @@ +include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/texteditor/texteditor.pri) +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/vcsbase/vcsbase.pri) +include(../../libs/utils/utils.pri) diff --git a/src/plugins/perforce/perforceconstants.h b/src/plugins/perforce/perforceconstants.h new file mode 100644 index 00000000000..57cfb7ef3a3 --- /dev/null +++ b/src/plugins/perforce/perforceconstants.h @@ -0,0 +1,53 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PERFORCE_CONSTANTS_H +#define PERFORCE_CONSTANTS_H + +namespace Perforce { + namespace Constants { + const char * const C_PERFORCEEDITOR = "Perforce Editor"; + + const char * const PERFORCEEDITOR_KIND = "Perforce Editor"; + const char * const C_PERFORCESUBMITEDITOR = "Perforce Submit Editor"; + const char * const PERFORCESUBMITEDITOR_KIND = "Perforce Submit Editor"; + const char * const ICON_SUBMIT = ":/trolltech.perforce/images/submit.png"; + const char * const ICON_DIFF = ":/trolltech.perforce/images/diff.png"; + const char * const SUBMIT_CURRENT = "Nokia.Perforce.SubmitCurrentLog"; + const char * const DIFF_SELECTED = "Nokia.Perforce.DiffSelectedFilesInLog"; + const char * const SUBMIT_MIMETYPE = "application/vnd.nokia.text.p4.submit"; + + enum { debug = 0 }; + } +} + +#endif // PERFORCE_CONSTANTS_H diff --git a/src/plugins/perforce/perforceeditor.cpp b/src/plugins/perforce/perforceeditor.cpp new file mode 100644 index 00000000000..dd9da666f29 --- /dev/null +++ b/src/plugins/perforce/perforceeditor.cpp @@ -0,0 +1,159 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "perforceeditor.h" +#include "annotationhighlighter.h" +#include "perforceplugin.h" +#include "perforceconstants.h" +#include "perforceplugin.h" + +#include <vcsbase/diffhighlighter.h> +#include <coreplugin/editormanager/editormanager.h> + +#include <QtCore/QFileInfo> +#include <QtCore/QTextStream> +#include <QtCore/QSet> +#include <QtCore/QRegExp> +#include <QtCore/QDebug> +#include <QtGui/QKeyEvent> +#include <QtGui/QLayout> +#include <QtGui/QTextEdit> +#include <QtGui/QMenu> +#include <QtGui/QAction> +#include <QtGui/QTextCursor> +#include <QtCore/QProcess> + +namespace Perforce { +namespace Internal { + +// ------------ PerforceEditor +PerforceEditor::PerforceEditor(const VCSBase::VCSBaseEditorParameters *type, + QWidget *parent) : + VCSBase::VCSBaseEditor(type, parent), + m_changeNumberPattern(QLatin1String("^\\d+$")), + m_plugin(PerforcePlugin::perforcePluginInstance()) +{ + Q_ASSERT(m_changeNumberPattern.isValid()); + if (Perforce::Constants::debug) + qDebug() << "PerforceEditor::PerforceEditor" << type->type << type->kind; +} + +QSet<QString> PerforceEditor::annotationChanges() const +{ + QSet<QString> changes; + const QString txt = toPlainText(); + if (txt.isEmpty()) + return changes; + // Hunt for first change number in annotation: "<change>:" + QRegExp r(QLatin1String("^(\\d+):")); + Q_ASSERT(r.isValid()); + if (r.indexIn(txt) != -1) { + changes.insert(r.cap(1)); + r.setPattern(QLatin1String("\n(\\d+):")); + Q_ASSERT(r.isValid()); + int pos = 0; + while ((pos = r.indexIn(txt, pos)) != -1) { + pos += r.matchedLength(); + changes.insert(r.cap(1)); + } + } + if (Perforce::Constants::debug) + qDebug() << "PerforceEditor::annotationChanges() returns #" << changes.size(); + return changes; +} + +QString PerforceEditor::changeUnderCursor(const QTextCursor &c) const +{ + QTextCursor cursor = c; + // Any number is regarded as change number. + cursor.select(QTextCursor::WordUnderCursor); + if (!cursor.hasSelection()) + return QString(); + const QString change = cursor.selectedText(); + return m_changeNumberPattern.exactMatch(change) ? change : QString(); +} + +VCSBase::DiffHighlighter *PerforceEditor::createDiffHighlighter() const +{ + const QRegExp filePattern(QLatin1String("^====.*")); + return new VCSBase::DiffHighlighter(filePattern); +} + +VCSBase::BaseAnnotationHighlighter *PerforceEditor::createAnnotationHighlighter(const QSet<QString> &changes) const +{ + return new PerforceAnnotationHighlighter(changes); +} + +QString PerforceEditor::fileNameFromDiffSpecification(const QTextBlock &inBlock) const +{ + QString errorMessage; + const QString diffIndicator = QLatin1String("==== "); + const QString diffEndIndicator = QLatin1String(" ===="); + // Go back chunks. Note that for 'describe', an extra, empty line + // occurs. + for (QTextBlock block = inBlock; block.isValid(); block = block.previous()) { + QString diffFileName = block.text(); + if (diffFileName.startsWith(diffIndicator) && diffFileName.endsWith(diffEndIndicator)) { + // Split: + // 1) "==== //depot/.../mainwindow.cpp#2 - /depot/.../mainwindow.cpp ====" + // (as created by p4 diff) or + // 2) "==== //depot/.../mainwindow.cpp#15 (text) ====" + // (as created by p4 describe). + diffFileName.remove(0, diffIndicator.size()); + diffFileName.truncate(diffFileName.size() - diffEndIndicator.size()); + const int separatorPos = diffFileName.indexOf(QLatin1String(" - ")); + if (separatorPos == -1) { + // ==== depot path (text) ==== (p4 describe) + const int blankPos = diffFileName.indexOf(QLatin1Char(' ')); + if (blankPos == -1) + return QString(); + diffFileName.truncate(blankPos); + } else { + // ==== depot path - local path ==== (p4 diff) + diffFileName.truncate(separatorPos); + } + // Split off revision "#4" + const int revisionPos = diffFileName.lastIndexOf(QLatin1Char('#')); + if (revisionPos != -1 && revisionPos < diffFileName.length() - 1) + diffFileName.truncate(revisionPos); + // Ask plugin to map back + const QString fileName = m_plugin->fileNameFromPerforceName(diffFileName.trimmed(), &errorMessage); + if (fileName.isEmpty()) + qWarning(errorMessage.toUtf8().constData()); + return fileName; + } + } + return QString(); +} + +} // namespace Internal +} // namespace Perforce diff --git a/src/plugins/perforce/perforceeditor.h b/src/plugins/perforce/perforceeditor.h new file mode 100644 index 00000000000..2ca9b0304eb --- /dev/null +++ b/src/plugins/perforce/perforceeditor.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PERFORCEEDITOR_H +#define PERFORCEEDITOR_H + +#include <vcsbase/vcsbaseeditor.h> + +#include <QtCore/QRegExp> + +namespace Perforce { +namespace Internal { + +class PerforcePlugin; + +class PerforceEditor : public VCSBase::VCSBaseEditor +{ + Q_OBJECT + +public: + explicit PerforceEditor(const VCSBase::VCSBaseEditorParameters *type, + QWidget *parent); + +private: + virtual QSet<QString> annotationChanges() const; + virtual QString changeUnderCursor(const QTextCursor &) const; + virtual VCSBase::DiffHighlighter *createDiffHighlighter() const; + virtual VCSBase::BaseAnnotationHighlighter *createAnnotationHighlighter(const QSet<QString> &changes) const; + virtual QString fileNameFromDiffSpecification(const QTextBlock &diffFileName) const; + + const QRegExp m_changeNumberPattern; + PerforcePlugin *m_plugin; +}; + +} // namespace Perforce +} // namespace Internal + +#endif // PERFORCEEDITOR_H diff --git a/src/plugins/perforce/perforceoutputwindow.cpp b/src/plugins/perforce/perforceoutputwindow.cpp new file mode 100644 index 00000000000..4d37d6a9afb --- /dev/null +++ b/src/plugins/perforce/perforceoutputwindow.cpp @@ -0,0 +1,166 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtGui/QKeyEvent> +#include <QtGui/QMouseEvent> +#include <QtGui/QMenu> +#include <QtGui/QAction> +#include <QtGui/QListWidget> + +#include "perforceoutputwindow.h" +#include "perforceplugin.h" + +using namespace Perforce::Internal; + +PerforceOutputWindow::PerforceOutputWindow(PerforcePlugin *p4Plugin) + : m_p4Plugin(p4Plugin) +{ + m_outputListWidget = new QListWidget; + m_outputListWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_outputListWidget->setFrameStyle(QFrame::NoFrame); + + m_outputListWidget->setWindowTitle(tr("Perforce Output")); + + m_diffAction = new QAction(tr("Diff"), this); + connect(m_diffAction, SIGNAL(triggered()), this, SLOT(diff())); + + connect(m_outputListWidget, SIGNAL(itemActivated(QListWidgetItem*)), + this, SLOT(openFiles())); +} + +PerforceOutputWindow::~PerforceOutputWindow() +{ + delete m_outputListWidget; +} + +bool PerforceOutputWindow::hasFocus() +{ + return m_outputListWidget->hasFocus(); +} + +bool PerforceOutputWindow::canFocus() +{ + return false; +} + +void PerforceOutputWindow::setFocus() +{ + +} + +QWidget *PerforceOutputWindow::outputWidget(QWidget *parent) +{ + m_outputListWidget->setParent(parent); + return m_outputListWidget; +} + +QString PerforceOutputWindow::name() const +{ + return tr("Perforce"); +} + +void PerforceOutputWindow::clearContents() +{ + m_outputListWidget->clear(); +} + +void PerforceOutputWindow::visibilityChanged(bool /* b */) +{ + +} + +void PerforceOutputWindow::append(const QString &txt, bool doPopup) +{ + const QStringList lines = txt.split(QLatin1Char('\n')); + foreach (const QString &s, lines) + m_outputListWidget->addItem(s); + m_outputListWidget->scrollToBottom(); + if (doPopup) + popup(); +} + +void PerforceOutputWindow::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu *menu = new QMenu(m_outputListWidget); + menu->addAction(m_diffAction); + menu->exec(event->globalPos()); + delete menu; +} + +void PerforceOutputWindow::diff() +{ + QStringList files; + foreach (QListWidgetItem *i, m_outputListWidget->selectedItems()) { + if (m_outputListWidget->row(i) > 0) + files.append(getFileName(i)); + } + if (files.count() == 0 && m_outputListWidget->row(m_outputListWidget->currentItem()) > 0) + files.append(getFileName(m_outputListWidget->currentItem())); + + m_p4Plugin->p4Diff(files); +} + +QString PerforceOutputWindow::getFileName(const QListWidgetItem *item) +{ + QString fileName; + if (!item || item->text().isEmpty()) + return fileName; + + QString line = item->text(); + QRegExp regExp("(/.+)#\\d+\\s-\\s(.+)$"); + regExp.setMinimal(true); + if (regExp.indexIn(line) > -1 && regExp.numCaptures() >= 1) { + fileName = regExp.cap(1); + QString description; + if (regExp.numCaptures() >= 2) + description = regExp.cap(2); + } + return fileName; +} + +void PerforceOutputWindow::openFiles() +{ + QStringList files; + foreach (QListWidgetItem *i, m_outputListWidget->selectedItems()) { + if (m_outputListWidget->row(i) > 0) + files.append(getFileName(i)); + } + if (files.count() == 0 && m_outputListWidget->row(m_outputListWidget->currentItem()) > 0) + files.append(getFileName(m_outputListWidget->currentItem())); + + m_p4Plugin->openFiles(files); +} + +int PerforceOutputWindow::priorityInStatusBar() const +{ + return -1; +} diff --git a/src/plugins/perforce/perforceoutputwindow.h b/src/plugins/perforce/perforceoutputwindow.h new file mode 100644 index 00000000000..7391ca667de --- /dev/null +++ b/src/plugins/perforce/perforceoutputwindow.h @@ -0,0 +1,87 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PERFORCEOUTPUTWINDOW_H +#define PERFORCEOUTPUTWINDOW_H + +#include <coreplugin/ioutputpane.h> + +#include <QtGui/QAction> +#include <QtGui/QListWidget> +#include <QtGui/QListWidgetItem> + +namespace Perforce { +namespace Internal { + +class PerforcePlugin; + +class PerforceOutputWindow : public Core::IOutputPane +{ + Q_OBJECT + +public: + PerforceOutputWindow(PerforcePlugin *p4Plugin); + ~PerforceOutputWindow(); + + QWidget *outputWidget(QWidget *parent); + QList<QWidget*> toolBarWidgets(void) const { return QList<QWidget *>(); } + + QString name() const; + int priorityInStatusBar() const; + void clearContents(); + void visibilityChanged(bool visible); + + bool canFocus(); + bool hasFocus(); + void setFocus(); + +public slots: + void append(const QString &txt, bool doPopup = false); + +private slots:; + void diff(); + void openFiles(); + +private: + void contextMenuEvent(QContextMenuEvent *event); + + static QString getFileName(const QListWidgetItem *item); + + PerforcePlugin *m_p4Plugin; + QListWidget *m_outputListWidget; + QAction *m_diffAction; +}; + +} // namespace Perforce +} // namespace Internal + +#endif diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp new file mode 100644 index 00000000000..b3d2f43a26e --- /dev/null +++ b/src/plugins/perforce/perforceplugin.cpp @@ -0,0 +1,1270 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "p4.h" +#include "perforceplugin.h" +#include "perforceoutputwindow.h" +#include "settingspage.h" +#include "perforcesubmiteditor.h" +#include "changenumberdialog.h" +#include "perforceconstants.h" +#include "perforceversioncontrol.h" +#include "perforceeditor.h" +#include "pendingchangesdialog.h" + +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/filemanager.h> +#include <coreplugin/messagemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> +#include <utils/synchronousprocess.h> +#include <vcsbase/basevcseditorfactory.h> +#include <vcsbase/basevcssubmiteditorfactory.h> +#include <vcsbase/vcsbaseeditor.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtCore/QTemporaryFile> +#include <QtCore/QDir> +#include <QtCore/QSettings> +#include <QtCore/QTextCodec> +#include <QtGui/QAction> +#include <QtGui/QMenu> +#include <QtGui/QMessageBox> +#include <QtGui/QMainWindow> +#include <QtGui/QFileDialog> + +using namespace Perforce::Internal; + +enum { p4Timeout = 20000 }; + +static const VCSBase::VCSBaseEditorParameters editorParameters[] = { +{ + VCSBase::RegularCommandOutput, + "Perforce Command Log Editor", + Perforce::Constants::C_PERFORCEEDITOR, + "application/vnd.nokia.text.scs_commandlog", + "scslog"}, +{ VCSBase::LogOutput, + "Perforce File Log Editor", + Perforce::Constants::C_PERFORCEEDITOR, + "application/vnd.nokia.text.scs_filelog", + "scsfilelog"}, +{ VCSBase::AnnotateOutput, + "Perforce Annotation Editor", + Perforce::Constants::C_PERFORCEEDITOR, + "application/vnd.nokia.text.scs_annotation", + "scsannotate"}, +{ VCSBase::DiffOutput, + "Perforce Diff Editor", + Perforce::Constants::C_PERFORCEEDITOR, + "text/x-patch","diff"} +}; + +// Utility to find a parameter set by type +static inline const VCSBase::VCSBaseEditorParameters *findType(int ie) +{ + const VCSBase::EditorContentType et = static_cast<VCSBase::EditorContentType>(ie); + return VCSBase::VCSBaseEditor::findType(editorParameters, sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters), et); +} + +static inline QString debugCodec(const QTextCodec *c) +{ + return c ? QString::fromAscii(c->name()) : QString::fromAscii("Null codec"); +} + +const char * const PerforcePlugin::PERFORCE_MENU = "Perforce.Menu"; +const char * const PerforcePlugin::EDIT = "Perforce.Edit"; +const char * const PerforcePlugin::ADD = "Perforce.Add"; +const char * const PerforcePlugin::DELETE_FILE = "Perforce.Delete"; +const char * const PerforcePlugin::OPENED = "Perforce.Opened"; +const char * const PerforcePlugin::REVERT = "Perforce.Revert"; +const char * const PerforcePlugin::DIFF_CURRENT = "Perforce.DiffCurrent"; +const char * const PerforcePlugin::DIFF_PROJECT = "Perforce.DiffProject"; +const char * const PerforcePlugin::DIFF_ALL = "Perforce.DiffAll"; +const char * const PerforcePlugin::RESOLVE = "Perforce.Resolve"; +const char * const PerforcePlugin::SUBMIT = "Perforce.Submit"; +const char * const PerforcePlugin::PENDING_CHANGES = "Perforce.PendingChanges"; +const char * const PerforcePlugin::DESCRIBE = "Perforce.Describe"; +const char * const PerforcePlugin::ANNOTATE_CURRENT = "Perforce.AnnotateCurrent"; +const char * const PerforcePlugin::ANNOTATE = "Perforce.Annotate"; +const char * const PerforcePlugin::FILELOG_CURRENT = "Perforce.FilelogCurrent"; +const char * const PerforcePlugin::FILELOG = "Perforce.Filelog"; +const char * const PerforcePlugin::SEPARATOR1 = "Perforce.Separator1"; +const char * const PerforcePlugin::SEPARATOR2 = "Perforce.Separator2"; +const char * const PerforcePlugin::SEPARATOR3 = "Perforce.Separator3"; + +//// +// CoreListener +//// + +bool CoreListener::editorAboutToClose(Core::IEditor *editor) +{ + return m_plugin->editorAboutToClose(editor); +} + +//// +// PerforcePlugin +//// + +Core::ICore *PerforcePlugin::m_coreInstance = NULL; +PerforcePlugin *PerforcePlugin::m_perforcePluginInstance = NULL; + +PerforcePlugin::PerforcePlugin() : + m_perforceOutputWindow(0), + m_settingsPage(0), + m_editAction(0), + m_addAction(0), + m_deleteAction(0), + m_openedAction(0), + m_revertAction(0), + m_diffCurrentAction(0), + m_diffProjectAction(0), + m_diffAllAction(0), + m_resolveAction(0), + m_submitAction(0), + m_pendingAction(0), + m_describeAction(0), + m_annotateCurrentAction(0), + m_annotateAction(0), + m_filelogCurrentAction(0), + m_filelogAction(0), + m_changeTmpFile(0), +#ifdef USE_P4_API + m_workbenchClientUser(0), +#endif + m_coreListener(0), + m_submitEditorFactory(0), + m_versionControl(0) +{ +} + +static const VCSBase::VCSBaseSubmitEditorParameters submitParameters = { + Perforce::Constants::SUBMIT_MIMETYPE, + Perforce::Constants::PERFORCESUBMITEDITOR_KIND, + Perforce::Constants::C_PERFORCESUBMITEDITOR, + Core::Constants::UNDO, + Core::Constants::REDO, + Perforce::Constants::SUBMIT_CURRENT, + Perforce::Constants::DIFF_SELECTED +}; + +bool PerforcePlugin::initialize(const QStringList & /*arguments*/, QString *errorMessage) +{ + typedef VCSBase::VCSEditorFactory<PerforceEditor> PerforceEditorFactory; + typedef VCSBase::VCSSubmitEditorFactory<PerforceSubmitEditor> PerforceSubmitEditorFactory; + + m_coreInstance = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + if (!m_coreInstance->mimeDatabase()->addMimeTypes(QLatin1String(":/trolltech.perforce/Perforce.mimetypes.xml"), errorMessage)) + return false; + m_perforcePluginInstance = this; + + if (QSettings *settings = m_coreInstance->settings()) + m_settings.fromSettings(settings); + + m_perforceOutputWindow = new PerforceOutputWindow(this); + addObject(m_perforceOutputWindow); + + m_settingsPage = new SettingsPage; + addObject(m_settingsPage); + + // Editor factories + m_submitEditorFactory = new PerforceSubmitEditorFactory(&submitParameters); + addObject(m_submitEditorFactory); + + static const char *describeSlot = SLOT(describe(QString,QString)); + const int editorCount = sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters); + for (int i = 0; i < editorCount; i++) { + m_editorFactories.push_back(new PerforceEditorFactory(editorParameters + i, m_coreInstance, this, describeSlot)); + addObject(m_editorFactories.back()); + } + + m_versionControl = new PerforceVersionControl(this); + addObject(m_versionControl); + +#ifdef USE_P4_API + m_workbenchClientUser = new WorkbenchClientUser(m_perforceOutputWindow, this); + m_enableP4APIActions = true; +#endif + + m_coreListener = new CoreListener(this); + addObject(m_coreListener); + + + //register actions + Core::ActionManagerInterface *am = m_coreInstance->actionManager(); + + Core::IActionContainer *mtools = + am->actionContainer(Core::Constants::M_TOOLS); + + Core::IActionContainer *mperforce = + am->createMenu(QLatin1String(PERFORCE_MENU)); + mperforce->menu()->setTitle(tr("&Perforce")); + mtools->addMenu(mperforce); + + QList<int> globalcontext; + globalcontext << Core::Constants::C_GLOBAL_ID; + + QList<int> perforcesubmitcontext; + perforcesubmitcontext << + m_coreInstance->uniqueIDManager()->uniqueIdentifier(Constants::C_PERFORCESUBMITEDITOR); + + Core::ICommand *command; + QAction *tmpaction; + + m_editAction = new QAction(tr("Edit"), this); + command = am->registerAction(m_editAction, PerforcePlugin::EDIT, globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+E"))); + command->setDefaultText(tr("Edit File")); + connect(m_editAction, SIGNAL(triggered()), this, SLOT(openCurrentFile())); + mperforce->addAction(command); + + m_addAction = new QAction(tr("Add"), this); + command = am->registerAction(m_addAction, PerforcePlugin::ADD, globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+A"))); + command->setDefaultText(tr("Add File")); + connect(m_addAction, SIGNAL(triggered()), this, SLOT(addCurrentFile())); + mperforce->addAction(command); + + m_deleteAction = new QAction(tr("Delete"), this); + command = am->registerAction(m_deleteAction, PerforcePlugin::DELETE_FILE, globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + command->setDefaultText(tr("Delete File")); + connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(deleteCurrentFile())); + mperforce->addAction(command); + + m_revertAction = new QAction(tr("Revert"), this); + command = am->registerAction(m_revertAction, PerforcePlugin::REVERT, globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+R"))); + command->setDefaultText(tr("Revert File")); + connect(m_revertAction, SIGNAL(triggered()), this, SLOT(revertCurrentFile())); + mperforce->addAction(command); + + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + command = am->registerAction(tmpaction, QLatin1String("Perforce.Sep.Edit"), globalcontext); + mperforce->addAction(command); + + m_diffCurrentAction = new QAction(tr("Diff Current File"), this); + command = am->registerAction(m_diffCurrentAction, PerforcePlugin::DIFF_CURRENT, globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + command->setDefaultText(tr("Diff Current File")); + connect(m_diffCurrentAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile())); + mperforce->addAction(command); + + m_diffProjectAction = new QAction(tr("Diff Current Project/Session"), this); + command = am->registerAction(m_diffProjectAction, PerforcePlugin::DIFF_PROJECT, globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+D"))); + command->setDefaultText(tr("Diff Current Project/Session")); + connect(m_diffProjectAction, SIGNAL(triggered()), this, SLOT(diffCurrentProject())); + mperforce->addAction(command); + + m_diffAllAction = new QAction(tr("Diff Opened Files"), this); + command = am->registerAction(m_diffAllAction, PerforcePlugin::DIFF_ALL, globalcontext); + connect(m_diffAllAction, SIGNAL(triggered()), this, SLOT(diffAllOpened())); + mperforce->addAction(command); + + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + command = am->registerAction(tmpaction, QLatin1String("Perforce.Sep.Diff"), globalcontext); + mperforce->addAction(command); + + m_openedAction = new QAction(tr("Opened"), this); + command = am->registerAction(m_openedAction, PerforcePlugin::OPENED, globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+O"))); + connect(m_openedAction, SIGNAL(triggered()), this, SLOT(printOpenedFileList())); + mperforce->addAction(command); + +#ifdef USE_P4_API + m_resolveAction = new QAction(tr("Resolve"), this); + command = am->registerAction(m_resolveAction, PerforcePlugin::RESOLVE, globalcontext); + connect(m_resolveAction, SIGNAL(triggered()), this, SLOT(resolve())); + mperforce->addAction(command); +#endif + + m_submitAction = new QAction(tr("Submit Project"), this); + command = am->registerAction(m_submitAction, PerforcePlugin::SUBMIT, globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+S"))); + connect(m_submitAction, SIGNAL(triggered()), this, SLOT(submit())); + mperforce->addAction(command); + + m_pendingAction = new QAction(tr("Pending Changes..."), this); + command = am->registerAction(m_pendingAction, PerforcePlugin::PENDING_CHANGES, globalcontext); + connect(m_pendingAction, SIGNAL(triggered()), this, SLOT(printPendingChanges())); + mperforce->addAction(command); + + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + command = am->registerAction(tmpaction, QLatin1String("Perforce.Sep.Changes"), globalcontext); + mperforce->addAction(command); + + m_describeAction = new QAction(tr("Describe..."), this); + command = am->registerAction(m_describeAction, PerforcePlugin::DESCRIBE, globalcontext); + connect(m_describeAction, SIGNAL(triggered()), this, SLOT(describeChange())); + mperforce->addAction(command); + + m_annotateCurrentAction = new QAction(tr("Annotate Current File"), this); + command = am->registerAction(m_annotateCurrentAction, PerforcePlugin::ANNOTATE_CURRENT, globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + command->setDefaultText(tr("Annotate Current File")); + connect(m_annotateCurrentAction, SIGNAL(triggered()), this, SLOT(annotateCurrentFile())); + mperforce->addAction(command); + + m_annotateAction = new QAction(tr("Annotate..."), this); + command = am->registerAction(m_annotateAction, PerforcePlugin::ANNOTATE, globalcontext); + connect(m_annotateAction, SIGNAL(triggered()), this, SLOT(annotate())); + mperforce->addAction(command); + + m_filelogCurrentAction = new QAction(tr("Filelog Current File"), this); + command = am->registerAction(m_filelogCurrentAction, PerforcePlugin::FILELOG_CURRENT, globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + command->setDefaultKeySequence(QKeySequence(tr("Alt+P,Alt+F"))); + command->setDefaultText(tr("Filelog Current File")); + connect(m_filelogCurrentAction, SIGNAL(triggered()), this, SLOT(filelogCurrentFile())); + mperforce->addAction(command); + + m_filelogAction = new QAction(tr("Filelog..."), this); + command = am->registerAction(m_filelogAction, PerforcePlugin::FILELOG, globalcontext); + connect(m_filelogAction, SIGNAL(triggered()), this, SLOT(filelog())); + mperforce->addAction(command); + + m_submitCurrentLogAction = new QAction(QIcon(Constants::ICON_SUBMIT), tr("Submit"), this); + command = am->registerAction(m_submitCurrentLogAction, Constants::SUBMIT_CURRENT, perforcesubmitcontext); + connect(m_submitCurrentLogAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog())); + + m_diffSelectedFiles = new QAction(QIcon(Constants::ICON_DIFF), tr("Diff Selected Files"), this); + command = am->registerAction(m_diffSelectedFiles, Constants::DIFF_SELECTED, perforcesubmitcontext); + + m_undoAction = new QAction(tr("&Undo"), this); + command = am->registerAction(m_undoAction, Core::Constants::UNDO, perforcesubmitcontext); + + m_redoAction = new QAction(tr("&Redo"), this); + command = am->registerAction(m_redoAction, Core::Constants::REDO, perforcesubmitcontext); + + connect(m_coreInstance, SIGNAL(contextChanged(Core::IContext *)), + this, SLOT(updateActions())); + + connect(m_coreInstance->fileManager(), SIGNAL(currentFileChanged(const QString &)), + this, SLOT(updateActions())); + + return true; +} + +void PerforcePlugin::extensionsInitialized() +{ + m_projectExplorer = ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::ProjectExplorerPlugin>(); + if (m_projectExplorer) { + connect(m_projectExplorer, + SIGNAL(currentProjectChanged(ProjectExplorer::Project*)), + this, SLOT(updateActions())); + } + updateActions(); +} + +void PerforcePlugin::openCurrentFile() +{ + runP4Cmd(QStringList() << QLatin1String("edit") << currentFileName(), QStringList(), true); +} + +void PerforcePlugin::addCurrentFile() +{ + runP4Cmd(QStringList() << QLatin1String("add") << currentFileName(), QStringList(), true); +} + +void PerforcePlugin::deleteCurrentFile() +{ + runP4Cmd(QStringList() << QLatin1String("delete") << currentFileName(), QStringList(), true); +} + +void PerforcePlugin::revertCurrentFile() +{ + Q_ASSERT(m_coreInstance); + + const QString fileName = currentFileName(); + QTextCodec *codec = VCSBase::VCSBaseEditor::getCodec(m_coreInstance, fileName); + QStringList args; + args << QLatin1String("diff") << QLatin1String("-sa"); + PerforceResponse result = runP4Cmd(args, QStringList(), false, true, codec); + if (result.error) + return; + + if (!result.stdOut.isEmpty()) { + bool doNotRevert = QMessageBox::warning(0, tr("p4 revert"), + tr("The file has been changed. Do you want to revert it?"), + QMessageBox::Yes, QMessageBox::No) + == QMessageBox::No; + if (doNotRevert) + return; + } + + Core::FileManager *fm = m_coreInstance->fileManager(); + QList<Core::IFile *> files = fm->managedFiles(fileName); + foreach (Core::IFile *file, files) { + fm->blockFileChange(file); + } + PerforceResponse result2 = runP4Cmd(QStringList() << QLatin1String("revert") << fileName, QStringList(), true); + Core::IFile::ReloadBehavior tempBehavior = + Core::IFile::ReloadAll; + foreach (Core::IFile *file, files) { + file->modified(&tempBehavior); + fm->unblockFileChange(file); + } +} + +void PerforcePlugin::diffCurrentFile() +{ + p4Diff(QStringList(currentFileName())); +} + +void PerforcePlugin::diffCurrentProject() +{ + Q_ASSERT(m_projectExplorer); + QStringList files; + QString name; + ProjectExplorer::Project *currentProject = m_projectExplorer->currentProject(); + if (currentProject) { + files << currentProject->files(ProjectExplorer::Project::ExcludeGeneratedFiles); + name = currentProject->name(); + } else if (m_projectExplorer->session()) { + name = m_projectExplorer->session()->file()->fileName(); + QList<ProjectExplorer::Project *> projects = m_projectExplorer->session()->projects(); + foreach (ProjectExplorer::Project *project, projects) + files << project->files(ProjectExplorer::Project::ExcludeGeneratedFiles); + } + QStringList nativeFiles; + foreach (const QString &f, files) + nativeFiles << QDir::toNativeSeparators(f); + p4Diff(nativeFiles, name); +} + +void PerforcePlugin::diffAllOpened() +{ + p4Diff(QStringList()); +} + +void PerforcePlugin::printOpenedFileList() +{ + Core::IEditor *e = m_coreInstance->editorManager()->currentEditor(); + if (e) + e->widget()->setFocus(); + PerforceResponse result = runP4Cmd(QStringList() << QLatin1String("opened"), QStringList(), true); +} + +#ifdef USE_P4_API +void PerforcePlugin::resolve() +{ + m_workbenchClientUser->setMode(WorkbenchClientUser::Resolve); + runP4APICmd(QLatin1String("resolve")); +} +#endif + +void PerforcePlugin::submit() +{ + Q_ASSERT(m_coreInstance); + if (!checkP4Command()) { + showOutput(tr("No p4 executable specified!")); + return; + } + + if (m_changeTmpFile) { + showOutput(tr("Another submit is currently executed.")); + m_perforceOutputWindow->popup(false); + return; + } + + m_changeTmpFile = new QTemporaryFile(this); + if (!m_changeTmpFile->open()) { + showOutput(tr("Cannot create temporary file.")); + delete m_changeTmpFile; + m_changeTmpFile = 0; + return; + } + + PerforceResponse result = runP4Cmd(QStringList()<< QLatin1String("change") << QLatin1String("-o"), QStringList(), false); + if (result.error) { + delete m_changeTmpFile; + m_changeTmpFile = 0; + return; + } + + m_changeTmpFile->write(result.stdOut.toAscii()); + m_changeTmpFile->seek(0); + + // Assemble file list of project + Q_ASSERT(m_projectExplorer); + QStringList files; + QString name; + ProjectExplorer::Project *currentProject = m_projectExplorer->currentProject(); + if (currentProject) { + files << currentProject->files(ProjectExplorer::Project::ExcludeGeneratedFiles); + name = currentProject->name(); + } else if (m_projectExplorer->session()) { + name = m_projectExplorer->session()->file()->fileName(); + QList<ProjectExplorer::Project *> projects = m_projectExplorer->session()->projects(); + foreach (ProjectExplorer::Project *project, projects) + files << project->files(ProjectExplorer::Project::ExcludeGeneratedFiles); + } + QStringList nativeFiles; + foreach (const QString &f, files) + nativeFiles << QDir::toNativeSeparators(f); + + PerforceResponse result2 = runP4Cmd(QStringList(QLatin1String("fstat")), nativeFiles, false); + if (result2.error) { + delete m_changeTmpFile; + m_changeTmpFile = 0; + return; + } + + QStringList stdOutLines = result2.stdOut.split(QLatin1Char('\n')); + QStringList depotFileNames; + foreach(const QString &line, stdOutLines) { + if (line.startsWith("... depotFile")) { + depotFileNames.append(line.mid(14)); + } + } + if (depotFileNames.isEmpty()) { + showOutput(tr("Project has no files")); + delete m_changeTmpFile; + m_changeTmpFile = 0; + return; + } + + openPerforceSubmitEditor(m_changeTmpFile->fileName(), depotFileNames); +} + +Core::IEditor *PerforcePlugin::openPerforceSubmitEditor(const QString &fileName, const QStringList &depotFileNames) +{ + Core::IEditor *editor = + m_coreInstance->editorManager()->openEditor(fileName, Constants::PERFORCESUBMITEDITOR_KIND); + m_coreInstance->editorManager()->ensureEditorManagerVisible(); + PerforceSubmitEditor *submitEditor = dynamic_cast<PerforceSubmitEditor*>(editor); + Q_ASSERT(submitEditor); + submitEditor->restrictToProjectFiles(depotFileNames); + connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(slotDiff(QStringList))); + // The actions are for some reason enabled by the context switching + // mechanism. Disable them correctly. + m_diffSelectedFiles->setEnabled(false); + m_undoAction->setEnabled(false); + m_redoAction->setEnabled(false); + return editor; +} + +void PerforcePlugin::printPendingChanges() +{ + qApp->setOverrideCursor(Qt::WaitCursor); + PendingChangesDialog dia(pendingChangesData(), m_coreInstance->mainWindow()); + qApp->restoreOverrideCursor(); + if (dia.exec() == QDialog::Accepted) { + int i = dia.changeNumber(); + PerforceResponse result = runP4Cmd(QStringList()<<"submit"<<"-c"<<QString::number(i), QStringList(), true); + } +} + +void PerforcePlugin::describeChange() +{ + ChangeNumberDialog dia; + if (dia.exec() == QDialog::Accepted && dia.number() > 0) + describe(QString(), QString::number(dia.number())); +} + +void PerforcePlugin::annotateCurrentFile() +{ + const QString file = currentFileName(); + if (!file.isEmpty()) + annotate(file); +} + +void PerforcePlugin::annotate() +{ + const QString file = QFileDialog::getOpenFileName(0, tr("p4 annotate"), currentFileName()); + if (!file.isEmpty()) + annotate(file); +} + +void PerforcePlugin::annotate(const QString &fileName) +{ + QTextCodec *codec = VCSBase::VCSBaseEditor::getCodec(m_coreInstance, fileName); + QStringList args; + args << QLatin1String("annotate") << QLatin1String("-cqi") << fileName; + const PerforceResponse result = runP4Cmd(args, QStringList(), false, true, codec); + if (!result.error) { + const QFileInfo fi(fileName); + showOutputInEditor(tr("p4 annotate %1").arg(fi.fileName()), result.stdOut, VCSBase::AnnotateOutput, codec); + } +} + +void PerforcePlugin::filelogCurrentFile() +{ + const QString file = currentFileName(); + if (!file.isEmpty()) + filelog(file); +} + +void PerforcePlugin::filelog() +{ + const QString file = QFileDialog::getOpenFileName(0, tr("p4 filelog"), currentFileName()); + if (!file.isEmpty()) + filelog(file); +} + +void PerforcePlugin::filelog(const QString &fileName) +{ + QTextCodec *codec = VCSBase::VCSBaseEditor::getCodec(m_coreInstance, fileName); + QStringList args; + args << QLatin1String("filelog") << QLatin1String("-li") << fileName; + const PerforceResponse result = runP4Cmd(args, QStringList(), false, true, codec); + if (!result.error) { + const QFileInfo fi(fileName); + showOutputInEditor(tr("p4 filelog %1").arg(fi.fileName()), result.stdOut, VCSBase::LogOutput, codec); + } +} + +void PerforcePlugin::updateActions() +{ + QString fileName = currentFileName(); + QString baseName = QFileInfo(fileName).fileName(); + const bool hasFile = !currentFileName().isEmpty(); + m_editAction->setEnabled(hasFile); + m_addAction->setEnabled(hasFile); + m_deleteAction->setEnabled(hasFile); + m_revertAction->setEnabled(hasFile); + m_diffCurrentAction->setEnabled(hasFile); + m_annotateCurrentAction->setEnabled(hasFile); + m_filelogCurrentAction->setEnabled(hasFile); + if (hasFile) { + m_editAction->setText(tr("Edit %1").arg(baseName)); + m_addAction->setText(tr("Add %1").arg(baseName)); + m_deleteAction->setText(tr("Delete %1").arg(baseName)); + m_revertAction->setText(tr("Revert %1").arg(baseName)); + m_diffCurrentAction->setText(tr("Diff %1").arg(baseName)); + m_annotateCurrentAction->setText(tr("Annotate %1").arg(baseName)); + m_filelogCurrentAction->setText(tr("Filelog %1").arg(baseName)); + } else { + m_editAction->setText(tr("Edit")); + m_addAction->setText(tr("Add")); + m_deleteAction->setText(tr("Delete")); + m_revertAction->setText(tr("Revert")); + m_diffCurrentAction->setText(tr("Diff")); + m_annotateCurrentAction->setText(tr("Annotate Current File")); + m_filelogCurrentAction->setText(tr("Filelog Current File")); + } + if (m_projectExplorer && m_projectExplorer->currentProject()) { + m_diffProjectAction->setEnabled(true); + m_diffProjectAction->setText(tr("Diff Project %1").arg(m_projectExplorer->currentProject()->name())); + m_submitAction->setEnabled(true); + } else { + m_diffProjectAction->setEnabled(false); + m_diffProjectAction->setText(tr("Diff Current Project/Soluion")); + m_submitAction->setEnabled(false); + } + m_diffAllAction->setEnabled(true); + m_openedAction->setEnabled(true); + m_describeAction->setEnabled(true); + m_annotateAction->setEnabled(true); + m_filelogAction->setEnabled(true); + m_pendingAction->setEnabled(true); + + +#ifdef USE_P4_API + m_resolveAction->setEnabled(m_enableP4APIActions); +#endif +} + +bool PerforcePlugin::managesDirectory(const QString &directory) const +{ + const QString p4Path = directory + QLatin1String("/..."); + QStringList args; + args << QLatin1String("fstat") << QLatin1String("-m1") << p4Path; + + const PerforceResponse result = runP4Cmd(args, QStringList(), false, false); + return result.stdOut.contains("depotFile") || result.stdErr.contains("... - no such file(s)"); +} + +QString PerforcePlugin::findTopLevelForDirectory(const QString & /* dir */) const +{ + // First check with p4 client -o + PerforceResponse result = runP4Cmd(QStringList() << QLatin1String("client") << QLatin1String("-o"), QStringList(), false, false); + if (result.error) + return QString::null; + + QRegExp regExp(QLatin1String("(\\n|\\r\\n|\\r)Root:\\s*(.*)(\\n|\\r\\n|\\r)")); + regExp.setMinimal(true); + if (regExp.indexIn(result.stdOut) != -1) { + QString file = regExp.cap(2).trimmed(); + if (QFileInfo(file).exists()) + return file; + } + return QString::null; +} + +bool PerforcePlugin::vcsOpen(const QString &fileName) +{ + PerforceResponse result = runP4Cmd(QStringList() << QLatin1String("edit") << QDir::toNativeSeparators(fileName), QStringList(), true); + return !result.error; +} + +bool PerforcePlugin::vcsAdd(const QString &fileName) +{ + PerforceResponse result = runP4Cmd(QStringList() << QLatin1String("add") << fileName, QStringList(), true); + return !result.error; +} + +bool PerforcePlugin::vcsDelete(const QString &fileName) +{ + PerforceResponse result = runP4Cmd(QStringList() << QLatin1String("revert") << fileName, QStringList(), true); + PerforceResponse result2 = runP4Cmd(QStringList() << QLatin1String("delete") << fileName, QStringList(), true); + // TODO need to carefully parse the actual messages from perforce + // or do a fstat before to decide what to do + + // Different states are: + // File is in depot and unopened => p4 delete % + // File is in depot and opened => p4 revert %, p4 delete % + // File is not in depot => p4 revert % + return !(result.error && result2.error); +} + +PerforceResponse PerforcePlugin::runP4Cmd(const QStringList &args, + const QStringList &extraArgs, + bool showStdOutInOutputWindow, + bool showStdErrInOutputWindow, + QTextCodec *outputCodec) const +{ + if (Perforce::Constants::debug) + qDebug() << "PerforcePlugin::runP4Cmd" << args << extraArgs << debugCodec(outputCodec); + PerforceResponse response; + response.error = true; + Q_ASSERT(m_coreInstance); + if (!checkP4Command()) { + response.message = tr("No p4 executable specified!"); + m_perforceOutputWindow->append(response.message, true); + return response; + } + + // handle extra args + QTemporaryFile tempfile; + tempfile.setAutoRemove(true); + const QChar newLine = QLatin1Char('\n'); + const QChar blank = QLatin1Char(' '); + QStringList actualArgs = basicP4Args(); + if (!extraArgs.isEmpty()) { + if (tempfile.open()) { + QTextStream stream(&tempfile); + stream << extraArgs.join(QString(newLine)); + actualArgs << QLatin1String("-x") << tempfile.fileName(); + tempfile.close(); + } else { + qWarning()<<"Could not create temporary file. Appending all file names to command line."; + actualArgs << extraArgs; + } + } + actualArgs << args; + + response.command = m_settings.p4Command; + response.command += blank; + response.command += actualArgs.join(QString(blank)); + const QString timeStamp = QTime::currentTime().toString(QLatin1String("HH:mm")); + const QString outputText = tr("%1 Executing: %2\n").arg(timeStamp, response.command); + showOutput(outputText, false); + + // Run, connect stderr to the output window + Core::Utils::SynchronousProcess process; + process.setTimeout(p4Timeout); + process.setStdOutCodec(outputCodec); + process.setEnvironment(environment()); + + // connect stderr to the output window if desired + if (showStdErrInOutputWindow) { + process.setStdErrBufferedSignalsEnabled(true); + connect(&process, SIGNAL(stdErrBuffered(QString,bool)), m_perforceOutputWindow, SLOT(append(QString,bool))); + } + + // connect stdout to the output window if desired + if (showStdOutInOutputWindow) { + process.setStdOutBufferedSignalsEnabled(true); + connect(&process, SIGNAL(stdOutBuffered(QString,bool)), m_perforceOutputWindow, SLOT(append(QString,bool))); + } + + const Core::Utils::SynchronousProcessResponse sp_resp = process.run(m_settings.p4Command, actualArgs); + if (Perforce::Constants::debug) + qDebug() << sp_resp; + + response.error = true; + response.stdErr = sp_resp.stdErr; + response.stdOut = sp_resp.stdOut; + switch (sp_resp.result) { + case Core::Utils::SynchronousProcessResponse::Finished: + response.error = false; + break; + case Core::Utils::SynchronousProcessResponse::FinishedError: + response.message = tr("The process terminated with exit code %1.").arg(sp_resp.exitCode); + break; + case Core::Utils::SynchronousProcessResponse::TerminatedAbnormally: + response.message = tr("The process terminated abnormally."); + break; + case Core::Utils::SynchronousProcessResponse::StartFailed: + response.message = tr("Could not start perforce '%1'. Please check your settings in the preferences.").arg(m_settings.p4Command); + break; + case Core::Utils::SynchronousProcessResponse::Hang: + response.message = tr("Subversion did not respond within timeout limit (%1 ms).").arg(p4Timeout ); + break; + } + if (response.error) + m_perforceOutputWindow->append(response.message, true); + + + return response; +} + +Core::IEditor * PerforcePlugin::showOutputInEditor(const QString& title, const QString output, + int editorType, QTextCodec *codec) +{ + const VCSBase::VCSBaseEditorParameters *params = findType(editorType); + Q_ASSERT(params); + const QString kind = QLatin1String(params->kind); + if (Perforce::Constants::debug) + qDebug() << "PerforcePlugin::showOutputInEditor" << title << kind << "Size= " << output.size() << " Type=" << editorType << debugCodec(codec); + QString s = title; + Core::IEditor *ediface = m_coreInstance->editorManager()-> + newFile(kind, &s, output.toLocal8Bit()); + PerforceEditor *e = qobject_cast<PerforceEditor*>(ediface->widget()); + if (!e) + return 0; + s.replace(QLatin1Char(' '), QLatin1Char('_')); + e->setSuggestedFileName(s); + if (codec) + e->setCodec(codec); + return e->editableInterface(); +} + +QStringList PerforcePlugin::environment() const +{ + QStringList newEnv = QProcess::systemEnvironment(); + const QString name = "P4DIFF"; + for (int i=0; i<newEnv.count(); ++i) { + if (newEnv.at(i).startsWith(name)) { + newEnv.removeAt(i); + return newEnv; + } + } + return newEnv; +} + +void PerforcePlugin::slotDiff(const QStringList &files) +{ + p4Diff(files); +} + +void PerforcePlugin::p4Diff(const QStringList &files, QString diffname) +{ + Core::IEditor *editor = 0; + bool displayInEditor = true; + Core::IEditor *existingEditor = 0; + QTextCodec *codec = files.empty() ? static_cast<QTextCodec *>(0) : VCSBase::VCSBaseEditor::getCodec(m_coreInstance, files.front()); + if (Perforce::Constants::debug) + qDebug() << Q_FUNC_INFO << files << debugCodec(codec); + + // diff of a single file? re-use an existing view if possible to support the common + // usage pattern of continuously changing and diffing a file + if (files.count() == 1) { + const QString fileName = files.at(0); + if (diffname.isEmpty()) { + const QFileInfo fi(fileName); + diffname = fi.fileName(); + } + + foreach (Core::IEditor *ed, m_coreInstance->editorManager()->openedEditors()) { + if (ed->property("originalFileName").toString() == fileName) { + existingEditor = ed; + displayInEditor = false; + break; + } + } + } + + const PerforceResponse result = runP4Cmd(QStringList() << QLatin1String("diff") << QLatin1String("-du"), files, false, codec); + if (result.error) + return; + + if (displayInEditor) + editor = showOutputInEditor(tr("p4 diff %1").arg(diffname), result.stdOut, VCSBase::DiffOutput, codec); + + + if (files.count() == 1) { + if (displayInEditor && editor != 0) { + editor->setProperty("originalFileName", files.at(0)); + } else if (!displayInEditor && existingEditor) { + if (existingEditor) { + existingEditor->createNew(result.stdOut); + m_coreInstance->editorManager()->setCurrentEditor(existingEditor); + } + } + } +} + +void PerforcePlugin::describe(const QString & source, const QString &n) +{ + QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VCSBase::VCSBaseEditor::getCodec(m_coreInstance, source); + QStringList args; + args << QLatin1String("describe") << QLatin1String("-du") << n; + const PerforceResponse result = runP4Cmd(args, QStringList(), codec); + if (!result.error) + showOutputInEditor(tr("p4 describe %1").arg(n), result.stdOut, VCSBase::DiffOutput, codec); +} + +void PerforcePlugin::submitCurrentLog() +{ + m_coreInstance->editorManager()->closeEditors(QList<Core::IEditor*>() + << m_coreInstance->editorManager()->currentEditor()); +} + +bool PerforcePlugin::editorAboutToClose(Core::IEditor *editor) +{ + if (!m_changeTmpFile || !editor) + return true; + Core::IFile *fileIFace = editor->file(); + if (!fileIFace) + return true; + QFileInfo editorFile(fileIFace->fileName()); + QFileInfo changeFile(m_changeTmpFile->fileName()); + if (editorFile.absoluteFilePath() == changeFile.absoluteFilePath()) { + const QMessageBox::StandardButton answer = QMessageBox::question(m_coreInstance->mainWindow(), tr("Closing p4 Editor"), tr("Do you want to submit this change list?"), + QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes); + if (answer == QMessageBox::Cancel) { + return false; + } + + m_coreInstance->fileManager()->blockFileChange(fileIFace); + fileIFace->save(); + m_coreInstance->fileManager()->unblockFileChange(fileIFace); + if (answer == QMessageBox::Yes) { + QByteArray change = m_changeTmpFile->readAll(); + m_changeTmpFile->close(); + if (!checkP4Command()) { + showOutput(tr("No p4 executable specified!")); + delete m_changeTmpFile; + m_changeTmpFile = 0; + return false; + } + QProcess proc; + proc.setEnvironment(environment()); + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + proc.start(m_settings.p4Command, + basicP4Args() << QLatin1String("submit") << QLatin1String("-i")); + if (!proc.waitForStarted(3000)) { + showOutput(tr("Cannot execute p4 submit.")); + QApplication::restoreOverrideCursor(); + delete m_changeTmpFile; + m_changeTmpFile = 0; + return false; + } + proc.write(change); + proc.closeWriteChannel(); + + if (!proc.waitForFinished()) { + showOutput(tr("Cannot execute p4 submit.")); + QApplication::restoreOverrideCursor(); + delete m_changeTmpFile; + m_changeTmpFile = 0; + return false; + } + QString output = QString::fromUtf8(proc.readAll()); + showOutput(output); + if (output.contains("Out of date files must be resolved or reverted")) { + QMessageBox::warning(editor->widget(), "Pending change", "Could not submit the change, because your workspace was out of date. Created a pending submit instead."); + } + QApplication::restoreOverrideCursor(); + } + m_changeTmpFile->close(); + delete m_changeTmpFile; + m_changeTmpFile = 0; + } + return true; +} + +void PerforcePlugin::openFiles(const QStringList &files) +{ + foreach (QString s, files) { + m_coreInstance->editorManager()->openEditor(clientFilePath(s)); + } + m_coreInstance->editorManager()->ensureEditorManagerVisible(); +} + +QString PerforcePlugin::clientFilePath(const QString &serverFilePath) +{ + QString path; + Q_ASSERT(m_coreInstance); + if (!checkP4Command()) + return path; + + QApplication::setOverrideCursor(Qt::WaitCursor); + QProcess proc; + proc.setEnvironment(environment()); + proc.start(m_settings.p4Command, + basicP4Args() << QLatin1String("fstat") << serverFilePath); + + if (proc.waitForFinished(3000)) { + QString output = QString::fromUtf8(proc.readAllStandardOutput()); + if (!output.isEmpty()) { + QRegExp r(QLatin1String("\\.\\.\\.\\sclientFile\\s(.+)\n")); + r.setMinimal(true); + if (r.indexIn(output) != -1) + path = r.cap(1).trimmed(); + } + } + QApplication::restoreOverrideCursor(); + return path; +} + +QString PerforcePlugin::currentFileName() +{ + QString fileName = m_coreInstance->fileManager()->currentFile(); + + // TODO: Use FileManager::fixPath + const QFileInfo fileInfo(fileName); + if (fileInfo.exists()) + fileName = fileInfo.absoluteFilePath(); + fileName = QDir::toNativeSeparators(fileName); + return fileName; +} + +QStringList PerforcePlugin::basicP4Args() const +{ + QStringList lst; + if (!m_settings.defaultEnv) { + lst << QLatin1String("-c") << m_settings.p4Client; + lst << QLatin1String("-p") << m_settings.p4Port; + lst << QLatin1String("-u") << m_settings.p4User; + } + return lst; +} + +bool PerforcePlugin::checkP4Command() const +{ + if (m_settings.p4Command.isEmpty()) + return false; + return true; +} + +#ifdef USE_P4_API +void PerforcePlugin::runP4APICmd(const QString &cmd, const QStringList &args) +{ + m_enableP4APIActions = false; + updateActions(); + + ClientApi client; + if (!m_settings.defaultEnv) { + client.SetClient(m_settings.p4Client.toLatin1().constData()); + client.SetPort(m_settings.p4Port.toLatin1().constData()); + client.SetUser(m_settings.p4User.toLatin1().constData()); + } + + Error err; + m_coreInstance->messageManager()->displayStatusBarMessage(tr("Connecting to p4 server...")); + client.SetProtocol("api", "56"); + client.Init(&err); + if (err.Test()) { + StrBuf msg; + err.Fmt(&msg); + QMessageBox::critical(m_coreInstance->mainWindow(), tr("Perforce Plugin"), tr("Failed to connect to p4 server <b>%1</b>!").arg(msg.Text())); + client.Final(&err); + m_coreInstance->messageManager()->displayStatusBarMessage(tr("Connection to p4 server failed!"), 3000); + return; + } + m_coreInstance->messageManager()->displayStatusBarMessage(tr("Connection to p4 server established."), 3000); + + // ???? + //client.SetCwd("c:\\depot\\research\\qworkbench\\src"); + + int argc = args.count(); + char **argv = (char**)malloc(argc*sizeof(char*)); + int i = 0; + foreach (QString s, args) + argv[i++] = qstrdup(s.toLatin1().constData()); + + client.SetArgv( argc, argv ); + try { + client.Run(cmd.toLatin1().constData(), m_workbenchClientUser); + } catch (...) { + QMessageBox::critical(m_coreInstance->mainWindow(), tr("Perforce Plugin"), tr("Failed to run command <b>%1</b>!").arg(cmd)); + } + client.Final(&err); + i = 0; + while (i<argc) + free(argv[i++]); + free(argv); + + m_enableP4APIActions = true; + updateActions(); + + Core::IEditor *edt = m_coreInstance->editorManager()->currentEditor(); + if (edt && edt->widget()) + edt->widget()->setFocus(); +} +#endif + +QString PerforcePlugin::pendingChangesData() +{ + QString data; + Q_ASSERT(m_coreInstance); + if (!checkP4Command()) + return data; + + QString user; + QProcess proc; + proc.setEnvironment(environment()); + proc.start(m_settings.p4Command, + basicP4Args() << QLatin1String("info")); + if (proc.waitForFinished(3000)) { + QString output = QString::fromUtf8(proc.readAllStandardOutput()); + if (!output.isEmpty()) { + QRegExp r(QLatin1String("User\\sname:\\s(\\S+)\\s*\n")); + r.setMinimal(true); + if (r.indexIn(output) != -1) + user = r.cap(1).trimmed(); + } + } + if (user.isEmpty()) + return data; + proc.start(m_settings.p4Command, + basicP4Args() << QLatin1String("changes") << QLatin1String("-s") << QLatin1String("pending") << QLatin1String("-u") << user); + if (proc.waitForFinished(3000)) + data = QString::fromUtf8(proc.readAllStandardOutput()); + return data; +} + +void PerforcePlugin::showOutput(const QString &output, bool popup) const +{ + m_perforceOutputWindow->append(output, popup); +} + +PerforcePlugin::~PerforcePlugin() +{ + if (m_settingsPage) { + removeObject(m_settingsPage); + delete m_settingsPage; + m_settingsPage = 0; + } + +#ifdef USE_P4_API + if (m_workbenchClientUser) { + delete m_workbenchClientUser; + m_workbenchClientUser = 0; + } +#endif + if (m_perforceOutputWindow) { + removeObject(m_perforceOutputWindow); + delete m_perforceOutputWindow; + m_perforceOutputWindow = 0; + } + if (m_submitEditorFactory) { + removeObject(m_submitEditorFactory); + delete m_submitEditorFactory; + m_submitEditorFactory = 0; + } + if (m_versionControl) { + removeObject(m_versionControl); + delete m_versionControl; + m_versionControl = 0; + } + + if (!m_editorFactories.empty()) { + foreach(Core::IEditorFactory* pf, m_editorFactories) + removeObject(pf); + qDeleteAll(m_editorFactories); + m_editorFactories.clear(); + } + + if (m_coreListener) { + removeObject(m_coreListener); + delete m_coreListener; + m_coreListener = 0; + } +} + +PerforceSettings PerforcePlugin::settings() const +{ + return m_settings; +} + +void PerforcePlugin::setSettings(const PerforceSettings &s) +{ + if (s != m_settings) { + m_settings = s; + if (QSettings *settings = m_coreInstance->settings()) + m_settings.toSettings(settings); + } +} + +// Map a perforce name "//xx" to its real name in the file system +QString PerforcePlugin::fileNameFromPerforceName(const QString& perforceName, + QString *errorMessage) const +{ + // All happy, already mapped + if (!perforceName.startsWith(QLatin1String("//"))) + return perforceName; + // "where" remaps the file to client file tree + QProcess proc; + QStringList args(basicP4Args()); + args << QLatin1String("where") << perforceName; + proc.start(m_settings.p4Command, args); + if (!proc.waitForFinished()) { + *errorMessage = tr("Timeout waiting for \"where\" (%1).").arg(perforceName); + return QString(); + } + + QString output = QString::fromLocal8Bit(proc.readAllStandardOutput()); + if (output.endsWith(QLatin1Char('\r'))) + output.chop(1); + if (output.endsWith(QLatin1Char('\n'))) + output.chop(1); + + if (output.isEmpty()) { + *errorMessage = tr("Error running \"where\" on %1: The file is not mapped").arg(perforceName); + return QString(); + } + const QString rc = output.mid(output.lastIndexOf(QLatin1Char(' ')) + 1); + if (Perforce::Constants::debug) + qDebug() << "fileNameFromPerforceName" << perforceName << rc; + return rc; +} + +Q_EXPORT_PLUGIN(PerforcePlugin) + diff --git a/src/plugins/perforce/perforceplugin.h b/src/plugins/perforce/perforceplugin.h new file mode 100644 index 00000000000..d03d24515f9 --- /dev/null +++ b/src/plugins/perforce/perforceplugin.h @@ -0,0 +1,250 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PERFORCEPLUGIN_H +#define PERFORCEPLUGIN_H + +#include "perforcesettings.h" + +#include <coreplugin/editormanager/ieditorfactory.h> +#include <coreplugin/iversioncontrol.h> +#include <coreplugin/icorelistener.h> +#include <projectexplorer/ProjectExplorerInterfaces> +#include <extensionsystem/iplugin.h> + +#ifdef USE_P4_API +#include "workbenchclientuser.h" +#else + +#endif + +#include <QtCore/QObject> +#include <QtCore/QProcess> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE +class QFile; +class QAction; +class QTemporaryFile; +class QTextCodec; +QT_END_NAMESPACE + +namespace Core { + class IEditorFactory; +} + +namespace Perforce { +namespace Internal { +class PerforceOutputWindow; +class SettingsPage; +class PerforceVersionControl; +class PerforcePlugin; + +// Just a proxy for PerforcePlugin +class CoreListener : public Core::ICoreListener +{ + Q_OBJECT +public: + CoreListener(PerforcePlugin *plugin) : m_plugin(plugin) { } + bool editorAboutToClose(Core::IEditor *editor); + bool coreAboutToClose() { return true; } +private: + PerforcePlugin *m_plugin; +}; + +struct PerforceResponse +{ + bool error; + QString command; + QString stdOut; + QString stdErr; + QString message; +}; + +class PerforcePlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + PerforcePlugin(); + ~PerforcePlugin(); + + QStringList basicP4Args() const; + inline SettingsPage *settingsPage() const { return m_settingsPage; } + + bool initialize(const QStringList &arguments, QString *error_message); + void extensionsInitialized(); + + bool managesDirectory(const QString &directory) const; + QString findTopLevelForDirectory(const QString &directory) const; + bool vcsOpen(const QString &fileName); + bool vcsAdd(const QString &fileName); + bool vcsDelete(const QString &filename); + // Displays the message for the submit mesage + bool editorAboutToClose(Core::IEditor *editor); + + void p4Diff(const QStringList &files, QString diffname = QString()); + + Core::IEditor *openPerforceSubmitEditor(const QString &fileName, const QStringList &depotFileNames); + + static Core::ICore *coreInstance() {Q_ASSERT(m_coreInstance); return m_coreInstance;} + static PerforcePlugin *perforcePluginInstance() {Q_ASSERT(m_perforcePluginInstance); return m_perforcePluginInstance;} + + PerforceSettings settings() const; + void setSettings(const PerforceSettings &s); + + // Map a perforce name "//xx" to its real name in the file system + QString fileNameFromPerforceName(const QString& perforceName, QString *errorMessage) const; + +public slots: + void describe(const QString &source, const QString &n); + +private slots:; + void openCurrentFile(); + void addCurrentFile(); + void deleteCurrentFile(); + void revertCurrentFile(); + void printOpenedFileList(); + void diffCurrentFile(); + void diffCurrentProject(); + void diffAllOpened(); + void submit(); + void describeChange(); + void annotateCurrentFile(); + void annotate(); + void filelogCurrentFile(); + void filelog(); + + void updateActions(); + void submitCurrentLog(); + void printPendingChanges(); + void slotDiff(const QStringList &files); + +#ifdef USE_P4_API + void resolve(); +#endif + +private: + QStringList environment() const; + + Core::IEditor *showOutputInEditor(const QString& title, const QString output, + int editorType, + QTextCodec *codec = 0); + // args are passed as command line arguments + // extra args via a tempfile and the option -x "temp-filename" + PerforceResponse runP4Cmd(const QStringList &args, + const QStringList &extraArgs = QStringList(), + bool showStdOutInOutputWindow = false, + bool showStdErrInOutputWindow = true, + QTextCodec *outputCodec = 0) const; + + void openFiles(const QStringList &files); + + QString clientFilePath(const QString &serverFilePath); + QString currentFileName(); + bool checkP4Command() const; + void showOutput(const QString &output, bool popup = false) const; + void annotate(const QString &fileName); + void filelog(const QString &fileName); + + ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer; + PerforceOutputWindow *m_perforceOutputWindow; + SettingsPage *m_settingsPage; + QList<Core::IEditorFactory*> m_editorFactories; + + QAction *m_editAction; + QAction *m_addAction; + QAction *m_deleteAction; + QAction *m_openedAction; + QAction *m_revertAction; + QAction *m_diffCurrentAction; + QAction *m_diffProjectAction; + QAction *m_diffAllAction; + QAction *m_resolveAction; + QAction *m_submitAction; + QAction *m_pendingAction; + QAction *m_describeAction; + QAction *m_annotateCurrentAction; + QAction *m_annotateAction; + QAction *m_filelogCurrentAction; + QAction *m_filelogAction; + QAction *m_submitCurrentLogAction; + QAction *m_diffSelectedFiles; + + QAction *m_undoAction; + QAction *m_redoAction; + + QTemporaryFile *m_changeTmpFile; + + static const char * const PERFORCE_MENU; + static const char * const EDIT; + static const char * const ADD; + static const char * const DELETE_FILE; + static const char * const OPENED; + static const char * const REVERT; + static const char * const DIFF_ALL; + static const char * const DIFF_PROJECT; + static const char * const DIFF_CURRENT; + static const char * const RESOLVE; + static const char * const SUBMIT; + static const char * const PENDING_CHANGES; + static const char * const DESCRIBE; + static const char * const ANNOTATE_CURRENT; + static const char * const ANNOTATE; + static const char * const FILELOG_CURRENT; + static const char * const FILELOG; + static const char * const SEPARATOR1; + static const char * const SEPARATOR2; + static const char * const SEPARATOR3; + + static Core::ICore *m_coreInstance; + static PerforcePlugin *m_perforcePluginInstance; + QString pendingChangesData(); + +#ifdef USE_P4_API + void runP4APICmd(const QString &cmd, const QStringList &args = QStringList()); + WorkbenchClientUser *m_workbenchClientUser; + bool m_enableP4APIActions; +#endif + + CoreListener *m_coreListener; + Core::IEditorFactory *m_submitEditorFactory; + PerforceVersionControl *m_versionControl; + PerforceSettings m_settings; + + friend class PerforceOutputWindow; +}; + +} // namespace Perforce +} // namespace Internal + +#endif // PERFORCEPLUGIN_H diff --git a/src/plugins/perforce/perforcesettings.cpp b/src/plugins/perforce/perforcesettings.cpp new file mode 100644 index 00000000000..d01df8a5457 --- /dev/null +++ b/src/plugins/perforce/perforcesettings.cpp @@ -0,0 +1,95 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "perforcesettings.h" + +#include <QtCore/QSettings> + +static const char *groupC = "Perforce"; +static const char *commandKeyC = "Command"; +static const char *defaultKeyC = "Default"; +static const char *portKeyC = "Port"; +static const char *clientKeyC = "Client"; +static const char *userKeyC = "User"; + +static QString defaultCommand() +{ + QString rc; + rc = QLatin1String("p4"); +#if defined(Q_OS_WIN32) + rc.append(QLatin1String(".exe")); +#endif + return rc; +} + +namespace Perforce { +namespace Internal { + +PerforceSettings::PerforceSettings() : + p4Command(defaultCommand()), + defaultEnv(true) +{ +} + +void PerforceSettings::fromSettings(QSettings *settings) +{ + settings->beginGroup(QLatin1String(groupC)); + p4Command = settings->value(QLatin1String(commandKeyC), defaultCommand()).toString(); + defaultEnv = settings->value(QLatin1String(defaultKeyC), true).toBool(); + p4Port = settings->value(QLatin1String(portKeyC), QString()).toString(); + p4Client = settings->value(QLatin1String(clientKeyC), QString()).toString(); + p4User = settings->value(QLatin1String(userKeyC), QString()).toString(); + settings->endGroup(); + +} + +void PerforceSettings::toSettings(QSettings *settings) const +{ + settings->beginGroup(QLatin1String(groupC)); + settings->setValue(commandKeyC, p4Command); + settings->setValue(defaultKeyC, defaultEnv); + settings->setValue(portKeyC, p4Port); + settings->setValue(clientKeyC, p4Client); + settings->setValue(userKeyC, p4User); + settings->endGroup(); +} + +bool PerforceSettings::equals(const PerforceSettings &s) const +{ + return p4Command == s.p4Command && p4Port == s.p4Port + && p4Client == s.p4Client && p4User == s.p4User + && defaultEnv == s.defaultEnv; +} + +} +} + diff --git a/src/plugins/perforce/perforcesettings.h b/src/plugins/perforce/perforcesettings.h new file mode 100644 index 00000000000..aa2782e47bf --- /dev/null +++ b/src/plugins/perforce/perforcesettings.h @@ -0,0 +1,65 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PERFOCESETTINGS_H +#define PERFOCESETTINGS_H + +#include <QtCore/QString> + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace Perforce { +namespace Internal { + +struct PerforceSettings { + PerforceSettings(); + void fromSettings(QSettings *); + void toSettings(QSettings *) const; + bool equals(const PerforceSettings &s) const; + + QString p4Command; + QString p4Port; + QString p4Client; + QString p4User; + bool defaultEnv; +}; + +inline bool operator==(const PerforceSettings &p1, const PerforceSettings &p2) + { return p1.equals(p2); } +inline bool operator!=(const PerforceSettings &p1, const PerforceSettings &p2) + { return !p1.equals(p2); } +} +} + +#endif diff --git a/src/plugins/perforce/perforcesubmiteditor.cpp b/src/plugins/perforce/perforcesubmiteditor.cpp new file mode 100644 index 00000000000..3e5188e5d8d --- /dev/null +++ b/src/plugins/perforce/perforcesubmiteditor.cpp @@ -0,0 +1,194 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "perforcesubmiteditor.h" +#include "perforcesubmiteditorwidget.h" +#include "perforceplugin.h" +#include "perforceconstants.h" + +#include <QtCore/QDebug> + +namespace Perforce { +namespace Internal { + +PerforceSubmitEditor::PerforceSubmitEditor(const VCSBase::VCSBaseSubmitEditorParameters *parameters, QWidget *parent) : + VCSBaseSubmitEditor(parameters, new PerforceSubmitEditorWidget(parent)) +{ + setDisplayName(tr("Perforce Submit")); +} + +PerforceSubmitEditorWidget *PerforceSubmitEditor::submitEditorWidget() +{ + return static_cast<PerforceSubmitEditorWidget *>(widget()); +} + +QStringList PerforceSubmitEditor::vcsFileListToFileList(const QStringList &rawList) const +{ + QStringList rc; + foreach (const QString &rf, rawList) + rc.push_back(fileFromChangeLine(rf)); + return rc; +} + +QString PerforceSubmitEditor::fileContents() const +{ + const_cast<PerforceSubmitEditor*>(this)->updateEntries(); + QString text; + QTextStream out(&text); + QMapIterator<QString, QString> it(m_entries); + while (it.hasNext()) { + it.next(); + out << it.key() << ":" << it.value(); + } + if (Perforce::Constants::debug) + qDebug() << Q_FUNC_INFO << text; + return text; +} + +bool PerforceSubmitEditor::setFileContents(const QString &contents) +{ + if (Perforce::Constants::debug) + qDebug() << Q_FUNC_INFO << contents; + if (!parseText(contents)) + return false; + updateFields(); + return true; +} + +bool PerforceSubmitEditor::parseText(QString text) +{ + const QRegExp formField(QLatin1String("^\\S+:")); + const QString newLine = QString(QLatin1Char('\n')); + + int match; + int matchLen; + QTextStream stream(&text, QIODevice::ReadOnly); + QString line; + QString key; + QString value; + line = stream.readLine(); + while (!stream.atEnd()) { + match = formField.indexIn(line); + if (match == 0) { + matchLen = formField.matchedLength(); + key = line.left(matchLen-1); + value = line.mid(matchLen) + newLine; + while (!stream.atEnd()) { + line = stream.readLine(); + if (formField.indexIn(line) != -1) + break; + value += line + newLine; + } + m_entries.insert(key, value); + } else { + line = stream.readLine(); + } + } + return true; +} + +void PerforceSubmitEditor::restrictToProjectFiles(const QStringList &knownProjectFiles) +{ + QStringList allFiles = submitEditorWidget()->fileList(); + const int oldSize = allFiles.size(); + for (int i = oldSize - 1; i >= 0; i--) + if (!knownProjectFiles.contains(fileFromChangeLine(allFiles.at(i)))) + allFiles.removeAt(i); + if (allFiles.size() != oldSize) + submitEditorWidget()->setFileList(allFiles); + if (Perforce::Constants::debug) + qDebug() << Q_FUNC_INFO << oldSize << "->" << allFiles.size(); +} + +QString PerforceSubmitEditor::fileFromChangeLine(const QString &line) +{ + QString rc = line; + // " foo.cpp#add" + const int index = rc.lastIndexOf(QLatin1Char('#')); + if (index != -1) + rc.truncate(index); + return rc.trimmed(); +} + +void PerforceSubmitEditor::updateFields() +{ + PerforceSubmitEditorWidget *widget = submitEditorWidget(); + widget->setData(m_entries.value(QLatin1String("Change")).trimmed(), + m_entries.value(QLatin1String("Client")).trimmed(), + m_entries.value(QLatin1String("User")).trimmed()); + + const QString newLine = QString(QLatin1Char('\n')); + QStringList lines = m_entries.value(QLatin1String("Description")).split(newLine); + lines.removeFirst(); // that is the line break after 'Description:' + lines.removeLast(); // that is the empty line at the end + + const QRegExp leadingTabPattern = QRegExp(QLatin1String("^\\t")); + Q_ASSERT(leadingTabPattern.isValid()); + + lines.replaceInStrings(leadingTabPattern, QString()); + widget->setDescriptionText(lines.join(newLine)); + + lines = m_entries.value(QLatin1String("Files")).split(newLine); + lines.replaceInStrings(leadingTabPattern, QString()); + QStringList fileList; + foreach (const QString &line, lines) + if (!line.isEmpty()) + fileList.push_back(line); + widget->setFileList(fileList); +} + +void PerforceSubmitEditor::updateEntries() +{ + const QString newLine = QString(QLatin1Char('\n')); + const QString tab = QString(QLatin1Char('\t')); + + QStringList lines = submitEditorWidget()->trimmedDescriptionText().split(newLine); + while (lines.last().isEmpty()) + lines.removeLast(); + // Description + lines.replaceInStrings(QRegExp(QLatin1String("^")), tab); + m_entries.insert(QLatin1String("Description"), newLine + lines.join(newLine) + QLatin1String("\n\n")); + QString files = newLine; + // Files + const QStringList fileList = submitEditorWidget()->fileList(); + const int count = fileList.size(); + for (int i = 0; i < count; i++) { + files += tab; + files += fileList.at(i); + files += newLine; + } + files += newLine; + m_entries.insert(QLatin1String("Files"), files); +} + +} +} diff --git a/src/plugins/perforce/perforcesubmiteditor.h b/src/plugins/perforce/perforcesubmiteditor.h new file mode 100644 index 00000000000..af0d3d0e2c6 --- /dev/null +++ b/src/plugins/perforce/perforcesubmiteditor.h @@ -0,0 +1,84 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PERFORCESUBMITEDITOR_H +#define PERFORCESUBMITEDITOR_H + +#include <vcsbase/vcsbasesubmiteditor.h> + +#include <QtCore/QStringList> +#include <QtCore/QMap> + +namespace Perforce { +namespace Internal { + +class PerforceSubmitEditorWidget; +class PerforcePlugin; + +/* PerforceSubmitEditor: In p4, the file list is contained in the + * submit message file (change list). On setting the file contents, + * it is split apart in message and file list and re-assembled + * when retrieving the file list. + * As a p4 submit starts with all opened files, there is API to restrict + * the file list to current project files in question + * (restrictToProjectFiles()). */ +class PerforceSubmitEditor : public VCSBase::VCSBaseSubmitEditor +{ + Q_OBJECT + +public: + explicit PerforceSubmitEditor(const VCSBase::VCSBaseSubmitEditorParameters *parameters, QWidget *parent); + + /* The p4 submit starts with all opened files. Restrict + * it to the current project files in question. */ + void restrictToProjectFiles(const QStringList &files); + + static QString fileFromChangeLine(const QString &line); + +protected: + virtual QStringList vcsFileListToFileList(const QStringList &) const; + virtual QString fileContents() const; + virtual bool setFileContents(const QString &contents); + +private: + inline PerforceSubmitEditorWidget *submitEditorWidget(); + bool parseText(QString text); + void updateFields(); + void updateEntries(); + + QMap<QString, QString> m_entries; +}; + +} // namespace Internal +} // namespace Perforce + +#endif // PERFORCESUBMITEDITOR_H diff --git a/src/plugins/perforce/perforcesubmiteditorwidget.cpp b/src/plugins/perforce/perforcesubmiteditorwidget.cpp new file mode 100644 index 00000000000..1a4c8251b1b --- /dev/null +++ b/src/plugins/perforce/perforcesubmiteditorwidget.cpp @@ -0,0 +1,55 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "perforcesubmiteditorwidget.h" + +namespace Perforce { +namespace Internal { + +PerforceSubmitEditorWidget::PerforceSubmitEditorWidget(QWidget *parent) : + Core::Utils::SubmitEditorWidget(parent), + m_submitPanel(new QGroupBox) +{ + m_submitPanelUi.setupUi(m_submitPanel); + insertTopWidget(m_submitPanel); +} + +void PerforceSubmitEditorWidget::setData(const QString &change, + const QString &client, + const QString &userName) +{ + m_submitPanelUi.changeNumber->setText(change); + m_submitPanelUi.clientName->setText(client); + m_submitPanelUi.userName->setText(userName); +} +} +} diff --git a/src/plugins/perforce/perforcesubmiteditorwidget.h b/src/plugins/perforce/perforcesubmiteditorwidget.h new file mode 100644 index 00000000000..fe79f65da2a --- /dev/null +++ b/src/plugins/perforce/perforcesubmiteditorwidget.h @@ -0,0 +1,60 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PERFORCESUBMITEDITORWIDGET_H +#define PERFORCESUBMITEDITORWIDGET_H + +#include "ui_submitpanel.h" +#include <utils/submiteditorwidget.h> + +namespace Perforce { +namespace Internal { + +/* Submit editor widget with additional information pane + * at the top. */ +class PerforceSubmitEditorWidget : public Core::Utils::SubmitEditorWidget +{ + +public: + explicit PerforceSubmitEditorWidget(QWidget *parent = 0); + + void setData(const QString &change, const QString &client, const QString &userName); + +private: + QGroupBox *m_submitPanel; + Ui::SubmitPanel m_submitPanelUi; +}; + +} // namespace Internal +} // namespace Perforce + +#endif // PERFORCESUBMITEDITORWIDGET_H diff --git a/src/plugins/perforce/perforceversioncontrol.cpp b/src/plugins/perforce/perforceversioncontrol.cpp new file mode 100644 index 00000000000..5cbc8e4880f --- /dev/null +++ b/src/plugins/perforce/perforceversioncontrol.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "perforceversioncontrol.h" +#include "perforceplugin.h" + +namespace Perforce { +namespace Internal { + +PerforceVersionControl::PerforceVersionControl(PerforcePlugin *plugin) : + m_plugin(plugin) +{ +} + +bool PerforceVersionControl::vcsOpen(const QString &fileName) +{ + return m_plugin->vcsOpen(fileName); +} + +bool PerforceVersionControl::vcsAdd(const QString &fileName) +{ + return m_plugin->vcsAdd(fileName); +} + +bool PerforceVersionControl::vcsDelete(const QString &fileName) +{ + return m_plugin->vcsDelete(fileName); +} + +bool PerforceVersionControl::managesDirectory(const QString &directory) const +{ + return m_plugin->managesDirectory(directory); +} + +QString PerforceVersionControl::findTopLevelForDirectory(const QString &directory) const +{ + return m_plugin->findTopLevelForDirectory(directory); +} +} +} diff --git a/src/plugins/perforce/perforceversioncontrol.h b/src/plugins/perforce/perforceversioncontrol.h new file mode 100644 index 00000000000..5f109357490 --- /dev/null +++ b/src/plugins/perforce/perforceversioncontrol.h @@ -0,0 +1,60 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PERFORCEVERSIONCONTROL_H +#define PERFORCEVERSIONCONTROL_H + +#include <coreplugin/iversioncontrol.h> + +namespace Perforce { +namespace Internal { +class PerforcePlugin; + +// Just a proxy for PerforcePlugin +class PerforceVersionControl : public Core::IVersionControl +{ + Q_OBJECT +public: + explicit PerforceVersionControl(PerforcePlugin *plugin); + bool managesDirectory(const QString &directory) const; + virtual QString findTopLevelForDirectory(const QString &directory) const; + virtual bool vcsOpen(const QString &fileName); + virtual bool vcsAdd(const QString &fileName); + virtual bool vcsDelete(const QString &filename); + +private: + PerforcePlugin *m_plugin; +}; + +} +} +#endif diff --git a/src/plugins/perforce/promptdialog.ui b/src/plugins/perforce/promptdialog.ui new file mode 100644 index 00000000000..aecf8d33378 --- /dev/null +++ b/src/plugins/perforce/promptdialog.ui @@ -0,0 +1,128 @@ +<ui version="4.0" > + <class>Perforce::Internal::PromptDialog</class> + <widget class="QDialog" name="Perforce::Internal::PromptDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>308</width> + <height>113</height> + </rect> + </property> + <property name="windowTitle" > + <string>Perforce Prompt</string> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="msgLabel" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Expanding" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string/> + </property> + <property name="textFormat" > + <enum>Qt::AutoText</enum> + </property> + </widget> + </item> + <item> + <layout class="QGridLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + </layout> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>30</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="okButton" > + <property name="text" > + <string>OK</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>okButton</sender> + <signal>clicked()</signal> + <receiver>Perforce::Internal::PromptDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>278</x> + <y>253</y> + </hint> + <hint type="destinationlabel" > + <x>96</x> + <y>254</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/perforce/settingspage.cpp b/src/plugins/perforce/settingspage.cpp new file mode 100644 index 00000000000..7413e9526c8 --- /dev/null +++ b/src/plugins/perforce/settingspage.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "settingspage.h" +#include "perforcesettings.h" +#include "perforceplugin.h" + +#include <QtGui/QLineEdit> +#include <QtGui/QFileDialog> + +using namespace Perforce::Internal; + +SettingsPageWidget::SettingsPageWidget(QWidget *parent) : + QWidget(parent) +{ + m_ui.setupUi(this); + connect(m_ui.browseButton, SIGNAL(clicked()), this, SLOT(browseForCommand())); +} + +PerforceSettings SettingsPageWidget::settings() const +{ + PerforceSettings rc; + rc.p4Command = m_ui.p4CmdLineEdit->text(); + rc.defaultEnv = m_ui.defaultCheckBox->isChecked(); + rc.p4Port = m_ui.portLineEdit->text(); + rc.p4Client = m_ui.clientLineEdit->text(); + rc.p4User = m_ui.userLineEdit->text(); + return rc; +} + +void SettingsPageWidget::setSettings(const PerforceSettings &s) +{ + m_ui.p4CmdLineEdit->setText(s.p4Command); + m_ui.defaultCheckBox->setChecked(s.defaultEnv); + m_ui.portLineEdit->setText(s.p4Port); + m_ui.clientLineEdit->setText(s.p4Client); + m_ui.userLineEdit->setText(s.p4User); +} + +void SettingsPageWidget::browseForCommand() +{ + const QString cmd = QFileDialog::getOpenFileName(window(), tr("Perforce Command")); + if (!cmd.isEmpty()) + m_ui.p4CmdLineEdit->setText(cmd); +} + + +SettingsPage::SettingsPage() +{ +} + +QString SettingsPage::name() const +{ + return tr("General"); +} + +QString SettingsPage::category() const +{ + return QLatin1String("Perforce"); +} + +QString SettingsPage::trCategory() const +{ + return tr("Perforce"); +} + +QWidget *SettingsPage::createPage(QWidget *parent) +{ + if (!m_widget) + m_widget = new SettingsPageWidget(parent); + m_widget->setSettings(PerforcePlugin::perforcePluginInstance()->settings()); + return m_widget; +} + +void SettingsPage::finished(bool accepted) +{ + if (!accepted || !m_widget) + return; + + PerforcePlugin::perforcePluginInstance()->setSettings(m_widget->settings()); +} diff --git a/src/plugins/perforce/settingspage.h b/src/plugins/perforce/settingspage.h new file mode 100644 index 00000000000..bbbbce923c0 --- /dev/null +++ b/src/plugins/perforce/settingspage.h @@ -0,0 +1,84 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SETTINGSPAGE_H +#define SETTINGSPAGE_H + +#include <QtCore/QPointer> +#include <QtGui/QWidget> + +#include <coreplugin/dialogs/ioptionspage.h> + +#include "ui_settingspage.h" + +namespace Perforce { +namespace Internal { + +struct PerforceSettings; + +class SettingsPageWidget : public QWidget { + Q_OBJECT +public: + explicit SettingsPageWidget(QWidget *parent); + + PerforceSettings settings() const; + void setSettings(const PerforceSettings &); + +private slots:; + void browseForCommand(); + +private: + Ui::SettingsPage m_ui; +}; + +class SettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + SettingsPage(); + + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + +private: + QPointer<SettingsPageWidget> m_widget; +}; + +} // namespace Internal +} // namespace Perforce + +#endif // SETTINGSPAGE_H diff --git a/src/plugins/perforce/settingspage.ui b/src/plugins/perforce/settingspage.ui new file mode 100644 index 00000000000..1379b7b6666 --- /dev/null +++ b/src/plugins/perforce/settingspage.ui @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Perforce::Internal::SettingsPage</class> + <widget class="QWidget" name="Perforce::Internal::SettingsPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>276</width> + <height>198</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>9</number> + </property> + <item> + <layout class="QHBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>P4 Command:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="p4CmdLineEdit"/> + </item> + <item> + <widget class="QToolButton" name="browseButton"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QCheckBox" name="defaultCheckBox"> + <property name="text"> + <string>Use default P4 environment variables</string> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Environment variables</string> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QGridLayout"> + <property name="margin"> + <number>9</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item row="1" column="1"> + <widget class="QLineEdit" name="clientLineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>P4 Client:</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>P4 User:</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>P4 Port:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="userLineEdit"/> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="portLineEdit"/> + </item> + </layout> + </widget> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>141</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <tabstops> + <tabstop>portLineEdit</tabstop> + <tabstop>clientLineEdit</tabstop> + <tabstop>userLineEdit</tabstop> + <tabstop>p4CmdLineEdit</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>defaultCheckBox</sender> + <signal>toggled(bool)</signal> + <receiver>groupBox</receiver> + <slot>setDisabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>134</x> + <y>51</y> + </hint> + <hint type="destinationlabel"> + <x>139</x> + <y>65</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/perforce/submitpanel.ui b/src/plugins/perforce/submitpanel.ui new file mode 100644 index 00000000000..5ce1f259f3f --- /dev/null +++ b/src/plugins/perforce/submitpanel.ui @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Perforce::Internal::SubmitPanel</class> + <widget class="QGroupBox" name="Perforce::Internal::SubmitPanel"> + <property name="geometry"> + <rect> + <x>0</x> + <y>-2</y> + <width>402</width> + <height>134</height> + </rect> + </property> + <property name="title"> + <string>Submit</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QFormLayout" name="formLayout"> + <property name="horizontalSpacing"> + <number>5</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Change:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="changeNumber"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Client:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="clientName"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse</set> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>User:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="userName"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse</set> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/perforce/workbenchclientuser.cpp b/src/plugins/perforce/workbenchclientuser.cpp new file mode 100644 index 00000000000..084a31e5a07 --- /dev/null +++ b/src/plugins/perforce/workbenchclientuser.cpp @@ -0,0 +1,285 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "workbenchclientuser.h" +#include "perforceoutputwindow.h" +#include "perforceplugin.h" + +#include <coreplugin/filemanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> + +#include <QtCore/QEventLoop> +#include <QtCore/QTemporaryFile> +#include <QtGui/QMessageBox> +#include <QtGui/QRadioButton> +#include <QtGui/QMessageBox> +#include <QtGui/QMainWindow> + +using namespace Perforce::Internal; + +PromptDialog::PromptDialog(const QString &choice, const QString &text, + QWidget *parent) + : QDialog(parent) +{ + m_ui.setupUi(this); + m_ui.msgLabel->setText(text); + + const QChar closingParenthesis = QLatin1Char(')'); + const QStringList opts = choice.split(QString(closingParenthesis)); + int row = 0; + int column = 0; + QString opt; + QRadioButton *rb = 0; + for (int i=0; i<opts.count(); ++i) { + opt = opts.at(i).trimmed(); + if (opt.isEmpty() || opt.startsWith(QLatin1String("Help"))) + continue; + if (i == opts.count()-1) + opt = QLatin1String("Default(") + opt.left(opt.length()-1); + opt.append(QLatin1String(")")); + rb = new QRadioButton(opt, this); + rb->setChecked(true); + if (column>0 && column%3==0) + ++row; + m_ui.gridLayout->addWidget(rb, row, column%3, 1, 1); + ++column; + + const int j = opt.lastIndexOf(QLatin1Char('(')); + opt = opt.mid(j+1, opt.lastIndexOf(closingParenthesis)-j-1); + m_optionsMap.insert(rb, opt); + } +} + +QString PromptDialog::input() const +{ + QMapIterator<QRadioButton*, QString> it(m_optionsMap); + while (it.hasNext()) { + it.next(); + if (it.key()->isChecked()) + return it.value(); + } + return QString(); +} + +WorkbenchClientUser::WorkbenchClientUser(PerforceOutputWindow *out, PerforcePlugin *plugin) : + QObject(out), + m_plugin(plugin), + m_coreIFace(PerforcePlugin::coreInstance()), + m_currentEditorIface(0), + m_userCancelled(false), + m_mode(Submit), + m_perforceOutputWindow(out), + m_skipNextMsg(false), + m_eventLoop(new QEventLoop(this)) +{ + connect(m_coreIFace, SIGNAL(coreAboutToClose()), + this, SLOT(cancelP4Command())); +} + +WorkbenchClientUser::~WorkbenchClientUser() +{ +} + +void WorkbenchClientUser::setMode(WorkbenchClientUser::Mode mode) +{ + m_mode = mode; +} + +void WorkbenchClientUser::cancelP4Command() +{ + m_userCancelled = true; + m_eventLoop->quit(); +} + +void WorkbenchClientUser::Message(Error* err) +{ + StrBuf buf; + err->Fmt(&buf); + QString s = buf.Text(); + m_perforceOutputWindow->append(s); + if (!m_skipNextMsg) { + if (err->GetSeverity() == E_FAILED || err->GetSeverity() == E_FATAL) { + if (!s.startsWith("Client side operation(s) failed.")) + m_errMsg.append(s); + } else { + m_msg.append(s); + } + } + m_skipNextMsg = false; +} + +void WorkbenchClientUser::displayErrorMsg(const QString &msg) +{ + if (msg.isEmpty()) + return; + + const QString title = tr("Perforce Error"); + switch (m_mode) { + case Submit: { + QMessageBox msgBox(QMessageBox::Critical, title, msg, QMessageBox::Ok, m_coreIFace->mainWindow()); + msgBox.setDetailedText(m_msg); + msgBox.exec(); + } + break; + default: + QMessageBox::critical(m_coreIFace->mainWindow(), title, msg); + break; + } + m_errMsg.clear(); +} + +void WorkbenchClientUser::OutputError(const char *errBuf) +{ + QString s(errBuf); + s = s.trimmed(); + m_perforceOutputWindow->append(s); + displayErrorMsg(s); +} + +void WorkbenchClientUser::Finished() +{ + m_errMsg = m_errMsg.trimmed(); + displayErrorMsg(m_errMsg); + m_msg.clear(); + m_currentEditorIface = 0; + m_userCancelled = false; + m_skipNextMsg = false; +} + +bool WorkbenchClientUser::editorAboutToClose(Core::IEditor *editor) +{ + if (editor && editor == m_currentEditorIface) { + if (m_mode == WorkbenchClientUser::Submit) { + const QMessageBox::StandardButton answer = + QMessageBox::question(m_coreIFace->mainWindow(), + tr("Closing p4 Editor"), + tr("Do you want to submit this change list?"), + QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes); + if (answer == QMessageBox::Cancel) + return false; + if (answer == QMessageBox::No) + m_userCancelled = true; + m_coreIFace->fileManager()->blockFileChange(m_currentEditorIface->file()); + m_currentEditorIface->file()->save(); + m_coreIFace->fileManager()->unblockFileChange(m_currentEditorIface->file()); + } + m_eventLoop->quit(); + m_currentEditorIface = 0; + } + return true; +} + +void WorkbenchClientUser::Diff(FileSys *f1, FileSys *f2, int, char *, Error *err) +{ + if (!f1->IsTextual() || !f2->IsTextual()) + return; + + FileSys *file1 = File(FST_BINARY); + file1->Set(f1->Name()); + + FileSys *file2 = File(FST_BINARY); + file2->Set(f2->Name()); + + QTemporaryFile tmp; + tmp.open(); + QString fileName = tmp.fileName(); + + { + ::Diff d; + d.SetInput(file1, file2, DiffFlags(), err); + if (!err->Test()) + d.SetOutput(fileName.toLatin1().constData(), err); + if (!err->Test()) + d.DiffUnified(); + d.CloseOutput(err); + } + delete file1; + delete file2; + + QString title = QString("diff %1").arg(f1->Name()); + m_currentEditorIface = m_coreIFace->editorManager()->newFile("Perforce Editor", &title, tmp.readAll()); + if (!m_currentEditorIface) { + err->Set(E_FAILED, "p4 data could not be opened!"); + return; + } + m_userCancelled = false; + m_eventLoop->exec(); + if (m_userCancelled) + err->Set(E_FAILED, ""); +} + +void WorkbenchClientUser::Edit(FileSys *f, Error *err) +{ + QString fileName(f->Name()); + if (m_mode == Submit) { + m_currentEditorIface = m_plugin->openPerforceSubmitEditor(fileName, QStringList()); + } + else { + m_currentEditorIface = m_coreIFace->editorManager()->openEditor(fileName); + m_coreIFace->editorManager()->ensureEditorManagerVisible(); + } + if (!m_currentEditorIface) { + err->Set(E_FAILED, "p4 data could not be opened!"); + return; + } + m_userCancelled = false; + m_eventLoop->exec(); + if (m_userCancelled) + err->Set(E_FAILED, ""); +} + +void WorkbenchClientUser::Prompt(const StrPtr &msg, StrBuf &answer, int , Error *err) +{ + if (m_userCancelled) { + err->Set(E_FATAL, ""); + return; + } + PromptDialog dia(msg.Text(), m_msg, qobject_cast<QWidget*>(m_coreIFace)); + dia.exec(); + answer = qstrdup(dia.input().toLatin1().constData()); + if (m_mode == WorkbenchClientUser::Resolve) { + if (strcmp(answer.Text(), "e") == 0) { + ; + } else if (strcmp(answer.Text(), "d") == 0) { + ; + } else { + m_msg.clear(); + m_skipNextMsg = true; + } + } +} + +void WorkbenchClientUser::ErrorPause(char *msg, Error *) +{ + QMessageBox::warning(m_coreIFace->mainWindow(), tr("Perforce Error"), QString::fromUtf8(msg)); +} diff --git a/src/plugins/perforce/workbenchclientuser.h b/src/plugins/perforce/workbenchclientuser.h new file mode 100644 index 00000000000..9ee25e18576 --- /dev/null +++ b/src/plugins/perforce/workbenchclientuser.h @@ -0,0 +1,111 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef WORKBENCHCLIENTUSER_H +#define WORKBENCHCLIENTUSER_H + +#include <QtCore/QObject> +#include <QtCore/QMap> +#include <coreplugin/icorelistener.h> +#include "p4.h" + +#include "ui_promptdialog.h" + +QT_BEGIN_NAMESPACE +class QRadioButton; +class QEventLoop; +QT_END_NAMESPACE + +namespace Core { +class ICore; +class IEditor; +} + +namespace Perforce { +namespace Internal { + +class PerforceOutputWindow; +class PerforcePlugin; + +class PromptDialog : public QDialog +{ +public: + PromptDialog(const QString &choice, const QString &text, + QWidget *parent = 0); + QString input() const; + +private: + Ui::PromptDialog m_ui; + QMap<QRadioButton*, QString> m_optionsMap; +}; + +class WorkbenchClientUser : public QObject, public ClientUser +{ + Q_OBJECT + +public: + enum Mode {Submit, Resolve}; + WorkbenchClientUser(PerforceOutputWindow *out, PerforcePlugin *plugin); + ~WorkbenchClientUser(); + void setMode(WorkbenchClientUser::Mode mode); + + void Message(Error* err); + void OutputError(const char *errBuf); + void Finished(); + void Diff(FileSys *f1, FileSys *f2, int, char *, Error *err); + void Edit( FileSys *f, Error *err); + void Prompt(const StrPtr &msg, StrBuf &answer, int , Error *err); + void ErrorPause(char *msg, Error *); + bool editorAboutToClose(Core::IEditor *editor); + +private slots: + void cancelP4Command(); + +private: + void displayErrorMsg(const QString &msg); + + PerforcePlugin *m_plugin; + Core::ICore *m_coreIFace; + Core::IEditor *m_currentEditorIface; + bool m_userCancelled; + Mode m_mode; + PerforceOutputWindow *m_perforceOutputWindow; + QString m_msg; + QString m_errMsg; + bool m_skipNextMsg; + QEventLoop *m_eventLoop; +}; + +} // namespace Perforce +} // namespace Internal + +#endif diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro new file mode 100644 index 00000000000..4af2af4195c --- /dev/null +++ b/src/plugins/plugins.pro @@ -0,0 +1,150 @@ +# USE .subdir AND .depends ! +# OTHERWISE PLUGINS WILL BUILD IN WRONG ORDER (DIRECTORIES ARE COMPILED IN PARALLEL) + +TEMPLATE = subdirs + +SUBDIRS = plugin_coreplugin \ + plugin_find \ + plugin_texteditor \ + plugin_cppeditor \ + plugin_bineditor \ + plugin_bookmarks \ + plugin_projectexplorer \ + plugin_vcsbase \ + plugin_perforce \ + plugin_subversion \ + plugin_git \ + plugin_cpptools \ + plugin_qt4projectmanager \ +# plugin_snippets \ # buggy and annoying + plugin_quickopen \ + plugin_debugger \ +# plugin_qtestlib \ # this seems to be dead +# plugin_helloworld \ # sample plugin + plugin_help \ +# plugin_regexp \ # don't know what to do with this + plugin_qtscripteditor \ + plugin_cpaster \ + plugin_cmakeprojectmanager + +# These two plugins require private headers from Qt and therefore don't work +# with an installed/released version of Qt. +exists($$(QTDIR)/.qmake.cache) { + SUBDIRS += plugin_designer plugin_resourceeditor +} else { + message(Designer and Resource Editor plugins are not build! They require private headers and do not compile with your released/installed version of Qt) +} + +plugin_coreplugin.subdir = coreplugin + +plugin_find.subdir = find +plugin_find.depends += plugin_coreplugin + +plugin_texteditor.subdir = texteditor +plugin_texteditor.depends = plugin_find +plugin_texteditor.depends += plugin_quickopen +plugin_texteditor.depends += plugin_coreplugin + +plugin_cppeditor.subdir = cppeditor +plugin_cppeditor.depends = plugin_texteditor +plugin_cppeditor.depends += plugin_coreplugin +plugin_cppeditor.depends += plugin_cpptools + +plugin_bineditor.subdir = bineditor +plugin_bineditor.depends = plugin_texteditor +plugin_bineditor.depends += plugin_coreplugin + +plugin_designer.subdir = designer +plugin_designer.depends = plugin_coreplugin plugin_cppeditor plugin_projectexplorer + +plugin_vcsbase.subdir = vcsbase +plugin_vcsbase.depends = plugin_find +plugin_vcsbase.depends += plugin_texteditor +plugin_vcsbase.depends += plugin_coreplugin +plugin_vcsbase.depends += plugin_projectexplorer + +plugin_perforce.subdir = perforce +plugin_perforce.depends = plugin_vcsbase +plugin_perforce.depends += plugin_projectexplorer +plugin_perforce.depends += plugin_coreplugin + +plugin_git.subdir = git +plugin_git.depends = plugin_texteditor +plugin_git.depends = plugin_vcsbase +plugin_git.depends += plugin_projectexplorer +plugin_git.depends += plugin_coreplugin + +plugin_subversion.subdir = subversion +plugin_subversion.depends = plugin_vcsbase +plugin_subversion.depends += plugin_projectexplorer +plugin_subversion.depends += plugin_coreplugin + +plugin_projectexplorer.subdir = projectexplorer +plugin_projectexplorer.depends = plugin_quickopen +plugin_projectexplorer.depends += plugin_find +plugin_projectexplorer.depends += plugin_coreplugin +plugin_projectexplorer.depends += plugin_texteditor + +plugin_qt4projectmanager.subdir = qt4projectmanager +plugin_qt4projectmanager.depends = plugin_texteditor +plugin_qt4projectmanager.depends += plugin_projectexplorer +plugin_qt4projectmanager.depends += plugin_cpptools +plugin_qt4projectmanager.depends += plugin_cppeditor +plugin_qt4projectmanager.depends += plugin_help + +plugin_quickopen.subdir = quickopen +plugin_quickopen.depends = plugin_coreplugin + +plugin_cpptools.subdir = cpptools +plugin_cpptools.depends = plugin_projectexplorer +plugin_cpptools.depends += plugin_coreplugin +plugin_cpptools.depends += plugin_texteditor + +plugin_bookmarks.subdir = bookmarks +plugin_bookmarks.depends = plugin_projectexplorer +plugin_bookmarks.depends += plugin_coreplugin +plugin_bookmarks.depends += plugin_texteditor + +plugin_snippets.subdir = snippets +plugin_snippets.depends = plugin_projectexplorer +plugin_snippets.depends += plugin_coreplugin +plugin_snippets.depends += plugin_texteditor + +plugin_debugger.subdir = debugger +plugin_debugger.depends = plugin_projectexplorer +plugin_debugger.depends += plugin_coreplugin +plugin_debugger.depends += plugin_cppeditor + +plugin_qtestlib.subdir = qtestlib +plugin_qtestlib.depends = plugin_projectexplorer +plugin_qtestlib.depends += plugin_coreplugin + +plugin_helloworld.subdir = helloworld +plugin_helloworld.depends += plugin_coreplugin + +plugin_help.subdir = help +plugin_help.depends = plugin_find +plugin_help.depends += plugin_quickopen +plugin_help.depends += plugin_coreplugin + +plugin_resourceeditor.subdir = resourceeditor +plugin_resourceeditor.depends = plugin_coreplugin + +plugin_regexp.subdir = regexp +plugin_regexp.depends = plugin_coreplugin + +plugin_qtscripteditor.subdir = qtscripteditor +plugin_qtscripteditor.depends = plugin_texteditor +plugin_qtscripteditor.depends += plugin_coreplugin + +plugin_cpaster.subdir = cpaster +plugin_cpaster.depends += plugin_texteditor +plugin_cpaster.depends += plugin_coreplugin +plugin_cpaster.depends += plugin_projectexplorer + +plugin_cmakeprojectmanager.subdir = cmakeprojectmanager +plugin_cmakeprojectmanager.depends = plugin_texteditor +plugin_cmakeprojectmanager.depends += plugin_projectexplorer +plugin_cmakeprojectmanager.depends += plugin_cpptools +plugin_cmakeprojectmanager.depends += plugin_cppeditor +plugin_cmakeprojectmanager.depends += plugin_help diff --git a/src/plugins/projectexplorer/ProjectExplorer.pluginspec b/src/plugins/projectexplorer/ProjectExplorer.pluginspec new file mode 100644 index 00000000000..1673c3f3999 --- /dev/null +++ b/src/plugins/projectexplorer/ProjectExplorer.pluginspec @@ -0,0 +1,13 @@ +<plugin name="ProjectExplorer" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>ProjectExplorer framework that can be extended with different kind of project types.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> + <dependency name="Find" version="0.9.1"/> + <dependency name="QuickOpen" version="0.9.1"/> + <dependency name="TextEditor" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/projectexplorer/ProjectExplorerInterfaces b/src/plugins/projectexplorer/ProjectExplorerInterfaces new file mode 100644 index 00000000000..6f1c8840fe8 --- /dev/null +++ b/src/plugins/projectexplorer/ProjectExplorerInterfaces @@ -0,0 +1,11 @@ +#include "projectexplorer/buildparserinterface.h" +#include "projectexplorer/projectexplorerconstants.h" +#include "projectexplorer/project.h" +#include "projectexplorer/buildstep.h" +#include "projectexplorer/buildconfiguration.h" +#include "projectexplorer/buildmanager.h" +#include "projectexplorer/projectexplorer.h" +#include "projectexplorer/persistentsettings.h" +#include "projectexplorer/environment.h" +#include "projectexplorer/environmenteditmodel.h" +#include "projectexplorer/abstractprocessstep.h" diff --git a/src/plugins/projectexplorer/abstractprocess.h b/src/plugins/projectexplorer/abstractprocess.h new file mode 100644 index 00000000000..10d0e984b72 --- /dev/null +++ b/src/plugins/projectexplorer/abstractprocess.h @@ -0,0 +1,72 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ABSTRACTPROCESS_H +#define ABSTRACTPROCESS_H + +#include <QtCore/QStringList> + +namespace ProjectExplorer { +namespace Internal { + +class AbstractProcess +{ +public: + AbstractProcess() {} + virtual ~AbstractProcess() {} + + QString workingDirectory() const { return m_workingDir; } + void setWorkingDirectory(const QString &dir) { m_workingDir = dir; } + + QStringList environment() const { return m_environment; } + void setEnvironment(const QStringList &env) { m_environment = env; } + + virtual bool start(const QString &program, const QStringList &args) = 0; + virtual void stop() = 0; + + virtual bool isRunning() const = 0; + virtual qint64 applicationPID() const = 0; + virtual int exitCode() const = 0; + +//signals: + virtual void processError(const QString &error) = 0; + +private: + QString m_workingDir; + QStringList m_environment; +}; + +} //namespace Internal +} //namespace Qt4ProjectManager + +#endif + diff --git a/src/plugins/projectexplorer/abstractprocessstep.cpp b/src/plugins/projectexplorer/abstractprocessstep.cpp new file mode 100644 index 00000000000..2c089b3e5b8 --- /dev/null +++ b/src/plugins/projectexplorer/abstractprocessstep.cpp @@ -0,0 +1,237 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "abstractprocessstep.h" +#include "buildstep.h" +#include "project.h" +#include <QtCore/QProcess> +#include <QtCore/QEventLoop> +#include <QtCore/QDebug> +#include <QtCore/QTimer> + +using namespace ProjectExplorer; + +AbstractProcessStep::AbstractProcessStep(Project *pro) + : BuildStep(pro) +{ +} + +void AbstractProcessStep::setCommand(const QString &buildConfiguration, const QString &cmd) +{ + setValue(buildConfiguration, "abstractProcess.command", cmd); +} + +QString AbstractProcessStep::command(const QString &buildConfiguration) const +{ + return value(buildConfiguration, "abstractProcess.command").toString(); +} + +void AbstractProcessStep::setWorkingDirectory(const QString &buildConfiguration, const QString &workingDirectory) +{ + setValue(buildConfiguration, "abstractProcess.workingDirectory", workingDirectory); +} + +QString AbstractProcessStep::workingDirectory(const QString &buildConfiguration) const +{ + return value(buildConfiguration, "abstractProcess.workingDirectory").toString(); +} + +void AbstractProcessStep::setArguments(const QString &buildConfiguration, const QStringList &arguments) +{ + setValue(buildConfiguration, "abstractProcess.arguments", arguments); +} + +QStringList AbstractProcessStep::arguments(const QString &buildConfiguration) const +{ + return value(buildConfiguration, "abstractProcess.arguments").toStringList(); +} + +void AbstractProcessStep::setEnabled(const QString &buildConfiguration, bool b) +{ + setValue(buildConfiguration, "abstractProcess.enabled", b); +} + +bool AbstractProcessStep::enabled(const QString &buildConfiguration) const +{ + return value(buildConfiguration, "abstractProcess.enabled").toBool(); +} + +void AbstractProcessStep::setEnvironment(const QString &buildConfiguration, Environment env) +{ + setValue(buildConfiguration, "abstractProcess.Environment", env.toStringList()); +} + +Environment AbstractProcessStep::environment(const QString &buildConfiguration) const +{ + return Environment(value(buildConfiguration, "abstractProcess.Environment").toStringList()); +} + +bool AbstractProcessStep::init(const QString &name) +{ + m_command = value(name, "abstractProcess.command").toString(); + m_arguments = value(name, "abstractProcess.arguments").toStringList(); + QVariant var = value(name, "abstractProcess.enabled"); + m_enabled = var.isValid() && var.toBool(); + m_workingDirectory = value(name, "abstractProcess.workingDirectory").toString(); + m_environment = Environment(value(name, "abstractProcess.Environment").toStringList()); + return true; +} + +void AbstractProcessStep::run(QFutureInterface<bool> & fi) +{ + m_futureInterface = &fi; + if(!m_enabled) { + fi.reportResult(true); + return; + } + QDir wd(m_workingDirectory); + if (!wd.exists()) + wd.mkpath(wd.absolutePath()); + + m_process = new QProcess(); + m_process->setWorkingDirectory(m_workingDirectory); + m_process->setEnvironment(m_environment.toStringList()); + + connect(m_process, SIGNAL(readyReadStandardOutput()), + this, SLOT(processReadyReadStdOutput()), + Qt::DirectConnection); + connect(m_process, SIGNAL(readyReadStandardError()), + this, SLOT(processReadyReadStdError()), + Qt::DirectConnection); + + connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(slotProcessFinished(int, QProcess::ExitStatus)), + Qt::DirectConnection); + + m_process->start(m_command, m_arguments); + if(!m_process->waitForStarted()) { + processStartupFailed(); + delete m_process; + m_process = 0; + fi.reportResult(false); + return; + } + processStarted(); + + m_timer = new QTimer(); + connect(m_timer, SIGNAL(timeout()), this, SLOT(checkForCancel()), Qt::DirectConnection); + m_timer->start(500); + m_eventLoop = new QEventLoop; + m_eventLoop->exec(); + m_timer->stop(); + delete m_timer; + + // The process has finished, leftover data is read in processFinished + bool returnValue = processFinished(m_process->exitCode(), m_process->exitStatus()); + + delete m_process; + m_process = 0; + delete m_eventLoop; + m_eventLoop = 0; + fi.reportResult(returnValue); + return; +} + +void AbstractProcessStep::processStarted() +{ + emit addToOutputWindow(tr("<font color=\"#0000ff\">Starting: %1 %2</font>\n").arg(m_command, m_arguments.join(" "))); +} + +bool AbstractProcessStep::processFinished(int exitCode, QProcess::ExitStatus status) +{ + const bool ok = (status == QProcess::NormalExit && exitCode == 0); + if (ok) { + emit addToOutputWindow(tr("<font color=\"#0000ff\">Exited with code %1.</font>").arg(m_process->exitCode())); + } else { + emit addToOutputWindow(tr("<font color=\"#ff0000\"><b>Exited with code %1.</b></font>").arg(m_process->exitCode())); + } + return ok; +} + +void AbstractProcessStep::processStartupFailed() +{ + emit addToOutputWindow(tr("<font color=\"#ff0000\">Could not start process %1 </b></font>").arg(m_command)); +} + +void AbstractProcessStep::processReadyReadStdOutput() +{ + m_process->setReadChannel(QProcess::StandardOutput); + while(m_process->canReadLine()) + { + QString line = QString::fromLocal8Bit(m_process->readLine()).trimmed(); + stdOut(line); + } +} + +void AbstractProcessStep::stdOut(const QString &line) +{ + emit addToOutputWindow(line); +} + +void AbstractProcessStep::processReadyReadStdError() +{ + m_process->setReadChannel(QProcess::StandardError); + while (m_process->canReadLine()) + { + QString line = QString::fromLocal8Bit(m_process->readLine()).trimmed(); + stdError(line); + } +} + +void AbstractProcessStep::stdError(const QString &line) +{ + emit addToOutputWindow(QLatin1String("<font color=\"#ff0000\">") + line + QLatin1String("</font>")); +} + +void AbstractProcessStep::checkForCancel() +{ + if(m_futureInterface->isCanceled() && m_timer->isActive()) { + m_timer->stop(); + m_process->terminate(); + m_process->waitForFinished(5000); + m_process->kill(); + } +} + +void AbstractProcessStep::slotProcessFinished(int, QProcess::ExitStatus) +{ + QString line = QString::fromLocal8Bit(m_process->readAllStandardError()).trimmed(); + if (!line.isEmpty()) { + stdOut(line); + } + + line = QString::fromLocal8Bit(m_process->readAllStandardOutput()).trimmed(); + if (!line.isEmpty()) { + stdError(line); + } + m_eventLoop->exit(0); +} diff --git a/src/plugins/projectexplorer/abstractprocessstep.h b/src/plugins/projectexplorer/abstractprocessstep.h new file mode 100644 index 00000000000..33ddcbe6e5d --- /dev/null +++ b/src/plugins/projectexplorer/abstractprocessstep.h @@ -0,0 +1,141 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ABSTRACTPROCESSSTEP_H +#define ABSTRACTPROCESSSTEP_H + +#include "buildstep.h" +#include "environment.h" +#include <QtCore/QString> +#include <QtCore/QProcess> + +QT_BEGIN_NAMESPACE +class QEventLoop; +QT_END_NAMESPACE + +namespace ProjectExplorer { + +/*! + AbstractProcessStep is a convenience class, which can be used as a base class instead of BuildStep. + It should be used as a base class if your buildstep just needs to run a process. + + Usage: + Use setCommand(), setArguments(), setWorkingDirectory() to specify the process you want to run. + (You need to do that before calling AbstractProcess::init()) + Inside YourBuildStep::init() call AbstractProcessStep::init() + Inside YourBuildStep::run() call AbstractProcessStep::run(), which automatically starts the proces + and by default adds the output on stdOut and stdErr to the OutputWindow. + If you need to process the process output override stdOut() and/or stdErr. + The two functions processStarted() and processFinished() are called after starting/finishing the process. + By default they add a message to the output window. + + Use setEnabled() to control wheter the BuildStep needs to run. (A disabled BuildStep immediately returns true, + from the run function.) + +*/ + +class PROJECTEXPLORER_EXPORT AbstractProcessStep : public BuildStep +{ + Q_OBJECT +public: + AbstractProcessStep(Project *pro); + // reimplemented from BuildStep::init() + // You need to call this from YourBuildStep::init() + virtual bool init(const QString & name); + // reimplemented from BuildStep::init() + // You need to call this from YourBuildStep::run() + virtual void run(QFutureInterface<bool> &); + + // pure virtual functions inheritated from BuildStep + virtual QString name() = 0; + virtual QString displayName() = 0; + virtual BuildStepConfigWidget *createConfigWidget() = 0; + virtual bool immutable() const = 0; + + // setCommand() sets the executable to run in the \p buildConfiguration + void setCommand(const QString &buildConfiguration, const QString &cmd); + // returns the executable that is run for the \p buildConfiguration + QString command(const QString &buildConfiguration) const; + + // sets the workingDirectory for the process for a buildConfiguration + // if no workingDirectory is set, it falls back to the projects workingDirectory TODO remove that magic, thats bad + void setWorkingDirectory(const QString &buildConfiguration, const QString &workingDirectory); + //returns the workingDirectory for a \p buildConfiguration + QString workingDirectory(const QString &buildConfiguration) const; + + // sets the command line arguments used by the process for a \p buildConfiguration + void setArguments(const QString &buildConfiguration, const QStringList &arguments); + // returns the arguments used in the \p buildCOnfiguration + QStringList arguments(const QString &buildConfiguration) const; + + // enables or disables a BuildStep + // Disabled BuildSteps immediately return true from their run method + void setEnabled(const QString &buildConfiguration, bool b); + // returns wheter the BuildStep is disabled + bool enabled(const QString &buildConfiguration) const; + + void setEnvironment(const QString &buildConfiguration, Environment env); + Environment environment(const QString &buildConfiguration) const; + +protected: + // Called after the process is started + // the default implementation adds a process started message to the output message + virtual void processStarted(); + // Called after the process Finished + // the default implementation adds a line to the output window + virtual bool processFinished(int exitCode, QProcess::ExitStatus status); + // Called if the process could not be started, + // by default adds a message to the output window + virtual void processStartupFailed(); + virtual void stdOut(const QString &line); + virtual void stdError(const QString &line); +private slots: + void processReadyReadStdOutput(); + void processReadyReadStdError(); + void slotProcessFinished(int, QProcess::ExitStatus); + void checkForCancel(); +private: + + QTimer *m_timer; + QFutureInterface<bool> *m_futureInterface; + QString m_workingDirectory; + QString m_command; + QStringList m_arguments; + bool m_enabled; + QProcess *m_process; + QEventLoop *m_eventLoop; + ProjectExplorer::Environment m_environment; +}; + +} + +#endif // ABSTRACTPROCESSSTEP_H diff --git a/src/plugins/projectexplorer/allprojectsfilter.cpp b/src/plugins/projectexplorer/allprojectsfilter.cpp new file mode 100644 index 00000000000..7bed021323f --- /dev/null +++ b/src/plugins/projectexplorer/allprojectsfilter.cpp @@ -0,0 +1,75 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "allprojectsfilter.h" +#include "projectexplorer.h" +#include "session.h" +#include "project.h" + +#include <QtCore/QVariant> + +using namespace Core; +using namespace QuickOpen; +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +AllProjectsFilter::AllProjectsFilter(ProjectExplorerPlugin *pe, + ICore *core) + : BaseFileFilter(core) +{ + m_projectExplorer = pe; + connect(m_projectExplorer, SIGNAL(fileListChanged()), + this, SLOT(refreshInternally())); + setShortcutString("a"); + setIncludedByDefault(true); +} + +void AllProjectsFilter::refreshInternally() +{ + m_files.clear(); + SessionManager *session = m_projectExplorer->session(); + if (!session) + return; + foreach (Project *project, session->projects()) + m_files += project->files(Project::AllFiles); + qSort(m_files); + generateFileNames(); +} + +void AllProjectsFilter::refresh(QFutureInterface<void> &future) +{ + Q_UNUSED(future); + // invokeAsyncronouslyOnGuiThread + connect(this, SIGNAL(invokeRefresh()), this, SLOT(refreshInternally())); + emit invokeRefresh(); + disconnect(this, SIGNAL(invokeRefresh()), this, SLOT(refreshInternally())); +} diff --git a/src/plugins/projectexplorer/allprojectsfilter.h b/src/plugins/projectexplorer/allprojectsfilter.h new file mode 100644 index 00000000000..673c5da99ea --- /dev/null +++ b/src/plugins/projectexplorer/allprojectsfilter.h @@ -0,0 +1,73 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ALLPROJECTSFILTER_H +#define ALLPROJECTSFILTER_H + +#include <quickopen/basefilefilter.h> + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QByteArray> +#include <QtCore/QFutureInterface> +#include <QtGui/QWidget> + + +namespace ProjectExplorer { + +class ProjectExplorerPlugin; + +namespace Internal { + +class AllProjectsFilter : public QuickOpen::BaseFileFilter +{ + Q_OBJECT + +public: + AllProjectsFilter(ProjectExplorerPlugin *pe, Core::ICore *core); + QString trName() const { return tr("File in any project"); } + QString name() const { return "File in any project"; } + QuickOpen::IQuickOpenFilter::Priority priority() const { return QuickOpen::IQuickOpenFilter::Low; } + void refresh(QFutureInterface<void> &future); + +private slots: + void refreshInternally(); +signals: + void invokeRefresh(); +private: + ProjectExplorerPlugin *m_projectExplorer; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // ALLPROJECTSFILTER_H diff --git a/src/plugins/projectexplorer/allprojectsfind.cpp b/src/plugins/projectexplorer/allprojectsfind.cpp new file mode 100644 index 00000000000..2c9c3875f0f --- /dev/null +++ b/src/plugins/projectexplorer/allprojectsfind.cpp @@ -0,0 +1,133 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "allprojectsfind.h" +#include "projectexplorer.h" +#include "project.h" + +#include <QtDebug> +#include <QtCore/QRegExp> +#include <QtGui/QGridLayout> + +using namespace Find; +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; +using namespace TextEditor; + +AllProjectsFind::AllProjectsFind(ProjectExplorerPlugin *plugin, Core::ICore *core, SearchResultWindow *resultWindow) + : BaseFileFind(core, resultWindow), + m_plugin(plugin), + m_configWidget(0) +{ + connect(m_plugin, SIGNAL(fileListChanged()), this, SIGNAL(changed())); +} + +QString AllProjectsFind::name() const +{ + return tr("All Projects"); +} + +bool AllProjectsFind::isEnabled() const +{ + return BaseFileFind::isEnabled() + && m_plugin->session() != 0 + && m_plugin->session()->projects().count() > 0; +} + +QKeySequence AllProjectsFind::defaultShortcut() const +{ + return QKeySequence("Ctrl+Shift+F"); +} + +QStringList AllProjectsFind::files() +{ + Q_ASSERT(m_plugin->session()); + if (!m_plugin->session()) + return QStringList(); + QList<QRegExp> filterRegs; + QStringList nameFilters = fileNameFilters(); + foreach (const QString &filter, nameFilters) { + filterRegs << QRegExp(filter, Qt::CaseInsensitive, QRegExp::Wildcard); + } + QStringList files; + QStringList projectFiles; + foreach (const Project *project, m_plugin->session()->projects()) { + projectFiles = project->files(Project::AllFiles); + if (!filterRegs.isEmpty()) { + foreach (const QString &file, projectFiles) { + foreach (const QRegExp ®, filterRegs) { + if (reg.exactMatch(file)) { + files.append(file); + break; + } + } + } + } else { + files += projectFiles; + } + } + files.removeDuplicates(); + return files; +} + +QWidget *AllProjectsFind::createConfigWidget() +{ + if (!m_configWidget) { + m_configWidget = new QWidget; + QGridLayout * const gridLayout = new QGridLayout(m_configWidget); + gridLayout->setMargin(0); + m_configWidget->setLayout(gridLayout); + gridLayout->addWidget(createRegExpWidget(), 0, 1); + QLabel * const filePatternLabel = new QLabel(tr("File pattern:")); + filePatternLabel->setMinimumWidth(80); + filePatternLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + filePatternLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + gridLayout->addWidget(filePatternLabel, 1, 0, Qt::AlignRight); + gridLayout->addWidget(createPatternWidget(), 1, 1); + m_configWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + } + return m_configWidget; +} + +void AllProjectsFind::writeSettings(QSettings *settings) +{ + settings->beginGroup("AllProjectsFind"); + writeCommonSettings(settings); + settings->endGroup(); +} + +void AllProjectsFind::readSettings(QSettings *settings) +{ + settings->beginGroup("AllProjectsFind"); + readCommonSettings(settings, "*"); + settings->endGroup(); +} diff --git a/src/plugins/projectexplorer/allprojectsfind.h b/src/plugins/projectexplorer/allprojectsfind.h new file mode 100644 index 00000000000..f5bba54a034 --- /dev/null +++ b/src/plugins/projectexplorer/allprojectsfind.h @@ -0,0 +1,80 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ALLPROJECTSFIND_H +#define ALLPROJECTSFIND_H + +#include <coreplugin/icore.h> +#include <find/ifindfilter.h> +#include <find/searchresultwindow.h> +#include <texteditor/basefilefind.h> + +#include <QtCore/QPointer> +#include <QtGui/QLabel> +#include <QtGui/QComboBox> +#include <QtGui/QStringListModel> + + +namespace ProjectExplorer { + +class ProjectExplorerPlugin; + +namespace Internal { + +class AllProjectsFind : public TextEditor::BaseFileFind +{ + Q_OBJECT + +public: + AllProjectsFind(ProjectExplorerPlugin *plugin, Core::ICore *core, Find::SearchResultWindow *resultWindow); + + QString name() const; + + bool isEnabled() const; + QKeySequence defaultShortcut() const; + + QWidget *createConfigWidget(); + void writeSettings(QSettings *settings); + void readSettings(QSettings *settings); + +protected: + QStringList files(); + +private: + ProjectExplorerPlugin *m_plugin; + QPointer<QWidget> m_configWidget; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // ALLPROJECTSFIND_H diff --git a/src/plugins/projectexplorer/applicationlauncher.h b/src/plugins/projectexplorer/applicationlauncher.h new file mode 100644 index 00000000000..6c4e6a2a86e --- /dev/null +++ b/src/plugins/projectexplorer/applicationlauncher.h @@ -0,0 +1,96 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef APPLICATIONLAUNCHER_H +#define APPLICATIONLAUNCHER_H + +#include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtCore/QProcess> + +namespace ProjectExplorer { +namespace Internal { + +class ConsoleProcess; +class WinGuiProcess; + +class ApplicationLauncher : public QObject +{ + Q_OBJECT + +public: + enum Mode { + Console, + Gui + }; + + ApplicationLauncher(QObject *parent = 0); + void setWorkingDirectory(const QString &dir); + void setEnvironment(const QStringList &env); + + void start(Mode mode, const QString &program, + const QStringList &args = QStringList()); + void stop(); + bool isRunning() const; + qint64 applicationPID() const; + +signals: + void applicationError(const QString &error); + void appendOutput(const QString &line); + void processExited(int exitCode); + void bringToForegroundRequested(qint64 pid); + +private slots: + void processStopped(); +#ifdef Q_OS_WIN + void readWinDebugOutput(const QString &output); + void processFinished(int exitCode); +#else + void guiProcessError(); + void readStandardOutput(); + void processDone(int, QProcess::ExitStatus); +#endif + + void bringToForeground(); + +private: + QProcess *m_guiProcess; + ConsoleProcess *m_consoleProcess; + Mode m_currentMode; + + WinGuiProcess *m_winGuiProcess; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // APPLICATIONLAUNCHER_H diff --git a/src/plugins/projectexplorer/applicationlauncher_win.cpp b/src/plugins/projectexplorer/applicationlauncher_win.cpp new file mode 100644 index 00000000000..b7cac9a7437 --- /dev/null +++ b/src/plugins/projectexplorer/applicationlauncher_win.cpp @@ -0,0 +1,137 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <projectexplorer/ProjectExplorerInterfaces> + +#include <QDebug> +#include "applicationlauncher.h" +#include "consoleprocess.h" +#include "winguiprocess.h" + +using namespace ProjectExplorer::Internal; + +ApplicationLauncher::ApplicationLauncher(QObject *parent) + : QObject(parent) +{ + m_currentMode = Gui; + + m_consoleProcess = new ConsoleProcess(this); + connect(m_consoleProcess, SIGNAL(processError(const QString&)), + this, SIGNAL(applicationError(const QString&))); + connect(m_consoleProcess, SIGNAL(processStopped()), + this, SLOT(processStopped())); + + m_winGuiProcess = new WinGuiProcess(this); + connect(m_winGuiProcess, SIGNAL(processError(const QString&)), + this, SIGNAL(applicationError(const QString&))); + connect(m_winGuiProcess, SIGNAL(receivedDebugOutput(const QString&)), + this, SLOT(readWinDebugOutput(const QString&))); + connect(m_winGuiProcess, SIGNAL(processFinished(int)), + this, SLOT(processFinished(int))); + +} + +void ApplicationLauncher::setWorkingDirectory(const QString &dir) +{ + m_winGuiProcess->setWorkingDirectory(dir); + m_consoleProcess->setWorkingDirectory(dir); +} + +void ApplicationLauncher::setEnvironment(const QStringList &env) +{ + m_winGuiProcess->setEnvironment(env); + m_consoleProcess->setEnvironment(env); +} + +void ApplicationLauncher::start(Mode mode, const QString &program, const QStringList &args) +{ + qDebug()<<"ApplicationLauncher::start"<<program<<args; + m_currentMode = mode; + if (mode == Gui) { + m_winGuiProcess->start(program, args); + } else { + m_consoleProcess->start(program, args); + } +} + +void ApplicationLauncher::stop() +{ + if (m_currentMode == Gui) { + m_winGuiProcess->stop(); + } else { + m_consoleProcess->stop(); + } +} + +bool ApplicationLauncher::isRunning() const +{ + if (m_currentMode == Gui) + return m_winGuiProcess->isRunning(); + else + return m_consoleProcess->isRunning(); +} + +qint64 ApplicationLauncher::applicationPID() const +{ + qint64 result = 0; + if (!isRunning()) + return result; + + if (m_currentMode == Console) { + result = m_consoleProcess->applicationPID(); + } else { + result = m_winGuiProcess->applicationPID(); + } + return result; +} + +void ApplicationLauncher::readWinDebugOutput(const QString &output) +{ + QString s = output; + if (s.endsWith(QLatin1Char('\n'))) + s.chop(1); + emit appendOutput(s); +} + +void ApplicationLauncher::processStopped() +{ + emit processExited(0); +} + +void ApplicationLauncher::processFinished(int exitCode) +{ + emit processExited(exitCode); +} + +void ApplicationLauncher::bringToForeground() +{ +} diff --git a/src/plugins/projectexplorer/applicationlauncher_x11.cpp b/src/plugins/projectexplorer/applicationlauncher_x11.cpp new file mode 100644 index 00000000000..84054271d0a --- /dev/null +++ b/src/plugins/projectexplorer/applicationlauncher_x11.cpp @@ -0,0 +1,159 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "applicationlauncher.h" +#include "consoleprocess.h" + +#include <projectexplorer/ProjectExplorerInterfaces> + +#include <QtCore/QTimer> +#include <QtDebug> + +using namespace ProjectExplorer::Internal; + +ApplicationLauncher::ApplicationLauncher(QObject *parent) + : QObject(parent) +{ + m_currentMode = Gui; + m_guiProcess = new QProcess(this); + m_guiProcess->setReadChannelMode(QProcess::MergedChannels); + connect(m_guiProcess, SIGNAL(error(QProcess::ProcessError)), + this, SLOT(guiProcessError())); + connect(m_guiProcess, SIGNAL(readyReadStandardOutput()), + this, SLOT(readStandardOutput())); + connect(m_guiProcess, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(processDone(int, QProcess::ExitStatus))); + connect(m_guiProcess, SIGNAL(started()), + this, SLOT(bringToForeground())); + + m_consoleProcess = new ConsoleProcess(this); + connect(m_consoleProcess, SIGNAL(processError(const QString&)), + this, SIGNAL(applicationError(const QString&))); + connect(m_consoleProcess, SIGNAL(processStopped()), + this, SLOT(processStopped())); +} + +void ApplicationLauncher::setWorkingDirectory(const QString &dir) +{ + m_guiProcess->setWorkingDirectory(dir); + m_consoleProcess->setWorkingDirectory(dir); +} + +void ApplicationLauncher::setEnvironment(const QStringList &env) +{ + m_guiProcess->setEnvironment(env); + m_consoleProcess->setEnvironment(env); +} + +void ApplicationLauncher::start(Mode mode, const QString &program, const QStringList &args) +{ + m_currentMode = mode; + if (mode == Gui) { + m_guiProcess->start(program, args); + } else { + m_consoleProcess->start(program, args); + } +} + +void ApplicationLauncher::stop() +{ + if (m_currentMode == Gui) { + m_guiProcess->terminate(); + m_guiProcess->waitForFinished(); + } else { + m_consoleProcess->stop(); + } +} + +bool ApplicationLauncher::isRunning() const +{ + if (m_currentMode == Gui) + return m_guiProcess->state() != QProcess::NotRunning; + else + return m_consoleProcess->isRunning(); +} + +qint64 ApplicationLauncher::applicationPID() const +{ + qint64 result = 0; + if (!isRunning()) + return result; + + if (m_currentMode == Console) { + result = m_consoleProcess->applicationPID(); + } else { + result = (qint64)m_guiProcess->pid(); + } + return result; +} + +void ApplicationLauncher::guiProcessError() +{ + QString error; + switch (m_guiProcess->error()) { + case QProcess::FailedToStart: + error = tr("Failed to start program. Path or permissions wrong?"); + break; + case QProcess::Crashed: + error = tr("The program has unexpectedly finished."); + break; + default: + error = tr("Some error has occurred while running the program."); + } + emit applicationError(error); +} + +void ApplicationLauncher::readStandardOutput() +{ + m_guiProcess->setReadChannel(QProcess::StandardOutput); + while (m_guiProcess->canReadLine()) { + QString line = QString::fromLocal8Bit(m_guiProcess->readLine()); + if (line.endsWith(QLatin1Char('\n'))) + line.chop(1); + emit appendOutput(line); + } +} + +void ApplicationLauncher::processStopped() +{ + emit processExited(0); +} + +void ApplicationLauncher::processDone(int exitCode, QProcess::ExitStatus) +{ + emit processExited(exitCode); +} + +void ApplicationLauncher::bringToForeground() +{ + emit bringToForegroundRequested(applicationPID()); +} diff --git a/src/plugins/projectexplorer/applicationrunconfiguration.cpp b/src/plugins/projectexplorer/applicationrunconfiguration.cpp new file mode 100644 index 00000000000..9f4cca193be --- /dev/null +++ b/src/plugins/projectexplorer/applicationrunconfiguration.cpp @@ -0,0 +1,169 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "applicationrunconfiguration.h" +#include "persistentsettings.h" +#include "environment.h" +#include <projectexplorer/projectexplorerconstants.h> + +#include <QtGui/QLabel> +#include <QDebug> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +/// ApplicationRunConfiguration + +ApplicationRunConfiguration::ApplicationRunConfiguration(Project *pro) + : RunConfiguration(pro) +{ +} + +ApplicationRunConfiguration::~ApplicationRunConfiguration() +{ +} + +QString ApplicationRunConfiguration::type() const +{ + return "ProjectExplorer.ApplicationRunConfiguration"; +} + +void ApplicationRunConfiguration::save(PersistentSettingsWriter &writer) const +{ + RunConfiguration::save(writer); +} + +void ApplicationRunConfiguration::restore(const PersistentSettingsReader &reader) +{ + RunConfiguration::restore(reader); +} + +/// ApplicationRunConfigurationRunner + +ApplicationRunConfigurationRunner::ApplicationRunConfigurationRunner() +{ +} + +ApplicationRunConfigurationRunner::~ApplicationRunConfigurationRunner() +{ +} + +bool ApplicationRunConfigurationRunner::canRun(QSharedPointer<RunConfiguration> runConfiguration, const QString &mode) +{ + return (mode == ProjectExplorer::Constants::RUNMODE) + && (!qSharedPointerCast<ApplicationRunConfiguration>(runConfiguration).isNull()); +} + +QString ApplicationRunConfigurationRunner::displayName() const +{ + return QObject::tr("Run"); +} + +RunControl* ApplicationRunConfigurationRunner::run(QSharedPointer<RunConfiguration> runConfiguration, const QString &mode) +{ + QSharedPointer<ApplicationRunConfiguration> rc = qSharedPointerCast<ApplicationRunConfiguration>(runConfiguration); + Q_ASSERT(rc); + Q_ASSERT(mode == ProjectExplorer::Constants::RUNMODE); + + ApplicationRunControl *runControl = new ApplicationRunControl(rc); + return runControl; +} + +QWidget *ApplicationRunConfigurationRunner::configurationWidget(QSharedPointer<RunConfiguration> runConfiguration) +{ + Q_UNUSED(runConfiguration); + return new QLabel("TODO add Configuration widget"); +} + +// ApplicationRunControl + +ApplicationRunControl::ApplicationRunControl(QSharedPointer<ApplicationRunConfiguration> runConfiguration) + : RunControl(runConfiguration), m_applicationLauncher() +{ + connect(&m_applicationLauncher, SIGNAL(applicationError(const QString &)), + this, SLOT(slotError(const QString &))); + connect(&m_applicationLauncher, SIGNAL(appendOutput(const QString &)), + this, SLOT(slotAddToOutputWindow(const QString &))); + connect(&m_applicationLauncher, SIGNAL(processExited(int)), + this, SLOT(processExited(int))); + connect(&m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)), + this, SLOT(bringApplicationToForeground(qint64))); +} + +ApplicationRunControl::~ApplicationRunControl() +{ +} + +void ApplicationRunControl::start() +{ + QSharedPointer<ApplicationRunConfiguration> rc = qSharedPointerCast<ApplicationRunConfiguration>(runConfiguration()); + Q_ASSERT(rc); + + m_applicationLauncher.setEnvironment(rc->environment().toStringList()); + m_applicationLauncher.setWorkingDirectory(rc->workingDirectory()); + + m_executable = rc->executable(); + + m_applicationLauncher.start(static_cast<Internal::ApplicationLauncher::Mode>(rc->runMode()), + m_executable, rc->commandLineArguments()); + emit started(); + + emit addToOutputWindow(this, tr("Starting %1").arg(m_executable)); +} + +void ApplicationRunControl::stop() +{ + m_applicationLauncher.stop(); +} + +bool ApplicationRunControl::isRunning() const +{ + return m_applicationLauncher.isRunning(); +} + +void ApplicationRunControl::slotError(const QString & err) +{ + emit error(this, err); + emit finished(); +} + +void ApplicationRunControl::slotAddToOutputWindow(const QString &line) +{ + emit addToOutputWindow(this, line); +} + +void ApplicationRunControl::processExited(int exitCode) +{ + emit addToOutputWindow(this, tr("%1 exited with code %2").arg(m_executable).arg(exitCode)); + emit finished(); +} + diff --git a/src/plugins/projectexplorer/applicationrunconfiguration.h b/src/plugins/projectexplorer/applicationrunconfiguration.h new file mode 100644 index 00000000000..e8378691857 --- /dev/null +++ b/src/plugins/projectexplorer/applicationrunconfiguration.h @@ -0,0 +1,100 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef APPLICATIONRUNCONFIGURATION_H +#define APPLICATIONRUNCONFIGURATION_H + +#include "runconfiguration.h" +#include "applicationlauncher.h" + +namespace ProjectExplorer { + +class Environment; + +class PROJECTEXPLORER_EXPORT ApplicationRunConfiguration : public RunConfiguration +{ + Q_OBJECT +public: + enum RunMode { + Console = Internal::ApplicationLauncher::Console, + Gui + }; + + ApplicationRunConfiguration(Project *pro); + virtual ~ApplicationRunConfiguration(); + virtual QString type() const; + virtual QString executable() const = 0; + virtual RunMode runMode() const = 0; + virtual QString workingDirectory() const = 0; + virtual QStringList commandLineArguments() const = 0; + virtual Environment environment() const = 0; + + virtual void save(PersistentSettingsWriter &writer) const; + virtual void restore(const PersistentSettingsReader &reader); +}; + +namespace Internal { + +class ApplicationRunConfigurationRunner : public IRunConfigurationRunner +{ + Q_OBJECT +public: + ApplicationRunConfigurationRunner(); + virtual ~ApplicationRunConfigurationRunner(); + virtual bool canRun(QSharedPointer<RunConfiguration> runConfiguration, const QString &mode); + virtual QString displayName() const; + virtual RunControl* run(QSharedPointer<RunConfiguration> runConfiguration, const QString &mode); + virtual QWidget *configurationWidget(QSharedPointer<RunConfiguration> runConfiguration); +}; + +class ApplicationRunControl : public RunControl +{ + Q_OBJECT +public: + ApplicationRunControl(QSharedPointer<ApplicationRunConfiguration> runConfiguration); + virtual ~ApplicationRunControl(); + virtual void start(); + virtual void stop(); + virtual bool isRunning() const; +private slots: + void processExited(int exitCode); + void slotAddToOutputWindow(const QString &line); + void slotError(const QString & error); +private: + ApplicationLauncher m_applicationLauncher; + QString m_executable; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // APPLICATIONRUNCONFIGURATION_H diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp new file mode 100644 index 00000000000..a886af04601 --- /dev/null +++ b/src/plugins/projectexplorer/buildconfiguration.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "buildconfiguration.h" + +using namespace ProjectExplorer; + +BuildConfiguration::BuildConfiguration(const QString &name) + : m_name(name) +{ + setDisplayName(name); +} + +BuildConfiguration::BuildConfiguration(const QString &name, BuildConfiguration *source) + :m_values(source->m_values), m_name(name) +{ + +} + +QString BuildConfiguration::name() const +{ + return m_name; +} + +QString BuildConfiguration::displayName() +{ + QVariant v = getValue("ProjectExplorer.BuildConfiguration.DisplayName"); + if (v.isValid()) { + return v.toString(); + } else { + setDisplayName(m_name); + return m_name; + } +} + +void BuildConfiguration::setDisplayName(const QString &name) +{ + setValue("ProjectExplorer.BuildConfiguration.DisplayName", name); +} + +QVariant BuildConfiguration::getValue(const QString & key) const +{ + QHash<QString, QVariant>::const_iterator it = m_values.find(key); + if(it != m_values.constEnd()) + return *it; + else + return QVariant(); +} + +void BuildConfiguration::setValue(const QString & key, QVariant value) +{ + m_values[key] = value; +} + +void BuildConfiguration::setValuesFromMap(QMap<QString, QVariant> map) +{ + QMap<QString, QVariant>::const_iterator it, end; + end = map.constEnd(); + for(it = map.constBegin(); it != end; ++it) + setValue(it.key(), it.value()); +} + +QMap<QString, QVariant> BuildConfiguration::toMap() const +{ + QMap<QString, QVariant> result; + QHash<QString, QVariant>::const_iterator it, end; + end = m_values.constEnd(); + for(it = m_values.constBegin(); it != end; ++it) + result.insert(it.key(), it.value()); + return result; +} + diff --git a/src/plugins/projectexplorer/buildconfiguration.h b/src/plugins/projectexplorer/buildconfiguration.h new file mode 100644 index 00000000000..a9e41e039e0 --- /dev/null +++ b/src/plugins/projectexplorer/buildconfiguration.h @@ -0,0 +1,64 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BUILDCONFIGURATION_H +#define BUILDCONFIGURATION_H + +#include <QtCore/QString> +#include <QtCore/QVariant> +#include <QtCore/QHash> + +namespace ProjectExplorer { + +class BuildConfiguration +{ +public: + BuildConfiguration(const QString &name); + BuildConfiguration(const QString &name, BuildConfiguration *source); + QString name() const; + QVariant getValue(const QString & key) const; + void setValue(const QString & key, QVariant value); + + QString displayName(); + void setDisplayName(const QString &name); + + QMap<QString, QVariant> toMap() const; + void setValuesFromMap(QMap<QString, QVariant> map); + +private: + QHash<QString, QVariant> m_values; + QString m_name; +}; + +} + +#endif diff --git a/src/plugins/projectexplorer/buildmanager.cpp b/src/plugins/projectexplorer/buildmanager.cpp new file mode 100644 index 00000000000..87bc38aa863 --- /dev/null +++ b/src/plugins/projectexplorer/buildmanager.cpp @@ -0,0 +1,385 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "buildmanager.h" +#include "buildstep.h" +#include "compileoutputwindow.h" +#include "taskwindow.h" +#include "projectexplorer.h" +#include "projectexplorerconstants.h" +#include "buildprogress.h" + +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/progressmanager/progressmanagerinterface.h> +#include <coreplugin/progressmanager/futureprogress.h> + +#include <QtCore/QDir> +#include <QtCore/QTimer> +#include <QtGui/QHeaderView> +#include <QtGui/QIcon> +#include <QtGui/QLabel> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +BuildManager::BuildManager(ProjectExplorerPlugin *parent) + : QObject(parent) + , m_running(false) + , m_previousBuildStepProject(0) + , m_canceling(false) + , m_maxProgress(0) + , m_progressFutureInterface(0) +{ + ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); + m_projectExplorerPlugin = parent; + + connect(&m_watcher, SIGNAL(finished()), + this, SLOT(nextBuildQueue())); + + m_outputWindow = new CompileOutputWindow(this); + pm->addObject(m_outputWindow); + + m_taskWindow = new TaskWindow; + pm->addObject(m_taskWindow); + + connect(m_taskWindow, SIGNAL(tasksChanged()), + this, SIGNAL(tasksChanged())); + + connect(&m_progressWatcher, SIGNAL(canceled()), + this, SLOT(cancel())); +} + +BuildManager::~BuildManager() +{ + cancel(); + ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); + + pm->removeObject(m_taskWindow); + delete m_taskWindow; + + pm->removeObject(m_outputWindow); + delete m_outputWindow; +} + +bool BuildManager::isBuilding() const +{ + // we are building even if we are not running yet + return !m_buildQueue.isEmpty() || m_running; +} + +void BuildManager::cancel() +{ + if (m_running) { + m_canceling = true; + m_watcher.cancel(); + m_watcher.waitForFinished(); + + // The cancel message is added to the output window via a single shot timer + // since the canceling is likely to have generated new addToOutputWindow signals + // which are waiting in the event queue to be processed + // (And we want those to be before the cancel message.) + QTimer::singleShot(0, this, SLOT(emitCancelMessage())); + + disconnect(m_currentBuildStep, SIGNAL(addToTaskWindow(QString, int, int, QString)), + this, SLOT(addToTaskWindow(QString, int, int, QString))); + disconnect(m_currentBuildStep, SIGNAL(addToOutputWindow(QString)), + this, SLOT(addToOutputWindow(QString))); + decrementActiveBuildSteps(m_currentBuildStep->project()); + + m_progressFutureInterface->setProgressValueAndText(m_progress, "Build canceled"); //TODO NBS fix in qtconcurrent + clearBuildQueue(); + } + return; +} + +void BuildManager::emitCancelMessage() +{ + emit addToOutputWindow(tr("<font color=\"#ff0000\">Canceled build.</font>")); +} + +void BuildManager::clearBuildQueue() +{ + foreach (BuildStep * bs, m_buildQueue) + decrementActiveBuildSteps(bs->project()); + + m_buildQueue.clear(); + m_configurations.clear(); + m_running = false; + m_previousBuildStepProject = 0; + + m_progressFutureInterface->reportCanceled(); + m_progressFutureInterface->reportFinished(); + delete m_progressFutureInterface; + m_progressFutureInterface = 0; + m_maxProgress = 0; + + emit buildQueueFinished(false); +} + + +void BuildManager::toggleOutputWindow() +{ + m_outputWindow->toggle(false); +} + +void BuildManager::showTaskWindow() +{ + m_taskWindow->popup(false); +} + +void BuildManager::toggleTaskWindow() +{ + m_taskWindow->toggle(false); +} + +bool BuildManager::tasksAvailable() const +{ + return m_taskWindow->numberOfTasks() > 0; +} + +void BuildManager::gotoTaskWindow() +{ + m_taskWindow->popup(true); +} + +void BuildManager::startBuildQueue() +{ + if (!m_running) { + // Progress Reporting + Core::ProgressManagerInterface *progressManager = + ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()->progressManager(); + m_progressFutureInterface = new QFutureInterface<void>; + m_progressWatcher.setFuture(m_progressFutureInterface->future()); + Core::FutureProgress *progress = progressManager->addTask(m_progressFutureInterface->future(), + tr("Build"), + Constants::TASK_BUILD); + connect(progress, SIGNAL(clicked()), this, SLOT(showBuildResults())); + progress->setWidget(new BuildProgress(m_taskWindow)); + m_progress = 0; + m_progressFutureInterface->setProgressRange(0, m_maxProgress); + + m_running = true; + m_canceling = false; + m_progressFutureInterface->reportStarted(); + m_outputWindow->clearContents(); + m_taskWindow->clearContents(); + nextStep(); + } else { + // Already running + m_progressFutureInterface->setProgressRange(0, m_maxProgress); + const QString &progressText = tr("Finished %1 of %2 build steps").arg(m_progress).arg(m_maxProgress); + m_progressFutureInterface->setProgressValueAndText(m_progress, progressText); + } +} + +void BuildManager::showBuildResults() +{ + if (m_taskWindow->numberOfTasks() != 0) + toggleTaskWindow(); + else + toggleOutputWindow(); + //toggleTaskWindow(); +} + +void BuildManager::addToTaskWindow(const QString &file, int type, int line, const QString &description) +{ + m_taskWindow->addItem(BuildParserInterface::PatternType(type), description, file, line); +} + +void BuildManager::addToOutputWindow(const QString &string) +{ + m_outputWindow->appendText(string); +} + +void BuildManager::nextBuildQueue() +{ + if (m_canceling) + return; + + disconnect(m_currentBuildStep, SIGNAL(addToTaskWindow(QString, int, int, QString)), + this, SLOT(addToTaskWindow(QString, int, int, QString))); + disconnect(m_currentBuildStep, SIGNAL(addToOutputWindow(QString)), + this, SLOT(addToOutputWindow(QString))); + + ++m_progress; + const QString &progressText = tr("Finished %1 of %2 build steps").arg(m_progress).arg(m_maxProgress); + m_progressFutureInterface->setProgressValueAndText(m_progress, progressText); + + bool result = m_watcher.result(); + if (!result) { + // Build Failure + addToOutputWindow(tr("<font color=\"#ff0000\">Error while building project %1</font>").arg(m_currentBuildStep->project()->name())); + addToOutputWindow(tr("<font color=\"#ff0000\">When executing build step '%1'</font>").arg(m_currentBuildStep->displayName())); + // NBS TODO fix in qtconcurrent + m_progressFutureInterface->setProgressValueAndText(m_progress, tr("Error while building project %1").arg(m_currentBuildStep->project()->name())); + } + + decrementActiveBuildSteps(m_currentBuildStep->project()); + if (result) + nextStep(); + else + clearBuildQueue(); +} + +void BuildManager::nextStep() +{ + if (!m_buildQueue.empty()) { + m_currentBuildStep = m_buildQueue.front(); + m_currentConfiguration = m_configurations.front(); + m_buildQueue.pop_front(); + m_configurations.pop_front(); + + connect(m_currentBuildStep, SIGNAL(addToTaskWindow(QString, int, int, QString)), + this, SLOT(addToTaskWindow(QString, int, int, QString))); + connect(m_currentBuildStep, SIGNAL(addToOutputWindow(QString)), + this, SLOT(addToOutputWindow(QString))); + + bool init = m_currentBuildStep->init(m_currentConfiguration); + if (!init) { + addToOutputWindow(tr("<font color=\"#ff0000\">Error while building project %1</font>").arg(m_currentBuildStep->project()->name())); + addToOutputWindow(tr("<font color=\"#ff0000\">When executing build step '%1'</font>").arg(m_currentBuildStep->displayName())); + cancel(); + return; + } + + if (m_currentBuildStep->project() != m_previousBuildStepProject) { + const QString projectName = m_currentBuildStep->project()->name(); + addToOutputWindow(tr("<b>Running build steps for project %2...</b>") + .arg(projectName)); + m_previousBuildStepProject = m_currentBuildStep->project(); + } + m_watcher.setFuture(QtConcurrent::run(&BuildStep::run, m_currentBuildStep)); + } else { + m_running = false; + m_previousBuildStepProject = 0; + m_progressFutureInterface->reportFinished(); + delete m_progressFutureInterface; + m_progressFutureInterface = 0; + m_maxProgress = 0; + emit buildQueueFinished(true); + } +} + +void BuildManager::buildQueueAppend(BuildStep * bs, const QString &configuration) +{ + m_buildQueue.append(bs); + ++m_maxProgress; + incrementActiveBuildSteps(bs->project()); + m_configurations.append(configuration); +} + +void BuildManager::buildProjects(const QList<Project *> &projects, const QList<QString> &configurations) +{ + Q_ASSERT(projects.count() == configurations.count()); + QList<QString>::const_iterator cit = configurations.constBegin(); + QList<Project *>::const_iterator it, end; + end = projects.constEnd(); + + for (it = projects.constBegin(); it != end; ++it, ++cit) { + QList<BuildStep *> buildSteps = (*it)->buildSteps(); + foreach (BuildStep *bs, buildSteps) { + buildQueueAppend(bs, *cit); + } + } + startBuildQueue(); +} + +void BuildManager::cleanProjects(const QList<Project *> &projects, const QList<QString> &configurations) +{ + Q_ASSERT(projects.count() == configurations.count()); + QList<QString>::const_iterator cit = configurations.constBegin(); + QList<Project *>::const_iterator it, end; + end = projects.constEnd(); + + for (it = projects.constBegin(); it != end; ++it, ++cit) { + QList<BuildStep *> cleanSteps = (*it)->cleanSteps(); + foreach (BuildStep *bs, cleanSteps) { + buildQueueAppend(bs, *cit); + } + } + startBuildQueue(); +} + +void BuildManager::buildProject(Project *p, const QString &configuration) +{ + buildProjects(QList<Project *>() << p, QList<QString>() << configuration); +} + +void BuildManager::cleanProject(Project *p, const QString &configuration) +{ + cleanProjects(QList<Project *>() << p, QList<QString>() << configuration); +} + +void BuildManager::appendStep(BuildStep *step, const QString &configuration) +{ + buildQueueAppend(step, configuration); + startBuildQueue(); +} + +bool BuildManager::isBuilding(Project *pro) +{ + QHash<Project *, int>::iterator it = m_activeBuildSteps.find(pro); + QHash<Project *, int>::iterator end = m_activeBuildSteps.end(); + if (it == end || *it == 0) + return false; + else + return true; +} + +void BuildManager::incrementActiveBuildSteps(Project *pro) +{ + QHash<Project *, int>::iterator it = m_activeBuildSteps.find(pro); + QHash<Project *, int>::iterator end = m_activeBuildSteps.end(); + if (it == end) { + m_activeBuildSteps.insert(pro, 1); + emit buildStateChanged(pro); + } else if (*it == 0) { + ++*it; + emit buildStateChanged(pro); + } else { + ++*it; + } +} + +void BuildManager::decrementActiveBuildSteps(Project *pro) +{ + QHash<Project *, int>::iterator it = m_activeBuildSteps.find(pro); + QHash<Project *, int>::iterator end = m_activeBuildSteps.end(); + if (it == end) { + Q_ASSERT(false && "BuildManager m_activeBuildSteps says project is not building, but apparently a build step was still in the queue."); + } else if (*it == 1) { + --*it; + emit buildStateChanged(pro); + } else { + --*it; + } +} diff --git a/src/plugins/projectexplorer/buildmanager.h b/src/plugins/projectexplorer/buildmanager.h new file mode 100644 index 00000000000..b3754358e09 --- /dev/null +++ b/src/plugins/projectexplorer/buildmanager.h @@ -0,0 +1,139 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BUILDMANAGER_H +#define BUILDMANAGER_H + +#include "projectexplorer_export.h" + +#include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtCore/QList> +#include <QtCore/QHash> +#include <QtCore/QtConcurrentRun> +#include <QtCore/QFutureWatcher> +#include <qtconcurrent/QtConcurrentTools> + +namespace ProjectExplorer { + +namespace Internal { + class CompileOutputWindow; + class TaskWindow; + class BuildProgressFuture; +} + +class BuildStep; +class Project; +class ProjectExplorerPlugin; + +class PROJECTEXPLORER_EXPORT BuildManager + : public QObject +{ + Q_OBJECT + + //NBS TODO this class has to many different variables which hold state: + // m_buildQueue, m_running, m_canceled, m_progress, m_maxProgress, m_activeBuildSteps and ... + // I might need to reduce that + +public: + BuildManager(ProjectExplorerPlugin *parent); + ~BuildManager(); + + bool isBuilding() const; + + bool tasksAvailable() const; + //shows with focus + void gotoTaskWindow(); + + void buildProject(Project *p, const QString &configuration); + void buildProjects(const QList<Project *> &projects, const QList<QString> &configurations); + void cleanProject(Project *p, const QString &configuration); + void cleanProjects(const QList<Project *> &projects, const QList<QString> &configurations); + bool isBuilding(Project *p); + + // Append any build step to the list of build steps (currently only used to add the QMakeStep) + void appendStep(BuildStep *step, const QString& configuration); + +public slots: + void cancel(); + // Shows without focus + void showTaskWindow(); + void toggleTaskWindow(); + void toggleOutputWindow(); + +signals: + void buildStateChanged(ProjectExplorer::Project *pro); + void buildQueueFinished(bool success); + void tasksChanged(); + +private slots: + void addToTaskWindow(const QString &file, int type, int line, const QString &description); + void addToOutputWindow(const QString &string); + + void nextBuildQueue(); + void emitCancelMessage(); + void showBuildResults(); + +private: + void startBuildQueue(); + void nextStep(); + void clearBuildQueue(); + void buildQueueAppend(BuildStep * bs, const QString &configuration); + void incrementActiveBuildSteps(Project *pro); + void decrementActiveBuildSteps(Project *pro); + + Internal::CompileOutputWindow *m_outputWindow; + Internal::TaskWindow *m_taskWindow; + + QList<BuildStep *> m_buildQueue; + QStringList m_configurations; // the corresponding configuration to the m_buildQueue + ProjectExplorerPlugin *m_projectExplorerPlugin; + bool m_running; + QFutureWatcher<bool> m_watcher; + BuildStep *m_currentBuildStep; + QString m_currentConfiguration; + // used to decide if we are building a project to decide when to emit buildStateChanged(Project *) + QHash<Project *, int> m_activeBuildSteps; + Project *m_previousBuildStepProject; + // is set to true while canceling, so that nextBuildStep knows that the BuildStep finished because of canceling + bool m_canceling; + + // Progress reporting to the progress manager + int m_progress; + int m_maxProgress; + QFutureInterface<void> *m_progressFutureInterface; + QFutureWatcher<void> m_progressWatcher; +}; + +} // namespace ProjectExplorer + +#endif // BUILDMANAGER_H diff --git a/src/plugins/projectexplorer/buildparserinterface.cpp b/src/plugins/projectexplorer/buildparserinterface.cpp new file mode 100644 index 00000000000..5824fbbd608 --- /dev/null +++ b/src/plugins/projectexplorer/buildparserinterface.cpp @@ -0,0 +1,41 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "buildparserinterface.h" + +using namespace ProjectExplorer; + +IBuildParserFactory::~IBuildParserFactory() +{ +} + + diff --git a/src/plugins/projectexplorer/buildparserinterface.h b/src/plugins/projectexplorer/buildparserinterface.h new file mode 100644 index 00000000000..e75ba2f2b43 --- /dev/null +++ b/src/plugins/projectexplorer/buildparserinterface.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BUILDPARSERINTERFACE_H +#define BUILDPARSERINTERFACE_H + +#include "projectexplorer_export.h" + +#include <QtCore/QObject> +#include <QtCore/QStack> + +namespace ProjectExplorer { + +class PROJECTEXPLORER_EXPORT BuildParserInterface : public QObject +{ + Q_OBJECT +public: + enum PatternType { Unknown, Warning, Error }; + + virtual ~BuildParserInterface() {} + virtual QString name() const = 0; + + virtual void stdOutput(const QString & line) = 0; + virtual void stdError(const QString & line) = 0; + +Q_SIGNALS: + void enterDirectory(const QString &dir); + void leaveDirectory(const QString &dir); + void addToOutputWindow(const QString & string); + void addToTaskWindow(const QString & filename, int type, int lineNumber, const QString & description); +}; + +class PROJECTEXPLORER_EXPORT IBuildParserFactory + : public QObject +{ + Q_OBJECT + +public: + IBuildParserFactory() {}; + virtual ~IBuildParserFactory(); + virtual bool canCreate(const QString & name) const = 0; + virtual BuildParserInterface * create(const QString & name) const = 0; +}; + +} // namespace ProjectExplorer + +#endif // BUILDPARSERINTERFACE_H diff --git a/src/plugins/projectexplorer/buildprogress.cpp b/src/plugins/projectexplorer/buildprogress.cpp new file mode 100644 index 00000000000..d8222903c0c --- /dev/null +++ b/src/plugins/projectexplorer/buildprogress.cpp @@ -0,0 +1,98 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "buildprogress.h" + +#include <coreplugin/stylehelper.h> + +#include <QtGui/QVBoxLayout> +#include <QtGui/QHBoxLayout> +#include <QtGui/QFont> +#include <QtGui/QPixmap> + +using namespace ProjectExplorer::Internal; + +BuildProgress::BuildProgress(TaskWindow *taskWindow) + : m_errorIcon(new QLabel), + m_warningIcon(new QLabel), + m_errorLabel(new QLabel), + m_warningLabel(new QLabel), + m_taskWindow(taskWindow) +{ + QVBoxLayout *layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + setLayout(layout); + QHBoxLayout *errorLayout = new QHBoxLayout; + errorLayout->setSpacing(4); + layout->addLayout(errorLayout); + errorLayout->addWidget(m_errorIcon); + errorLayout->addWidget(m_errorLabel); + QHBoxLayout *warningLayout = new QHBoxLayout; + warningLayout->setSpacing(4); + layout->addLayout(warningLayout); + warningLayout->addWidget(m_warningIcon); + warningLayout->addWidget(m_warningLabel); + + // ### TODO this setup should be done by style + QFont f = this->font(); + f.setPointSizeF(StyleHelper::sidebarFontSize()); + f.setBold(true); + m_errorLabel->setFont(f); + m_warningLabel->setFont(f); + m_errorLabel->setPalette(StyleHelper::sidebarFontPalette(m_errorLabel->palette())); + m_warningLabel->setPalette(StyleHelper::sidebarFontPalette(m_warningLabel->palette())); + + m_errorIcon->setAlignment(Qt::AlignRight); + m_warningIcon->setAlignment(Qt::AlignRight); + m_errorIcon->setPixmap(QPixmap(":/projectexplorer/images/compile_error.png")); + m_warningIcon->setPixmap(QPixmap(":/projectexplorer/images/compile_warning.png")); + + connect(m_taskWindow, SIGNAL(tasksChanged()), this, SLOT(updateState())); + updateState(); +} + +void BuildProgress::updateState() +{ + if (!m_taskWindow) + return; + int errors = m_taskWindow->numberOfErrors(); + bool haveErrors = (errors > 0); + m_errorIcon->setEnabled(haveErrors); + m_errorLabel->setEnabled(haveErrors); + m_errorLabel->setText(QString("%1").arg(errors)); + int warnings = m_taskWindow->numberOfTasks()-errors; + bool haveWarnings = (warnings > 0); + m_warningIcon->setEnabled(haveWarnings); + m_warningLabel->setEnabled(haveWarnings); + m_warningLabel->setText(QString("%1").arg(warnings)); +} diff --git a/src/plugins/projectexplorer/buildprogress.h b/src/plugins/projectexplorer/buildprogress.h new file mode 100644 index 00000000000..1b93dd50fc7 --- /dev/null +++ b/src/plugins/projectexplorer/buildprogress.h @@ -0,0 +1,65 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BUILDPROGRESS_H +#define BUILDPROGRESS_H + +#include "taskwindow.h" + +#include <QtCore/QPointer> +#include <QtGui/QWidget> +#include <QtGui/QLabel> + +namespace ProjectExplorer { +namespace Internal { + +class BuildProgress : public QWidget +{ + Q_OBJECT +public: + BuildProgress(TaskWindow *taskWindow); + +private slots: + void updateState(); + +private: + QLabel *m_errorIcon; + QLabel *m_warningIcon; + QLabel *m_errorLabel; + QLabel *m_warningLabel; + QPointer<TaskWindow> m_taskWindow; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // BUILDPROGRESS_H diff --git a/src/plugins/projectexplorer/buildsettingspropertiespage.cpp b/src/plugins/projectexplorer/buildsettingspropertiespage.cpp new file mode 100644 index 00000000000..335dc786261 --- /dev/null +++ b/src/plugins/projectexplorer/buildsettingspropertiespage.cpp @@ -0,0 +1,464 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "buildsettingspropertiespage.h" +#include "buildstep.h" +#include "buildstepspage.h" +#include "project.h" + +#include <extensionsystem/pluginmanager.h> + +#include <QtCore/QDebug> +#include <QtCore/QHash> +#include <QtCore/QPair> +#include <QtGui/QInputDialog> +#include <QtGui/QLabel> +#include <QtGui/QMenu> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +/// +/// BuildSettingsPanelFactory +/// + +bool BuildSettingsPanelFactory::supports(Project * /* project */) +{ + return true; +} + +PropertiesPanel *BuildSettingsPanelFactory::createPanel(Project *project) +{ + return new BuildSettingsPanel(project); +} + +/// +/// BuildSettingsPanel +/// + +BuildSettingsPanel::BuildSettingsPanel(Project *project) + : PropertiesPanel(), + m_widget(new BuildSettingsWidget(project)) +{ +} + +BuildSettingsPanel::~BuildSettingsPanel() +{ + delete m_widget; +} + +QString BuildSettingsPanel::name() const +{ + return tr("Build Settings"); +} + +QWidget *BuildSettingsPanel::widget() +{ + return m_widget; +} + +/// +/// BuildSettingsWidget +/// + +BuildSettingsWidget::~BuildSettingsWidget() +{ +} + +BuildSettingsWidget::BuildSettingsWidget(Project *project) + : m_project(project) +{ + m_ui.setupUi(this); + m_ui.splitter->setStretchFactor(1,10); + m_ui.buildSettingsList->setContextMenuPolicy(Qt::CustomContextMenu); + + m_ui.addButton->setIcon(QIcon(":/qworkbench/images/plus.png")); + m_ui.addButton->setText(""); + m_ui.removeButton->setIcon(QIcon(":/qworkbench/images/minus.png")); + m_ui.removeButton->setText(""); + + QMenu *addButtonMenu = new QMenu(this); + addButtonMenu->addAction(tr("Create &New"), + this, SLOT(createConfiguration())); + addButtonMenu->addAction(tr("&Clone Selected"), + this, SLOT(cloneConfiguration())); + m_ui.addButton->setMenu(addButtonMenu); + + + connect(m_ui.buildSettingsList, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + this, SLOT(updateSettingsWidget(QTreeWidgetItem *, QTreeWidgetItem *))); + connect(m_ui.buildSettingsList, SIGNAL(customContextMenuRequested (const QPoint &) ), + this, SLOT(showContextMenu(const QPoint &))); + connect(m_ui.buildSettingsList, SIGNAL(itemChanged(QTreeWidgetItem*,int) ), + this, SLOT(itemChanged(QTreeWidgetItem*)), Qt::QueuedConnection); + + connect(m_ui.removeButton, SIGNAL(clicked()), + this, SLOT(deleteConfiguration())); + connect(m_project, SIGNAL(activeBuildConfigurationChanged()), + this, SLOT(updateBuildSettings())); + connect(m_project, SIGNAL(buildConfigurationDisplayNameChanged(const QString &)), + this, SLOT(buildConfigurationDisplayNameChanged(const QString &))); + + + // remove dummy designer widget + while (QWidget *widget = m_ui.buildSettingsWidgets->currentWidget()) { + m_ui.buildSettingsWidgets->removeWidget(widget); + delete widget; + } + + updateBuildSettings(); +} + +void BuildSettingsWidget::buildConfigurationDisplayNameChanged(const QString &buildConfiguration) +{ + QTreeWidgetItem *rootItem = m_ui.buildSettingsList->invisibleRootItem(); + for(int i = 0; i < rootItem->childCount(); ++i) { + QTreeWidgetItem *child = rootItem->child(i); + if (child->data(0, Qt::UserRole).toString() == buildConfiguration) { + child->setText(0, m_project->displayNameFor(buildConfiguration)); + if (m_ui.buildSettingsList->currentItem() == child) { + QWidget *widget = m_itemToWidget.value(child); + if (BuildStepConfigWidget *buildStepWidget = qobject_cast<BuildStepConfigWidget*>(widget)) { + QString title; + title = buildStepWidget->displayName(); + m_ui.titleLabel->setText(tr("%1 - %2").arg(m_project->displayNameFor(buildConfiguration)).arg(title)); + } + } + } + } +} + + +void BuildSettingsWidget::updateBuildSettings() +{ + QTreeWidgetItem *rootItem = m_ui.buildSettingsList->invisibleRootItem(); + + // update buttons + m_ui.removeButton->setEnabled(m_project->buildConfigurations().size() > 1); + + // Save current selection + QString lastCurrentItem; + if (m_ui.buildSettingsList->currentItem()) + lastCurrentItem = m_ui.buildSettingsList->currentItem()->text(0); + + m_itemToWidget.clear(); + + // Delete old tree items + while (rootItem->childCount()) { + QTreeWidgetItem *configPageItem = rootItem->child(0); + rootItem->removeChild(configPageItem); + delete configPageItem; // does that delete also subitems? + } + + // Delete old pages + while (m_ui.buildSettingsWidgets->count()) { + QWidget *w = m_ui.buildSettingsWidgets->widget(0); + m_ui.buildSettingsWidgets->removeWidget(w); + delete w; + } + + // Add pages + QWidget *dummyWidget = new QWidget(this); + QWidget *buildStepsWidget = new BuildStepsPage(m_project); + BuildStepConfigWidget *generalConfigWidget = m_project->createConfigWidget(); + QList<BuildStepConfigWidget *> subConfigWidgets = m_project->subConfigWidgets(); + + m_ui.buildSettingsWidgets->addWidget(dummyWidget); + m_ui.buildSettingsWidgets->addWidget(buildStepsWidget); + m_ui.buildSettingsWidgets->addWidget(generalConfigWidget); + foreach (BuildStepConfigWidget *subConfigWidget, subConfigWidgets) + m_ui.buildSettingsWidgets->addWidget(subConfigWidget); + + // Add tree items + QTreeWidgetItem *activeConfigurationItem = 0; + QString activeBuildConfiguration = m_project->activeBuildConfiguration(); + foreach (const QString &buildConfiguration, m_project->buildConfigurations()) { + QString displayName = m_project->displayNameFor(buildConfiguration); + QTreeWidgetItem *buildConfigItem = new QTreeWidgetItem(); + m_itemToWidget.insert(buildConfigItem, generalConfigWidget); + buildConfigItem->setText(0, displayName); + buildConfigItem->setData(0, Qt::UserRole, buildConfiguration); + buildConfigItem->setCheckState(0, Qt::Unchecked); + if (activeBuildConfiguration == buildConfiguration) { + QFont font = buildConfigItem->font(0); + font.setBold(true); + buildConfigItem->setFont(0, font); + buildConfigItem->setCheckState(0, Qt::Checked); + + activeConfigurationItem = buildConfigItem; + } + rootItem->addChild(buildConfigItem); + + QTreeWidgetItem *generalItem = new QTreeWidgetItem(); + m_itemToWidget.insert(generalItem, generalConfigWidget); + generalItem->setText(0, tr("General")); + buildConfigItem->addChild(generalItem); + + foreach (BuildStepConfigWidget *subConfigWidget, subConfigWidgets) { + QTreeWidgetItem *subConfigItem = new QTreeWidgetItem(); + m_itemToWidget.insert(subConfigItem, subConfigWidget); + subConfigItem->setText(0, subConfigWidget->displayName()); + buildConfigItem->addChild(subConfigItem); + } + + QTreeWidgetItem *buildStepsItem = new QTreeWidgetItem(); + m_itemToWidget.insert(buildStepsItem, buildStepsWidget); + buildStepsItem->setText(0, tr("Build Steps")); + buildConfigItem->addChild(buildStepsItem); + } + + m_ui.buildSettingsList->expandAll(); + + // Restore selection + if (!lastCurrentItem.isEmpty()) { + for (int i = rootItem->childCount() - 1; i >= 0; --i) { + if (rootItem->child(i)->text(0) == lastCurrentItem) { + m_ui.buildSettingsList->setCurrentItem(rootItem->child(i)); + break; + } + } + } + + if (!m_ui.buildSettingsList->currentItem()) { + if (activeConfigurationItem) + m_ui.buildSettingsList->setCurrentItem(activeConfigurationItem); + else + m_ui.buildSettingsList->setCurrentItem(m_ui.buildSettingsList->invisibleRootItem()->child(0)); + } +} + +/* switch from one tree item / build step to another */ +void BuildSettingsWidget::updateSettingsWidget(QTreeWidgetItem *newItem, QTreeWidgetItem *oldItem) +{ + if (oldItem == newItem) + return; + + if (!newItem) { + QWidget *dummyWidget = m_ui.buildSettingsWidgets->widget(0); + m_ui.buildSettingsWidgets->setCurrentWidget(dummyWidget); + m_ui.titleLabel->clear(); + return; + } + + if (QWidget *widget = m_itemToWidget.value(newItem)) { + QString buildConfiguration; + { + QTreeWidgetItem *configurationItem = newItem; + while (configurationItem && configurationItem->parent()) + configurationItem = configurationItem->parent(); + if (configurationItem) + buildConfiguration = configurationItem->data(0, Qt::UserRole).toString(); + } + + QString title; + if (BuildStepConfigWidget *buildStepWidget = qobject_cast<BuildStepConfigWidget*>(widget)) { + title = buildStepWidget->displayName(); + buildStepWidget->init(buildConfiguration); + } + + m_ui.titleLabel->setText(tr("%1 - %2").arg(m_project->displayNameFor(buildConfiguration)).arg(title)); + m_ui.buildSettingsWidgets->setCurrentWidget(widget); + } +} + + +void BuildSettingsWidget::showContextMenu(const QPoint &point) +{ + if (QTreeWidgetItem *item = m_ui.buildSettingsList->itemAt(point)) { + if (!item->parent()) { + const QString buildConfiguration = item->data(0, Qt::UserRole).toString(); + + QMenu menu; + QAction *setAsActiveAction = new QAction(tr("Set as Active"), &menu); + QAction *cloneAction = new QAction(tr("Clone"), &menu); + QAction *deleteAction = new QAction(tr("Delete"), &menu); + + if (m_project->activeBuildConfiguration() == buildConfiguration) + setAsActiveAction->setEnabled(false); + if (m_project->buildConfigurations().size() < 2) + deleteAction->setEnabled(false); + + menu.addActions(QList<QAction*>() << setAsActiveAction << cloneAction << deleteAction); + QPoint globalPoint = m_ui.buildSettingsList->mapToGlobal(point); + QAction *action = menu.exec(globalPoint); + if (action == setAsActiveAction) { + setActiveConfiguration(buildConfiguration); + } else if (action == cloneAction) { + cloneConfiguration(buildConfiguration); + } else if (action == deleteAction) { + deleteConfiguration(buildConfiguration); + } + + updateBuildSettings(); + } + } +} + +void BuildSettingsWidget::setActiveConfiguration() +{ + const QString configuration = m_ui.buildSettingsList->currentItem()->data(0, Qt::UserRole).toString(); + setActiveConfiguration(configuration); +} + +void BuildSettingsWidget::createConfiguration() +{ + bool ok; + QString newBuildConfiguration = QInputDialog::getText(this, tr("New configuration"), tr("New Configuration Name:"), QLineEdit::Normal, QString(), &ok); + if(!ok || newBuildConfiguration.isEmpty()) + return; + + QString newDisplayName = newBuildConfiguration; + // Check that the internal name is not taken and use a different one otherwise + const QStringList &buildConfigurations = m_project->buildConfigurations(); + if (buildConfigurations.contains(newBuildConfiguration)) { + int i = 2; + while(buildConfigurations.contains(newBuildConfiguration + QString::number(i))) { + ++i; + } + newBuildConfiguration += QString::number(i); + } + + // Check that we don't have a configuration with the same displayName + QStringList displayNames; + foreach(const QString &bc, buildConfigurations) + displayNames << m_project->displayNameFor(bc); + + if (displayNames.contains(newDisplayName)) { + int i = 2; + while(displayNames.contains(newDisplayName + QString::number(i))) { + ++i; + } + newDisplayName += QString::number(i); + } + + m_project->addBuildConfiguration(newBuildConfiguration); + m_project->setDisplayNameFor(newBuildConfiguration, newDisplayName); + m_project->newBuildConfiguration(newBuildConfiguration); + m_project->setActiveBuildConfiguration(newBuildConfiguration); + + updateBuildSettings(); +} + +void BuildSettingsWidget::cloneConfiguration() +{ + QTreeWidgetItem *configItem = m_ui.buildSettingsList->currentItem(); + while (configItem->parent()) + configItem = configItem->parent(); + const QString configuration = configItem->data(0, Qt::UserRole).toString(); + cloneConfiguration(configuration); +} + +void BuildSettingsWidget::deleteConfiguration() +{ + QTreeWidgetItem *configItem = m_ui.buildSettingsList->currentItem(); + while (configItem->parent()) + configItem = configItem->parent(); + const QString configuration = configItem->data(0, Qt::UserRole).toString(); + deleteConfiguration(configuration); +} + +void BuildSettingsWidget::itemChanged(QTreeWidgetItem *item) +{ + // do not allow unchecking + if (item->checkState(0) == Qt::Unchecked) + item->setCheckState(0, Qt::Checked); + else { + setActiveConfiguration(item->data(0, Qt::UserRole).toString()); + } +} + +void BuildSettingsWidget::setActiveConfiguration(const QString &configuration) +{ + if (configuration.isEmpty()) + return; + + m_project->setActiveBuildConfiguration(configuration); +} + +void BuildSettingsWidget::cloneConfiguration(const QString &sourceConfiguration) +{ + if(sourceConfiguration.isEmpty()) + return; + + QString newBuildConfiguration = QInputDialog::getText(this, tr("Clone configuration"), tr("New Configuration Name:")); + if(newBuildConfiguration.isEmpty()) + return; + + QString newDisplayName = newBuildConfiguration; + // Check that the internal name is not taken and use a different one otherwise + const QStringList &buildConfigurations = m_project->buildConfigurations(); + if (buildConfigurations.contains(newBuildConfiguration)) { + int i = 2; + while(buildConfigurations.contains(newBuildConfiguration + QString::number(i))) { + ++i; + } + newBuildConfiguration += QString::number(i); + } + + // Check that we don't have a configuration with the same displayName + QStringList displayNames; + foreach(const QString &bc, buildConfigurations) + displayNames << m_project->displayNameFor(bc); + + if (displayNames.contains(newDisplayName)) { + int i = 2; + while(displayNames.contains(newDisplayName + QString::number(i))) { + ++i; + } + newDisplayName += QString::number(i); + } + + m_project->copyBuildConfiguration(sourceConfiguration, newBuildConfiguration); + m_project->setDisplayNameFor(newBuildConfiguration, newDisplayName); + m_project->setActiveBuildConfiguration(newBuildConfiguration); + + updateBuildSettings(); +} + +void BuildSettingsWidget::deleteConfiguration(const QString &deleteConfiguration) +{ + if (deleteConfiguration.isEmpty() || m_project->buildConfigurations().size() <= 1) + return; + + if (m_project->activeBuildConfiguration() == deleteConfiguration) { + foreach (const QString &otherConfiguration, m_project->buildConfigurations()) { + if (otherConfiguration != deleteConfiguration) { + m_project->setActiveBuildConfiguration(otherConfiguration); + break; + } + } + } + + m_project->removeBuildConfiguration(deleteConfiguration); + + updateBuildSettings(); +} diff --git a/src/plugins/projectexplorer/buildsettingspropertiespage.h b/src/plugins/projectexplorer/buildsettingspropertiespage.h new file mode 100644 index 00000000000..012ff67c212 --- /dev/null +++ b/src/plugins/projectexplorer/buildsettingspropertiespage.h @@ -0,0 +1,102 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BUILDSETTINGSPROPERTIESPAGE_H +#define BUILDSETTINGSPROPERTIESPAGE_H + +#include "iprojectproperties.h" +#include "ui_buildsettingspropertiespage.h" + +namespace ProjectExplorer { + +class IBuildStepFactory; + +namespace Internal { + +class BuildSettingsPanelFactory : public IPanelFactory +{ +public: + bool supports(Project *project); + PropertiesPanel *createPanel(Project *project); +}; + +class BuildSettingsWidget; + +class BuildSettingsPanel : public PropertiesPanel +{ + Q_OBJECT +public: + BuildSettingsPanel(Project *project); + ~BuildSettingsPanel(); + QString name() const; + QWidget *widget(); + +private: + BuildSettingsWidget *m_widget; +}; + +class BuildConfigurationsWidget; + +class BuildSettingsWidget : public QWidget +{ + Q_OBJECT +public: + BuildSettingsWidget(Project *project); + ~BuildSettingsWidget(); + +private slots: + void buildConfigurationDisplayNameChanged(const QString &buildConfiguration); + void updateBuildSettings(); + void updateSettingsWidget(QTreeWidgetItem *newItem, QTreeWidgetItem *oldItem); + void showContextMenu(const QPoint & pos); + + void setActiveConfiguration(); + void createConfiguration(); + void cloneConfiguration(); + void deleteConfiguration(); + + void itemChanged(QTreeWidgetItem *item); + +private: + void setActiveConfiguration(const QString &configuration); + void cloneConfiguration(const QString &toClone); + void deleteConfiguration(const QString &toDelete); + + Ui::BuildSettingsPropertiesPage m_ui; + Project *m_project; + QHash<QTreeWidgetItem*, QWidget*> m_itemToWidget; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // BUILDSETTINGSPROPERTIESPAGE_H diff --git a/src/plugins/projectexplorer/buildsettingspropertiespage.ui b/src/plugins/projectexplorer/buildsettingspropertiespage.ui new file mode 100644 index 00000000000..819f17da58e --- /dev/null +++ b/src/plugins/projectexplorer/buildsettingspropertiespage.ui @@ -0,0 +1,190 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ProjectExplorer::Internal::BuildSettingsPropertiesPage</class> + <widget class="QWidget" name="ProjectExplorer::Internal::BuildSettingsPropertiesPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>799</width> + <height>525</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QSplitter" name="splitter"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <widget class="QWidget" name="layoutWidget1"> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="100,1,0"> + <item> + <widget class="QTreeWidget" name="buildSettingsList"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>800</height> + </size> + </property> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <property name="uniformRowHeights"> + <bool>true</bool> + </property> + <property name="itemsExpandable"> + <bool>false</bool> + </property> + <property name="headerHidden"> + <bool>true</bool> + </property> + <property name="expandsOnDoubleClick"> + <bool>false</bool> + </property> + <attribute name="headerVisible"> + <bool>false</bool> + </attribute> + <column> + <property name="text"> + <string>Configurations</string> + </property> + </column> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <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> + <item> + <widget class="QPushButton" name="addButton"> + <property name="text"> + <string>+</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="removeButton"> + <property name="text"> + <string>-</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="layoutWidget2"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0"> + <item> + <widget class="QLabel" name="titleLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>16</pointsize> + <weight>50</weight> + <bold>false</bold> + </font> + </property> + <property name="text"> + <string>TextLabel</string> + </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> + </item> + <item> + <widget class="QStackedWidget" name="buildSettingsWidgets"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>10</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="page_2"/> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <resources> + <include location="../../libs/cplusplus/cplusplus.qrc"/> + <include location="../../libs/extensionsystem/pluginview.qrc"/> + <include location="../bookmarks/bookmarks.qrc"/> + <include location="../coreplugin/core.qrc"/> + <include location="../coreplugin/fancyactionbar.qrc"/> + <include location="../cppeditor/cppeditor.qrc"/> + <include location="../cpptools/cpptools.qrc"/> + <include location="../designer/designer.qrc"/> + <include location="../find/find.qrc"/> + <include location="../gdbdebugger/gdbdebugger.qrc"/> + <include location="../help/help.qrc"/> + <include location="../perforce/perforce.qrc"/> + <include location="projectexplorer.qrc"/> + <include location="../../../shared/proparser/proparser.qrc"/> + <include location="../qt4projectmanager/qt4projectmanager.qrc"/> + <include location="../qt4projectmanager/wizards/wizards.qrc"/> + <include location="../quickopen/quickopen.qrc"/> + <include location="../resourceeditor/resourceeditor.qrc"/> + <include location="../texteditor/texteditor.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp new file mode 100644 index 00000000000..76ab3f65f49 --- /dev/null +++ b/src/plugins/projectexplorer/buildstep.cpp @@ -0,0 +1,146 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "buildstep.h" +#include "buildconfiguration.h" + +namespace ProjectExplorer { + +BuildStep::BuildStep(Project * pro) + : m_project(pro) +{ + m_configuration = new BuildConfiguration(""); +} + +BuildStep::~BuildStep() +{ + qDeleteAll(m_buildConfigurations); + delete m_configuration; +} + +Project * BuildStep::project() const +{ + return m_project; +} + +void BuildStep::addBuildConfiguration(const QString &name) +{ + m_buildConfigurations.push_back(new BuildConfiguration(name)); +} + +void BuildStep::removeBuildConfiguration(const QString &name) +{ + for(int i = 0; i != m_buildConfigurations.size(); ++i) + if(m_buildConfigurations.at(i)->name() == name) { + delete m_buildConfigurations.at(i); + m_buildConfigurations.removeAt(i); + break; + } +} + +void BuildStep::copyBuildConfiguration(const QString &source, const QString &dest) +{ + for(int i = 0; i != m_buildConfigurations.size(); ++i) + if(m_buildConfigurations.at(i)->name() == source) + m_buildConfigurations.push_back(new BuildConfiguration(dest, m_buildConfigurations.at(i))); +} + +void BuildStep::setValue(const QString &buildConfiguration, const QString &name, const QVariant &value) +{ + BuildConfiguration *bc = getBuildConfiguration(buildConfiguration); + Q_ASSERT(bc); + bc->setValue(name, value); +} + +void BuildStep::setValue(const QString &name, const QVariant &value) +{ + m_configuration->setValue(name, value); +} + +QVariant BuildStep::value(const QString &buildConfiguration, const QString &name) const +{ + BuildConfiguration *bc = getBuildConfiguration(buildConfiguration); + if (bc) + return bc->getValue(name); + else + return QVariant(); +} + +QVariant BuildStep::value(const QString &name) const +{ + return m_configuration->getValue(name); +} + +void BuildStep::setValuesFromMap(const QMap<QString, QVariant> & values) +{ + m_configuration->setValuesFromMap(values); +} + +void BuildStep::setValuesFromMap(const QString & buildConfiguration, const QMap<QString, QVariant> & values) +{ + getBuildConfiguration(buildConfiguration)->setValuesFromMap(values); +} + +QMap<QString, QVariant> BuildStep::valuesToMap() +{ + return m_configuration->toMap(); +} + +QMap<QString, QVariant> BuildStep::valuesToMap(const QString & buildConfiguration) +{ + return getBuildConfiguration(buildConfiguration)->toMap(); +} + +BuildConfiguration * BuildStep::getBuildConfiguration(const QString & name) const +{ + for (int i = 0; i != m_buildConfigurations.size(); ++i) + if (m_buildConfigurations.at(i)->name() == name) + return m_buildConfigurations.at(i); + return 0; +} + +bool BuildStep::immutable() const +{ + return false; +} + +IBuildStepFactory::IBuildStepFactory() +{ + +} + +IBuildStepFactory::~IBuildStepFactory() +{ + +} + +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/buildstep.h b/src/plugins/projectexplorer/buildstep.h new file mode 100644 index 00000000000..32f37fff8cf --- /dev/null +++ b/src/plugins/projectexplorer/buildstep.h @@ -0,0 +1,181 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BUILDSTEP_H +#define BUILDSTEP_H + +#include "buildparserinterface.h" +#include "project.h" +#include "projectexplorer_export.h" + +#include <QtGui/QWidget> +#include <QtCore/QFutureInterface> + +namespace ProjectExplorer { + +class BuildConfiguration; + +/* +// BuildSteps are the primary way plugin developers can customize +// how their projects (or projects from other plugins) are build. +// +// Building a project, is done by taking the list of buildsteps +// from the project and calling first init() than run() on them. +// +// That means to change the way your project is build, reimplemnt +// this class and add your Step to the buildStep list of the project. +// +// Note: The projects own the buildstep, do not delete them yourself. +// +// init() is called in the GUI thread and can be used to query the +// project for any information you need. +// +// run() is run via QtConccurrent in a own thread, if you need an +// eventloop you need to create it yourself! +// +// You can use setValue() to store settings which a specific +// to your buildstep. (You can set settings to apply only +// to one buildconfiguration. +// And later retrieve the same information with value() + +*/ + +class BuildStepConfigWidget; + +class PROJECTEXPLORER_EXPORT BuildStep : public QObject +{ + Q_OBJECT + friend class Project; //for managing BuildConfigurations +public: + BuildStep(Project *p); + virtual ~BuildStep(); + + // This function is run in the gui thread, + // use it to retrieve any information that you need in run() + virtual bool init(const QString &buildConfiguration) = 0; + + // Reimplement this. This function is called when the project is build. + // This function is NOT run in the gui thread. It runs in its own thread + // If you need an event loop, you need to create one. + // The absolute minimal implementation is: + // fi.reportResult(true); + virtual void run(QFutureInterface<bool> &fi) = 0; + + // The internal name + virtual QString name() = 0; + // The name shown to the user + virtual QString displayName() = 0; + + // sets a value, which can be retrieved with value() + // these values are automatically saved and restored when qt creator is quit and restarted + void setValue(const QString &name, const QVariant &value); + // sets a value specific to a buildConfiguration + void setValue(const QString &buildConfiguration, const QString &name, const QVariant &value); + + // retrieves a value + QVariant value(const QString &name) const; + // retrieves a value specific to a buildConfiguration + QVariant value(const QString &buildConfiguration, const QString & name) const; + + // the Widget shown in the project settings dialog for this buildStep + // ownership is transfered to the caller + virtual BuildStepConfigWidget *createConfigWidget() = 0; + + // if this function returns true, the user can't delete this BuildStep for this project + // and the user is prevented from changing the order immutable steps are run + // the default implementation returns false + virtual bool immutable() const; + + Project *project() const; + +protected: + // internal function for restoring the configuration + void setValuesFromMap(const QMap<QString, QVariant> &values); + // internal function for restoring the configuration + void setValuesFromMap(const QString &buildConfiguration, const QMap<QString, QVariant> &values); + + // internal function for storing the configuration + QMap<QString, QVariant> valuesToMap(); + // internal function for storing the configuration + QMap<QString, QVariant> valuesToMap(const QString & buildConfiguration); + +Q_SIGNALS: + void addToTaskWindow(const QString &filename, int type, int linenumber, const QString &description); + void addToOutputWindow(const QString &string); + + void displayNameChanged(BuildStep *, const QString &displayName); + +private: + QList<BuildConfiguration *> buildConfigurations(); + void addBuildConfiguration(const QString & name); + void removeBuildConfiguration(const QString & name); + BuildConfiguration *getBuildConfiguration(const QString & name) const; + void copyBuildConfiguration(const QString &source, const QString &dest); + + QList<BuildConfiguration *> m_buildConfigurations; + BuildConfiguration *m_configuration; + Project *m_project; +}; + +class PROJECTEXPLORER_EXPORT IBuildStepFactory + : public QObject +{ + Q_OBJECT + +public: + IBuildStepFactory(); + virtual ~IBuildStepFactory(); + virtual bool canCreate(const QString &name) const = 0; + virtual BuildStep *create(Project *pro, const QString &name) const = 0; + virtual QStringList canCreateForProject(Project *pro) const = 0; + virtual QString displayNameForName(const QString &name) const = 0; +}; + +class PROJECTEXPLORER_EXPORT BuildStepConfigWidget + : public QWidget +{ + Q_OBJECT +public: + BuildStepConfigWidget() + :QWidget(0) + {} + + virtual QString displayName() const = 0; + + // This is called to set up the config widget before showing it + // buildConfiguration is QString::null for the non buildConfiguration specific page + virtual void init(const QString &buildConfiguration) = 0; +}; + +} // namespace ProjectExplorer + +#endif // BUILDSTEP_H diff --git a/src/plugins/projectexplorer/buildstepspage.cpp b/src/plugins/projectexplorer/buildstepspage.cpp new file mode 100644 index 00000000000..3777281dcc7 --- /dev/null +++ b/src/plugins/projectexplorer/buildstepspage.cpp @@ -0,0 +1,266 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "buildstepspage.h" +#include "ui_buildstepspage.h" +#include "project.h" + +#include <extensionsystem/pluginmanager.h> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +BuildStepsPage::BuildStepsPage(Project *project) : + BuildStepConfigWidget(), + m_ui(new Ui::BuildStepsPage), + m_pro(project) +{ + m_ui->setupUi(this); + + m_ui->buildStepAddButton->setMenu(new QMenu(this)); + m_ui->buildStepAddButton->setIcon(QIcon(":/qworkbench/images/plus.png")); + m_ui->buildStepRemoveToolButton->setIcon(QIcon(":/qworkbench/images/minus.png")); + m_ui->buildStepUpToolButton->setArrowType(Qt::UpArrow); + m_ui->buildStepDownToolButton->setArrowType(Qt::DownArrow); + + connect(m_ui->buildSettingsList, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + this, SLOT(updateBuildStepWidget(QTreeWidgetItem *, QTreeWidgetItem *))); + + connect(m_ui->buildStepAddButton->menu(), SIGNAL(aboutToShow()), + this, SLOT(updateAddBuildStepMenu())); + + connect(m_ui->buildStepAddButton, SIGNAL(clicked()), + this, SLOT(addBuildStep())); + connect(m_ui->buildStepRemoveToolButton, SIGNAL(clicked()), + this, SLOT(removeBuildStep())); + connect(m_ui->buildStepUpToolButton, SIGNAL(clicked()), + this, SLOT(upBuildStep())); + connect(m_ui->buildStepDownToolButton, SIGNAL(clicked()), + this, SLOT(downBuildStep())); + + // Remove dummy pages + while (QWidget *widget = m_ui->buildSettingsWidget->currentWidget()) { + m_ui->buildSettingsWidget->removeWidget(widget); + delete widget; + } + + // Add buildsteps + foreach (BuildStep *bs, m_pro->buildSteps()) { + + connect(bs, SIGNAL(displayNameChanged(BuildStep *, QString)), + this, SLOT(displayNameChanged(BuildStep *,QString))); + + QTreeWidgetItem *buildStepItem = new QTreeWidgetItem(); + buildStepItem->setText(0, bs->displayName()); + m_ui->buildSettingsWidget->addWidget(bs->createConfigWidget()); + m_ui->buildSettingsList->invisibleRootItem()->addChild(buildStepItem); + } +} + +BuildStepsPage::~BuildStepsPage() +{ + // Also deletes all added widgets + delete m_ui; +} + +void BuildStepsPage::displayNameChanged(BuildStep *bs, const QString &displayName) +{ + int index = m_pro->buildSteps().indexOf(bs); + m_ui->buildSettingsList->invisibleRootItem()->child(index)->setText(0, bs->displayName()); +} + +QString BuildStepsPage::displayName() const +{ + return tr("Build Steps"); +} + +void BuildStepsPage::init(const QString &buildConfiguration) +{ + m_configuration = buildConfiguration; + + m_ui->buildSettingsList->setCurrentItem(m_ui->buildSettingsList->invisibleRootItem()->child(0)); + // make sure widget is updated + BuildStepConfigWidget *widget = qobject_cast<BuildStepConfigWidget *>(m_ui->buildSettingsWidget->currentWidget()); + widget->init(m_configuration); +} + +/* switch from one tree item / build step to another */ +void BuildStepsPage::updateBuildStepWidget(QTreeWidgetItem *newItem, QTreeWidgetItem *oldItem) +{ + if(oldItem == newItem) + return; + Q_ASSERT(m_pro); + + if(newItem) { + int row = m_ui->buildSettingsList->indexOfTopLevelItem(newItem); + m_ui->buildSettingsWidget->setCurrentIndex(row); + BuildStepConfigWidget *widget = qobject_cast<BuildStepConfigWidget *>(m_ui->buildSettingsWidget->currentWidget()); + Q_ASSERT(widget); + if (widget) + widget->init(m_configuration); + } + updateBuildStepButtonsState(); +} + + +void BuildStepsPage::updateAddBuildStepMenu() +{ + QMap<QString, QPair<QString, IBuildStepFactory *> > map; + //Build up a list of possible steps and save map the display names to the (internal) name and factories. + QList<IBuildStepFactory *> factories = ExtensionSystem::PluginManager::instance()->getObjects<IBuildStepFactory>(); + foreach (IBuildStepFactory * factory, factories) { + QStringList names = factory->canCreateForProject(m_pro); + foreach (const QString &name, names) { + map.insert(factory->displayNameForName(name), QPair<QString, IBuildStepFactory *>(name, factory)); + } + } + + // Ask the user which one to add + QMenu *menu = m_ui->buildStepAddButton->menu(); + m_addBuildStepHash.clear(); + menu->clear(); + if(!map.isEmpty()) { + QStringList names; + QMap<QString, QPair<QString, IBuildStepFactory *> >::const_iterator it, end; + end = map.constEnd(); + for(it = map.constBegin(); it != end; ++it) { + QAction *action = menu->addAction(it.key()); + connect(action, SIGNAL(triggered()), + this, SLOT(addBuildStep())); + m_addBuildStepHash.insert(action, it.value()); + } + } +} + + +void BuildStepsPage::addBuildStep() +{ + if(QAction *action = qobject_cast<QAction *>(sender())) { + QPair<QString, IBuildStepFactory *> pair = m_addBuildStepHash.value(action); + BuildStep *newStep = pair.second->create(m_pro, pair.first); + m_pro->insertBuildStep(0, newStep); + QTreeWidgetItem *buildStepItem = new QTreeWidgetItem(); + buildStepItem->setText(0, newStep->displayName()); + m_ui->buildSettingsList->invisibleRootItem()->insertChild(0, buildStepItem); + m_ui->buildSettingsWidget->insertWidget(0, newStep->createConfigWidget()); + m_ui->buildSettingsList->setCurrentItem(buildStepItem); + } +} + +void BuildStepsPage::removeBuildStep() +{ + int pos = m_ui->buildSettingsList->currentIndex().row(); + if(m_pro->buildSteps().at(pos)->immutable()) + return; + bool blockSignals = m_ui->buildSettingsList->blockSignals(true); + delete m_ui->buildSettingsList->invisibleRootItem()->takeChild(pos); + m_ui->buildSettingsList->blockSignals(blockSignals); + QWidget *widget = m_ui->buildSettingsWidget->widget(pos); + m_ui->buildSettingsWidget->removeWidget(widget); + delete widget; + if(pos < m_ui->buildSettingsList->invisibleRootItem()->childCount()) + m_ui->buildSettingsList->setCurrentItem(m_ui->buildSettingsList->invisibleRootItem()->child(pos)); + else + m_ui->buildSettingsList->setCurrentItem(m_ui->buildSettingsList->invisibleRootItem()->child(pos - 1)); + m_pro->removeBuildStep(pos); + updateBuildStepButtonsState(); +} + +void BuildStepsPage::upBuildStep() +{ + int pos = m_ui->buildSettingsList->currentIndex().row(); + if(pos < 1) + return; + if(pos > m_ui->buildSettingsList->invisibleRootItem()->childCount()-1) + return; + if(m_pro->buildSteps().at(pos)->immutable() && m_pro->buildSteps().at(pos-1)->immutable()) + return; + + bool blockSignals = m_ui->buildSettingsList->blockSignals(true); + m_pro->moveBuildStepUp(pos); + buildStepMoveUp(pos); + QTreeWidgetItem *item = m_ui->buildSettingsList->invisibleRootItem()->child(pos - 1); + m_ui->buildSettingsList->blockSignals(blockSignals); + m_ui->buildSettingsList->setCurrentItem(item); + updateBuildStepButtonsState(); +} + +void BuildStepsPage::downBuildStep() +{ + int pos = m_ui->buildSettingsList->currentIndex().row() + 1; + if(pos < 1) + return; + if(pos > m_ui->buildSettingsList->invisibleRootItem()->childCount() - 1) + return; + if(m_pro->buildSteps().at(pos)->immutable() && m_pro->buildSteps().at(pos - 1)->immutable()) + return; + + bool blockSignals = m_ui->buildSettingsList->blockSignals(true); + m_pro->moveBuildStepUp(pos); + buildStepMoveUp(pos); + QTreeWidgetItem *item = m_ui->buildSettingsList->invisibleRootItem()->child(pos); + m_ui->buildSettingsList->blockSignals(blockSignals); + m_ui->buildSettingsList->setCurrentItem(item); + updateBuildStepButtonsState(); +} + +void BuildStepsPage::changeEvent(QEvent *e) +{ + switch(e->type()) { + case QEvent::LanguageChange: + m_ui->retranslateUi(this); + break; + default: + break; + } +} + +void BuildStepsPage::buildStepMoveUp(int pos) +{ + QWidget *widget = m_ui->buildSettingsWidget->widget(pos); + m_ui->buildSettingsWidget->removeWidget(widget); + m_ui->buildSettingsWidget->insertWidget(pos -1, widget); + QTreeWidgetItem *item = m_ui->buildSettingsList->invisibleRootItem()->takeChild(pos); + m_ui->buildSettingsList->invisibleRootItem()->insertChild(pos - 1, item); +} + +void BuildStepsPage::updateBuildStepButtonsState() +{ + int pos = m_ui->buildSettingsList->currentIndex().row(); + + m_ui->buildStepRemoveToolButton->setEnabled(!m_pro->buildSteps().at(pos)->immutable()); + bool enableUp = pos>0 && !(m_pro->buildSteps().at(pos)->immutable() && m_pro->buildSteps().at(pos-1)->immutable()); + m_ui->buildStepUpToolButton->setEnabled(enableUp); + bool enableDown = pos < (m_ui->buildSettingsList->invisibleRootItem()->childCount() - 1) && + !(m_pro->buildSteps().at(pos)->immutable() && m_pro->buildSteps().at(pos+1)->immutable()); + m_ui->buildStepDownToolButton->setEnabled(enableDown); +} diff --git a/src/plugins/projectexplorer/buildstepspage.h b/src/plugins/projectexplorer/buildstepspage.h new file mode 100644 index 00000000000..4af2fde6245 --- /dev/null +++ b/src/plugins/projectexplorer/buildstepspage.h @@ -0,0 +1,87 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BUILDSTEPSPAGE_H +#define BUILDSTEPSPAGE_H + +#include "buildstep.h" + +QT_BEGIN_NAMESPACE +class QTreeWidgetItem; +QT_END_NAMESPACE + +namespace ProjectExplorer { + +class Project; + +namespace Internal { + +namespace Ui { + class BuildStepsPage; +} + +class BuildStepsPage : public BuildStepConfigWidget { + Q_OBJECT + Q_DISABLE_COPY(BuildStepsPage) +public: + explicit BuildStepsPage(Project *project); + virtual ~BuildStepsPage(); + + QString displayName() const; + void init(const QString &buildConfiguration); + +protected: + virtual void changeEvent(QEvent *e); + +private slots: + void displayNameChanged(BuildStep *bs, const QString &displayName); + void updateBuildStepWidget(QTreeWidgetItem *newItem, QTreeWidgetItem *oldItem); + void updateAddBuildStepMenu(); + void addBuildStep(); + void removeBuildStep(); + void upBuildStep(); + void downBuildStep(); + +private: + void buildStepMoveUp(int pos); + void updateBuildStepButtonsState(); + + Ui::BuildStepsPage *m_ui; + Project *m_pro; + QString m_configuration; + QHash<QAction *, QPair<QString, ProjectExplorer::IBuildStepFactory *> > m_addBuildStepHash; +}; + +} // Internal +} // ProjectExplorer + +#endif // BUILDSTEPSPAGE_H diff --git a/src/plugins/projectexplorer/buildstepspage.ui b/src/plugins/projectexplorer/buildstepspage.ui new file mode 100644 index 00000000000..85267824b0a --- /dev/null +++ b/src/plugins/projectexplorer/buildstepspage.ui @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ProjectExplorer::Internal::BuildStepsPage</class> + <widget class="QWidget" name="ProjectExplorer::Internal::BuildStepsPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QTreeWidget" name="buildSettingsList"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>150</width> + <height>16777215</height> + </size> + </property> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <property name="uniformRowHeights"> + <bool>true</bool> + </property> + <property name="headerHidden"> + <bool>true</bool> + </property> + <column> + <property name="text"> + <string>1</string> + </property> + </column> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </spacer> + </item> + <item> + <widget class="QToolButton" name="buildStepAddButton"> + <property name="text"> + <string>+</string> + </property> + <property name="popupMode"> + <enum>QToolButton::InstantPopup</enum> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="buildStepRemoveToolButton"> + <property name="text"> + <string>-</string> + </property> + <property name="popupMode"> + <enum>QToolButton::DelayedPopup</enum> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="buildStepUpToolButton"> + <property name="text"> + <string>^</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="buildStepDownToolButton"> + <property name="text"> + <string>v</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item row="0" column="1" rowspan="2"> + <widget class="QStackedWidget" name="buildSettingsWidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>10</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="page_2"/> + </widget> + </item> + <item row="1" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>47</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp new file mode 100644 index 00000000000..2f138ad8574 --- /dev/null +++ b/src/plugins/projectexplorer/compileoutputwindow.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "compileoutputwindow.h" +#include "buildmanager.h" + +#include <find/basetextfind.h> +#include <aggregation/aggregate.h> + +#include <QtGui/QKeyEvent> +#include <QtGui/QIcon> +#include <QtGui/QTextEdit> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +CompileOutputWindow::CompileOutputWindow(BuildManager * /*bm*/) +{ + m_textEdit = new QTextEdit(); + m_textEdit->setWindowTitle(tr("Compile Output")); + m_textEdit->setWindowIcon(QIcon(":/qt4projectmanager/images/window.png")); + m_textEdit->setReadOnly(true); + m_textEdit->setFrameStyle(QFrame::NoFrame); + Aggregation::Aggregate *agg = new Aggregation::Aggregate; + agg->add(m_textEdit); + agg->add(new Find::BaseTextFind(m_textEdit)); +} + +bool CompileOutputWindow::hasFocus() +{ + return m_textEdit->hasFocus(); +} + +bool CompileOutputWindow::canFocus() +{ + return true; +} + +void CompileOutputWindow::setFocus() +{ + m_textEdit->setFocus(); +} + +QWidget *CompileOutputWindow::outputWidget(QWidget *) +{ + return m_textEdit; +} + +void CompileOutputWindow::appendText(const QString &text) +{ + m_textEdit->append(text); +} + +void CompileOutputWindow::clearContents() +{ + m_textEdit->clear(); +} + +void CompileOutputWindow::visibilityChanged(bool /* b */) +{ + +} + +int CompileOutputWindow::priorityInStatusBar() const +{ + return 50; +} diff --git a/src/plugins/projectexplorer/compileoutputwindow.h b/src/plugins/projectexplorer/compileoutputwindow.h new file mode 100644 index 00000000000..aaf90172b36 --- /dev/null +++ b/src/plugins/projectexplorer/compileoutputwindow.h @@ -0,0 +1,70 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef COMPILEOUTPUTWINDOW_H +#define COMPILEOUTPUTWINDOW_H + +#include <coreplugin/ioutputpane.h> + +#include <QtGui/QTextEdit> + +namespace ProjectExplorer { + +class BuildManager; + +namespace Internal { + +class CompileOutputWindow : public Core::IOutputPane +{ + Q_OBJECT + +public: + CompileOutputWindow(BuildManager *bm); + QWidget *outputWidget(QWidget *); + QList<QWidget*> toolBarWidgets(void) const { return QList<QWidget *>(); } + QString name() const { return tr("Compile"); } + int priorityInStatusBar() const; + void clearContents(); + void visibilityChanged(bool visible); + void appendText(const QString &text); + bool canFocus(); + bool hasFocus(); + void setFocus(); + +private: + QTextEdit *m_textEdit; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // COMPILEOUTPUTWINDOW_H diff --git a/src/plugins/projectexplorer/consoleprocess.h b/src/plugins/projectexplorer/consoleprocess.h new file mode 100644 index 00000000000..0969618b7e8 --- /dev/null +++ b/src/plugins/projectexplorer/consoleprocess.h @@ -0,0 +1,99 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CONSOLEPROCESS_H +#define CONSOLEPROCESS_H + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QProcess> + +#ifdef Q_OS_WIN +#include <windows.h> +class QWinEventNotifier; +#endif + +#include "abstractprocess.h" + + +namespace ProjectExplorer { +namespace Internal { + +class ConsoleProcess : public QObject, public AbstractProcess +{ + Q_OBJECT + +public: + ConsoleProcess(QObject *parent); + ~ConsoleProcess(); + + bool start(const QString &program, const QStringList &args); + void stop(); + + bool isRunning() const; + qint64 applicationPID() const; + int exitCode() const; + +signals: + void processError(const QString &error); + void processStarted(); + void processStopped(); + +private: + bool m_isRunning; + +#ifdef Q_OS_WIN +public: + static QString createCommandline(const QString &program, + const QStringList &args); + static QByteArray createEnvironment(const QStringList &env); + +private slots: + void processDied(); + +private: + PROCESS_INFORMATION *m_pid; + QWinEventNotifier *processFinishedNotifier; +#elif defined(Q_OS_UNIX) +private: + QProcess *m_process; +private slots: + void processFinished(int, QProcess::ExitStatus); +#endif + +}; + +} //namespace Internal +} //namespace Qt4ProjectManager + +#endif diff --git a/src/plugins/projectexplorer/consoleprocess_unix.cpp b/src/plugins/projectexplorer/consoleprocess_unix.cpp new file mode 100644 index 00000000000..325df913345 --- /dev/null +++ b/src/plugins/projectexplorer/consoleprocess_unix.cpp @@ -0,0 +1,102 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "consoleprocess.h" + +using namespace ProjectExplorer::Internal; + +ConsoleProcess::ConsoleProcess(QObject *parent) + : QObject(parent) +{ + m_isRunning = false; + m_process = new QProcess(this); +} + +ConsoleProcess::~ConsoleProcess() +{ +} + +bool ConsoleProcess::start(const QString &program, const QStringList &args) +{ + if (m_process->state() != QProcess::NotRunning) + return false; + QString shellArgs; + shellArgs += QLatin1String("cd "); + shellArgs += workingDirectory(); + shellArgs += QLatin1Char(';'); + shellArgs += program; + foreach (const QString &arg, args) { + shellArgs += QLatin1Char(' '); + shellArgs += QLatin1Char('\''); + shellArgs += arg; + shellArgs += QLatin1Char('\''); + } + shellArgs += QLatin1String("; echo; echo \"Press enter to close this window\"; read"); + + m_process->setEnvironment(environment()); + + connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(processFinished(int, QProcess::ExitStatus))); + + m_process->start(QLatin1String("xterm"), QStringList() << QLatin1String("-e") << shellArgs); + if (!m_process->waitForStarted()) + return false; + emit processStarted(); + return true; +} + +void ConsoleProcess::processFinished(int, QProcess::ExitStatus) +{ + emit processStopped(); +} + +bool ConsoleProcess::isRunning() const +{ + return m_process->state() != QProcess::NotRunning; +} + +void ConsoleProcess::stop() +{ + m_process->terminate(); + m_process->waitForFinished(); +} + +qint64 ConsoleProcess::applicationPID() const +{ + return m_process->pid(); +} + +int ConsoleProcess::exitCode() const +{ + return m_process->exitCode(); +} + diff --git a/src/plugins/projectexplorer/consoleprocess_win.cpp b/src/plugins/projectexplorer/consoleprocess_win.cpp new file mode 100644 index 00000000000..4133e154880 --- /dev/null +++ b/src/plugins/projectexplorer/consoleprocess_win.cpp @@ -0,0 +1,239 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtCore/QDir> +#include <QtCore/private/qwineventnotifier_p.h> +#include <QtCore/QAbstractEventDispatcher> + +#include <Tlhelp32.h> + +#include "consoleprocess.h" + +using namespace ProjectExplorer::Internal; + +ConsoleProcess::ConsoleProcess(QObject *parent) + : QObject(parent) +{ + m_isRunning = false; + m_pid = 0; +} + +ConsoleProcess::~ConsoleProcess() +{ + stop(); +} + +void ConsoleProcess::stop() +{ + if (m_pid) + TerminateProcess(m_pid->hProcess, -1); + m_isRunning = false; +} + +bool ConsoleProcess::start(const QString &program, const QStringList &args) +{ + if (m_isRunning) + return false; + + STARTUPINFO si; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + if (m_pid) { + CloseHandle(m_pid->hThread); + CloseHandle(m_pid->hProcess); + delete m_pid; + m_pid = 0; + } + m_pid = new PROCESS_INFORMATION; + ZeroMemory(m_pid, sizeof(PROCESS_INFORMATION)); + + QString cmdLine = QLatin1String("cmd /k ") + + createCommandline(program, args) + + QLatin1String(" & pause & exit"); + + bool success = CreateProcessW(0, (WCHAR*)cmdLine.utf16(), + 0, 0, TRUE, CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, + environment().isEmpty() ? 0 + : createEnvironment(environment()).data(), + workingDirectory().isEmpty() ? 0 + : (WCHAR*)QDir::convertSeparators(workingDirectory()).utf16(), + &si, m_pid); + + if (!success) { + emit processError(tr("The process could not be started!")); + return false; + } + + if (QAbstractEventDispatcher::instance(thread())) { + processFinishedNotifier = new QWinEventNotifier(m_pid->hProcess, this); + QObject::connect(processFinishedNotifier, SIGNAL(activated(HANDLE)), this, SLOT(processDied())); + processFinishedNotifier->setEnabled(true); + } + m_isRunning = true; + emit processStarted(); + return success; +} + +bool ConsoleProcess::isRunning() const +{ + return m_isRunning; +} + +void ConsoleProcess::processDied() +{ + if (processFinishedNotifier) { + processFinishedNotifier->setEnabled(false); + delete processFinishedNotifier; + processFinishedNotifier = 0; + } + delete m_pid; + m_pid = 0; + m_isRunning = false; + emit processStopped(); +} + +qint64 ConsoleProcess::applicationPID() const +{ + if (m_pid) { + HANDLE hProcList = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32 procEntry; + procEntry.dwSize = sizeof(PROCESSENTRY32); + DWORD procId = 0; + BOOL moreProc = Process32First(hProcList, &procEntry); + while (moreProc) { + if (procEntry.th32ParentProcessID == m_pid->dwProcessId) { + procId = procEntry.th32ProcessID; + break; + } + moreProc = Process32Next(hProcList, &procEntry); + } + + CloseHandle(hProcList); + return procId; + } + return 0; +} + +int ConsoleProcess::exitCode() const +{ + DWORD exitCode; + if (GetExitCodeProcess(m_pid->hProcess, &exitCode)) + return exitCode; + return -1; +} + +QByteArray ConsoleProcess::createEnvironment(const QStringList &env) +{ + QByteArray envlist; + if (!env.isEmpty()) { + QStringList envStrings = env; + int pos = 0; + // add PATH if necessary (for DLL loading) + if (envStrings.filter(QRegExp("^PATH=",Qt::CaseInsensitive)).isEmpty()) { + QByteArray path = qgetenv("PATH"); + if (!path.isEmpty()) + envStrings.prepend(QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path))); + } + // add systemroot if needed + if (envStrings.filter(QRegExp("^SystemRoot=",Qt::CaseInsensitive)).isEmpty()) { + QByteArray systemRoot = qgetenv("SystemRoot"); + if (!systemRoot.isEmpty()) + envStrings.prepend(QString(QLatin1String("SystemRoot=%1")).arg(QString::fromLocal8Bit(systemRoot))); + } +#ifdef UNICODE + if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) { + for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); it++ ) { + QString tmp = *it; + uint tmpSize = sizeof(TCHAR) * (tmp.length()+1); + envlist.resize(envlist.size() + tmpSize); + memcpy(envlist.data()+pos, tmp.utf16(), tmpSize); + pos += tmpSize; + } + // add the 2 terminating 0 (actually 4, just to be on the safe side) + envlist.resize(envlist.size() + 4); + envlist[pos++] = 0; + envlist[pos++] = 0; + envlist[pos++] = 0; + envlist[pos++] = 0; + } else +#endif // UNICODE + { + for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); it++) { + QByteArray tmp = (*it).toLocal8Bit(); + uint tmpSize = tmp.length() + 1; + envlist.resize(envlist.size() + tmpSize); + memcpy(envlist.data()+pos, tmp.data(), tmpSize); + pos += tmpSize; + } + // add the terminating 0 (actually 2, just to be on the safe side) + envlist.resize(envlist.size() + 2); + envlist[pos++] = 0; + envlist[pos++] = 0; + } + } + return envlist; +} + +QString ConsoleProcess::createCommandline(const QString &program, const QStringList &args) +{ + QString programName = program; + if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(" ")) + programName = "\"" + programName + "\""; + programName.replace("/", "\\"); + + QString cmdLine; + // add the prgram as the first arrg ... it works better + cmdLine = programName + " "; + for (int i = 0; i < args.size(); ++i) { + QString tmp = args.at(i); + // in the case of \" already being in the string the \ must also be escaped + tmp.replace( "\\\"", "\\\\\"" ); + // escape a single " because the arguments will be parsed + tmp.replace( "\"", "\\\"" ); + if (tmp.isEmpty() || tmp.contains(' ') || tmp.contains('\t')) { + // The argument must not end with a \ since this would be interpreted + // as escaping the quote -- rather put the \ behind the quote: e.g. + // rather use "foo"\ than "foo\" + QString endQuote("\""); + int i = tmp.length(); + while (i > 0 && tmp.at(i - 1) == '\\') { + --i; + endQuote += "\\"; + } + cmdLine += QString(" \"") + tmp.left(i) + endQuote; + } else { + cmdLine += ' ' + tmp; + } + } + return cmdLine; +} diff --git a/src/plugins/projectexplorer/currentprojectfilter.cpp b/src/plugins/projectexplorer/currentprojectfilter.cpp new file mode 100644 index 00000000000..1a029db7449 --- /dev/null +++ b/src/plugins/projectexplorer/currentprojectfilter.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + + + +#include "currentprojectfilter.h" +#include "projectexplorer.h" +#include "project.h" +#include "session.h" + +#include <QtCore/QVariant> +#include <QtCore/QTimer> + +#include <QtCore/QThread> +#include <QtDebug> + +using namespace Core; +using namespace QuickOpen; +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +CurrentProjectFilter::CurrentProjectFilter(ProjectExplorerPlugin *pe, + ICore *core) + : BaseFileFilter(core), + m_project(0) +{ + m_projectExplorer = pe; + + connect(m_projectExplorer, SIGNAL(currentProjectChanged(ProjectExplorer::Project*)), + this, SLOT(currentProjectChanged(ProjectExplorer::Project*))); + setShortcutString("p"); + setIncludedByDefault(false); +} + +void CurrentProjectFilter::refreshInternally() +{ + m_files.clear(); + if (!m_project) + return; + m_files = m_project->files(Project::AllFiles); + qSort(m_files); + generateFileNames(); +} + +void CurrentProjectFilter::currentProjectChanged(ProjectExplorer::Project *project) +{ + if (project == m_project) + return; + if (m_project) { + disconnect(m_project, SIGNAL(fileListChanged()), this, SLOT(refreshInternally())); + } + if (project) { + connect(project, SIGNAL(fileListChanged()), this, SLOT(refreshInternally())); + } + m_project = project; + refreshInternally(); +} + +void CurrentProjectFilter::refresh(QFutureInterface<void> &future) +{ + Q_UNUSED(future); + // invokeAsyncronouslyOnGuiThread + connect(this, SIGNAL(invokeRefresh()), this, SLOT(refreshInternally())); + emit invokeRefresh(); + disconnect(this, SIGNAL(invokeRefresh()), this, SLOT(refreshInternally())); +} diff --git a/src/plugins/projectexplorer/currentprojectfilter.h b/src/plugins/projectexplorer/currentprojectfilter.h new file mode 100644 index 00000000000..77bbbd1f523 --- /dev/null +++ b/src/plugins/projectexplorer/currentprojectfilter.h @@ -0,0 +1,78 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CURRENTPROJECTFILTER_H +#define CURRENTPROJECTFILTER_H + +#include <quickopen/basefilefilter.h> + +#include <QtCore/QString> +#include <QtCore/QByteArray> +#include <QtCore/QFutureInterface> +#include <QtCore/QTimer> +#include <QtGui/QWidget> + +namespace ProjectExplorer { + +class ProjectExplorerPlugin; +class Project; + +namespace Internal { + +class CurrentProjectFilter : public QuickOpen::BaseFileFilter +{ + Q_OBJECT + +public: + CurrentProjectFilter(ProjectExplorerPlugin *pe, Core::ICore *core); + QString trName() const { return tr("File in current project"); } + QString name() const { return "File in current project"; } + QuickOpen::IQuickOpenFilter::Priority priority() const { return QuickOpen::IQuickOpenFilter::Low; } + void refresh(QFutureInterface<void> &future); + +private slots: + void currentProjectChanged(ProjectExplorer::Project *project); + void refreshInternally(); + +signals: + void invokeRefresh(); + +private: + + ProjectExplorerPlugin *m_projectExplorer; + Project *m_project; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // CURRENTPROJECTFILTER_H diff --git a/src/plugins/projectexplorer/currentprojectfind.cpp b/src/plugins/projectexplorer/currentprojectfind.cpp new file mode 100644 index 00000000000..da560894fb9 --- /dev/null +++ b/src/plugins/projectexplorer/currentprojectfind.cpp @@ -0,0 +1,129 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "currentprojectfind.h" +#include "projectexplorer.h" +#include "project.h" + +#include <QtDebug> +#include <QtCore/QRegExp> +#include <QtGui/QGridLayout> + +using namespace Find; +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; +using namespace TextEditor; + +CurrentProjectFind::CurrentProjectFind(ProjectExplorerPlugin *plugin, Core::ICore *core, SearchResultWindow *resultWindow) + : BaseFileFind(core, resultWindow), + m_plugin(plugin), + m_configWidget(0) +{ + connect(m_plugin, SIGNAL(currentProjectChanged(ProjectExplorer::Project*)), + this, SIGNAL(changed())); +} + +QString CurrentProjectFind::name() const +{ + return tr("Current Project"); +} + +bool CurrentProjectFind::isEnabled() const +{ + return m_plugin->currentProject() != 0 && BaseFileFind::isEnabled(); +} + +QKeySequence CurrentProjectFind::defaultShortcut() const +{ + return QKeySequence("Ctrl+Alt+F"); +} + +QStringList CurrentProjectFind::files() +{ + Project *project = m_plugin->currentProject(); + Q_ASSERT(project); + if (!project) + return QStringList(); + QList<QRegExp> filterRegs; + QStringList nameFilters = fileNameFilters(); + foreach (const QString &filter, nameFilters) { + filterRegs << QRegExp(filter, Qt::CaseInsensitive, QRegExp::Wildcard); + } + QStringList files; + if (!filterRegs.isEmpty()) { + foreach (const QString &file, project->files(Project::AllFiles)) { + foreach (const QRegExp ®, filterRegs) { + if (reg.exactMatch(file)) { + files.append(file); + break; + } + } + } + } else { + files += project->files(Project::AllFiles); + } + files.removeDuplicates(); + return files; +} + +QWidget *CurrentProjectFind::createConfigWidget() +{ + if (!m_configWidget) { + m_configWidget = new QWidget; + QGridLayout * const layout = new QGridLayout(m_configWidget); + layout->setMargin(0); + m_configWidget->setLayout(layout); + layout->addWidget(createRegExpWidget(), 0, 1); + QLabel * const filePatternLabel = new QLabel(tr("File pattern:")); + filePatternLabel->setMinimumWidth(80); + filePatternLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + filePatternLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout->addWidget(filePatternLabel, 1, 0, Qt::AlignRight); + layout->addWidget(createPatternWidget(), 1, 1); + m_configWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + } + return m_configWidget; +} + +void CurrentProjectFind::writeSettings(QSettings *settings) +{ + settings->beginGroup("CurrentProjectFind"); + writeCommonSettings(settings); + settings->endGroup(); +} + +void CurrentProjectFind::readSettings(QSettings *settings) +{ + settings->beginGroup("CurrentProjectFind"); + readCommonSettings(settings, "*"); + settings->endGroup(); +} diff --git a/src/plugins/projectexplorer/currentprojectfind.h b/src/plugins/projectexplorer/currentprojectfind.h new file mode 100644 index 00000000000..a70567b7e4c --- /dev/null +++ b/src/plugins/projectexplorer/currentprojectfind.h @@ -0,0 +1,75 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CURRENTPROJECTFIND_H +#define CURRENTPROJECTFIND_H + +#include <coreplugin/icore.h> +#include <find/ifindfilter.h> +#include <find/searchresultwindow.h> +#include <texteditor/basefilefind.h> + +#include <QtCore/QPointer> +#include <QtGui/QWidget> + +namespace ProjectExplorer { + +class ProjectExplorerPlugin; + +namespace Internal { + +class CurrentProjectFind : public TextEditor::BaseFileFind +{ +public: + CurrentProjectFind(ProjectExplorerPlugin *plugin, Core::ICore *core, Find::SearchResultWindow *resultWindow); + + QString name() const; + + bool isEnabled() const; + QKeySequence defaultShortcut() const; + + QWidget *createConfigWidget(); + void writeSettings(QSettings *settings); + void readSettings(QSettings *settings); + +protected: + QStringList files(); + +private: + ProjectExplorerPlugin *m_plugin; + QPointer<QWidget> m_configWidget; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // CURRENTPROJECTFIND_H diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp new file mode 100644 index 00000000000..4f2b470aec1 --- /dev/null +++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp @@ -0,0 +1,316 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "customexecutablerunconfiguration.h" +#include "environment.h" +#include "project.h" + +#include <QtGui/QFormLayout> +#include <QtGui/QLineEdit> +#include <QtGui/QLabel> +#include <QtGui/QHBoxLayout> +#include <QtGui/QToolButton> +#include <QtGui/QFileDialog> +#include <QDialogButtonBox> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +CustomExecutableConfigurationWidget::CustomExecutableConfigurationWidget(CustomExecutableRunConfiguration *rc) + : m_ignoreChange(false) +{ + m_runConfiguration = rc; + + QFormLayout *layout = new QFormLayout(); + layout->setMargin(0); + + m_executableLineEdit = new QLineEdit; + QToolButton *exectuableToolButton = new QToolButton(); + exectuableToolButton->setText("..."); + QHBoxLayout *hl = new QHBoxLayout; + hl->addWidget(m_executableLineEdit); + hl->addWidget(exectuableToolButton); + layout->addRow("Executable", hl); + + m_commandLineArgumentsLineEdit = new QLineEdit; + layout->addRow("Arguments", m_commandLineArgumentsLineEdit); + + m_workingDirectoryLineEdit = new QLineEdit(); + QToolButton *workingDirectoryToolButton = new QToolButton(); + workingDirectoryToolButton->setText("..."); + hl = new QHBoxLayout; + hl->addWidget(m_workingDirectoryLineEdit); + hl->addWidget(workingDirectoryToolButton); + layout->addRow("Working Directory", hl); + + setLayout(layout); + changed(); + + connect(m_executableLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(setExecutable(const QString&))); + connect(m_commandLineArgumentsLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(setCommandLineArguments(const QString&))); + connect(m_workingDirectoryLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(setWorkingDirectory(const QString&))); + connect(exectuableToolButton, SIGNAL(clicked(bool)), + this, SLOT(executableToolButtonClicked())); + connect(workingDirectoryToolButton, SIGNAL(clicked(bool)), + this, SLOT(workingDirectoryToolButtonClicked())); + + connect(m_runConfiguration, SIGNAL(changed()), this, SLOT(changed())); +} + +void CustomExecutableConfigurationWidget::setExecutable(const QString &executable) +{ + m_ignoreChange = true; + m_runConfiguration->setExecutable(executable); + m_ignoreChange = false; +} +void CustomExecutableConfigurationWidget::setCommandLineArguments(const QString &commandLineArguments) +{ + m_ignoreChange = true; + m_runConfiguration->setCommandLineArguments(commandLineArguments); + m_ignoreChange = false; +} +void CustomExecutableConfigurationWidget::setWorkingDirectory(const QString &workingDirectory) +{ + m_ignoreChange = true; + m_runConfiguration->setWorkingDirectory(workingDirectory); + m_ignoreChange = false; +} + +void CustomExecutableConfigurationWidget::executableToolButtonClicked() +{ + QString newValue; + QString executableFilter; +#ifdef Q_OS_WIN + executableFilter = "Executable (*.exe)"; +#endif + newValue = QFileDialog::getOpenFileName(this, "Executable", "", executableFilter); + if (!newValue.isEmpty()) { + m_executableLineEdit->setText(newValue); + setExecutable(newValue); + } +} + +void CustomExecutableConfigurationWidget::workingDirectoryToolButtonClicked() +{ + QString newValue; + QString executableFilter; + + newValue = QFileDialog::getExistingDirectory(this, "Directory", m_workingDirectoryLineEdit->text()); + if (newValue.isEmpty()) { + m_workingDirectoryLineEdit->setText(newValue); + setWorkingDirectory(newValue); + } +} + +void CustomExecutableConfigurationWidget::changed() +{ + // We triggered the change, don't update us + if (m_ignoreChange) + return; + m_executableLineEdit->setText(m_runConfiguration->baseExecutable()); + m_commandLineArgumentsLineEdit->setText(ProjectExplorer::Environment::joinArgumentList(m_runConfiguration->commandLineArguments())); + m_workingDirectoryLineEdit->setText(m_runConfiguration->baseWorkingDirectory()); +} + +CustomExecutableRunConfiguration::CustomExecutableRunConfiguration(Project *pro) + : ApplicationRunConfiguration(pro) +{ + m_workingDirectory = "$BUILDDIR"; + setName("Custom Executable"); +} + +CustomExecutableRunConfiguration::~CustomExecutableRunConfiguration() +{ +} + +QString CustomExecutableRunConfiguration::type() const +{ + return "ProjectExplorer.CustomExecutableRunConfiguration"; +} + +QString CustomExecutableRunConfiguration::baseExecutable() const +{ + return m_executable; +} + +QString CustomExecutableRunConfiguration::executable() const +{ + QString exec; + if (QDir::isRelativePath(m_executable)) { + Environment env = project()->environment(project()->activeBuildConfiguration()); + exec = env.searchInPath(m_executable); + } else { + exec = m_executable; + } + + if (!QFileInfo(exec).exists()) { + // Oh the executable doesn't exists, ask the user. + QWidget *confWidget = const_cast<CustomExecutableRunConfiguration *>(this)->configurationWidget(); + QDialog dialog; + dialog.setLayout(new QVBoxLayout()); + dialog.layout()->addWidget(new QLabel("Could not find the executable, please specify one.")); + dialog.layout()->addWidget(confWidget); + QDialogButtonBox *dbb = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(dbb, SIGNAL(accepted()), &dialog, SLOT(accept())); + connect(dbb, SIGNAL(rejected()), &dialog, SLOT(reject())); + dialog.layout()->addWidget(dbb); + + QString oldExecutable = m_executable; + QString oldWorkingDirectory = m_workingDirectory; + QStringList oldCmdArguments = m_cmdArguments; + + if (dialog.exec()) { + return executable(); + } else { + CustomExecutableRunConfiguration *that = const_cast<CustomExecutableRunConfiguration *>(this); + that->m_executable = oldExecutable; + that->m_workingDirectory = oldWorkingDirectory; + that->m_cmdArguments = oldCmdArguments; + emit that->changed(); + return QString::null; + } + } + return exec; +} + +ApplicationRunConfiguration::RunMode CustomExecutableRunConfiguration::runMode() const +{ + return ApplicationRunConfiguration::Gui; +} + +QString CustomExecutableRunConfiguration::baseWorkingDirectory() const +{ + return m_workingDirectory; +} + +QString CustomExecutableRunConfiguration::workingDirectory() const +{ + QString wd = m_workingDirectory; + QString bd = project()->buildDirectory(project()->activeBuildConfiguration()); + return wd.replace("$BUILDDIR", QDir::cleanPath(bd)); +} + +QStringList CustomExecutableRunConfiguration::commandLineArguments() const +{ + return m_cmdArguments; +} + +Environment CustomExecutableRunConfiguration::environment() const +{ + return project()->environment(project()->activeBuildConfiguration()); +} + + +void CustomExecutableRunConfiguration::save(PersistentSettingsWriter &writer) const +{ + writer.saveValue("Executable", m_executable); + writer.saveValue("Arguments", m_cmdArguments); + writer.saveValue("WorkingDirectory", m_workingDirectory); + ApplicationRunConfiguration::save(writer); +} + +void CustomExecutableRunConfiguration::restore(const PersistentSettingsReader &reader) +{ + m_executable = reader.restoreValue("Executable").toString(); + m_cmdArguments = reader.restoreValue("Arguments").toStringList(); + m_workingDirectory = reader.restoreValue("WorkingDirectory").toString(); + ApplicationRunConfiguration::restore(reader); +} + +void CustomExecutableRunConfiguration::setExecutable(const QString &executable) +{ + m_executable = executable; + setName(tr("Run %1").arg(m_executable)); + emit changed(); +} + +void CustomExecutableRunConfiguration::setCommandLineArguments(const QString &commandLineArguments) +{ + m_cmdArguments = ProjectExplorer::Environment::parseCombinedArgString(commandLineArguments); + emit changed(); +} + +void CustomExecutableRunConfiguration::setWorkingDirectory(const QString &workingDirectory) +{ + m_workingDirectory = workingDirectory; + emit changed(); +} + +QWidget *CustomExecutableRunConfiguration::configurationWidget() +{ + return new CustomExecutableConfigurationWidget(this); +} + +// Factory + +CustomExecutableRunConfigurationFactory::CustomExecutableRunConfigurationFactory() +{ +} + +CustomExecutableRunConfigurationFactory::~CustomExecutableRunConfigurationFactory() +{ + +} + +// used to recreate the runConfigurations when restoring settings +bool CustomExecutableRunConfigurationFactory::canCreate(const QString &type) const +{ + return type == "ProjectExplorer.CustomExecutableRunConfiguration"; +} + +QSharedPointer<RunConfiguration> CustomExecutableRunConfigurationFactory::create(Project *project, const QString &type) +{ + if (type == "ProjectExplorer.CustomExecutableRunConfiguration") { + QSharedPointer<RunConfiguration> rc(new CustomExecutableRunConfiguration(project)); + rc->setName("Custom Executable"); + return rc; + } else { + return QSharedPointer<RunConfiguration>(0); + } +} + +QStringList CustomExecutableRunConfigurationFactory::canCreate(Project *pro) const +{ + Q_UNUSED(pro) + return QStringList()<< "ProjectExplorer.CustomExecutableRunConfiguration"; +} + +QString CustomExecutableRunConfigurationFactory::nameForType(const QString &type) const +{ + if (type == "ProjectExplorer.CustomExecutableRunConfiguration") + return "Custom Executable"; + else + return QString::null; +} diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.h b/src/plugins/projectexplorer/customexecutablerunconfiguration.h new file mode 100644 index 00000000000..8f53582bfc4 --- /dev/null +++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.h @@ -0,0 +1,126 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CUSTOMEXECUTABLERUNCONFIGURATION_H +#define CUSTOMEXECUTABLERUNCONFIGURATION_H + +#include "applicationrunconfiguration.h" + +#include <QtGui/QToolButton> + +QT_BEGIN_NAMESPACE +class QLineEdit; +QT_END_NAMESPACE + +namespace ProjectExplorer { + +namespace Internal { + class CustomExecutableConfigurationWidget; +} + +class PROJECTEXPLORER_EXPORT CustomExecutableRunConfiguration : public ApplicationRunConfiguration +{ + // the configuration widget needs to setExecutable setWorkingDirectory and setCommandLineArguments + friend class Internal::CustomExecutableConfigurationWidget; + Q_OBJECT +public: + CustomExecutableRunConfiguration(Project *pro); + ~CustomExecutableRunConfiguration(); + virtual QString type() const; + // returns the executable, + // looks in the environment for it + // and might even ask the user if none is specified + virtual QString executable() const; + // Returns only what is stored in the internal variable + // not what we might get after extending it with a path + // or asking the user. This value is needed for the configuration widget + QString baseExecutable() const; + virtual ApplicationRunConfiguration::RunMode runMode() const; + virtual QString workingDirectory() const; + QString baseWorkingDirectory() const; + virtual QStringList commandLineArguments() const; + virtual Environment environment() const; + + virtual void save(PersistentSettingsWriter &writer) const; + virtual void restore(const PersistentSettingsReader &reader); + + virtual QWidget *configurationWidget(); +signals: + void changed(); +private slots: + void setExecutable(const QString &executable); + void setCommandLineArguments(const QString &commandLineArguments); + void setWorkingDirectory(const QString &workingDirectory); +private: + QString m_executable; + QString m_workingDirectory; + QStringList m_cmdArguments; +}; + +class CustomExecutableRunConfigurationFactory : public IRunConfigurationFactory +{ + Q_OBJECT +public: + CustomExecutableRunConfigurationFactory(); + virtual ~CustomExecutableRunConfigurationFactory(); + // used to recreate the runConfigurations when restoring settings + virtual bool canCreate(const QString &type) const; + virtual QSharedPointer<RunConfiguration> create(Project *project, const QString &type); + QStringList canCreate(Project *pro) const; + QString nameForType(const QString &type) const; +}; + +namespace Internal { +class CustomExecutableConfigurationWidget : public QWidget +{ + Q_OBJECT +public: + CustomExecutableConfigurationWidget(CustomExecutableRunConfiguration *rc); +private slots: + void changed(); + void executableToolButtonClicked(); + void workingDirectoryToolButtonClicked(); + + void setExecutable(const QString &executable); + void setCommandLineArguments(const QString &commandLineArguments); + void setWorkingDirectory(const QString &workingDirectory); +private: + bool m_ignoreChange; + CustomExecutableRunConfiguration *m_runConfiguration; + QLineEdit *m_executableLineEdit; + QLineEdit *m_commandLineArgumentsLineEdit; + QLineEdit *m_workingDirectoryLineEdit; +}; +} +} + +#endif // CUSTOMEXECUTABLERUNCONFIGURATION_H diff --git a/src/plugins/projectexplorer/dependenciesdialog.cpp b/src/plugins/projectexplorer/dependenciesdialog.cpp new file mode 100644 index 00000000000..5314262f48e --- /dev/null +++ b/src/plugins/projectexplorer/dependenciesdialog.cpp @@ -0,0 +1,244 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "dependenciesdialog.h" +#include "project.h" +#include "session.h" + +#include <QtCore/QVector> +#include <QtCore/QDebug> +#include <QtCore/QAbstractTableModel> +#include <QtGui/QPushButton> +#include <QtGui/QHeaderView> + +namespace ProjectExplorer { +namespace Internal { + +// ------ DependencyModel + +class DependencyModel : public QAbstractTableModel { +public: + typedef ProjectExplorer::Project Project; + typedef DependenciesDialog::ProjectList ProjectList; + + DependencyModel(SessionManager *sln, const ProjectList &projectList, QObject * parent = 0); + + virtual int rowCount(const QModelIndex&) const { return m_projects.size(); } + virtual int columnCount(const QModelIndex&) const { return m_projects.size(); } + + virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; + bool setData ( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); + + virtual Qt::ItemFlags flags ( const QModelIndex & index ) const; + + QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; + + // Apply changed items + unsigned apply(SessionManager *sln) const; + + void resetDependencies(); + +private: + + struct Entry { + Entry(SessionManager *sln, Project *rootProject, Project *dependentProject); + Entry() : m_dependentProject(0), m_dependent(false), m_defaultValue(false), m_canAddDependency(false) {} + Project* m_dependentProject; + bool m_dependent; + bool m_defaultValue; + bool m_canAddDependency; + }; + + // column + typedef QVector<Entry> ProjectDependencies; + typedef QList<ProjectDependencies> Projects; + Projects m_projects; + ProjectList m_projectList; +}; + +DependencyModel::Entry::Entry(SessionManager *sln, + Project *rootProject, + Project *dependentProject) : + m_dependentProject(dependentProject), + m_dependent(sln->hasDependency(rootProject, dependentProject)), + m_defaultValue(m_dependent), + m_canAddDependency(sln->canAddDependency(rootProject, dependentProject)) +{ +} + +DependencyModel::DependencyModel(SessionManager *sln, + const ProjectList &projectList, + QObject * parent) : + QAbstractTableModel(parent), + m_projectList(projectList) +{ + const int count = projectList.size(); + for (int p = 0; p < count; p++) { + Project *rootProject = projectList.at(p); + ProjectDependencies dependencies; + dependencies.reserve(count); + for (int d = 0; d < count ; d++) + dependencies.push_back(p == d ? Entry() : Entry(sln, rootProject, projectList.at(d))); + + m_projects += dependencies; + } +} + +QVariant DependencyModel::data ( const QModelIndex & index, int role ) const +{ + static const QVariant empty = QVariant(QString()); + // TO DO: find a checked icon + static const QVariant checked = QVariant(QString(QLatin1Char('x'))); + + const int p = index.column(); + const int d = index.row(); + switch (role) { + case Qt::EditRole: + return QVariant(m_projects[p][d].m_dependent); + case Qt::DisplayRole: + return m_projects[p][d].m_dependent ? checked : empty; + default: + break; + } + return QVariant(); +} + +bool DependencyModel::setData ( const QModelIndex & index, const QVariant & value, int role) +{ + switch (role) { + case Qt::EditRole: { + const int p = index.column(); + const int d = index.row(); + if (d == p) + return false; + Entry &e(m_projects[p][d]); + e.m_dependent = value.toBool(); + emit dataChanged(index, index); + } + return true; + default: + break; + } + return false; +} + +Qt::ItemFlags DependencyModel::flags ( const QModelIndex & index ) const +{ + const int p = index.column(); + const int d = index.row(); + + if (d == p) + return 0; + + const Entry &e(m_projects[p][d]); + Qt::ItemFlags rc = Qt::ItemIsEnabled|Qt::ItemIsUserCheckable | Qt::ItemIsSelectable; + if (e.m_canAddDependency) + rc |= Qt::ItemIsEditable; + return rc; +} + +QVariant DependencyModel::headerData ( int section, Qt::Orientation , int role ) const +{ + switch (role) { + case Qt::DisplayRole: + return QVariant(m_projectList.at(section)->name()); + default: + break; + } + return QVariant(); +} + +void DependencyModel::resetDependencies() +{ + if (const int count = m_projectList.size()) { + for (int p = 0; p < count; p++) + for (int d = 0; d < count; d++) + m_projects[p][d].m_dependent = false; + reset(); + } +} + +unsigned DependencyModel::apply(SessionManager *sln) const +{ + unsigned rc = 0; + const int count = m_projectList.size(); + for (int p = 0; p < count; p++) { + Project *rootProject = m_projectList.at(p); + for (int d = 0; d < count; d++) { + if (d != p) { + const Entry &e(m_projects[p][d]); + if (e.m_dependent != e. m_defaultValue) { + rc++; + if (e.m_dependent) { + sln->addDependency(rootProject, e.m_dependentProject); + } else { + sln->removeDependency(rootProject, e.m_dependentProject); + } + } + } + } + } + return rc; +} + +// ------ DependenciesDialog +DependenciesDialog::DependenciesDialog(QWidget *parent, SessionManager *sln) : + QDialog(parent), + m_sln(sln), + m_projectList(m_sln->projects()), + m_model(new DependencyModel(sln, m_projectList)) +{ + m_ui.setupUi(this); + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + QPushButton *resetButton = m_ui.buttonBox->addButton (QDialogButtonBox::Reset); + connect(resetButton, SIGNAL(clicked()), this, SLOT(reset())); + + m_ui.dependencyTable->setModel(m_model); +} + +void DependenciesDialog::accept() +{ + m_model->apply(m_sln); + QDialog::accept(); +} + +void DependenciesDialog::reset() +{ + m_model->resetDependencies(); +} + +DependenciesDialog::~DependenciesDialog() +{ +} + +} +} diff --git a/src/plugins/projectexplorer/dependenciesdialog.h b/src/plugins/projectexplorer/dependenciesdialog.h new file mode 100644 index 00000000000..7292a3affab --- /dev/null +++ b/src/plugins/projectexplorer/dependenciesdialog.h @@ -0,0 +1,73 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEPENDENCIESDIALOG_H +#define DEPENDENCIESDIALOG_H + +#include "ui_dependenciesdialog.h" + +#include <QtGui/QDialog> + +namespace ProjectExplorer { + +class Project; +class SessionManager; + +namespace Internal { + +class DependencyModel; + +// NBS kill DependenciesDialog? +class DependenciesDialog : public QDialog +{ + Q_OBJECT +public: + typedef QList<ProjectExplorer::Project *> ProjectList; + + DependenciesDialog(QWidget *parent, SessionManager *sln); + virtual ~DependenciesDialog(); + +public slots: + virtual void accept(); + void reset(); + +private: + Ui::DependenciesDialog m_ui; + SessionManager *m_sln; + ProjectList m_projectList; + DependencyModel *m_model; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // DEPENDENCIESDIALOG_H diff --git a/src/plugins/projectexplorer/dependenciesdialog.ui b/src/plugins/projectexplorer/dependenciesdialog.ui new file mode 100644 index 00000000000..25ffdff8948 --- /dev/null +++ b/src/plugins/projectexplorer/dependenciesdialog.ui @@ -0,0 +1,98 @@ +<ui version="4.0" > + <class>ProjectExplorer::Internal::DependenciesDialog</class> + <widget class="QDialog" name="ProjectExplorer::Internal::DependenciesDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>618</width> + <height>660</height> + </rect> + </property> + <property name="windowTitle" > + <string>Project Dependencies</string> + </property> + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>6</number> + </property> + <property name="leftMargin" > + <number>9</number> + </property> + <property name="topMargin" > + <number>9</number> + </property> + <property name="rightMargin" > + <number>9</number> + </property> + <property name="bottomMargin" > + <number>9</number> + </property> + <item> + <widget class="QTableView" name="dependencyTable" > + <property name="minimumSize" > + <size> + <width>600</width> + <height>600</height> + </size> + </property> + <property name="selectionMode" > + <enum>QAbstractItemView::SingleSelection</enum> + </property> + </widget> + </item> + <item> + <widget class="Line" name="line" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <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> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ProjectExplorer::Internal::DependenciesDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>142</x> + <y>285</y> + </hint> + <hint type="destinationlabel" > + <x>142</x> + <y>155</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ProjectExplorer::Internal::DependenciesDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>142</x> + <y>285</y> + </hint> + <hint type="destinationlabel" > + <x>142</x> + <y>155</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/projectexplorer/directoryproject.cpp b/src/plugins/projectexplorer/directoryproject.cpp new file mode 100644 index 00000000000..95674a5efd8 --- /dev/null +++ b/src/plugins/projectexplorer/directoryproject.cpp @@ -0,0 +1,34 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "directoryproject.h" + diff --git a/src/plugins/projectexplorer/editorconfiguration.cpp b/src/plugins/projectexplorer/editorconfiguration.cpp new file mode 100644 index 00000000000..4b61b3ea83a --- /dev/null +++ b/src/plugins/projectexplorer/editorconfiguration.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "editorconfiguration.h" + +#include <QtCore/QTextCodec> + +using namespace ProjectExplorer; + +EditorConfiguration::EditorConfiguration() + : m_defaultTextCodec(QTextCodec::codecForLocale()) +{ +} + +QTextCodec *EditorConfiguration::defaultTextCodec() const +{ + return m_defaultTextCodec; +} + +void EditorConfiguration::setDefaultTextCodec(QTextCodec *codec) +{ + m_defaultTextCodec = codec; +} + diff --git a/src/plugins/projectexplorer/editorconfiguration.h b/src/plugins/projectexplorer/editorconfiguration.h new file mode 100644 index 00000000000..830e514c911 --- /dev/null +++ b/src/plugins/projectexplorer/editorconfiguration.h @@ -0,0 +1,59 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef EDITORCONFIGURATION_H +#define EDITORCONFIGURATION_H + +#include "projectexplorer_export.h" + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE +class QTextCodec; +QT_END_NAMESPACE + +namespace ProjectExplorer { + +class PROJECTEXPLORER_EXPORT EditorConfiguration +{ +public: + EditorConfiguration(); + QTextCodec *defaultTextCodec() const; + void setDefaultTextCodec(QTextCodec *codec); + +private: + QTextCodec *m_defaultTextCodec; +}; + +} + +#endif diff --git a/src/plugins/projectexplorer/editorsettingspropertiespage.cpp b/src/plugins/projectexplorer/editorsettingspropertiespage.cpp new file mode 100644 index 00000000000..0c98878d37e --- /dev/null +++ b/src/plugins/projectexplorer/editorsettingspropertiespage.cpp @@ -0,0 +1,111 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "editorsettingspropertiespage.h" +#include "editorconfiguration.h" + +#include <QtCore/QTextCodec> + +#include <QDebug> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +bool EditorSettingsPanelFactory::supports(Project * /*project*/) +{ + return true; +} +PropertiesPanel *EditorSettingsPanelFactory::createPanel(Project *project) +{ + return new EditorSettingsPanel(project); +} + +EditorSettingsPanel::EditorSettingsPanel(Project *project) + : PropertiesPanel(), + m_widget(new EditorSettingsWidget(project)) +{ +} + +EditorSettingsPanel::~EditorSettingsPanel() +{ + delete m_widget; +} + +QString EditorSettingsPanel::name() const +{ + return tr("Editor Settings"); +} + +QWidget *EditorSettingsPanel::widget() +{ + return m_widget; +} + +EditorSettingsWidget::EditorSettingsWidget(Project *project) + : QWidget(), + m_project(project) +{ + m_ui.setupUi(this); + QTextCodec *defaultTextCodec = m_project->editorConfiguration()->defaultTextCodec(); + QList<int> mibs = QTextCodec::availableMibs(); + qSort(mibs); + QList<int> sortedMibs; + foreach (int mib, mibs) + if (mib >= 0) + sortedMibs += mib; + foreach (int mib, mibs) + if (mib < 0) + sortedMibs += mib; + int i = 0; + foreach (int mib, sortedMibs) { + QTextCodec *codec = QTextCodec::codecForMib(mib); + m_codecs += codec; + QString name = codec->name(); + foreach (QByteArray alias, codec->aliases()) + name += QLatin1String(" / ") + alias; + m_ui.encodingComboBox->addItem(name); + if (defaultTextCodec == codec) + m_ui.encodingComboBox->setCurrentIndex(i); + i++; + } + + connect(m_ui.encodingComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(currentEncodingChanged(int))); +} + +void EditorSettingsWidget::currentEncodingChanged(int index) +{ + QList<int> codecs = QTextCodec::availableMibs(); + m_project->editorConfiguration()->setDefaultTextCodec(m_codecs.at(index)); +} + + diff --git a/src/plugins/projectexplorer/editorsettingspropertiespage.h b/src/plugins/projectexplorer/editorsettingspropertiespage.h new file mode 100644 index 00000000000..8d2e057ab25 --- /dev/null +++ b/src/plugins/projectexplorer/editorsettingspropertiespage.h @@ -0,0 +1,84 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef EDITORSETTINGSPROPERTIESPAGE_H +#define EDITORSETTINGSPROPERTIESPAGE_H + +#include "iprojectproperties.h" +#include "ui_editorsettingspropertiespage.h" + +namespace ProjectExplorer { + +namespace Internal { + +class EditorSettingsPanelFactory : public IPanelFactory +{ +public: + bool supports(Project *project); + PropertiesPanel *createPanel(Project *project); +}; + +class EditorSettingsWidget; + +class EditorSettingsPanel : public PropertiesPanel +{ + Q_OBJECT +public: + EditorSettingsPanel(Project *project); + ~EditorSettingsPanel(); + QString name() const; + QWidget *widget(); + +private: + EditorSettingsWidget *m_widget; +}; + +class EditorSettingsWidget : public QWidget +{ + Q_OBJECT +public: + EditorSettingsWidget(Project *project); + +private slots: + void currentEncodingChanged(int index); + +private: + + Ui::EditorSettingsPropertiesPage m_ui; + Project *m_project; + QList<QTextCodec *> m_codecs; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // EDITORSETTINGSPROPERTIESPAGE_H diff --git a/src/plugins/projectexplorer/editorsettingspropertiespage.ui b/src/plugins/projectexplorer/editorsettingspropertiespage.ui new file mode 100644 index 00000000000..704f5cd0b3d --- /dev/null +++ b/src/plugins/projectexplorer/editorsettingspropertiespage.ui @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ProjectExplorer::Internal::EditorSettingsPropertiesPage</class> + <widget class="QWidget" name="ProjectExplorer::Internal::EditorSettingsPropertiesPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="encodingLabel"> + <property name="text"> + <string>Default File Encoding:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="encodingComboBox"/> + </item> + <item row="0" column="2"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>232</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>249</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/projectexplorer/environment.cpp b/src/plugins/projectexplorer/environment.cpp new file mode 100644 index 00000000000..9233b2b9a1a --- /dev/null +++ b/src/plugins/projectexplorer/environment.cpp @@ -0,0 +1,346 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "environment.h" + +#include <QtCore/QProcess> +#include <QtCore/QDir> +#include <QtCore/QString> +#include <QtCore/QDebug> + +using namespace ProjectExplorer; + +QList<EnvironmentItem> EnvironmentItem::fromStringList(QStringList list) +{ + QList<EnvironmentItem> result; + foreach (const QString &string, list) { + int pos = string.indexOf(QLatin1Char('=')); + if(pos == -1) { + EnvironmentItem item(string, ""); + item.unset = true; + result.append(item); + } else { + EnvironmentItem item(string.left(pos), string.mid(pos+1)); + result.append(item); + } + } + return result; +} + +QStringList EnvironmentItem::toStringList(QList<EnvironmentItem> list) +{ + QStringList result; + foreach (const EnvironmentItem &item, list) { + if(item.unset) + result << QString(item.name); + else + result << QString(item.name + '=' + item.value); + } + return result; +} + +Environment::Environment() +{ + +} + +Environment::Environment(QStringList env) +{ + foreach(QString s, env) { + int i = s.indexOf("="); + if (i >=0 ) { +#ifdef Q_OS_WIN + m_values.insert(s.left(i).toUpper(), s.mid(i+1)); +#else + m_values.insert(s.left(i), s.mid(i+1)); +#endif + } + } +} + +QStringList Environment::toStringList() +{ + QStringList result; + QMap<QString, QString>::const_iterator it, end; + end = m_values.constEnd(); + for (it = m_values.constBegin(); it != end; ++it) + result<<(it.key() + "=" + it.value()); + + return result; +} + +void Environment::set(const QString &key, const QString &value) +{ +#ifdef Q_OS_WIN + QString _key = key.toUpper(); +#else + const QString &_key = key; +#endif + m_values.insert(_key, value); +} + +void Environment::unset(const QString &key) +{ +#ifdef Q_OS_WIN + QString _key = key.toUpper(); +#else + const QString &_key = key; +#endif + m_values.remove(_key); +} + +void Environment::appendOrSet(const QString &key, const QString &value, const QString &sep) +{ +#ifdef Q_OS_WIN + QString _key = key.toUpper(); +#else + const QString &_key = key; +#endif + QMap<QString, QString>::const_iterator it = m_values.constFind(key); + if (it == m_values.constEnd()) { + m_values.insert(_key, value); + } else { + QString tmp = *it + sep + value; + m_values.insert(_key, tmp); + } + +} + +void Environment::prependOrSet(const QString&key, const QString &value, const QString &sep) +{ +#ifdef Q_OS_WIN + QString _key = key.toUpper(); +#else + const QString &_key = key; +#endif + QMap<QString, QString>::const_iterator it = m_values.constFind(key); + if (it == m_values.constEnd()) { + m_values.insert(_key, value); + } else { + QString tmp = value + sep + *it; + m_values.insert(_key, tmp); + } +} + +void Environment::appendOrSetPath(const QString &value) +{ +#ifdef Q_OS_WIN + QString sep = ";"; +#else + QString sep = ":"; +#endif + appendOrSet("PATH", QDir::toNativeSeparators(value), sep); +} + +void Environment::prependOrSetPath(const QString &value) +{ +#ifdef Q_OS_WIN + QString sep = ";"; +#else + QString sep = ":"; +#endif + prependOrSet("PATH", QDir::toNativeSeparators(value), sep); +} + +Environment Environment::systemEnvironment() +{ + return Environment(QProcess::systemEnvironment()); +} + +void Environment::clear() +{ + m_values.clear(); +} + +// currently it returns the string that was passed in, except +// under windows and if the executable does not end in .exe +// then it returns executable appended with .exe +// that is clearly wrong +QString Environment::searchInPath(QString executable) +{ +// qDebug()<<"looking for "<<executable<< "in PATH: "<<m_values.value("PATH"); + if (executable.isEmpty()) + return QString::null; +#ifdef Q_OS_WIN + if (!executable.endsWith(QLatin1String(".exe"))) + executable.append(QLatin1String(".exe")); +#endif + const QChar slash = QLatin1Char('/'); + foreach(const QString &p, path()) { +// qDebug()<<"trying"<<path + '/' + executable; + QString fp = p; + fp += slash; + fp += executable; + const QFileInfo fi(fp); + if(fi.exists()) { +// qDebug()<<"returning "<<fi.absoluteFilePath(); + return fi.absoluteFilePath(); + } + } + return QString::null; +} + +QStringList Environment::path() const +{ +#ifdef Q_OS_WIN + QString sep = ";"; +#else + QString sep = ":"; +#endif + return m_values.value("PATH").split(sep); +} + +QString Environment::value(const QString &key) const +{ + return m_values.value(key); +} + +QString Environment::key(Environment::const_iterator it) const +{ + return it.key(); +} + +QString Environment::value(Environment::const_iterator it) const +{ + return it.value(); +} + +Environment::const_iterator Environment::constBegin() const +{ + return m_values.constBegin(); +} + +Environment::const_iterator Environment::constEnd() const +{ + return m_values.constEnd(); +} + +Environment::const_iterator Environment::find(const QString &name) +{ + QMap<QString, QString>::const_iterator it = m_values.constFind(name); + if(it == m_values.constEnd()) + return constEnd(); + else + return it; +} + +int Environment::size() const +{ + return m_values.size(); +} + +void Environment::modify(const QList<EnvironmentItem> & list) +{ + Environment resultEnvironment = *this; + foreach (const EnvironmentItem &item, list) { + if(item.unset) { + resultEnvironment.unset(item.name); + } else { + // TODO use variable expansion + QString value = item.value; + for(int i=0; i < value.size(); ++i) { + if(value.at(i) == QLatin1Char('$')) { + if((i + 1) < value.size()) { + const QChar &c = value.at(i+1); + int end = -1; + if (c == '(') + end = value.indexOf(')', i); + else if (c=='{') + end = value.indexOf('}', i); + if(end != -1) { + const QString &name = value.mid(i+2, end-i-2); + Environment::const_iterator it = find(name); + if(it != constEnd()) + value.replace(i, end-i+1, it.value()); + } + } + } + } + resultEnvironment.set(item.name, value); + } + } + *this = resultEnvironment; +} + +QStringList Environment::parseCombinedArgString(const QString &program) +{ + QStringList args; + QString tmp; + int quoteCount = 0; + bool inQuote = false; + + // handle quoting. tokens can be surrounded by double quotes + // "hello world". three consecutive double quotes represent + // the quote character itself. + for (int i = 0; i < program.size(); ++i) { + if (program.at(i) == QLatin1Char('"')) { + ++quoteCount; + if (quoteCount == 3) { + // third consecutive quote + quoteCount = 0; + tmp += program.at(i); + } + continue; + } + if (quoteCount) { + if (quoteCount == 1) + inQuote = !inQuote; + quoteCount = 0; + } + if (!inQuote && program.at(i).isSpace()) { + if (!tmp.isEmpty()) { + args += tmp; + tmp.clear(); + } + } else { + tmp += program.at(i); + } + } + if (!tmp.isEmpty()) + args += tmp; + return args; +} + +QString Environment::joinArgumentList(const QStringList &arguments) +{ + QString result; + foreach(QString arg, arguments) { + if (!result.isEmpty()) + result += QLatin1Char(' '); + arg.replace(QLatin1String("\""), QLatin1String("\"\"\"")); + if (arg.contains(QLatin1Char(' '))) + arg = "\"" + arg + "\""; + result += arg; + } + return result; +} + diff --git a/src/plugins/projectexplorer/environment.h b/src/plugins/projectexplorer/environment.h new file mode 100644 index 00000000000..0d8c8d4379c --- /dev/null +++ b/src/plugins/projectexplorer/environment.h @@ -0,0 +1,101 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ENVIRONMENT +#define ENVIRONMENT + +#include "projectexplorer_export.h" + +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QMap> +#include <QtCore/QList> + +namespace ProjectExplorer { + +struct PROJECTEXPLORER_EXPORT EnvironmentItem +{ + EnvironmentItem(QString n, QString v) + : name(n), value(v), unset(false) + {} + + QString name; + QString value; + bool unset; + + static QList<EnvironmentItem> fromStringList(QStringList list); + static QStringList toStringList(QList<EnvironmentItem> list); +}; + +class PROJECTEXPLORER_EXPORT Environment { +public: + typedef QMap<QString, QString>::const_iterator const_iterator; + + Environment(); + explicit Environment(QStringList env); + static Environment systemEnvironment(); + + QStringList toStringList(); + QString value(const QString &key) const; + void set(const QString &key, const QString &value); + void unset(const QString &key); + void modify(const QList<EnvironmentItem> & list); + + void appendOrSet(const QString &key, const QString &value, const QString &sep = ""); + void prependOrSet(const QString &key, const QString &value, const QString &sep = ""); + + void appendOrSetPath(const QString &value); + void prependOrSetPath(const QString &value); + + void clear(); + int size() const; + + Environment::const_iterator find(const QString &name); + QString key(Environment::const_iterator it) const; + QString value(Environment::const_iterator it) const; + + Environment::const_iterator constBegin() const; + Environment::const_iterator constEnd() const; + + QString searchInPath(QString executable); + QStringList path() const; + + static QStringList parseCombinedArgString(const QString &program); + static QString joinArgumentList(const QStringList &arguments); + +private: + QMap<QString, QString> m_values; +}; + +} + +#endif diff --git a/src/plugins/projectexplorer/environmenteditmodel.cpp b/src/plugins/projectexplorer/environmenteditmodel.cpp new file mode 100644 index 00000000000..eb7f42ebb7f --- /dev/null +++ b/src/plugins/projectexplorer/environmenteditmodel.cpp @@ -0,0 +1,421 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "environmenteditmodel.h" + +using namespace ProjectExplorer; + +EnvironmentModel::EnvironmentModel() + : m_mergedEnvironments(false) +{} +EnvironmentModel::~EnvironmentModel() {} + +QString EnvironmentModel::indexToVariable(const QModelIndex &index) const +{ + if(m_mergedEnvironments) + return m_resultEnvironment.key(m_resultEnvironment.constBegin() + index.row()); + else + return m_items.at(index.row()).name; +} + +void EnvironmentModel::updateResultEnvironment() +{ + m_resultEnvironment = m_baseEnvironment; + m_resultEnvironment.modify(m_items); + foreach (const EnvironmentItem &item, m_items) { + if(item.unset) { + m_resultEnvironment.set(item.name, "<UNSET>"); + } + } +} + +void EnvironmentModel::setBaseEnvironment(const ProjectExplorer::Environment &env) +{ + m_baseEnvironment = env; + updateResultEnvironment(); + reset(); +} + +void EnvironmentModel::setMergedEnvironments(bool b) +{ + if(m_mergedEnvironments == b) + return; + m_mergedEnvironments = b; + if(b) + updateResultEnvironment(); + reset(); +} + +bool EnvironmentModel::mergedEnvironments() +{ + return m_mergedEnvironments; +} + +int EnvironmentModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return m_mergedEnvironments ? m_resultEnvironment.size() : m_items.count(); +} +int EnvironmentModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 2; +} + +bool EnvironmentModel::changes(const QString &name) const +{ + foreach(const EnvironmentItem& item, m_items) { + if(item.name == name) { + return true; + } + } + return false; +} + +QVariant EnvironmentModel::data(const QModelIndex &index, int role) const +{ + if((role == Qt::DisplayRole || role == Qt::EditRole) && index.isValid()) { + if((m_mergedEnvironments && index.row() >= m_resultEnvironment.size()) || + (!m_mergedEnvironments && index.row() >= m_items.count())) { + return QVariant(); + } + + if(index.column() == 0) { + if(m_mergedEnvironments) { + return m_resultEnvironment.key(m_resultEnvironment.constBegin() + index.row()); + } else { + return m_items.at(index.row()).name; + } + } else if(index.column() == 1) { + if(m_mergedEnvironments) { + if(role == Qt::EditRole) { + int pos = findInChanges(indexToVariable(index)); + if(pos != -1) + return m_items.at(pos).value; + } + return m_resultEnvironment.value(m_resultEnvironment.constBegin() + index.row()); + } else { + if(m_items.at(index.row()).unset) + return "<UNSET>"; + else + return m_items.at(index.row()).value; + } + } + } + if(role == Qt::FontRole) { + if(m_mergedEnvironments) { + // check wheter this environment variable exists in m_items + if(changes(m_resultEnvironment.key(m_resultEnvironment.constBegin() + index.row()))) { + QFont f; + f.setBold(true); + return QVariant(f); + } + } + return QFont(); + } + return QVariant(); +} + + +Qt::ItemFlags EnvironmentModel::flags(const QModelIndex &index) const +{ + Q_UNUSED(index); + return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; +} + +bool EnvironmentModel::hasChildren(const QModelIndex &index) const +{ + if(!index.isValid()) + return true; + else + return false; +} + +QVariant EnvironmentModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Vertical || role != Qt::DisplayRole) + return QVariant(); + return section == 0 ? tr("Variable") : tr("Value"); +} + +QModelIndex EnvironmentModel::index(int row, int column, const QModelIndex &parent) const +{ + if(!parent.isValid()) + return createIndex(row, column, 0); + return QModelIndex(); +} + +QModelIndex EnvironmentModel::parent(const QModelIndex &index) const +{ + Q_UNUSED(index); + return QModelIndex(); +} + +/// ***************** +/// Utility functions +/// ***************** +int EnvironmentModel::findInChanges(const QString &name) const +{ + for(int i=0; i<m_items.size(); ++i) + if(m_items.at(i).name == name) + return i; + return -1; +} + +int EnvironmentModel::findInChangesInsertPosition(const QString &name) const +{ + for(int i=0; i<m_items.size(); ++i) + if(m_items.at(i).name > name) + return i; + return m_items.size(); +} + +int EnvironmentModel::findInResult(const QString &name) const +{ + Environment::const_iterator it; + int i = 0; + for(it = m_resultEnvironment.constBegin(); it != m_resultEnvironment.constEnd(); ++it, ++i) + if(m_resultEnvironment.key(it) == name) + return i; + return -1; +} + +int EnvironmentModel::findInResultInsertPosition(const QString &name) const +{ + Environment::const_iterator it; + int i = 0; + for(it = m_resultEnvironment.constBegin(); it != m_resultEnvironment.constEnd(); ++it, ++i) + if(m_resultEnvironment.key(it) > name) + return i; + return m_resultEnvironment.size(); +} + +bool EnvironmentModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if(role == Qt::EditRole && index.isValid()) { + if(index.column() == 0) { + //fail if a variable with the same name already exists +#ifdef Q_OS_WIN + if(findInChanges(value.toString().toUpper()) != -1) + return false; +#else + if(findInChanges(value.toString()) != -1) + return false; +#endif + EnvironmentItem old("", ""); + if(m_mergedEnvironments) { + int pos = findInChanges(indexToVariable(index)); + if(pos != -1) { + old = m_items.at(pos); + } else { + old.name = m_resultEnvironment.key(m_resultEnvironment.constBegin() + index.row()); + old.value = m_resultEnvironment.value(m_resultEnvironment.constBegin() + index.row()); + old.unset = false; + } + } else { + old = m_items.at(index.row()); + } +#ifdef Q_OS_WIN + const QString &newName = value.toString().toUpper(); +#else + const QString &newName = value.toString(); +#endif + if(changes(old.name)) + removeVariable(old.name); + old.name = newName; + addVariable(old); + return true; + } else if(index.column() == 1) { + if(m_mergedEnvironments) { + const QString &name = indexToVariable(index); + int pos = findInChanges(name); + if(pos != -1) { + m_items[pos].value = value.toString(); + m_items[pos].unset = false; + updateResultEnvironment(); + emit dataChanged(index, index); + emit userChangesUpdated(); + return true; + } + // not found in m_items, so add it as a new variable + addVariable(EnvironmentItem(name, value.toString())); + return true; + } else { + m_items[index.row()].value = value.toString(); + m_items[index.row()].unset = false; + emit dataChanged(index, index); + emit userChangesUpdated(); + return true; + } + } + } + return false; +} + +QModelIndex EnvironmentModel::addVariable() +{ + const QString &name = "<VARIABLE>"; + if(m_mergedEnvironments) { + int i = findInResult(name); + if(i != -1) + return index(i, 0, QModelIndex()); + } else { + int i = findInChanges(name); + if(i != -1) + return index(i, 0, QModelIndex()); + } + // Don't exist, really add them + return addVariable(EnvironmentItem(name, "<VALUE>")); +} + +QModelIndex EnvironmentModel::addVariable(const EnvironmentItem &item) +{ + if(m_mergedEnvironments) { + bool existsInBaseEnvironment = (m_baseEnvironment.find(item.name) != m_baseEnvironment.constEnd()); + int rowInResult; + if(existsInBaseEnvironment) + rowInResult = findInResult(item.name); + else + rowInResult = findInResultInsertPosition(item.name); + int rowInChanges = findInChangesInsertPosition(item.name); + + qDebug()<<"addVariable "<<item.name<<existsInBaseEnvironment<<rowInResult<<rowInChanges; + + if(existsInBaseEnvironment) { + m_items.insert(rowInChanges, item); + updateResultEnvironment(); + emit dataChanged(index(rowInResult, 0, QModelIndex()), index(rowInResult, 1, QModelIndex())); + emit userChangesUpdated(); + return index(rowInResult, 0, QModelIndex()); + } else { + beginInsertRows(QModelIndex(), rowInResult, rowInResult); + m_items.insert(rowInChanges, item); + updateResultEnvironment(); + endInsertRows(); + qDebug()<<"returning index: "<<rowInResult; + emit userChangesUpdated(); + return index(rowInResult, 0, QModelIndex()); + } + } else { + int newPos = findInChangesInsertPosition(item.name); + beginInsertRows(QModelIndex(), newPos, newPos); + m_items.insert(newPos, item); + endInsertRows(); + emit userChangesUpdated(); + return index(newPos, 0, QModelIndex()); + } +} + +void EnvironmentModel::removeVariable(const QString &name) +{ + if(m_mergedEnvironments) { + int rowInResult = findInResult(name); + int rowInChanges = findInChanges(name); + bool existsInBaseEnvironment = m_baseEnvironment.find(name) != m_baseEnvironment.constEnd(); + if(existsInBaseEnvironment) { + m_items.removeAt(rowInChanges); + updateResultEnvironment(); + emit dataChanged(index(rowInResult, 0, QModelIndex()), index(rowInResult, 1, QModelIndex())); + emit userChangesUpdated(); + } else { + beginRemoveRows(QModelIndex(), rowInResult, rowInResult); + m_items.removeAt(rowInChanges); + updateResultEnvironment(); + endRemoveRows(); + emit userChangesUpdated(); + } + } else { + int removePos = findInChanges(name); + beginRemoveRows(QModelIndex(), removePos, removePos); + m_items.removeAt(removePos); + updateResultEnvironment(); + endRemoveRows(); + emit userChangesUpdated(); + } +} + +void EnvironmentModel::unset(const QString &name) +{ + if(m_mergedEnvironments) { + int row = findInResult(name); + // look in m_items for the variable + int pos = findInChanges(name); + if(pos != -1) { + m_items[pos].unset = true; + updateResultEnvironment(); + emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex())); + emit userChangesUpdated(); + return; + } + pos = findInChangesInsertPosition(name); + m_items.insert(pos, EnvironmentItem(name, "")); + m_items[pos].unset = true; + updateResultEnvironment(); + emit dataChanged(index(row, 0, QModelIndex()), index(row, 1, QModelIndex())); + emit userChangesUpdated(); + return; + } else { + int pos = findInChanges(name); + m_items[pos].unset = true; + emit dataChanged(index(pos, 1, QModelIndex()), index(pos, 1, QModelIndex())); + emit userChangesUpdated(); + return; + } +} + +bool EnvironmentModel::isUnset(const QString &name) +{ + int pos = findInChanges(name); + if(pos != -1) + return m_items.at(pos).unset; + else + return false; +} + +bool EnvironmentModel::isInBaseEnvironment(const QString &name) +{ + return m_baseEnvironment.find(name) != m_baseEnvironment.constEnd(); +} + +QList<EnvironmentItem> EnvironmentModel::userChanges() const +{ + return m_items; +} + +void EnvironmentModel::setUserChanges(QList<EnvironmentItem> list) +{ + m_items = list; + updateResultEnvironment(); + emit reset(); +} diff --git a/src/plugins/projectexplorer/environmenteditmodel.h b/src/plugins/projectexplorer/environmenteditmodel.h new file mode 100644 index 00000000000..35762dd0d2a --- /dev/null +++ b/src/plugins/projectexplorer/environmenteditmodel.h @@ -0,0 +1,92 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ENVIRONMENTEDITMODEL_H +#define ENVIRONMENTEDITMODEL_H + +#include "environment.h" + +#include <QtCore/QString> +#include <QtCore/QAbstractItemModel> +#include <QtCore/QDebug> +#include <QtGui/QFont> + +namespace ProjectExplorer +{ + +class PROJECTEXPLORER_EXPORT EnvironmentModel : public QAbstractItemModel +{ + Q_OBJECT +public: + EnvironmentModel(); + ~EnvironmentModel(); + void setBaseEnvironment(const ProjectExplorer::Environment &env); + void setMergedEnvironments(bool b); + bool mergedEnvironments(); + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + Qt::ItemFlags flags(const QModelIndex &index) const; + bool hasChildren(const QModelIndex &index) const; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + QModelIndex addVariable(); + QModelIndex addVariable(const EnvironmentItem& item); + void removeVariable(const QString &name); + void unset(const QString &name); + bool isUnset(const QString &name); + bool isInBaseEnvironment(const QString &name); + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QString indexToVariable(const QModelIndex &index) const; + bool changes(const QString &key) const; + + QList<EnvironmentItem> userChanges() const; + void setUserChanges(QList<EnvironmentItem> list); +signals: + void userChangesUpdated(); +private: + void updateResultEnvironment(); + int findInChanges(const QString &name) const; + int findInResult(const QString &name) const; + int findInChangesInsertPosition(const QString &name) const; + int findInResultInsertPosition(const QString &name) const; + + ProjectExplorer::Environment m_baseEnvironment; + ProjectExplorer::Environment m_resultEnvironment; + QList<EnvironmentItem> m_items; + bool m_mergedEnvironments; +}; + +} +#endif // ENVIRONMENTEDITMODEL_H diff --git a/src/plugins/projectexplorer/foldernavigationwidget.cpp b/src/plugins/projectexplorer/foldernavigationwidget.cpp new file mode 100644 index 00000000000..8066c5899d0 --- /dev/null +++ b/src/plugins/projectexplorer/foldernavigationwidget.cpp @@ -0,0 +1,217 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "foldernavigationwidget.h" +#include "projectexplorer.h" +#include "projectexplorerconstants.h" + +#include <coreplugin/icore.h> +#include <coreplugin/filemanager.h> +#include <coreplugin/editormanager/editormanager.h> + +#include <utils/pathchooser.h> + +#include <QtCore/QDebug> +#include <QtGui/QVBoxLayout> +#include <QtGui/QToolButton> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +namespace { +bool debug = false; +} + +namespace ProjectExplorer { + namespace Internal { + class FirstRowFilter : public QSortFilterProxyModel { + Q_OBJECT + public: + FirstRowFilter(QObject *parent = 0) : QSortFilterProxyModel(parent) {} + protected: + bool filterAcceptsRow (int source_row, const QModelIndex & ) const { + return source_row != 0; + } + }; + } +} + +/*! + /class FolderNavigationWidget + + Shows a file system folder + */ +FolderNavigationWidget::FolderNavigationWidget(Core::ICore *core, QWidget *parent) + : QWidget(parent), + m_core(core), + m_explorer(ProjectExplorerPlugin::instance()), + m_view(new QListView(this)), + m_dirModel(new QDirModel(this)), + m_filter(new FirstRowFilter(this)), + m_title(new QLabel(this)), + m_autoSync(false) +{ + m_dirModel->setFilter(QDir::Dirs | QDir::Files | QDir::Drives | QDir::Readable | QDir::Writable + | QDir::Executable | QDir::Hidden); + m_dirModel->setSorting(QDir::Name | QDir::DirsFirst); + m_filter->setSourceModel(m_dirModel); + m_view->setModel(m_filter); + m_view->setFrameStyle(QFrame::NoFrame); + setFocusProxy(m_view); + + QVBoxLayout *layout = new QVBoxLayout(); + layout->addWidget(m_title); + layout->addWidget(m_view); + m_title->setMargin(5); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); + + // connections + connect(m_view, SIGNAL(activated(const QModelIndex&)), + this, SLOT(openItem(const QModelIndex&))); + + setAutoSynchronization(true); +} + +void FolderNavigationWidget::toggleAutoSynchronization() +{ + setAutoSynchronization(!m_autoSync); +} + +bool FolderNavigationWidget::autoSynchronization() const +{ + return m_autoSync; +} + +void FolderNavigationWidget::setAutoSynchronization(bool sync) +{ + if (sync == m_autoSync) + return; + + m_autoSync = sync; + + Core::FileManager *fileManager = m_core->fileManager(); + if (m_autoSync) { + connect(fileManager, SIGNAL(currentFileChanged(const QString&)), + this, SLOT(setCurrentFile(const QString&))); + setCurrentFile(fileManager->currentFile()); + } else { + disconnect(fileManager, SIGNAL(currentFileChanged(const QString&)), + this, SLOT(setCurrentFile(const QString&))); + } +} + +void FolderNavigationWidget::setCurrentFile(const QString &filePath) +{ + if (debug) + qDebug() << "FolderNavigationWidget::setCurrentFile(" << filePath << ")"; + + QString dir = QFileInfo(filePath).path(); + if (dir.isEmpty()) + dir = Core::Utils::PathChooser::homePath(); + + QModelIndex dirIndex = m_dirModel->index(dir); + QModelIndex fileIndex = m_dirModel->index(filePath); + + m_view->setRootIndex(m_filter->mapFromSource(dirIndex)); + if (dirIndex.isValid()) { + setCurrentTitle(QDir(m_dirModel->filePath(dirIndex))); + if (fileIndex.isValid()) { + QItemSelectionModel *selections = m_view->selectionModel(); + QModelIndex mainIndex = m_filter->mapFromSource(fileIndex); + selections->setCurrentIndex(mainIndex, QItemSelectionModel::SelectCurrent + | QItemSelectionModel::Clear); + m_view->scrollTo(mainIndex); + } + } else { + setCurrentTitle(QDir()); + } +} + +void FolderNavigationWidget::openItem(const QModelIndex &index) +{ + if (index.isValid()) { + const QModelIndex srcIndex = m_filter->mapToSource(index); + if (m_dirModel->isDir(srcIndex)) { + m_view->setRootIndex(index); + setCurrentTitle(QDir(m_dirModel->filePath(srcIndex))); + } else { + const QString filePath = m_dirModel->filePath(srcIndex); + m_core->editorManager()->openEditor(filePath); + m_core->editorManager()->ensureEditorManagerVisible(); + } + } +} + +void FolderNavigationWidget::setCurrentTitle(const QDir &dir) +{ + m_title->setText(dir.dirName()); + m_title->setToolTip(dir.absolutePath()); +} + +FolderNavigationWidgetFactory::FolderNavigationWidgetFactory(Core::ICore *core) + : m_core(core) +{ +} + +FolderNavigationWidgetFactory::~FolderNavigationWidgetFactory() +{ +} + +QString FolderNavigationWidgetFactory::displayName() +{ + return tr("File System"); +} + +QKeySequence FolderNavigationWidgetFactory::activationSequence() +{ + return QKeySequence(Qt::ALT + Qt::Key_Y); +} + +Core::NavigationView FolderNavigationWidgetFactory::createWidget() +{ + Core::NavigationView n; + FolderNavigationWidget *ptw = new FolderNavigationWidget(m_core); + n.widget = ptw; + QToolButton *toggleSync = new QToolButton; + toggleSync->setProperty("type", "dockbutton"); + toggleSync->setIcon(QIcon(":/qworkbench/images/linkicon.png")); + toggleSync->setCheckable(true); + toggleSync->setChecked(ptw->autoSynchronization()); + toggleSync->setToolTip(tr("Synchronize with Editor")); + connect(toggleSync, SIGNAL(clicked(bool)), ptw, SLOT(toggleAutoSynchronization())); + n.doockToolBarWidgets << toggleSync; + return n; +} + +#include "foldernavigationwidget.moc" diff --git a/src/plugins/projectexplorer/foldernavigationwidget.h b/src/plugins/projectexplorer/foldernavigationwidget.h new file mode 100644 index 00000000000..84909776e28 --- /dev/null +++ b/src/plugins/projectexplorer/foldernavigationwidget.h @@ -0,0 +1,101 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FOLDERNAVIGATIONWIDGET_H +#define FOLDERNAVIGATIONWIDGET_H + +#include <coreplugin/inavigationwidgetfactory.h> + +#include <QtGui/QWidget> +#include <QtGui/QListView> +#include <QtGui/QDirModel> +#include <QtGui/QLabel> +#include <QtGui/QSortFilterProxyModel> + +namespace Core { +class ICore; +} + +namespace ProjectExplorer { + +class ProjectExplorerPlugin; +class Project; +class Node; + +namespace Internal { + +class FolderNavigationWidget : public QWidget { + Q_OBJECT +public: + FolderNavigationWidget(Core::ICore *core, QWidget *parent = 0); + + bool autoSynchronization() const; + void setAutoSynchronization(bool sync); + + QString currentFolder() const; + +public slots: + void toggleAutoSynchronization(); + +private slots: + void openItem(const QModelIndex &mainIndex); + void setCurrentFile(const QString &filePath); + +private: + void setCurrentTitle(const QDir &directory); + + Core::ICore *m_core; + ProjectExplorerPlugin *m_explorer; + QListView *m_view; + QDirModel *m_dirModel; + QSortFilterProxyModel *m_filter; + QLabel *m_title; + bool m_autoSync; +}; + +class FolderNavigationWidgetFactory : public Core::INavigationWidgetFactory +{ +public: + FolderNavigationWidgetFactory(Core::ICore *core); + virtual ~FolderNavigationWidgetFactory(); + + virtual QString displayName(); + virtual QKeySequence activationSequence(); + virtual Core::NavigationView createWidget(); +private: + Core::ICore *m_core; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // FOLDERNAVIGATIONWIDGET_H diff --git a/src/plugins/projectexplorer/images/build.png b/src/plugins/projectexplorer/images/build.png Binary files differnew file mode 100644 index 00000000000..4b79d9d4706 --- /dev/null +++ b/src/plugins/projectexplorer/images/build.png diff --git a/src/plugins/projectexplorer/images/build_small.png b/src/plugins/projectexplorer/images/build_small.png Binary files differnew file mode 100644 index 00000000000..e5a0e8a4e7f --- /dev/null +++ b/src/plugins/projectexplorer/images/build_small.png diff --git a/src/plugins/projectexplorer/images/clean.png b/src/plugins/projectexplorer/images/clean.png Binary files differnew file mode 100644 index 00000000000..ab5e07ef797 --- /dev/null +++ b/src/plugins/projectexplorer/images/clean.png diff --git a/src/plugins/projectexplorer/images/clean_small.png b/src/plugins/projectexplorer/images/clean_small.png Binary files differnew file mode 100644 index 00000000000..6a6d16718ce --- /dev/null +++ b/src/plugins/projectexplorer/images/clean_small.png diff --git a/src/plugins/projectexplorer/images/closetab.png b/src/plugins/projectexplorer/images/closetab.png Binary files differnew file mode 100644 index 00000000000..ef9e02086c6 --- /dev/null +++ b/src/plugins/projectexplorer/images/closetab.png diff --git a/src/plugins/projectexplorer/images/compile_error.png b/src/plugins/projectexplorer/images/compile_error.png Binary files differnew file mode 100644 index 00000000000..162072e58d8 --- /dev/null +++ b/src/plugins/projectexplorer/images/compile_error.png diff --git a/src/plugins/projectexplorer/images/compile_unspecified.png b/src/plugins/projectexplorer/images/compile_unspecified.png Binary files differnew file mode 100644 index 00000000000..c9e8b7fb0ea --- /dev/null +++ b/src/plugins/projectexplorer/images/compile_unspecified.png diff --git a/src/plugins/projectexplorer/images/compile_warning.png b/src/plugins/projectexplorer/images/compile_warning.png Binary files differnew file mode 100644 index 00000000000..a42077a82a1 --- /dev/null +++ b/src/plugins/projectexplorer/images/compile_warning.png diff --git a/src/plugins/projectexplorer/images/debugger_start.png b/src/plugins/projectexplorer/images/debugger_start.png Binary files differnew file mode 100644 index 00000000000..36e5fc47804 --- /dev/null +++ b/src/plugins/projectexplorer/images/debugger_start.png diff --git a/src/plugins/projectexplorer/images/debugger_start_small.png b/src/plugins/projectexplorer/images/debugger_start_small.png Binary files differnew file mode 100644 index 00000000000..98e8ccd001f --- /dev/null +++ b/src/plugins/projectexplorer/images/debugger_start_small.png diff --git a/src/plugins/projectexplorer/images/filtericon.png b/src/plugins/projectexplorer/images/filtericon.png Binary files differnew file mode 100644 index 00000000000..7e46d226758 --- /dev/null +++ b/src/plugins/projectexplorer/images/filtericon.png diff --git a/src/plugins/projectexplorer/images/findallprojects.png b/src/plugins/projectexplorer/images/findallprojects.png Binary files differnew file mode 100644 index 00000000000..1847aebe0ff --- /dev/null +++ b/src/plugins/projectexplorer/images/findallprojects.png diff --git a/src/plugins/projectexplorer/images/findproject.png b/src/plugins/projectexplorer/images/findproject.png Binary files differnew file mode 100644 index 00000000000..ec7c39e8e69 --- /dev/null +++ b/src/plugins/projectexplorer/images/findproject.png diff --git a/src/plugins/projectexplorer/images/insert_line_small.png b/src/plugins/projectexplorer/images/insert_line_small.png Binary files differnew file mode 100644 index 00000000000..e80f06089f1 --- /dev/null +++ b/src/plugins/projectexplorer/images/insert_line_small.png diff --git a/src/plugins/projectexplorer/images/projectexplorer.png b/src/plugins/projectexplorer/images/projectexplorer.png Binary files differnew file mode 100644 index 00000000000..a84f2536f33 --- /dev/null +++ b/src/plugins/projectexplorer/images/projectexplorer.png diff --git a/src/plugins/projectexplorer/images/rebuild.png b/src/plugins/projectexplorer/images/rebuild.png Binary files differnew file mode 100644 index 00000000000..fe3a6504a3e --- /dev/null +++ b/src/plugins/projectexplorer/images/rebuild.png diff --git a/src/plugins/projectexplorer/images/rebuild_small.png b/src/plugins/projectexplorer/images/rebuild_small.png Binary files differnew file mode 100644 index 00000000000..3b36d527b20 --- /dev/null +++ b/src/plugins/projectexplorer/images/rebuild_small.png diff --git a/src/plugins/projectexplorer/images/run.png b/src/plugins/projectexplorer/images/run.png Binary files differnew file mode 100644 index 00000000000..046a8e3db9a --- /dev/null +++ b/src/plugins/projectexplorer/images/run.png diff --git a/src/plugins/projectexplorer/images/run_small.png b/src/plugins/projectexplorer/images/run_small.png Binary files differnew file mode 100644 index 00000000000..940af831f87 --- /dev/null +++ b/src/plugins/projectexplorer/images/run_small.png diff --git a/src/plugins/projectexplorer/images/session.png b/src/plugins/projectexplorer/images/session.png Binary files differnew file mode 100644 index 00000000000..6d526f6bee1 --- /dev/null +++ b/src/plugins/projectexplorer/images/session.png diff --git a/src/plugins/projectexplorer/images/stop.png b/src/plugins/projectexplorer/images/stop.png Binary files differnew file mode 100644 index 00000000000..5a9f49c1c61 --- /dev/null +++ b/src/plugins/projectexplorer/images/stop.png diff --git a/src/plugins/projectexplorer/iprojectmanager.h b/src/plugins/projectexplorer/iprojectmanager.h new file mode 100644 index 00000000000..869e5d2b8f4 --- /dev/null +++ b/src/plugins/projectexplorer/iprojectmanager.h @@ -0,0 +1,60 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROJECTMANAGERINTERFACE_H +#define PROJECTMANAGERINTERFACE_H + +#include "projectexplorer_export.h" +#include <QtCore/QObject> + +namespace ProjectExplorer { + +class Project; + +class PROJECTEXPLORER_EXPORT IProjectManager + : public QObject +{ + Q_OBJECT + +public: + IProjectManager() {} + + virtual int projectContext() const = 0; //TODO move into project + virtual int projectLanguage() const = 0; //TODO move into project + + virtual QString mimeType() const = 0; + virtual Project *openProject(const QString &fileName) = 0; +}; + +} // namespace ProjectExplorer + +#endif //PROJECTMANAGERINTERFACE_H diff --git a/src/plugins/projectexplorer/iprojectproperties.h b/src/plugins/projectexplorer/iprojectproperties.h new file mode 100644 index 00000000000..3a8d2e8609e --- /dev/null +++ b/src/plugins/projectexplorer/iprojectproperties.h @@ -0,0 +1,68 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IPROJECTPROPERTIES_H +#define IPROJECTPROPERTIES_H + +#include "projectexplorer_export.h" +#include "project.h" + +#include <coreplugin/icontext.h> + +#include <QtGui/QWidget> + +namespace ProjectExplorer { + +class PropertiesPanel; + +class PROJECTEXPLORER_EXPORT IPanelFactory : public QObject +{ + Q_OBJECT +public: + virtual bool supports(Project *project) = 0; + virtual PropertiesPanel *createPanel(Project *project) = 0; +}; + +class PROJECTEXPLORER_EXPORT PropertiesPanel : public Core::IContext +{ + Q_OBJECT +public: + virtual void finish() {}; + virtual QString name() const = 0; + + // IContext + virtual QList<int> context() const { return QList<int>(); } +}; + +} // namespace ProjectExplorer + +#endif // IPROJECTPROPERTIES_H diff --git a/src/plugins/projectexplorer/metatypedeclarations.h b/src/plugins/projectexplorer/metatypedeclarations.h new file mode 100644 index 00000000000..a53c4203afc --- /dev/null +++ b/src/plugins/projectexplorer/metatypedeclarations.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROJECTEXPLORERMETATYPEDECLARATIONS_H +#define PROJECTEXPLORERMETATYPEDECLARATIONS_H + +#include <QtCore/QMetaType> +#include <QtCore/QList> + +namespace QWorkbench { +class FileInterface; +} +namespace ProjectExplorer { +class ProjectInterface; +class IProjectManager; +class SessionManager; +class IApplicationOutput; +class BuildParserInterface; +class GlobalConfigManagerInterface; + +namespace Internal { +class CommandQObject; +} +} + +Q_DECLARE_METATYPE(QWorkbench::FileInterface*) +Q_DECLARE_METATYPE(QList<QWorkbench::FileInterface*>) + +Q_DECLARE_METATYPE(ProjectExplorer::Project*) +Q_DECLARE_METATYPE(QList<ProjectExplorer::Project*>) +Q_DECLARE_METATYPE(ProjectExplorer::SessionManager*) +Q_DECLARE_METATYPE(ProjectExplorer::IProjectManager*) +Q_DECLARE_METATYPE(ProjectExplorer::IApplicationOutput*) +Q_DECLARE_METATYPE(ProjectExplorer::Internal::CommandQObject*) +Q_DECLARE_METATYPE(QList<ProjectExplorer::Internal::CommandQObject*>) +Q_DECLARE_METATYPE(ProjectExplorer::BuildParserInterface*) +Q_DECLARE_METATYPE(ProjectExplorer::GlobalConfigManagerInterface*) +#endif // PROJECTEXPLORERMETATYPEDECLARATIONS_H diff --git a/src/plugins/projectexplorer/nodesvisitor.cpp b/src/plugins/projectexplorer/nodesvisitor.cpp new file mode 100644 index 00000000000..1997eda3095 --- /dev/null +++ b/src/plugins/projectexplorer/nodesvisitor.cpp @@ -0,0 +1,129 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "nodesvisitor.h" +#include "projectnodes.h" + +using namespace ProjectExplorer; + +/*! + \class NodesVisitor + + \short Base class for visitors that can be used to traverse a node hierarchy. + + The class follows the visitor pattern as described in Gamma et al. Pass + an instance of NodesVisitor to FolderNode::accept(): The visit methods + will be called for each node in the subtree, except for file nodes: + Access these through FolderNode::fileNodes() in visitProjectNode() + and visitoFolderNode(). +*/ + +/*! + \method NodesVisitor::visitSessionNode(SessionNode *) + + Called for the root session node. + + The default implementation does nothing. + */ + +/*! + \method NodesVisitor::visitProjectNode(SessionNode *) + + Called for a project node. + + The default implementation does nothing. + */ + +/*! + \method NodesVisitor::visitFolderNode(SessionNode *) + + Called for a folder node that is _not_ a SessionNode or a ProjectNode. + + The default implementation does nothing. + */ + + +/*! + \class FindNodeForFileVisitor + + Searches the first node that has the given file as it's path. + */ + +FindNodesForFileVisitor::FindNodesForFileVisitor(const QString &fileToSearch) + : m_path(fileToSearch) +{ +} + +QList<Node*> FindNodesForFileVisitor::nodes() const +{ + return m_nodes; +} + +void FindNodesForFileVisitor::visitProjectNode(ProjectNode *node) +{ + visitFolderNode(node); +} + +void FindNodesForFileVisitor::visitFolderNode(FolderNode *node) +{ + if (node->path() == m_path) { + m_nodes << node; + } + foreach (FileNode *fileNode, node->fileNodes()) { + if (fileNode->path() == m_path) { + m_nodes << fileNode; + } + } +} + +/*! + \class FindAllFilesVisitor + + Collects file information from all sub file nodes. + */ + +QStringList FindAllFilesVisitor::filePaths() const +{ + return m_filePaths; +} + +void FindAllFilesVisitor::visitProjectNode(ProjectNode *projectNode) +{ + visitFolderNode(projectNode); +} + +void FindAllFilesVisitor::visitFolderNode(FolderNode *folderNode) +{ + m_filePaths.append(folderNode->path()); + foreach (const FileNode *fileNode, folderNode->fileNodes()) + m_filePaths.append(fileNode->path()); +} diff --git a/src/plugins/projectexplorer/nodesvisitor.h b/src/plugins/projectexplorer/nodesvisitor.h new file mode 100644 index 00000000000..814ed42d9b6 --- /dev/null +++ b/src/plugins/projectexplorer/nodesvisitor.h @@ -0,0 +1,87 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef NODESVISITOR_H +#define NODESVISITOR_H + +#include "projectexplorer_export.h" + +#include <QtCore/QString> +#include <QtCore/QStringList> + +namespace ProjectExplorer { + +class Node; +class FileNode; +class SessionNode; +class ProjectNode; +class FolderNode; + +class NodesVisitor { +public: + virtual ~NodesVisitor() {} + + virtual void visitSessionNode(SessionNode *) {} + virtual void visitProjectNode(ProjectNode *) {} + virtual void visitFolderNode(FolderNode *) {} +protected: + NodesVisitor() {} +}; + +/* useful visitors */ + +class PROJECTEXPLORER_EXPORT FindNodesForFileVisitor : public NodesVisitor { +public: + FindNodesForFileVisitor(const QString &fileToSearch); + + QList<Node*> nodes() const; + + void visitProjectNode(ProjectNode *node); + void visitFolderNode(FolderNode *node); +private: + QString m_path; + QList<Node*> m_nodes; +}; + +class PROJECTEXPLORER_EXPORT FindAllFilesVisitor : public NodesVisitor { +public: + QStringList filePaths() const; + + void visitProjectNode(ProjectNode *projectNode); + void visitFolderNode(FolderNode *folderNode); +private: + QStringList m_filePaths; +}; + +} + +#endif // NODESVISITOR_H diff --git a/src/plugins/projectexplorer/outputwindow.cpp b/src/plugins/projectexplorer/outputwindow.cpp new file mode 100644 index 00000000000..891b882277f --- /dev/null +++ b/src/plugins/projectexplorer/outputwindow.cpp @@ -0,0 +1,680 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "outputwindow.h" +#include "projectexplorerconstants.h" +#include "runconfiguration.h" + +#include <find/basetextfind.h> +#include <aggregation/aggregate.h> + +#include <QtGui/QIcon> +#include <QtGui/QScrollBar> +#include <QtGui/QTextLayout> +#include <QtGui/QPainter> +#include <QtGui/QApplication> +#include <QtGui/QClipboard> +#include <QtGui/QMenu> +#include <QtGui/QMessageBox> +#include <QtGui/QVBoxLayout> +#include <QtGui/QTabWidget> + +using namespace ProjectExplorer::Internal; +using namespace ProjectExplorer; + +bool OutputPane::hasFocus() +{ + return m_tabWidget->currentWidget() && m_tabWidget->currentWidget()->hasFocus(); +} + +bool OutputPane::canFocus() +{ + return m_tabWidget->currentWidget(); +} + +void OutputPane::setFocus() +{ + if(m_tabWidget->currentWidget()) + m_tabWidget->currentWidget()->setFocus(); +} + +void OutputPane::appendOutput(const QString &/*out*/) +{ + // This function is in the interface, since we can't do anything sensible here, we don't do anything here. +} + +void OutputPane::appendOutput(RunControl *rc, const QString &out) +{ + OutputWindow *ow = m_outputWindows.value(rc); + ow->appendOutput(out); +} + +void OutputPane::showTabFor(RunControl *rc) +{ + OutputWindow *ow = m_outputWindows.value(rc); + m_tabWidget->setCurrentWidget(ow); +} + +void OutputPane::stopRunControl() +{ + RunControl *rc = runControlForTab(m_tabWidget->currentIndex()); + rc->stop(); +} + +void OutputPane::reRunRunControl() +{ + RunControl *rc = runControlForTab(m_tabWidget->currentIndex()); + if (rc->runConfiguration()->project() != 0) + rc->start(); +} + +void OutputPane::closeTab(int index) +{ + OutputWindow *ow = static_cast<OutputWindow *>(m_tabWidget->widget(index)); + RunControl *rc = m_outputWindows.key(ow); + + if (rc->isRunning()) { + QString msg = tr("The application is still running. Close it first."); + QMessageBox::critical(0, tr("Unable to close"), msg); + return; + } + + m_tabWidget->removeTab(index); + delete ow; + delete rc; +} + +OutputPane::OutputPane() + : m_mainWidget(new QWidget) +{ +// m_insertLineButton = new QToolButton; +// m_insertLineButton->setIcon(QIcon(ProjectExplorer::Constants::ICON_INSERT_LINE)); +// m_insertLineButton->setText(tr("Insert line")); +// m_insertLineButton->setToolTip(tr("Insert line")); +// m_insertLineButton->setAutoRaise(true); +// connect(m_insertLineButton, SIGNAL(clicked()), this, SLOT(insertLine())); + + QIcon runIcon(Constants::ICON_RUN); + runIcon.addFile(Constants::ICON_RUN_SMALL); + + //Rerun + m_reRunButton = new QToolButton; + m_reRunButton->setIcon(runIcon); + m_reRunButton->setToolTip(tr("Rerun this runconfiguration")); + m_reRunButton->setAutoRaise(true); + m_reRunButton->setEnabled(false); + connect(m_reRunButton, SIGNAL(clicked()), + this, SLOT(reRunRunControl())); + + //Stop + m_stopButton = new QToolButton; + m_stopButton->setIcon(QIcon(Constants::ICON_STOP)); + m_stopButton->setToolTip(tr("Stop")); + m_stopButton->setAutoRaise(true); + m_stopButton->setEnabled(false); + connect(m_stopButton, SIGNAL(clicked()), + this, SLOT(stopRunControl())); + + // Spacer (?) + + QVBoxLayout *layout = new QVBoxLayout; + layout->setMargin(0); + m_tabWidget = new QTabWidget; + m_tabWidget->setDocumentMode(true); + m_tabWidget->setTabsClosable(true); + m_tabWidget->setMovable(true); + connect(m_tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); + layout->addWidget(m_tabWidget); + + connect(m_tabWidget, SIGNAL(currentChanged(int)), + this, SLOT(tabChanged(int))); + + m_mainWidget->setLayout(layout); +} + +OutputPane::~OutputPane() +{ + while (m_tabWidget->count()) { + RunControl *rc = runControlForTab(0); + if (rc->isRunning()) + rc->stop(); + closeTab(0); + } + delete m_mainWidget; +} + +void OutputPane::projectRemoved() +{ + tabChanged(m_tabWidget->currentIndex()); +} + +void OutputPane::tabChanged(int i) +{ + if (i == -1) { + m_stopButton->setEnabled(false); + m_reRunButton->setEnabled(false); + } else { + RunControl *rc = runControlForTab(i); + m_stopButton->setEnabled(rc->isRunning()); + m_reRunButton->setEnabled(!rc->isRunning() && rc->runConfiguration()->project()); + } +} + +void OutputPane::createNewOutputWindow(RunControl *rc) +{ + connect(rc, SIGNAL(started()), + this, SLOT(runControlStarted())); + connect(rc, SIGNAL(finished()), + this, SLOT(runControlFinished())); + + // First look if we can reuse a tab + bool found = false; + for(int i=0; i<m_tabWidget->count(); ++i) { + RunControl *old = runControlForTab(i); + if (old->runConfiguration() == rc->runConfiguration() && !old->isRunning()) { + // Reuse this tab + delete old; + m_outputWindows.remove(old); + OutputWindow *ow = static_cast<OutputWindow *>(m_tabWidget->widget(i)); + ow->appendOutput("");//New line + m_outputWindows.insert(rc, ow); + found = true; + break; + } + } + if (!found) { + OutputWindow *ow = new OutputWindow(m_tabWidget); + Aggregation::Aggregate *agg = new Aggregation::Aggregate; + agg->add(ow); + agg->add(new Find::BaseTextFind(ow)); + m_outputWindows.insert(rc, ow); + m_tabWidget->addTab(ow, rc->runConfiguration()->name()); + } +} + +void OutputPane::runControlStarted() +{ + RunControl *rc = runControlForTab(m_tabWidget->currentIndex()); + if (rc == qobject_cast<RunControl *>(sender())) { + m_reRunButton->setEnabled(false); + m_stopButton->setEnabled(true); + } +} + +void OutputPane::runControlFinished() +{ + RunControl *rc = runControlForTab(m_tabWidget->currentIndex()); + if (rc == qobject_cast<RunControl *>(sender())) { + m_reRunButton->setEnabled(rc->runConfiguration()->project()); + m_stopButton->setEnabled(false); + } +} + +QWidget *OutputPane::outputWidget(QWidget *) +{ + return m_mainWidget; +} + +QList<QWidget*> OutputPane::toolBarWidgets(void) const +{ + return QList<QWidget*>() << m_reRunButton << m_stopButton + ; // << m_insertLineButton; +} + +QString OutputPane::name() const +{ + return tr("Application Output"); +} + +void OutputPane::clearContents() +{ + OutputWindow *currentWindow = qobject_cast<OutputWindow *>(m_tabWidget->currentWidget()); + if (currentWindow) + currentWindow->clear(); +} + +void OutputPane::visibilityChanged(bool /* b */) +{ + +} + +void OutputPane::insertLine() +{ + OutputWindow *currentWindow = qobject_cast<OutputWindow *>(m_tabWidget->currentWidget()); + if (currentWindow) + currentWindow->clear(); +} + +RunControl* OutputPane::runControlForTab(int index) const +{ + return m_outputWindows.key(qobject_cast<OutputWindow *>(m_tabWidget->widget(index))); +} + +int OutputPane::priorityInStatusBar() const +{ + return 60; +} + + +/*******************/ + +OutputWindow::OutputWindow(QWidget *parent) + : QPlainTextEdit(parent) +{ + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + //setCenterOnScroll(false); + //setMaximumBlockCount(10000); + setWindowTitle(tr("Application Output Window")); + setWindowIcon(QIcon(":/qt4projectmanager/images/window.png")); + setFrameShape(QFrame::NoFrame); +} + + +OutputWindow::~OutputWindow() +{ +} + +void OutputWindow::appendOutput(const QString &out) +{ + appendPlainText(out); +} + +void OutputWindow::insertLine() +{ + appendPlainText(QString()); +} + +#if 0 +OutputWindow::OutputWindow(QWidget *parent) + : QAbstractScrollArea(parent) +{ + max_lines = 1000; + width_used = 0; + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + same_height = true; + block_scroll = false; + setWindowTitle(tr("Application Output Window")); + setWindowIcon(QIcon(":/qt4projectmanager/images/window.png")); +} + +void OutputWindow::changed() { + int remove = lines.size() - max_lines; + if (remove > 0) { + selection_start.line -= remove; + selection_end.line -= remove; + selection_start = qMax(selection_start, Selection()); + selection_end = qMax(selection_end, Selection()); + if (remove > verticalScrollBar()->value()) { + if (same_height) + viewport()->scroll(0, -remove * fontMetrics().lineSpacing()); + else + viewport()->update(); + } else { + block_scroll = true; + verticalScrollBar()->setValue(verticalScrollBar()->value() - remove); + block_scroll = false; + } + while (remove--) + lines.removeFirst(); + } + + verticalScrollBar()->setRange(0, lines.size() - 1); + +} + + +bool OutputWindow::getCursorPos(int *lineNumber, int *position, const QPoint &pos) { + if (lines.isEmpty()) + return false; + *lineNumber = verticalScrollBar()->value(); + + int x = 4 - horizontalScrollBar()->value(); + + int spacing = fontMetrics().lineSpacing(); + int leading = fontMetrics().leading(); + int height = 0; + + QTextLayout textLayout; + textLayout.setFont(font()); + + if (same_height && pos.y() > 0) { + int skipLines = pos.y() / spacing; + height += skipLines * spacing; + *lineNumber = qMin(*lineNumber + skipLines, lines.size() - 1); + } + + same_height = true; + + while ( *lineNumber < lines.size()) { + textLayout.setText(lines.at(*lineNumber)); + + textLayout.beginLayout(); + while (1) { + QTextLine line = textLayout.createLine(); + if (!line.isValid()) + break; + line.setLineWidth(INT_MAX/256); + height += leading; + line.setPosition(QPoint(x, height)); + height += static_cast<int>(line.height()); + } + textLayout.endLayout(); + if (height > pos.y()) { + *position = textLayout.lineAt(0).xToCursor(pos.x()); + break; + } + ++*lineNumber; + } + return true; +} + +void OutputWindow::setNumberOfLines(int max) +{ + max_lines = qMax(1, max); + while (lines.size() > max_lines) + lines.removeLast(); + changed(); +} + +int OutputWindow::numberOfLines() const +{ + return max_lines; +} + +bool OutputWindow::hasSelectedText() const +{ + return selection_start != selection_end; +} + +void OutputWindow::clearSelection() +{ + bool hadSelectedText = hasSelectedText(); + selection_start = selection_end = Selection(); + if (hadSelectedText) + viewport()->update(); +} + +QString OutputWindow::selectedText() const +{ + Selection sel_start = qMin(selection_start, selection_end); + Selection sel_end = qMax(selection_start, selection_end); + QString text; + + if (sel_start.line == sel_end.line) { + text += lines.at(sel_start.line).mid(sel_start.pos, sel_end.pos - sel_start.pos); + } else { + int line = sel_start.line; + text += lines.at(line++).mid(sel_start.pos); + text += QLatin1Char('\n'); + while (line < sel_end.line) { + text += lines.at(line++); + text += QLatin1Char('\n'); + } + text += lines.at(sel_end.line).left(sel_end.pos); + } + return text; +} + +void OutputWindow::appendOutput(const QString &text) +{ + lines.append(text); + if (same_height) + viewport()->update( + QRect(0, (lines.size() - verticalScrollBar()->value() - 1) * fontMetrics().lineSpacing(), + viewport()->width(), viewport()->height())); + else + viewport()->update(); + + changed(); + int top = lines.size() - (viewport()->height() / fontMetrics().lineSpacing()); + if (verticalScrollBar()->value() == top - 1) + verticalScrollBar()->setValue(top); +} + +void OutputWindow::clear() +{ + clearSelection(); + lines.clear(); + viewport()->update(); +} + +void OutputWindow::copy() +{ + if (hasSelectedText()) + QApplication::clipboard()->setText(selectedText()); +} + +void OutputWindow::selectAll() +{ + selection_start = Selection(); + selection_end.line = lines.size() - 1; + selection_end.pos = lines.last().length() - 1; + viewport()->update(); +} + +void OutputWindow::scrollContentsBy(int dx, int dy) +{ + if (block_scroll) + return; + if (dx && dy) { + viewport()->update(); + } else if (dx && !dy) { + viewport()->scroll(dx, 0); + } else { + if (same_height) { + viewport()->scroll(0, fontMetrics().lineSpacing() * dy); + } else { + viewport()->update(); + } + } +} + +void OutputWindow::keyPressEvent(QKeyEvent *e) +{ + bool accept = true; + if (e == QKeySequence::Copy) { + copy(); + } else if (e == QKeySequence::SelectAll) { + selectAll(); + } else if (e->key() == Qt::Key_Enter + || e->key() == Qt::Key_Return) { + insertLine(); + } else { + accept = false; + } + + if (accept) + e->accept(); + else + QAbstractScrollArea::keyPressEvent(e); +} + +void OutputWindow::paintEvent(QPaintEvent *e) +{ + int lineNumber = verticalScrollBar()->value(); + + int x = 4 - horizontalScrollBar()->value(); + QPainter p(viewport()); + + int spacing = fontMetrics().lineSpacing(); + int leading = fontMetrics().leading(); + int height = 0; + + QTextLayout textLayout; + textLayout.setFont(font()); + + QTextCharFormat selectionFormat; + selectionFormat.setBackground(palette().highlight()); + selectionFormat.setForeground(palette().highlightedText()); + + if (e->rect().top() <= 0 && e->rect().bottom() >= viewport()->rect().bottom()) + width_used = 0; // recalculate + + if (same_height) { + int skipLines = e->rect().top() / spacing; + height += skipLines * spacing; + lineNumber += skipLines; + } + + same_height = true; + + Selection sel_start = qMin(selection_start, selection_end); + Selection sel_end = qMax(selection_start, selection_end); + + while ( lineNumber < lines.size() && height <= e->rect().bottom()) { + + QString line = lines.at(lineNumber); + + if (line.size() == 1 && line.at(0) == QChar::ParagraphSeparator) { + int y = height + spacing/2; + p.drawLine(e->rect().left(), y, e->rect().right(), y); + height += spacing; + + } else { + textLayout.setText(line); + textLayout.beginLayout(); + while (1) { + QTextLine line = textLayout.createLine(); + if (!line.isValid()) + break; + line.setLineWidth(INT_MAX/256); + height += leading; + line.setPosition(QPoint(x, height)); + height += static_cast<int>(line.height()); + + same_height = same_height && (line.height() + leading) == spacing; + width_used = qMax(width_used, 8 + static_cast<int>(line.naturalTextWidth())); + } + textLayout.endLayout(); + + if (lineNumber >= sel_start.line && lineNumber <= sel_end.line) { + QVector<QTextLayout::FormatRange> selection(1); + selection[0].start = (lineNumber == sel_start.line)? sel_start.pos : 0; + selection[0].length = ((lineNumber == sel_end.line) ? sel_end.pos : lines.at(lineNumber).size()) - selection[0].start; + selection[0].format = selectionFormat; + + textLayout.draw(&p, QPoint(0, 0), selection); + } else { + textLayout.draw(&p, QPoint(0, 0)); + } + } + + + ++lineNumber; + } + + horizontalScrollBar()->setRange(0, qMax(0, width_used - viewport()->width())); + if (horizontalScrollBar()->pageStep() != viewport()->width()) + horizontalScrollBar()->setPageStep(viewport()->width()); + if (height > viewport()->height()) + verticalScrollBar()->setPageStep(lineNumber - verticalScrollBar()->value()); + else if (verticalScrollBar()->pageStep() != viewport()->height() / fontMetrics().lineSpacing()) + verticalScrollBar()->setPageStep(viewport()->height() / fontMetrics().lineSpacing()); +} + +void OutputWindow::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == Qt::LeftButton) { + clearSelection(); + if (getCursorPos(&selection_start.line, &selection_start.pos, e->pos())) { + selection_end = selection_start; + autoscroll = 0; + } + } +} + +void OutputWindow::timerEvent(QTimerEvent *e) +{ + if (e->timerId() == autoscroll_timer.timerId()) { + int autoscroll = 0; + if (lastMouseMove.y() < 0) + autoscroll = -1; + else if (lastMouseMove.y() > viewport()->height()) + autoscroll = 1; + if (autoscroll) { + verticalScrollBar()->setValue(verticalScrollBar()->value() + autoscroll); + OutputWindow::mouseMoveEvent(0); + } + } + QAbstractScrollArea::timerEvent(e); +} + +void OutputWindow::mouseReleaseEvent(QMouseEvent *e) +{ + if (e->button() == Qt::LeftButton) { + autoscroll_timer.stop(); + if (hasSelectedText() && QApplication::clipboard()->supportsSelection()) + QApplication::clipboard()->setText(selectedText(), QClipboard::Selection); + } +} + +void OutputWindow::mouseMoveEvent(QMouseEvent *e) +{ + if (e) { + lastMouseMove = e->pos(); + if (viewport()->rect().contains(e->pos())) + autoscroll_timer.stop(); + else + autoscroll_timer.start(20, this); + } + + + Selection old = selection_end; + if (!getCursorPos(&selection_end.line, &selection_end.pos, lastMouseMove)) + return; + if (same_height) { + Selection from = qMin(old, selection_end); + Selection to = qMax(old, selection_end); + viewport()->update(QRect(0, -1 + (from.line - verticalScrollBar()->value()) * fontMetrics().lineSpacing(), + viewport()->width(), 2 + (to.line - from.line + 1) * fontMetrics().lineSpacing())); + } else { + viewport()->update(); + } +} + +void OutputWindow::contextMenuEvent(QContextMenuEvent * e) +{ + QMenu menu(this); + QAction *clearAction = menu.addAction("Clear", this, SLOT(clear())); + QAction *copyAction = menu.addAction("Copy", this, SLOT(copy()), QKeySequence::Copy); + QAction *selectAllAction = menu.addAction("Select All", this, SLOT(selectAll()), QKeySequence::SelectAll); + if (lines.empty()) { + clearAction->setDisabled(true); + selectAllAction->setDisabled(true); + } + if (!hasSelectedText()) + copyAction->setDisabled(true); + + menu.exec(e->globalPos()); +} + +#endif // 0 diff --git a/src/plugins/projectexplorer/outputwindow.h b/src/plugins/projectexplorer/outputwindow.h new file mode 100644 index 00000000000..5a25ea40a87 --- /dev/null +++ b/src/plugins/projectexplorer/outputwindow.h @@ -0,0 +1,198 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef OUTPUTWINDOW_H +#define OUTPUTWINDOW_H + +#include <coreplugin/ioutputpane.h> +#include <texteditor/plaintexteditor.h> + +#include <QtCore/QObject> +#include <QtCore/QHash> +#include <QtCore/QBasicTimer> +#include <QtGui/QAbstractScrollArea> +#include <QtGui/QToolButton> +#include <QtGui/QPlainTextEdit> + +QT_BEGIN_NAMESPACE +class QTabWidget; +QT_END_NAMESPACE + +namespace ProjectExplorer { + +class RunControl; + +namespace Internal { + +class OutputWindow; + +class OutputPane : public Core::IOutputPane +{ + Q_OBJECT + +public: + OutputPane(); + ~OutputPane(); + + QWidget *outputWidget(QWidget *); + QList<QWidget*> toolBarWidgets(void) const; + QString name() const; + int priorityInStatusBar() const; + void clearContents(); + void visibilityChanged(bool); + bool canFocus(); + bool hasFocus(); + void setFocus(); + + void appendOutput(const QString &out); + + // ApplicationOutputspecifics + void createNewOutputWindow(RunControl *); + void appendOutput(RunControl *, const QString &out); + void showTabFor(RunControl *); + +public slots: + void projectRemoved(); + +private slots: + void insertLine(); + void reRunRunControl(); + void stopRunControl(); + void closeTab(int index); + void tabChanged(int); + void runControlStarted(); + void runControlFinished(); + +private: + RunControl *runControlForTab(int index) const; + + QWidget *m_mainWidget; + QTabWidget *m_tabWidget; + QHash<RunControl *, OutputWindow *> m_outputWindows; +// QToolButton *m_insertLineButton; + QToolButton *m_reRunButton; + QToolButton *m_stopButton; +}; + + + +class OutputWindow : public QPlainTextEdit +{ + Q_OBJECT +public: + OutputWindow(QWidget *parent = 0); + ~OutputWindow(); + + void appendOutput(const QString &out); + void insertLine(); +}; + +#if 0 +class OutputWindow + : public QAbstractScrollArea +{ + Q_OBJECT + + int max_lines; + bool same_height; + int width_used; + bool block_scroll; + QStringList lines; + QBasicTimer autoscroll_timer; + int autoscroll; + QPoint lastMouseMove; + + + struct Selection { + Selection():line(0), pos(0){} + int line; + int pos; + + bool operator==(const Selection &other) const + { return line == other.line && pos == other.pos; } + bool operator!=(const Selection &other) const + { return !(*this == other); } + bool operator<(const Selection &other) const + { return line < other.line || (line == other.line && pos < other.pos); } + bool operator>=(const Selection &other) const + { return !(*this < other); } + bool operator<=(const Selection &other) const + { return line < other.line || (line == other.line && pos == other.pos); } + bool operator>(const Selection &other) const + { return !(*this <= other); } + }; + + Selection selection_start, selection_end; + void changed(); + bool getCursorPos(int *lineNumber, int *position, const QPoint &pos); + +public: + OutputWindow(QWidget *parent = 0); + ~OutputWindow(); + + void setNumberOfLines(int max); + int numberOfLines() const; + + bool hasSelectedText() const; + void clearSelection(); + + QString selectedText() const; + + void appendOutput(const QString &out); + void insertLine() { + appendOutput(QChar(QChar::ParagraphSeparator)); + } + + +public slots: + void clear(); + void copy(); + void selectAll(); + +signals: + void showPage(); + +protected: + void scrollContentsBy(int dx, int dy); + void keyPressEvent(QKeyEvent *e); + void paintEvent(QPaintEvent *e); + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void timerEvent(QTimerEvent *e); + void contextMenuEvent(QContextMenuEvent * e); +}; +#endif // 0 +} //namespace Internal +} //namespace ProjectExplorer + +#endif diff --git a/src/plugins/projectexplorer/persistentsettings.cpp b/src/plugins/projectexplorer/persistentsettings.cpp new file mode 100644 index 00000000000..2ce7ed8cfe5 --- /dev/null +++ b/src/plugins/projectexplorer/persistentsettings.cpp @@ -0,0 +1,225 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "persistentsettings.h" + +#include <QtCore/QDebug> +#include <QtCore/QFile> +#include <QtCore/QVariant> +#include <QtXml/QDomDocument> +#include <QtXml/QDomCDATASection> + + +using namespace ProjectExplorer; + +PersistentSettingsReader::PersistentSettingsReader() +{ + +} + +QVariant PersistentSettingsReader::restoreValue(const QString & variable) const +{ + const QString &name = m_prefix + variable; + if (m_valueMap.contains(name)) + return m_valueMap.value(name); + return QVariant(); +} + +bool PersistentSettingsReader::load(const QString & fileName) +{ + m_valueMap.clear(); + + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) + return false; + + QDomDocument doc; + if (!doc.setContent(&file)) + return false; + + QDomElement root = doc.documentElement(); + if (root.nodeName() != QLatin1String("qtcreator")) + return false; + + QDomElement child = root.firstChildElement(); + for (; !child.isNull(); child = child.nextSiblingElement()) { + if (child.nodeName() == QLatin1String("data")) + readValues(child); + } + + file.close(); + return true; +} + +void PersistentSettingsReader::setPrefix(const QString &prefix) +{ + m_prefix = prefix; +} + +QString PersistentSettingsReader::prefix() const +{ + return m_prefix; +} + +QVariant PersistentSettingsReader::readValue(const QDomElement &valElement) const +{ + QString name = valElement.nodeName(); + QString type = valElement.attribute(QLatin1String("type")); + QVariant v; + + if (name == QLatin1String("value")) { + v.setValue(valElement.text()); + v.convert(QVariant::nameToType(type.toLatin1().data())); + } else if (name == QLatin1String("valuelist")) { + QDomElement child = valElement.firstChildElement(); + QList<QVariant> valList; + for (; !child.isNull(); child = child.nextSiblingElement()) { + valList << readValue(child); + } + v.setValue(valList); + } else if (name == QLatin1String("valuemap")) { + QDomElement child = valElement.firstChildElement(); + QMap<QString, QVariant> valMap; + for (; !child.isNull(); child = child.nextSiblingElement()) { + QString key = child.attribute(QLatin1String("key")); + valMap.insert(key, readValue(child)); + } + v.setValue(valMap); + } + + return v; +} + +void PersistentSettingsReader::readValues(const QDomElement &data) +{ + QString variable; + QVariant v; + + QDomElement child = data.firstChildElement(); + for (; !child.isNull(); child = child.nextSiblingElement()) { + if (child.nodeName() == QLatin1String("variable")) { + variable = child.text(); + } else { + v = readValue(child); + } + } + + m_valueMap.insert(variable, v); +} + +/// +/// PersistentSettingsWriter +/// + +PersistentSettingsWriter::PersistentSettingsWriter() +{ + +} + +void PersistentSettingsWriter::writeValue(QDomElement &ps, const QVariant &variant) +{ + if (variant.type() == QVariant::StringList || variant.type() == QVariant::List) { + QDomElement values = ps.ownerDocument().createElement("valuelist"); + values.setAttribute("type", QVariant::typeToName(QVariant::List)); + QList<QVariant> varList = variant.toList(); + foreach(QVariant var, varList) { + writeValue(values, var); + } + ps.appendChild(values); + } else if (variant.type() == QVariant::Map) { + QDomElement values = ps.ownerDocument().createElement("valuemap"); + values.setAttribute("type", QVariant::typeToName(QVariant::Map)); + + QMap<QString, QVariant> varMap = variant.toMap(); + QMap<QString, QVariant>::const_iterator i = varMap.constBegin(); + while (i != varMap.constEnd()) { + writeValue(values, i.value()); + values.lastChild().toElement(). + setAttribute(QLatin1String("key"), i.key()); + ++i; + } + + ps.appendChild(values); + } else { + QDomElement value = ps.ownerDocument().createElement("value"); + ps.appendChild(value); + QDomText valueText = ps.ownerDocument().createTextNode(variant.toString()); + value.appendChild(valueText); + value.setAttribute("type", variant.typeName()); + ps.appendChild(value); + } +} + +void PersistentSettingsWriter::saveValue(const QString & variable, const QVariant &value) +{ + m_valueMap[m_prefix + variable] = value; +} + +bool PersistentSettingsWriter::save(const QString & fileName, const QString & docType) +{ + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly)) + return false; + + QDomDocument doc(docType); + + QDomElement root = doc.createElement("qtcreator"); + doc.appendChild(root); + + QMap<QString, QVariant>::const_iterator i = m_valueMap.constBegin(); + while (i != m_valueMap.constEnd()) { + QDomElement ps = doc.createElement("data"); + root.appendChild(ps); + + QDomElement variable = doc.createElement("variable"); + ps.appendChild(variable); + QDomText variableText = doc.createTextNode(i.key()); + variable.appendChild(variableText); + + writeValue(ps, i.value()); + ++i; + } + + file.write(doc.toByteArray()); + file.close(); + return true; +} + +void PersistentSettingsWriter::setPrefix(const QString &prefix) +{ + m_prefix = prefix; +} + +QString PersistentSettingsWriter::prefix() const +{ + return m_prefix; +} diff --git a/src/plugins/projectexplorer/persistentsettings.h b/src/plugins/projectexplorer/persistentsettings.h new file mode 100644 index 00000000000..179fc1f3abe --- /dev/null +++ b/src/plugins/projectexplorer/persistentsettings.h @@ -0,0 +1,75 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PERSISTENTSETTINGS_H +#define PERSISTENTSETTINGS_H + +#include "projectexplorer_export.h" + +#include <QtCore/QMap> +#include <QtCore/QVariant> +#include <QtXml/QDomElement> + +namespace ProjectExplorer { + +class PROJECTEXPLORER_EXPORT PersistentSettingsReader +{ +public: + PersistentSettingsReader(); + QVariant restoreValue(const QString & variable) const; + bool load(const QString & fileName); + void setPrefix(const QString &prefix); + QString prefix() const; +private: + QString m_prefix; + QVariant readValue(const QDomElement &valElement) const; + void readValues(const QDomElement &data); + QMap<QString, QVariant> m_valueMap; +}; + +class PROJECTEXPLORER_EXPORT PersistentSettingsWriter +{ +public: + PersistentSettingsWriter(); + void saveValue(const QString & variable, const QVariant &value); + bool save(const QString & fileName, const QString & docType); + void setPrefix(const QString &prefix); + QString prefix() const; +private: + QString m_prefix; + void writeValue(QDomElement &ps, const QVariant &value); + QMap<QString, QVariant> m_valueMap; +}; + +} //namespace ProjectExplorer + +#endif //PERSISTENTSETTINGS_H diff --git a/src/plugins/projectexplorer/pluginfilefactory.cpp b/src/plugins/projectexplorer/pluginfilefactory.cpp new file mode 100644 index 00000000000..9a56363ac85 --- /dev/null +++ b/src/plugins/projectexplorer/pluginfilefactory.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "pluginfilefactory.h" +#include "projectexplorer.h" +#include "projectexplorerconstants.h" +#include "iprojectmanager.h" + +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/messagemanager.h> + +#include <QtCore/QDebug> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +ProjectFileFactory::ProjectFileFactory(const Core::ICore* core, IProjectManager *manager) : + m_mimeTypes(manager->mimeType()), + m_kind(Constants::FILE_FACTORY_KIND), + m_core(core), + m_manager(manager) +{ +} + +QStringList ProjectFileFactory::mimeTypes() const +{ + return m_mimeTypes; +} + +QString ProjectFileFactory::kind() const +{ + return m_kind; +} + +Core::IFile *ProjectFileFactory::open(const QString &fileName) +{ + Core::IFile *fIFace = 0; + + ProjectExplorerPlugin *pe = ProjectExplorerPlugin::instance(); + if (!pe->openProject(fileName)) { + m_core->messageManager()->printToOutputPane(tr("Could not open the following project: '%1'").arg(fileName)); + } else if (pe->session()) { + SessionManager *session = pe->session(); + if (session->projects().count() == 1) + fIFace = session->projects().first()->file(); + else if (session->projects().count() > 1) + fIFace = session->file(); // TODO: Why return session file interface here ??? + } + return fIFace; +} + +QList<ProjectFileFactory*> ProjectFileFactory::createFactories(const Core::ICore* core, + QString *filterString) +{ + // Register factories for all project managers + QList<Internal::ProjectFileFactory*> rc; + QList<IProjectManager*> projectManagers = core->pluginManager()->getObjects<IProjectManager>(); + + const QString filterSeparator = QLatin1String(";;"); + filterString->clear(); + foreach(IProjectManager *manager, projectManagers) { + rc.push_back(new ProjectFileFactory(core, manager)); + if (!filterString->isEmpty()) + *filterString += filterSeparator; + const QString mimeType = manager->mimeType(); + const QString pFilterString = core->mimeDatabase()->findByType(mimeType).filterString(); + *filterString += pFilterString; + } + return rc; +} diff --git a/src/plugins/projectexplorer/pluginfilefactory.h b/src/plugins/projectexplorer/pluginfilefactory.h new file mode 100644 index 00000000000..11cc60845bd --- /dev/null +++ b/src/plugins/projectexplorer/pluginfilefactory.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLUGINFILEFACTORY_H +#define PLUGINFILEFACTORY_H + +#include <coreplugin/ifilefactory.h> +#include <QtCore/QObject> +#include <QtCore/QStringList> + +namespace Core { + class ICore; +} + +namespace ProjectExplorer { + class IProjectManager; + class ProjectExplorerPlugin; + +namespace Internal { + +/* Factory for project files. */ + +class ProjectFileFactory : public Core::IFileFactory +{ + Q_OBJECT + explicit ProjectFileFactory(const Core::ICore* core, ProjectExplorer::IProjectManager *manager); +public: + + virtual QStringList mimeTypes() const; + bool canOpen(const QString &fileName); + QString kind() const; + Core::IFile *open(const QString &fileName); + + static QList<ProjectFileFactory*> createFactories(const Core::ICore* core, QString *filterString); + +private: + const QStringList m_mimeTypes; + const QString m_kind; + const Core::ICore* m_core; + ProjectExplorer::IProjectManager *m_manager; +}; + + +} //namespace Internal +} //namespace ProjectExplorer + +#endif // PLUGINFILEFACTORY diff --git a/src/plugins/projectexplorer/processstep.cpp b/src/plugins/projectexplorer/processstep.cpp new file mode 100644 index 00000000000..2ab5d77a717 --- /dev/null +++ b/src/plugins/projectexplorer/processstep.cpp @@ -0,0 +1,220 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "processstep.h" +#include "buildstep.h" +#include "project.h" + +#include <coreplugin/ifile.h> + +#include <QtCore/QDebug> +#include <QtGui/QFileDialog> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +ProcessStep::ProcessStep(Project *pro) + : AbstractProcessStep(pro) +{ + +} + +bool ProcessStep::init(const QString &buildConfiguration) +{ + setEnvironment(buildConfiguration, project()->environment(buildConfiguration)); + QVariant wd = value(buildConfiguration, "workingDirectory").toString(); + QString workingDirectory; + if(!wd.isValid() || wd.toString().isEmpty()) + workingDirectory = "$BUILDDIR"; + else + workingDirectory = wd.toString(); + setWorkingDirectory(buildConfiguration, workingDirectory.replace("$BUILDDIR", project()->buildDirectory(buildConfiguration))); + return AbstractProcessStep::init(buildConfiguration); +} + +void ProcessStep::run(QFutureInterface<bool> & fi) +{ + return AbstractProcessStep::run(fi); +} + +QString ProcessStep::name() +{ + return "projectexplorer.processstep"; +} + +void ProcessStep::setDisplayName(const QString &name) +{ + setValue("ProjectExplorer.ProcessStep.DisplayName", name); + emit displayNameChanged(this, name); +} + +QString ProcessStep::displayName() +{ + QVariant displayName = value("ProjectExplorer.ProcessStep.DisplayName"); + if (displayName.isValid()) + return displayName.toString(); + else + return tr("Custom Process Step"); +} + +BuildStepConfigWidget *ProcessStep::createConfigWidget() +{ + return new ProcessStepConfigWidget(this); +} + +bool ProcessStep::immutable() const +{ + return false; +} + +//******* +// ProcessStepFactory +//******* + +ProcessStepFactory::ProcessStepFactory() +{ + +} + +bool ProcessStepFactory::canCreate(const QString &name) const +{ + return name == "projectexplorer.processstep"; +} + +BuildStep *ProcessStepFactory::create(Project *pro, const QString &name) const +{ + Q_UNUSED(name); + return new ProcessStep(pro); +} + +QStringList ProcessStepFactory::canCreateForProject(Project *pro) const +{ + Q_UNUSED(pro) + return QStringList()<<"projectexplorer.processstep"; +} +QString ProcessStepFactory::displayNameForName(const QString &name) const +{ + Q_UNUSED(name); + return "Custom Process Step"; +} + +//******* +// ProcessStepConfigWidget +//******* + +ProcessStepConfigWidget::ProcessStepConfigWidget(ProcessStep *step) + : m_step(step) +{ + m_ui.setupUi(this); + connect(m_ui.commandBrowseButton, SIGNAL(clicked(bool)), + this, SLOT(commandBrowseButtonClicked())); + connect(m_ui.workingDirBrowseButton, SIGNAL(clicked(bool)), + this, SLOT(workingDirBrowseButtonClicked())); + + connect(m_ui.nameLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(nameLineEditTextEdited())); + connect(m_ui.commandLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(commandLineEditTextEdited())); + connect(m_ui.workingDirectoryLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(workingDirectoryLineEditTextEdited())); + connect(m_ui.commandArgumentsLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(commandArgumentsLineEditTextEdited())); + connect(m_ui.enabledGroupBox, SIGNAL(clicked(bool)), + this, SLOT(enabledGroupBoxClicked(bool))); +} + +QString ProcessStepConfigWidget::displayName() const +{ + return m_step->name(); +} + +void ProcessStepConfigWidget::workingDirBrowseButtonClicked() +{ + QString workingDirectory = QFileDialog::getExistingDirectory(this, "Select the working directory", m_ui.workingDirectoryLineEdit->text()); + if(workingDirectory.isEmpty()) + return; + m_ui.workingDirectoryLineEdit->setText(workingDirectory); + workingDirectoryLineEditTextEdited(); +} + +void ProcessStepConfigWidget::commandBrowseButtonClicked() +{ + QString filename = QFileDialog::getOpenFileName(this, "Select the executable"); + if(filename.isEmpty()) + return; + m_ui.commandLineEdit->setText(filename); + commandLineEditTextEdited(); +} + +void ProcessStepConfigWidget::init(const QString &buildConfiguration) +{ + m_buildConfiguration = buildConfiguration; + if(buildConfiguration != QString::null) { + m_ui.commandLineEdit->setText(m_step->command(buildConfiguration)); + + QString workingDirectory = m_step->value(buildConfiguration, "workingDirectory").toString(); + if (workingDirectory.isEmpty()) + workingDirectory = "$BUILDDIR"; + m_ui.workingDirectoryLineEdit->setText(workingDirectory); + + m_ui.commandArgumentsLineEdit->setText(m_step->arguments(buildConfiguration).join(" ")); + m_ui.enabledGroupBox->setChecked(m_step->enabled(buildConfiguration)); + } + m_ui.nameLineEdit->setText(m_step->displayName()); +} + +void ProcessStepConfigWidget::nameLineEditTextEdited() +{ + m_step->setDisplayName(m_ui.nameLineEdit->text()); +} + +void ProcessStepConfigWidget::commandLineEditTextEdited() +{ + m_step->setCommand(m_buildConfiguration, m_ui.commandLineEdit->text()); +} + +void ProcessStepConfigWidget::workingDirectoryLineEditTextEdited() +{ + QString wd = m_ui.workingDirectoryLineEdit->text(); + m_step->setValue(m_buildConfiguration, "workingDirectory", wd); +} + +void ProcessStepConfigWidget::commandArgumentsLineEditTextEdited() +{ + m_step->setArguments(m_buildConfiguration, m_ui.commandArgumentsLineEdit->text().split(" ", + QString::SkipEmptyParts)); +} + +void ProcessStepConfigWidget::enabledGroupBoxClicked(bool) +{ + m_step->setEnabled(m_buildConfiguration, m_ui.enabledGroupBox->isChecked()); +} diff --git a/src/plugins/projectexplorer/processstep.h b/src/plugins/projectexplorer/processstep.h new file mode 100644 index 00000000000..8667cd42b55 --- /dev/null +++ b/src/plugins/projectexplorer/processstep.h @@ -0,0 +1,97 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROCESSSTEP_H +#define PROCESSSTEP_H + +#include "ui_processstep.h" +#include "abstractprocessstep.h" +#include "environment.h" + +namespace ProjectExplorer { + +class Project; + +namespace Internal { + +class ProcessStepFactory : public IBuildStepFactory +{ +public: + ProcessStepFactory(); + virtual bool canCreate(const QString &name) const; + virtual BuildStep *create(Project *pro, const QString &name) const; + virtual QStringList canCreateForProject(Project *pro) const; + virtual QString displayNameForName(const QString &name) const; +}; + +class ProcessStep : public ProjectExplorer::AbstractProcessStep +{ + Q_OBJECT +public: + ProcessStep(Project *pro); + virtual bool init(const QString & name); + virtual void run(QFutureInterface<bool> &); + + virtual QString name(); + void setDisplayName(const QString &name); + virtual QString displayName(); + virtual BuildStepConfigWidget *createConfigWidget(); + virtual bool immutable() const; +private: + QString m_name; +}; + +class ProcessStepConfigWidget : public BuildStepConfigWidget +{ + Q_OBJECT +public: + ProcessStepConfigWidget(ProcessStep *step); + virtual QString displayName() const; + virtual void init(const QString &buildConfiguration); +private slots: + void nameLineEditTextEdited(); + void commandLineEditTextEdited(); + void workingDirectoryLineEditTextEdited(); + void commandArgumentsLineEditTextEdited(); + void enabledGroupBoxClicked(bool); + void workingDirBrowseButtonClicked(); + void commandBrowseButtonClicked(); +private: + QString m_buildConfiguration; + ProcessStep *m_step; + Ui::ProcessStepWidget m_ui; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // PROCESSSTEP_H diff --git a/src/plugins/projectexplorer/processstep.ui b/src/plugins/projectexplorer/processstep.ui new file mode 100644 index 00000000000..9875ef474cb --- /dev/null +++ b/src/plugins/projectexplorer/processstep.ui @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ProjectExplorer::Internal::ProcessStepWidget</class> + <widget class="QWidget" name="ProjectExplorer::Internal::ProcessStepWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>428</width> + <height>222</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="enabledGroupBox"> + <property name="title"> + <string>Enable custom process step</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="1" column="0"> + <widget class="QLabel" name="commandLabel"> + <property name="text"> + <string>Command</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="commandLineEdit"/> + </item> + <item row="1" column="2"> + <widget class="QToolButton" name="commandBrowseButton"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="workingDirecoryLabel"> + <property name="text"> + <string>Working Directory</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="workingDirectoryLineEdit"/> + </item> + <item row="2" column="2"> + <widget class="QToolButton" name="workingDirBrowseButton"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="commandArgumentsLabel"> + <property name="text"> + <string>command arguments</string> + </property> + </widget> + </item> + <item row="3" column="1" colspan="2"> + <widget class="QLineEdit" name="commandArgumentsLineEdit"/> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="nameLabel"> + <property name="text"> + <string>Name</string> + </property> + </widget> + </item> + <item row="0" column="1" colspan="2"> + <widget class="QLineEdit" name="nameLineEdit"/> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>80</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp new file mode 100644 index 00000000000..88cdc530496 --- /dev/null +++ b/src/plugins/projectexplorer/project.cpp @@ -0,0 +1,522 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "buildstep.h" +#include "project.h" +#include "projectexplorer.h" +#include "runconfiguration.h" +#include "editorconfiguration.h" + +#include <coreplugin/ifile.h> +#include <extensionsystem/pluginmanager.h> + +#include <QtCore/QTextCodec> +#include <QtCore/QDebug> + +using namespace ProjectExplorer; +using ExtensionSystem::PluginManager; + +Project::Project() + : m_activeRunConfiguration(0), + m_editorConfiguration(new EditorConfiguration()) +{ +} + +void Project::insertBuildStep(int position, BuildStep *step) +{ + m_buildSteps.insert(position, step); + // check that the step has all the configurations + foreach(const QString & name, buildConfigurations()) + { + if (!step->getBuildConfiguration(name)) + step->addBuildConfiguration(name); + } +} + +void Project::removeBuildStep(int position) +{ + delete m_buildSteps.at(position); + m_buildSteps.removeAt(position); +} + +void Project::moveBuildStepUp(int position) +{ + BuildStep *bs = m_buildSteps.takeAt(position); + m_buildSteps.insert(position - 1, bs); +} + +void Project::insertCleanStep(int position, BuildStep *step) +{ + m_cleanSteps.insert(position, step); + // check that the step has all the configurations + foreach(const QString & name, buildConfigurations()) + { + if (!step->getBuildConfiguration(name)) + step->addBuildConfiguration(name); + } +} + +void Project::removeCleanStep(int position) +{ + delete m_cleanSteps.at(position); + m_cleanSteps.removeAt(position); +} + +void Project::addBuildConfiguration(const QString &name) +{ + if (buildConfigurations().contains(name) ) + return; + + m_buildConfigurationValues.push_back(new BuildConfiguration(name)); + + for (int i = 0; i!=m_buildSteps.size(); ++i) + m_buildSteps.at(i)->addBuildConfiguration(name); + + for (int i = 0; i!=m_cleanSteps.size(); ++i) + m_cleanSteps.at(i)->addBuildConfiguration(name); +} + +void Project::removeBuildConfiguration(const QString &name) +{ + if (!buildConfigurations().contains(name)) + return; + + for (int i = 0; i != m_buildConfigurationValues.size(); ++i) + if(m_buildConfigurationValues.at(i)->name() == name) { + delete m_buildConfigurationValues.at(i); + m_buildConfigurationValues.removeAt(i); + break; + } + + for (int i = 0; i!=m_buildSteps.size(); ++i) + m_buildSteps.at(i)->removeBuildConfiguration(name); + for (int i = 0; i!=m_cleanSteps.size(); ++i) + m_cleanSteps.at(i)->removeBuildConfiguration(name); + +} + +void Project::copyBuildConfiguration(const QString &source, const QString &dest) +{ + if (!buildConfigurations().contains(source)) + return; + + for (int i = 0; i != m_buildConfigurationValues.size(); ++i) + if(m_buildConfigurationValues.at(i)->name() == source) + m_buildConfigurationValues.push_back(new BuildConfiguration(dest, m_buildConfigurationValues.at(i))); + + for (int i = 0; i!= m_buildSteps.size(); ++i) + m_buildSteps.at(i)->copyBuildConfiguration(source, dest); + + for (int i = 0; i!= m_cleanSteps.size(); ++i) + m_cleanSteps.at(i)->copyBuildConfiguration(source, dest); +} + +QStringList Project::buildConfigurations() const +{ + QStringList result; + foreach (BuildConfiguration *bc, m_buildConfigurationValues) { + result << bc->name(); + } + return result; +} + +QList<BuildStep *> Project::buildSteps() const +{ + return m_buildSteps; +} + +QList<BuildStep *> Project::cleanSteps() const +{ + return m_cleanSteps; +} + + + +void Project::saveSettings() +{ + PersistentSettingsWriter writer; + saveSettingsImpl(writer); + writer.save(file()->fileName() + QLatin1String(".user"), "QtCreatorProject"); +} + +void Project::restoreSettings() +{ + PersistentSettingsReader reader; + reader.load(file()->fileName() + QLatin1String(".user")); + restoreSettingsImpl(reader); + + if (m_activeBuildConfiguration.isEmpty() && !m_buildConfigurations.isEmpty()) + setActiveBuildConfiguration(m_buildConfigurations.at(0)); + + if (!m_activeRunConfiguration && !m_runConfigurations.isEmpty()) + setActiveRunConfiguration(m_runConfigurations.at(0)); +} + +QList<BuildStepConfigWidget*> Project::subConfigWidgets() +{ + return QList<BuildStepConfigWidget*>(); +} + +void Project::saveSettingsImpl(PersistentSettingsWriter &writer) +{ + writer.saveValue("activebuildconfiguration", m_activeBuildConfiguration); + //save m_values + writer.saveValue("project", m_values); + + //save buildsettings + foreach(const QString & buildConfigurationName, buildConfigurations()) { + QMap<QString, QVariant> temp = + getBuildConfiguration(buildConfigurationName)->toMap(); + writer.saveValue("buildConfiguration-" + buildConfigurationName, temp); + } + + QStringList buildStepNames; + foreach(BuildStep * buildStep, buildSteps()) { + buildStepNames << buildStep->name(); + } + writer.saveValue("buildsteps", buildStepNames); + + QStringList cleanStepNames; + foreach(BuildStep * cleanStep, cleanSteps()) { + cleanStepNames << cleanStep->name(); + } + writer.saveValue("cleansteps", cleanStepNames); + QStringList buildConfigurationNames = buildConfigurations(); + writer.saveValue("buildconfigurations", buildConfigurationNames ); + + //save buildstep configuration + int buildstepnr = 0; + foreach(BuildStep * buildStep, buildSteps()) + { + QMap<QString, QVariant> buildConfiguration = buildStep->valuesToMap(); + writer.saveValue("buildstep" + QString().setNum(buildstepnr), buildConfiguration); + ++buildstepnr; + } + + // save each buildstep/buildConfiguration combination + foreach(const QString & buildConfigurationName, buildConfigurationNames) { + buildstepnr = 0; + foreach(BuildStep * buildStep, buildSteps()) { + QMap<QString, QVariant> temp = + buildStep->valuesToMap(buildConfigurationName); + writer.saveValue("buildconfiguration-" + buildConfigurationName + "-buildstep" + QString().setNum(buildstepnr), temp); + ++buildstepnr; + } + } + + //save cleansteps buildconfiguration + int cleanstepnr = 0; + foreach(BuildStep * cleanStep, cleanSteps()) + { + QMap<QString, QVariant> buildConfiguration = cleanStep->valuesToMap(); + writer.saveValue("cleanstep" + QString().setNum(cleanstepnr), buildConfiguration); + ++cleanstepnr; + } + + // save each cleanstep/buildConfiguration combination + foreach(const QString & buildConfigurationName, buildConfigurationNames) { + cleanstepnr = 0; + foreach(BuildStep * cleanStep, cleanSteps()) { + QMap<QString, QVariant> temp = cleanStep->valuesToMap(buildConfigurationName); + writer.saveValue("buildconfiguration-" + buildConfigurationName + "-cleanstep" + QString().setNum(cleanstepnr), temp); + ++cleanstepnr; + } + } + + // Running + int i = 0; + int activeId = 0; + foreach (QSharedPointer<RunConfiguration> rc, m_runConfigurations) { + writer.setPrefix("RunConfiguration" + QString().setNum(i) + "-"); + writer.saveValue("type", rc->type()); + rc->save(writer); + if (rc == m_activeRunConfiguration) + activeId = i; + ++i; + } + writer.setPrefix(QString::null); + writer.saveValue("activeRunConfiguration", activeId); + + writer.saveValue("defaultFileEncoding", m_editorConfiguration->defaultTextCodec()->name()); +} + +void Project::restoreSettingsImpl(PersistentSettingsReader &reader) +{ + m_activeBuildConfiguration = reader.restoreValue("activebuildconfiguration").toString(); + + m_values = reader.restoreValue("project").toMap(); + + //Build Settings + const QStringList buildConfigurationNames = reader.restoreValue("buildconfigurations").toStringList(); + foreach(const QString & buildConfigurationName, buildConfigurationNames) { + addBuildConfiguration(buildConfigurationName); + QMap<QString, QVariant> temp = + reader.restoreValue("buildConfiguration-" + buildConfigurationName).toMap(); + getBuildConfiguration(buildConfigurationName)->setValuesFromMap(temp); + } + + QVariant buildStepsVariant = reader.restoreValue("buildsteps"); + if(buildStepsVariant.isValid()) { + // restoring BuildSteps from settings + int pos = 0; + const QList<IBuildStepFactory *> buildStepFactories = + ExtensionSystem::PluginManager::instance()->getObjects<IBuildStepFactory>(); + QStringList buildStepNames = buildStepsVariant.toStringList(); + foreach(const QString & buildStepName, buildStepNames) { + foreach(IBuildStepFactory * factory, buildStepFactories) { + if(factory->canCreate(buildStepName)) { + BuildStep * buildStep = factory->create(this, buildStepName); + insertBuildStep(pos, buildStep); + ++pos; + break; + } + } + } + + QStringList cleanStepNames = reader.restoreValue("cleansteps").toStringList(); + // restoring BuildSteps from settings + pos = 0; + foreach(const QString & cleanStepName, cleanStepNames) { + foreach(IBuildStepFactory * factory, buildStepFactories) { + if(factory->canCreate(cleanStepName)) { + BuildStep * cleanStep = factory->create(this, cleanStepName); + insertCleanStep(pos, cleanStep); + ++pos; + break; + } + } + } + + // restoring BuldConfigurations from settings + + + + // restore BuildSteps configuration + int buildstepnr = 0; + foreach(BuildStep * buildStep, buildSteps()) { + QMap<QString, QVariant> buildConfiguration = reader.restoreValue("buildstep" + QString().setNum(buildstepnr)).toMap(); + buildStep->setValuesFromMap(buildConfiguration); + ++buildstepnr; + } + + foreach(const QString & buildConfigurationName, buildConfigurationNames) { + buildstepnr = 0; + foreach(BuildStep * buildStep, buildSteps()) { + //get the buildconfiguration for this build step + QMap<QString, QVariant> buildConfiguration = + reader.restoreValue("buildconfiguration-" + buildConfigurationName + "-buildstep" + QString().setNum(buildstepnr)).toMap(); + buildStep->setValuesFromMap(buildConfigurationName, buildConfiguration); + ++buildstepnr; + } + } + + // restore CleanSteps configuration + int cleanstepnr = 0; + foreach(BuildStep * cleanStep, cleanSteps()) + { + QMap<QString, QVariant> buildConfiguration = reader.restoreValue("cleanstep" + QString().setNum(cleanstepnr)).toMap(); + cleanStep->setValuesFromMap(buildConfiguration); + ++cleanstepnr; + } + + foreach(const QString & buildConfigurationName, buildConfigurationNames) { + cleanstepnr = 0; + foreach(BuildStep * cleanStep, cleanSteps()) { + //get the buildconfiguration for this clean step + QMap<QString, QVariant> buildConfiguration = + reader.restoreValue("buildconfiguration-" + buildConfigurationName + "-cleanstep" + QString().setNum(cleanstepnr)).toMap(); + cleanStep->setValuesFromMap(buildConfigurationName, buildConfiguration); + ++cleanstepnr; + } + } + } + + // Running + const int activeId = reader.restoreValue("activeRunConfiguration").toInt(); + int i = 0; + const QList<IRunConfigurationFactory *> factories = + ExtensionSystem::PluginManager::instance()->getObjects<IRunConfigurationFactory>(); + forever { + reader.setPrefix("RunConfiguration" + QString().setNum(i) + "-"); + const QVariant &typeVariant = reader.restoreValue("type"); + if (!typeVariant.isValid()) + break; + const QString &type = typeVariant.toString(); + foreach (IRunConfigurationFactory * factory, factories) { + if (factory->canCreate(type)) { + QSharedPointer<RunConfiguration> rc = factory->create(this, type); + rc->restore(reader); + addRunConfiguration(rc); + if (i == activeId) + setActiveRunConfiguration(rc); + } + } + ++i; + } + reader.setPrefix(QString::null); + + QTextCodec *codec = QTextCodec::codecForName(reader.restoreValue("defaultFileEncoding").toByteArray()); + if (codec) + m_editorConfiguration->setDefaultTextCodec(codec); + + if (!m_activeRunConfiguration && !m_runConfigurations.isEmpty()) + setActiveRunConfiguration(m_runConfigurations.at(0)); +} + +void Project::setValue(const QString &name, const QVariant & value) +{ + m_values.insert(name, value); +} + +QVariant Project::value(const QString &name) const +{ + QMap<QString, QVariant>::const_iterator it = + m_values.find(name); + if(it != m_values.constEnd()) + return it.value(); + else + return QVariant(); +} + +BuildConfiguration * Project::getBuildConfiguration(const QString &name) const +{ + for (int i = 0; i != m_buildConfigurationValues.size(); ++i) + if (m_buildConfigurationValues.at(i)->name() == name) + return m_buildConfigurationValues.at(i); + return 0; +} + +void Project::setValue(const QString &buildConfiguration, const QString &name, const QVariant &value) +{ + BuildConfiguration *bc = getBuildConfiguration(buildConfiguration); + Q_ASSERT(bc); + bc->setValue(name, value); +} + +QVariant Project::value(const QString &buildConfiguration, const QString &name) const +{ + BuildConfiguration *bc = getBuildConfiguration(buildConfiguration); + if (bc) + return bc->getValue(name); + else + return QVariant(); +} + +QString Project::activeBuildConfiguration() const +{ + return m_activeBuildConfiguration; +} + +void Project::setActiveBuildConfiguration(const QString &config) +{ + if (m_activeBuildConfiguration != config && buildConfigurations().contains(config)) { + m_activeBuildConfiguration = config; + emit activeBuildConfigurationChanged(); + } +} + + +QList<QSharedPointer<RunConfiguration> > Project::runConfigurations() const +{ + return m_runConfigurations; +} + +void Project::addRunConfiguration(QSharedPointer<RunConfiguration> runConfiguration) +{ + Q_ASSERT(!m_runConfigurations.contains(runConfiguration)); + m_runConfigurations.push_back(runConfiguration); +} + +void Project::removeRunConfiguration(QSharedPointer<RunConfiguration> runConfiguration) +{ + Q_ASSERT(m_runConfigurations.contains(runConfiguration)); + m_runConfigurations.removeOne(runConfiguration); + if (m_activeRunConfiguration == runConfiguration) { + if (m_runConfigurations.isEmpty()) + setActiveRunConfiguration(QSharedPointer<RunConfiguration>(0)); + else + setActiveRunConfiguration(m_runConfigurations.at(0)); + } +} + +QSharedPointer<RunConfiguration> Project::activeRunConfiguration() const +{ + return m_activeRunConfiguration; +} + +void Project::setActiveRunConfiguration(QSharedPointer<RunConfiguration> runConfiguration) +{ + if (runConfiguration == m_activeRunConfiguration) + return; + Q_ASSERT(m_runConfigurations.contains(runConfiguration) || runConfiguration == 0); + m_activeRunConfiguration = runConfiguration; + emit activeRunConfigurationChanged(); +} + +EditorConfiguration *Project::editorConfiguration() const +{ + return m_editorConfiguration; +} + +QString Project::displayNameFor(const QString &buildConfiguration) +{ + return getBuildConfiguration(buildConfiguration)->displayName(); +} + +void Project::setDisplayNameFor(const QString &buildConfiguration, const QString &displayName) +{ + QStringList displayNames; + foreach(const QString &bc, buildConfigurations()) { + if (bc != buildConfiguration) + displayNames << displayNameFor(bc); + } + if (displayNames.contains(displayName)) { + int i = 2; + while (displayNames.contains(displayName + QString::number(i))) + ++i; + getBuildConfiguration(buildConfiguration)->setDisplayName(displayName + QString::number(i)); + } else { + getBuildConfiguration(buildConfiguration)->setDisplayName(displayName); + } + emit buildConfigurationDisplayNameChanged(buildConfiguration); +} + + +Project::~Project() +{ + qDeleteAll(m_buildSteps); + qDeleteAll(m_cleanSteps); + qDeleteAll(m_buildConfigurationValues); + delete m_editorConfiguration; +} + + diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h new file mode 100644 index 00000000000..e6d43fcc374 --- /dev/null +++ b/src/plugins/projectexplorer/project.h @@ -0,0 +1,182 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROJECT_H +#define PROJECT_H + +#include "persistentsettings.h" +#include "projectexplorer_export.h" +#include "buildconfiguration.h" +#include "environment.h" +#include "projectnodes.h" + +#include <QtCore/QSharedPointer> +#include <QtCore/QObject> +#include <QtCore/QModelIndex> +#include <QtCore/QFileInfo> +#include <QtGui/QFileSystemModel> +#include <QtGui/QMenu> +#include <QtGui/QIcon> + +namespace Core { + class IFile; +} + +namespace ProjectExplorer { + +class BuildManager; +class BuildStep; +class BuildStepConfigWidget; +class IProjectManager; +class RunConfiguration; +class EditorConfiguration; + +class PROJECTEXPLORER_EXPORT Project + : public QObject +{ + Q_OBJECT + +public: + // Roles to be implemented by all models that are exported + // via model() + enum ModelRoles { + // Absolute file path + FilePathRole = QFileSystemModel::FilePathRole + }; + + Project(); + virtual ~Project(); + + virtual QString name() const = 0; + virtual Core::IFile *file() const = 0; + virtual IProjectManager *projectManager() const = 0; + + virtual QList<Core::IFile *> dependencies() = 0; //NBS TODO remove + virtual QList<Project *> dependsOn() = 0; //NBS TODO implement dependsOn + + virtual bool isApplication() const = 0; + + //Build/Clean Step functions + QList<BuildStep *> buildSteps() const; + void insertBuildStep(int position, BuildStep *step); + void removeBuildStep(int position); + void moveBuildStepUp(int position); + + QList<BuildStep *> cleanSteps() const; + void insertCleanStep(int position, BuildStep *step); + void removeCleanStep(int position); + + //Build configuration + void addBuildConfiguration(const QString &name); + void removeBuildConfiguration(const QString &name); + void copyBuildConfiguration(const QString &source, const QString &dest); + QStringList buildConfigurations() const; + QString displayNameFor(const QString &buildConfiguration); + void setDisplayNameFor(const QString &buildConfiguration, const QString &displayName); + + QString activeBuildConfiguration() const; + void setActiveBuildConfiguration(const QString& config); + + void setValue(const QString &name, const QVariant &value); + QVariant value(const QString &name) const; + + void setValue(const QString &buildConfiguration, const QString &name, const QVariant &value); + QVariant value(const QString &buildConfiguration, const QString &name) const; + + // Running + QList<QSharedPointer<RunConfiguration> > runConfigurations() const; + void addRunConfiguration(QSharedPointer<RunConfiguration> runConfiguration); + void removeRunConfiguration(QSharedPointer<RunConfiguration> runConfiguration); + + QSharedPointer<RunConfiguration> activeRunConfiguration() const; + void setActiveRunConfiguration(QSharedPointer<RunConfiguration> runConfiguration); + + EditorConfiguration *editorConfiguration() const; + + virtual Environment environment(const QString &buildConfiguration) const = 0; + virtual QString buildDirectory(const QString &buildConfiguration) const = 0; + + void saveSettings(); + void restoreSettings(); + + virtual BuildStepConfigWidget *createConfigWidget() = 0; + virtual QList<BuildStepConfigWidget*> subConfigWidgets(); + + // This method is called for new build configurations + // You should probably set some default values in this method + virtual void newBuildConfiguration(const QString &buildConfiguration) = 0; + + virtual ProjectNode *rootProjectNode() const = 0; + + enum FilesMode { AllFiles, ExcludeGeneratedFiles }; + virtual QStringList files(FilesMode fileMode) const = 0; + +signals: + void fileListChanged(); + void activeBuildConfigurationChanged(); + void activeRunConfigurationChanged(); + // This signal is jut there for updating the tree list in the buildsettings wizard + void buildConfigurationDisplayNameChanged(const QString &buildConfiguraiton); + +protected: + // This method is called when the project .user file is saved. + // Simply call writer.saveValue() for each value you want to save + // Make sure to always call your base class implementation + // Note: All the values from the project/buildsteps and buildconfigurations + // are automatically stored. + virtual void saveSettingsImpl(PersistentSettingsWriter &writer); + // This method is called when the project is opened + // You can retrieve all the values you saved in saveSettingsImpl() + // in this method. + + // Note: This function is also called if there is no .user file + // You should probably add some default build and run settings to the project + // so that it can be build and run + virtual void restoreSettingsImpl(PersistentSettingsReader &reader); + +private: + BuildConfiguration *getBuildConfiguration(const QString & name) const; + + QList<BuildStep *> m_buildSteps; + QList<BuildStep *> m_cleanSteps; + QStringList m_buildConfigurations; + QMap<QString, QVariant> m_values; + QList<BuildConfiguration *> m_buildConfigurationValues; + QString m_activeBuildConfiguration; + QList<QSharedPointer<RunConfiguration> > m_runConfigurations; + QSharedPointer<RunConfiguration> m_activeRunConfiguration; + EditorConfiguration *m_editorConfiguration; +}; + +} //namespace ProjectExplorer + +#endif //PROJECTINTERFACE_H diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp new file mode 100644 index 00000000000..5df721a9f95 --- /dev/null +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -0,0 +1,1814 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "applicationrunconfiguration.h" +#include "allprojectsfilter.h" +#include "allprojectsfind.h" +#include "currentprojectfind.h" +#include "buildmanager.h" +#include "buildsettingspropertiespage.h" +#include "editorsettingspropertiespage.h" +#include "currentprojectfilter.h" +#include "customexecutablerunconfiguration.h" +#include "foldernavigationwidget.h" +#include "iprojectmanager.h" +#include "metatypedeclarations.h" +#include "nodesvisitor.h" +#include "outputwindow.h" +#include "persistentsettings.h" +#include "pluginfilefactory.h" +#include "processstep.h" +#include "projectexplorer.h" +#include "projectexplorerconstants.h" +#include "projectfilewizardextension.h" +#include "projecttreewidget.h" +#include "projectwindow.h" +#include "removefiledialog.h" +#include "runsettingspropertiespage.h" +#include "scriptwrappers.h" +#include "session.h" +#include "sessiondialog.h" + +#include <coreplugin/basemode.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/mainwindow.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/filemanager.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/editormanager/ieditorfactory.h> +#include <coreplugin/findplaceholder.h> +#include <coreplugin/basefilewizard.h> +#include <coreplugin/mainwindow.h> +#include <coreplugin/welcomemode.h> +#include <coreplugin/vcsmanager.h> +#include <coreplugin/iversioncontrol.h> +#include <utils/listutils.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QDebug> +#include <QtCore/QSettings> +#include <QtCore/QDateTime> +#include <QtGui/QAction> +#include <QtGui/QFileDialog> +#include <QtGui/QFileSystemModel> +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QMessageBox> +#include <QtGui/QMenu> +#include <QtGui/QContextMenuEvent> +#include <QtGui/QToolBar> +#include <QtGui/QMainWindow> +#include <QtGui/QHeaderView> +#include <QtGui/QInputDialog> + +Q_DECLARE_METATYPE(QSharedPointer<ProjectExplorer::RunConfiguration>); +Q_DECLARE_METATYPE(Core::IEditorFactory *); + +namespace { +bool debug = false; +} + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +CoreListenerCheckingForRunningBuild::CoreListenerCheckingForRunningBuild(BuildManager *manager) + : Core::ICoreListener(0), m_manager(manager) +{ +} + +bool CoreListenerCheckingForRunningBuild::coreAboutToClose() +{ + if (m_manager->isBuilding()) { + QMessageBox box; + QPushButton *closeAnyway = box.addButton(tr("Cancel Build && Close"), QMessageBox::AcceptRole); + QPushButton *cancelClose = box.addButton(tr("Don't Close"), QMessageBox::RejectRole); + box.setDefaultButton(cancelClose); + box.setWindowTitle(tr("Close QtCreator?")); + box.setText(tr("A project is currently being built.")); + box.setInformativeText(tr("Do you want to cancel the build process and close QtCreator anyway?")); + box.exec(); + return (box.clickedButton() == closeAnyway); + } + return true; +} + +ProjectExplorerPlugin *ProjectExplorerPlugin::m_instance = 0; + +ProjectExplorerPlugin::ProjectExplorerPlugin() + : m_buildConfigurationActionGroup(0), + m_runConfigurationActionGroup(0), + m_currentProject(0), + m_currentNode(0), + m_delayedRunConfiguration(0), + m_debuggingRunControl(0) +{ + m_instance = this; +} + +ProjectExplorerPlugin::~ProjectExplorerPlugin() +{ + removeObject(this); +} + +ProjectExplorerPlugin *ProjectExplorerPlugin::instance() +{ + return m_instance; +} + +bool ProjectExplorerPlugin::initialize(const QStringList & /*arguments*/, QString *) +{ + ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); + m_core = pm->getObject<Core::ICore>(); + Core::ActionManagerInterface *am = m_core->actionManager(); + + addObject(this); + + connect(m_core->fileManager(), SIGNAL(currentFileChanged(const QString&)), + this, SLOT(setCurrentFile(const QString&))); + + m_session = new SessionManager(m_core, this); + + connect(m_session, SIGNAL(projectAdded(ProjectExplorer::Project *)), + this, SIGNAL(fileListChanged())); + connect(m_session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project *)), + this, SLOT(invalidateProject(ProjectExplorer::Project *))); + connect(m_session, SIGNAL(projectRemoved(ProjectExplorer::Project *)), + this, SIGNAL(fileListChanged())); + connect(m_session, SIGNAL(startupProjectChanged(ProjectExplorer::Project *)), + this, SLOT(startupProjectChanged())); + + m_proWindow = new ProjectWindow(m_core); + + QList<int> globalcontext; + globalcontext.append(Core::Constants::C_GLOBAL_ID); + + QList<int> pecontext; + pecontext << m_core->uniqueIDManager()->uniqueIdentifier(Constants::C_PROJECTEXPLORER); + + Core::BaseMode *mode = new Core::BaseMode(tr("Projects"), + Constants::MODE_SESSION, + QIcon(QLatin1String(":/fancyactionbar/images/mode_Project.png")), + Constants::P_MODE_SESSION, + m_proWindow); + mode->setContext(QList<int>() << pecontext); + addAutoReleasedObject(mode); + m_proWindow->layout()->addWidget(new Core::FindToolBarPlaceHolder(mode)); + + m_buildManager = new BuildManager(this); + connect(m_buildManager, SIGNAL(buildStateChanged(ProjectExplorer::Project *)), + this, SLOT(buildStateChanged(ProjectExplorer::Project *))); + connect(m_buildManager, SIGNAL(buildQueueFinished(bool)), + this, SLOT(buildQueueFinished(bool))); + connect(m_buildManager, SIGNAL(tasksChanged()), + this, SLOT(updateTaskActions())); + + addAutoReleasedObject(new CoreListenerCheckingForRunningBuild(m_buildManager)); + + m_outputPane = new OutputPane(); + addAutoReleasedObject(m_outputPane); + connect(m_session, SIGNAL(projectRemoved(ProjectExplorer::Project *)), + m_outputPane, SLOT(projectRemoved())); + + AllProjectsFilter *allProjectsFilter = new AllProjectsFilter(this, m_core); + addAutoReleasedObject(allProjectsFilter); + + CurrentProjectFilter *currentProjectFilter = new CurrentProjectFilter(this, m_core); + addAutoReleasedObject(currentProjectFilter); + + addAutoReleasedObject(new BuildSettingsPanelFactory); + addAutoReleasedObject(new RunSettingsPanelFactory); + addAutoReleasedObject(new EditorSettingsPanelFactory); + + ProcessStepFactory *processStepFactory = new ProcessStepFactory; + addAutoReleasedObject(processStepFactory); + + AllProjectsFind *allProjectsFind = new AllProjectsFind(this, m_core, + m_core->pluginManager()->getObject<Find::SearchResultWindow>()); + addAutoReleasedObject(allProjectsFind); + + CurrentProjectFind *currentProjectFind = new CurrentProjectFind(this, m_core, + m_core->pluginManager()->getObject<Find::SearchResultWindow>()); + addAutoReleasedObject(currentProjectFind); + + addAutoReleasedObject(new ApplicationRunConfigurationRunner); + addAutoReleasedObject(new CustomExecutableRunConfigurationFactory); + + addAutoReleasedObject(new ProjectFileWizardExtension(m_core)); + + // context menus + Core::IActionContainer *msessionContextMenu = + am->createMenu(Constants::M_SESSIONCONTEXT); + Core::IActionContainer *mproject = + am->createMenu(Constants::M_PROJECTCONTEXT); + Core::IActionContainer *msubProject = + am->createMenu(Constants::M_SUBPROJECTCONTEXT); + Core::IActionContainer *mfolder = + am->createMenu(Constants::M_FOLDERCONTEXT); + Core::IActionContainer *mfilec = + am->createMenu(Constants::M_FILECONTEXT); + + m_sessionContextMenu = msessionContextMenu->menu(); + m_projectMenu = mproject->menu(); + m_subProjectMenu = msubProject->menu(); + m_folderMenu = mfolder->menu(); + m_fileMenu = mfilec->menu(); + + Core::IActionContainer *mfile = + am->actionContainer(Core::Constants::M_FILE); + Core::IActionContainer *menubar = + am->actionContainer(Core::Constants::MENU_BAR); + + // mode manager (for fancy actions) + Core::ModeManager *modeManager = m_core->modeManager(); + + // build menu + Core::IActionContainer *mbuild = + am->createMenu(Constants::M_BUILDPROJECT); + mbuild->menu()->setTitle("&Build"); + menubar->addMenu(mbuild, Core::Constants::G_VIEW); + + // debug menu + Core::IActionContainer *mdebug = + am->createMenu(Constants::M_DEBUG); + mdebug->menu()->setTitle("&Debug"); + menubar->addMenu(mdebug, Core::Constants::G_VIEW); + + + // + // Groups + // + + mbuild->appendGroup(Constants::G_BUILD_SESSION); + mbuild->appendGroup(Constants::G_BUILD_PROJECT); + mbuild->appendGroup(Constants::G_BUILD_OTHER); + mbuild->appendGroup(Constants::G_BUILD_RUN); + mbuild->appendGroup(Constants::G_BUILD_TASK); + mbuild->appendGroup(Constants::G_BUILD_CANCEL); + + msessionContextMenu->appendGroup(Constants::G_SESSION_BUILD); + msessionContextMenu->appendGroup(Constants::G_SESSION_FILES); + msessionContextMenu->appendGroup(Constants::G_SESSION_OTHER); + msessionContextMenu->appendGroup(Constants::G_SESSION_CONFIG); + + mproject->appendGroup(Constants::G_PROJECT_OPEN); + mproject->appendGroup(Constants::G_PROJECT_NEW); + mproject->appendGroup(Constants::G_PROJECT_BUILD); + mproject->appendGroup(Constants::G_PROJECT_RUN); + mproject->appendGroup(Constants::G_PROJECT_FILES); + mproject->appendGroup(Constants::G_PROJECT_OTHER); + mproject->appendGroup(Constants::G_PROJECT_CONFIG); + + msubProject->appendGroup(Constants::G_PROJECT_OPEN); + msubProject->appendGroup(Constants::G_PROJECT_FILES); + msubProject->appendGroup(Constants::G_PROJECT_OTHER); + msubProject->appendGroup(Constants::G_PROJECT_CONFIG); + + mfolder->appendGroup(Constants::G_FOLDER_FILES); + mfolder->appendGroup(Constants::G_FOLDER_OTHER); + mfolder->appendGroup(Constants::G_FOLDER_CONFIG); + + mfilec->appendGroup(Constants::G_FILE_OPEN); + mfilec->appendGroup(Constants::G_FILE_OTHER); + mfilec->appendGroup(Constants::G_FILE_CONFIG); + + // "open with" submenu + Core::IActionContainer * const openWith = + am->createMenu(ProjectExplorer::Constants::M_OPENFILEWITHCONTEXT); + m_openWithMenu = openWith->menu(); + m_openWithMenu->setTitle(tr("Open With")); + + connect(mfilec->menu(), SIGNAL(aboutToShow()), this, SLOT(populateOpenWithMenu())); + connect(m_openWithMenu, SIGNAL(triggered(QAction *)), + this, SLOT(openWithMenuTriggered(QAction *))); + + // + // Separators + // + + Core::ICommand *cmd; + QAction *sep; + + sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("ProjectExplorer.Build.Sep"), globalcontext); + mbuild->addAction(cmd, Constants::G_BUILD_PROJECT); + + sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("ProjectExplorer.Files.Sep"), globalcontext); + msessionContextMenu->addAction(cmd, Constants::G_SESSION_FILES); + mproject->addAction(cmd, Constants::G_PROJECT_FILES); + msubProject->addAction(cmd, Constants::G_PROJECT_FILES); + + sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("ProjectExplorer.New.Sep"), globalcontext); + mproject->addAction(cmd, Constants::G_PROJECT_NEW); + + sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("ProjectExplorer.Config.Sep"), globalcontext); + msessionContextMenu->addAction(cmd, Constants::G_SESSION_CONFIG); + mproject->addAction(cmd, Constants::G_PROJECT_CONFIG); + msubProject->addAction(cmd, Constants::G_PROJECT_CONFIG); + + sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("ProjectExplorer.Close.Sep"), globalcontext); + mfile->addAction(cmd, Core::Constants::G_FILE_CLOSE); + + sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("ProjectExplorer.Projects.Sep"), globalcontext); + mfile->addAction(cmd, Core::Constants::G_FILE_PROJECT); + + sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("ProjectExplorer.Other.Sep"), globalcontext); + mbuild->addAction(cmd, Constants::G_BUILD_OTHER); + msessionContextMenu->addAction(cmd, Constants::G_SESSION_OTHER); + mproject->addAction(cmd, Constants::G_PROJECT_OTHER); + msubProject->addAction(cmd, Constants::G_PROJECT_OTHER); + + sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("ProjectExplorer.Run.Sep"), globalcontext); + mbuild->addAction(cmd, Constants::G_BUILD_RUN); + mproject->addAction(cmd, Constants::G_PROJECT_RUN); + + sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("ProjectExplorer.Task.Sep"), globalcontext); + mbuild->addAction(cmd, Constants::G_BUILD_TASK); + + sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("ProjectExplorer.CancelBuild.Sep"), globalcontext); + mbuild->addAction(cmd, Constants::G_BUILD_CANCEL); + + // + // Actions + // + + // new session action + m_sessionManagerAction = new QAction(tr("Session Manager..."), this); + cmd = am->registerAction(m_sessionManagerAction, Constants::NEWSESSION, globalcontext); + cmd->setDefaultKeySequence(QKeySequence()); + + // new action + m_newAction = new QAction(tr("New Project..."), this); + cmd = am->registerAction(m_newAction, Constants::NEWPROJECT, globalcontext); + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+N"))); + msessionContextMenu->addAction(cmd, Constants::G_SESSION_FILES); + +#if 0 + // open action + m_loadAction = new QAction(tr("Load Project..."), this); + cmd = am->registerAction(m_loadAction, Constants::LOAD, globalcontext); + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+O"))); + mfile->addAction(cmd, Core::Constants::G_FILE_PROJECT); + msessionContextMenu->addAction(cmd, Constants::G_SESSION_FILES); +#endif + +#if 0 + // recent projects menu + Core::IActionContainer *mrecent = + am->createMenu(Constants::M_RECENTPROJECTS); + mrecent->menu()->setTitle("Recent Projects"); + mfile->addMenu(mrecent, Core::Constants::G_FILE_PROJECT); + connect(mfile->menu(), SIGNAL(aboutToShow()), + this, SLOT(updateRecentProjectMenu())); +#endif + + // Default open action + m_openFileAction = new QAction(tr("Open File"), this); + cmd = am->registerAction(m_openFileAction, ProjectExplorer::Constants::OPENFILE, + globalcontext); + mfilec->addAction(cmd, Constants::G_FILE_OPEN); + + // Open With menu + mfilec->addMenu(openWith, ProjectExplorer::Constants::G_FILE_OPEN); + + // unload action + m_unloadAction = new QAction(tr("Unload Project"), this); + cmd = am->registerAction(m_unloadAction, Constants::UNLOAD, globalcontext); + cmd->setAttribute(Core::ICommand::CA_UpdateText); + cmd->setDefaultText(m_unloadAction->text()); + mfile->addAction(cmd, Core::Constants::G_FILE_PROJECT); + mproject->addAction(cmd, Constants::G_PROJECT_FILES); + + // unload session action + m_clearSession = new QAction(tr("Unload All Projects"), this); + cmd = am->registerAction(m_clearSession, Constants::CLEARSESSION, globalcontext); + mfile->addAction(cmd, Core::Constants::G_FILE_PROJECT); + msessionContextMenu->addAction(cmd, Constants::G_SESSION_FILES); + + // session menu + Core::IActionContainer *msession = am->createMenu(Constants::M_SESSION); + msession->menu()->setTitle("&Session"); + mfile->addMenu(msession, Core::Constants::G_FILE_PROJECT); + m_sessionMenu = msession->menu(); + connect(mfile->menu(), SIGNAL(aboutToShow()), + this, SLOT(updateSessionMenu())); + + // build menu + Core::IActionContainer *mbc = + am->createMenu(Constants::BUILDCONFIGURATIONMENU); + m_buildConfigurationMenu = mbc->menu(); + m_buildConfigurationMenu->setTitle(tr("Set Build Configuration")); + //TODO this means it is build twice, rrr + connect(mproject->menu(), SIGNAL(aboutToShow()), this, SLOT(populateBuildConfigurationMenu())); + connect(mbuild->menu(), SIGNAL(aboutToShow()), this, SLOT(populateBuildConfigurationMenu())); + connect(m_buildConfigurationMenu, SIGNAL(aboutToShow()), this, SLOT(populateBuildConfigurationMenu())); + connect(m_buildConfigurationMenu, SIGNAL(triggered(QAction *)), this, SLOT(buildConfigurationMenuTriggered(QAction *))); + + // build session action + QIcon buildIcon(Constants::ICON_BUILD); + buildIcon.addFile(Constants::ICON_BUILD_SMALL); + m_buildSessionAction = new QAction(buildIcon, tr("Build All"), this); + cmd = am->registerAction(m_buildSessionAction, Constants::BUILDSESSION, globalcontext); + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+B"))); + mbuild->addAction(cmd, Constants::G_BUILD_SESSION); + msessionContextMenu->addAction(cmd, Constants::G_SESSION_BUILD); + // Add to mode bar + modeManager->addAction(cmd, Constants::P_ACTION_BUILDSESSION, m_buildConfigurationMenu); + + + // rebuild session action + QIcon rebuildIcon(Constants::ICON_REBUILD); + rebuildIcon.addFile(Constants::ICON_REBUILD_SMALL); + m_rebuildSessionAction = new QAction(rebuildIcon, tr("Rebuild All"), this); + cmd = am->registerAction(m_rebuildSessionAction, Constants::REBUILDSESSION, globalcontext); + mbuild->addAction(cmd, Constants::G_BUILD_SESSION); + msessionContextMenu->addAction(cmd, Constants::G_SESSION_BUILD); + + // clean session + QIcon cleanIcon(Constants::ICON_CLEAN); + cleanIcon.addFile(Constants::ICON_CLEAN_SMALL); + m_cleanSessionAction = new QAction(cleanIcon, tr("Clean All"), this); + cmd = am->registerAction(m_cleanSessionAction, Constants::CLEANSESSION, globalcontext); + mbuild->addAction(cmd, Constants::G_BUILD_SESSION); + msessionContextMenu->addAction(cmd, Constants::G_SESSION_BUILD); + + + // build action + m_buildAction = new QAction(tr("Build Project"), this); + cmd = am->registerAction(m_buildAction, Constants::BUILD, globalcontext); + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+B"))); + mbuild->addAction(cmd, Constants::G_BUILD_PROJECT); + mproject->addAction(cmd, Constants::G_PROJECT_BUILD); + + // rebuild action + m_rebuildAction = new QAction(tr("Rebuild Project"), this); + cmd = am->registerAction(m_rebuildAction, Constants::REBUILD, globalcontext); + mbuild->addAction(cmd, Constants::G_BUILD_PROJECT); + mproject->addAction(cmd, Constants::G_PROJECT_BUILD); + + // clean action + m_cleanAction = new QAction(tr("Clean Project"), this); + cmd = am->registerAction(m_cleanAction, Constants::CLEAN, globalcontext); + mbuild->addAction(cmd, Constants::G_BUILD_PROJECT); + mproject->addAction(cmd, Constants::G_PROJECT_BUILD); + + // Add Set Build Configuration to menu + mbuild->addMenu(mbc, Constants::G_BUILD_PROJECT); + mproject->addMenu(mbc, Constants::G_PROJECT_CONFIG); + + + // run action + QIcon runIcon(Constants::ICON_RUN); + runIcon.addFile(Constants::ICON_RUN_SMALL); + m_runAction = new QAction(runIcon, tr("Run"), this); + cmd = am->registerAction(m_runAction, Constants::RUN, globalcontext); + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+R"))); + mbuild->addAction(cmd, Constants::G_BUILD_RUN); + mproject->addAction(cmd, Constants::G_PROJECT_RUN); + + Core::IActionContainer *mrc = am->createMenu(Constants::RUNCONFIGURATIONMENU); + m_runConfigurationMenu = mrc->menu(); + m_runConfigurationMenu->setTitle(tr("Set Run Configuration")); + mbuild->addMenu(mrc, Constants::G_BUILD_RUN); + mproject->addMenu(mrc, Constants::G_PROJECT_CONFIG); + // TODO this recreates the menu twice if shown in the Build or context menu + connect(m_runConfigurationMenu, SIGNAL(aboutToShow()), this, SLOT(populateRunConfigurationMenu())); + connect(mproject->menu(), SIGNAL(aboutToShow()), this, SLOT(populateRunConfigurationMenu())); + connect(mbuild->menu(), SIGNAL(aboutToShow()), this, SLOT(populateRunConfigurationMenu())); + connect(m_runConfigurationMenu, SIGNAL(triggered(QAction *)), this, SLOT(runConfigurationMenuTriggered(QAction *))); + + modeManager->addAction(cmd, Constants::P_ACTION_RUN, m_runConfigurationMenu); + + // jump to next task + m_taskAction = new QAction(tr("Go to Task Window"), this); + m_taskAction->setIcon(QIcon(Core::Constants::ICON_NEXT)); + cmd = am->registerAction(m_taskAction, Constants::GOTOTASKWINDOW, globalcontext); + // FIXME: Eike, look here! cmd->setDefaultKeySequence(QKeySequence(tr("F9"))); + mbuild->addAction(cmd, Constants::G_BUILD_TASK); + + // cancel build action + m_cancelBuildAction = new QAction(tr("Cancel Build"), this); + cmd = am->registerAction(m_cancelBuildAction, Constants::CANCELBUILD, globalcontext); + mbuild->addAction(cmd, Constants::G_BUILD_CANCEL); + + // debug action + QIcon debuggerIcon(":/projectexplorer/images/debugger_start_small.png"); + debuggerIcon.addFile(":/gdbdebugger/images/debugger_start.png"); + m_debugAction = new QAction(debuggerIcon, tr("Start Debugging"), this); + cmd = am->registerAction(m_debugAction, Constants::DEBUG, globalcontext); + cmd->setAttribute(Core::ICommand::CA_UpdateText); + cmd->setAttribute(Core::ICommand::CA_UpdateIcon); + cmd->setDefaultText(tr("Start Debugging")); + cmd->setDefaultKeySequence(QKeySequence(tr("F5"))); + mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE); + modeManager->addAction(cmd, Constants::P_ACTION_DEBUG, m_runConfigurationMenu); + + // dependencies action + m_dependenciesAction = new QAction(tr("Edit Dependencies..."), this); + cmd = am->registerAction(m_dependenciesAction, Constants::DEPENDENCIES, pecontext); + msessionContextMenu->addAction(cmd, Constants::G_SESSION_CONFIG); + + // add new file action + m_addNewFileAction = new QAction(tr("Add New..."), this); + cmd = am->registerAction(m_addNewFileAction, ProjectExplorer::Constants::ADDNEWFILE, + globalcontext); + mproject->addAction(cmd, Constants::G_PROJECT_FILES); + msubProject->addAction(cmd, Constants::G_PROJECT_FILES); + + // add existing file action + m_addExistingFilesAction = new QAction(tr("Add Existing Files..."), this); + cmd = am->registerAction(m_addExistingFilesAction, ProjectExplorer::Constants::ADDEXISTINGFILES, + globalcontext); + mproject->addAction(cmd, Constants::G_PROJECT_FILES); + msubProject->addAction(cmd, Constants::G_PROJECT_FILES); + + // remove file action + m_removeFileAction = new QAction(tr("Remove File..."), this); + cmd = am->registerAction(m_removeFileAction, ProjectExplorer::Constants::REMOVEFILE, + globalcontext); + mfilec->addAction(cmd, Constants::G_FILE_OTHER); + + // renamefile action (TODO: Not supported yet) + m_renameFileAction = new QAction(tr("Rename"), this); + cmd = am->registerAction(m_renameFileAction, ProjectExplorer::Constants::RENAMEFILE, + globalcontext); + mfilec->addAction(cmd, Constants::G_FILE_OTHER); + m_renameFileAction->setEnabled(false); + m_renameFileAction->setVisible(false); + + connect(m_core, SIGNAL(saveSettingsRequested()), + this, SLOT(savePersistentSettings())); + + addAutoReleasedObject(new ProjectTreeWidgetFactory(m_core)); + addAutoReleasedObject(new FolderNavigationWidgetFactory(m_core)); + + if (QSettings *s = m_core->settings()) + m_recentProjects = s->value("ProjectExplorer/RecentProjects/Files", QStringList()).toStringList(); + for (QStringList::iterator it = m_recentProjects.begin(); it != m_recentProjects.end(); ) { + if (QFileInfo(*it).isFile()) { + ++it; + } else { + it = m_recentProjects.erase(it); + } + } + + connect(m_sessionManagerAction, SIGNAL(triggered()), this, SLOT(sessionManager())); + connect(m_newAction, SIGNAL(triggered()), this, SLOT(newProject())); +#if 0 + connect(m_loadAction, SIGNAL(triggered()), this, SLOT(loadAction())); +#endif + connect(m_buildAction, SIGNAL(triggered()), this, SLOT(buildProject())); + connect(m_buildSessionAction, SIGNAL(triggered()), this, SLOT(buildSession())); + connect(m_rebuildAction, SIGNAL(triggered()), this, SLOT(rebuildProject())); + connect(m_rebuildSessionAction, SIGNAL(triggered()), this, SLOT(rebuildSession())); + connect(m_cleanAction, SIGNAL(triggered()), this, SLOT(cleanProject())); + connect(m_cleanSessionAction, SIGNAL(triggered()), this, SLOT(cleanSession())); + connect(m_runAction, SIGNAL(triggered()), this, SLOT(runProject())); + connect(m_cancelBuildAction, SIGNAL(triggered()), this, SLOT(cancelBuild())); + connect(m_debugAction, SIGNAL(triggered()), this, SLOT(debugProject())); + connect(m_dependenciesAction, SIGNAL(triggered()), this, SLOT(editDependencies())); + connect(m_unloadAction, SIGNAL(triggered()), this, SLOT(unloadProject())); + connect(m_clearSession, SIGNAL(triggered()), this, SLOT(clearSession())); + connect(m_taskAction, SIGNAL(triggered()), this, SLOT(goToTaskWindow())); + connect(m_addNewFileAction, SIGNAL(triggered()), this, SLOT(addNewFile())); + connect(m_addExistingFilesAction, SIGNAL(triggered()), this, SLOT(addExistingFiles())); + connect(m_openFileAction, SIGNAL(triggered()), this, SLOT(openFile())); + connect(m_removeFileAction, SIGNAL(triggered()), this, SLOT(removeFile())); + connect(m_renameFileAction, SIGNAL(triggered()), this, SLOT(renameFile())); + + updateActions(); + + connect(m_core, SIGNAL(coreOpened()), this, SLOT(restoreSession())); + + return true; +} + +// Find a factory by file mime type in a sequence of factories +template <class Factory, class Iterator> + Factory *findFactory(const QString &mimeType, Iterator i1, Iterator i2) +{ + for ( ; i1 != i2; ++i2) { + Factory *f = *i1; + if (f->mimeTypes().contains(mimeType)) + return f; + } + return 0; +} + +ProjectFileFactory * ProjectExplorerPlugin::findProjectFileFactory(const QString &filename) const +{ + // Find factory + if (const Core::MimeType mt = m_core->mimeDatabase()->findByFile(QFileInfo(filename))) + if (ProjectFileFactory *pf = findFactory<ProjectFileFactory>(mt.type(), m_fileFactories.constBegin(), m_fileFactories.constEnd())) + return pf; + qWarning("Unable to find project file factory for '%s'", filename.toUtf8().constData()); + return 0; +} + +void ProjectExplorerPlugin::loadAction() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::loadAction"; + + + QString dir = m_lastOpenDirectory; + + // for your special convenience, we preselect a pro file if it is + // the current file + if (Core::IEditor *editor = Core::EditorManager::instance()->currentEditor()) { + if (const Core::IFile *file = editor->file()) { + const QString fn = file->fileName(); + const bool isProject = m_profileMimeTypes.contains(file->mimeType()); + dir = isProject ? fn : QFileInfo(fn).absolutePath(); + } + } + + QString filename = QFileDialog::getOpenFileName(0, tr("Load Project"), + dir, + m_projectFilterString); + if (filename.isEmpty()) + return; + if (ProjectFileFactory *pf = findProjectFileFactory(filename)) + pf->open(filename); + updateActions(); +} + +bool ProjectExplorerPlugin::saveAction(Project *pro) +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::saveAction"; + + if (!pro) + pro = m_currentProject; + Q_ASSERT(pro); + + Core::IFile *fi = pro->file(); + + if (!fi) // TODO Why saving the session here???? + fi = m_session->file(); + + if (!fi || fi->fileName().isEmpty()) //nothing to save? + return false; + + QList<Core::IFile*> filesToSave; + + filesToSave << fi; + if (pro) + filesToSave << pro->dependencies(); + + // check the number of modified files + int readonlycount = 0; + foreach (const Core::IFile *file, filesToSave) { + if (file->isReadOnly()) + ++readonlycount; + } + + bool success = false; + if (readonlycount > 0) + success = m_core->fileManager()->saveModifiedFiles(filesToSave).isEmpty(); + else + success = m_core->fileManager()->saveModifiedFilesSilently(filesToSave).isEmpty(); + + if (success) + addToRecentProjects(fi->fileName()); + updateActions(); + return success; +} + +void ProjectExplorerPlugin::unloadProject() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::unloadProject"; + + if (!saveAction(m_currentProject)) + return; + + m_session->removeProject(m_currentProject); + updateActions(); +} + +void ProjectExplorerPlugin::clearSession() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::clearSession"; + + if (!m_session->clear()) + return; // Action has been cancelled + updateActions(); +} + +void ProjectExplorerPlugin::extensionsInitialized() +{ + m_fileFactories = ProjectFileFactory::createFactories(m_core, &m_projectFilterString); + foreach(ProjectFileFactory *pf, m_fileFactories) { + m_profileMimeTypes += pf->mimeTypes(); + addAutoReleasedObject(pf); + } +} + +void ProjectExplorerPlugin::shutdown() +{ + m_session->clear(); +// m_proWindow->saveConfigChanges(); +} + +void ProjectExplorerPlugin::newProject() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::newProject"; + + QString defaultLocation; + if (currentProject()) { + const QFileInfo file(currentProject()->file()->fileName()); + QDir dir = file.dir(); + dir.cdUp(); + defaultLocation = dir.absolutePath(); + } + + m_core->showNewItemDialog(tr("New Project", "Title of dialog"), + Core::BaseFileWizard::findWizardsOfKind(Core::IWizard::ProjectWizard), + defaultLocation); + updateActions(); +} + +void ProjectExplorerPlugin::sessionManager() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::newSession"; + + if (m_session->isDefaultVirgin()) { + // do not save new virgin default sessions + } else { + m_session->save(); + } + SessionDialog sessionDialog(m_session, m_session->activeSession(), false); + sessionDialog.exec(); + + updateActions(); +} + +void ProjectExplorerPlugin::setStartupProject(Project *project) +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::setStartupProject"; + + if (!project) + project = m_currentProject; + Q_ASSERT(project); + m_session->setStartupProject(project); + // NPE: Visually mark startup project + updateActions(); +} + +void ProjectExplorerPlugin::savePersistentSettings() +{ + if (debug) + qDebug()<<"ProjectExplorerPlugin::savePersistentSettings()"; + + foreach (Project *pro, m_session->projects()) + pro->saveSettings(); + + if (m_session->isDefaultVirgin()) { + // do not save new virgin default sessions + } else { + m_session->save(); + } + + QSettings *s = m_core->settings(); + if (s) { + s->setValue("ProjectExplorer/StartupSession", m_session->file()->fileName()); + s->setValue("ProjectExplorer/RecentProjects/Files", m_recentProjects); + } +} + +bool ProjectExplorerPlugin::openProject(const QString &fileName) +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::openProject"; + + if (openProjects(QStringList() << fileName)) { + addToRecentProjects(fileName); + return true; + } + return false; +} + +bool ProjectExplorerPlugin::openProjects(const QStringList &fileNames) +{ + if (debug) + qDebug() << "ProjectExplorerPlugin - opening projects " << fileNames; + + QList<IProjectManager*> projectManagers = + m_core->pluginManager()->getObjects<IProjectManager>(); + + //QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // bool blocked = blockSignals(true); + QList<Project*> openedPro; + foreach (QString fileName, fileNames) + if (const Core::MimeType mt = m_core->mimeDatabase()->findByFile(QFileInfo(fileName))) { + foreach (IProjectManager *manager, projectManagers) + if (manager->mimeType() == mt.type()) { + if (Project *pro = manager->openProject(fileName)) + openedPro += pro; + m_session->reportProjectLoadingProgress(); + break; + } + } + //blockSignals(blocked); + + if (openedPro.isEmpty()) { + if (debug) + qDebug() << "ProjectExplorerPlugin - Could not open any projects!"; + QApplication::restoreOverrideCursor(); + return false; + } + + foreach (Project *pro, openedPro) { + if (debug) + qDebug()<<"restoring settings for "<<pro->file()->fileName(); + pro->restoreSettings(); + connect(pro, SIGNAL(fileListChanged()), this, SIGNAL(fileListChanged())); + } + m_session->addProjects(openedPro); + + // Make sure we always have a current project / node + if (!m_currentProject) + setCurrentNode(openedPro.first()->rootProjectNode()); + + updateActions(); + + m_core->modeManager()->activateMode(Core::Constants::MODE_EDIT); + QApplication::restoreOverrideCursor(); + + return true; +} + +Project *ProjectExplorerPlugin::currentProject() const +{ + if (debug) { + if (m_currentProject) + qDebug() << "ProjectExplorerPlugin::currentProject returns " << m_currentProject->name(); + else + qDebug() << "ProjectExplorerPlugin::currentProject returns 0"; + } + return m_currentProject; +} + +Node *ProjectExplorerPlugin::currentNode() const +{ + return m_currentNode; +} + +void ProjectExplorerPlugin::setCurrentFile(Project *project, const QString &filePath) +{ + setCurrent(project, filePath, 0); +} + +void ProjectExplorerPlugin::setCurrentFile(const QString &filePath) +{ + Project *project = m_session->projectForFile(filePath); + setCurrent(project, filePath, 0); +} + +void ProjectExplorerPlugin::setCurrentNode(Node *node) +{ + setCurrent(m_session->projectForNode(node), QString(), node); +} + +SessionManager *ProjectExplorerPlugin::session() const +{ + return m_session; +} + +Project *ProjectExplorerPlugin::startupProject() const +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::startupProject"; + + Project *pro = m_session->startupProject(); + + if (!pro) + pro = m_currentProject; + + return pro; +} + +// update welcome page +void ProjectExplorerPlugin::updateWelcomePage(Core::Internal::WelcomeMode *welcomeMode) +{ + Core::Internal::WelcomeMode::WelcomePageData welcomePageData; + welcomePageData.sessionList = m_session->sessions(); + welcomePageData.activeSession = m_session->activeSession(); + welcomePageData.previousSession = m_session->lastSession(); + welcomePageData.projectList = m_recentProjects; + welcomeMode->updateWelcomePage(welcomePageData); +} + +void ProjectExplorerPlugin::currentModeChanged(Core::IMode *mode) +{ + if (Core::Internal::WelcomeMode *welcomeMode = qobject_cast<Core::Internal::WelcomeMode*>(mode)) + updateWelcomePage(welcomeMode); +} + +/*! + \fn void ProjectExplorerPlugin::restoreSession() + + This method is connected to the ICore::coreOpened signal. If + there was no session explicitly loaded, it creates an empty new + default session and puts the list of recent projects and sessions + onto the welcome page. +*/ + +void ProjectExplorerPlugin::restoreSession() +{ + + if (debug) + qDebug() << "ProjectExplorerPlugin::restoreSession"; + + QStringList sessions = m_session->sessions(); + + // We have command line arguments, try to find a session in them + QStringList arguments = ExtensionSystem::PluginManager::instance()->arguments(); + + // Default to no session loading + QString sessionToLoad = QString::null; + if (!arguments.isEmpty()) { + foreach (const QString &arg, arguments) { + if (sessions.contains(arg)) { + // Session argument + sessionToLoad = arg; + arguments.removeOne(arg); + if (debug) + qDebug()<< "Found session argument, loading session"<<sessionToLoad; + break; + } + } + } + + // Restore latest session or what was passed on the command line + if (sessionToLoad == QString::null) { + m_session->createAndLoadNewDefaultSession(); + } else { + m_session->loadSession(sessionToLoad); + } + + // update welcome page + Core::ModeManager *modeManager = m_core->modeManager(); + connect(modeManager, SIGNAL(currentModeChanged(Core::IMode*)), this, SLOT(currentModeChanged(Core::IMode*))); + if (Core::Internal::WelcomeMode *welcomeMode = qobject_cast<Core::Internal::WelcomeMode*>(modeManager->mode(Core::Constants::MODE_WELCOME))) { + updateWelcomePage(welcomeMode); + connect(welcomeMode, SIGNAL(requestSession(QString)), this, SLOT(loadSession(QString))); + connect(welcomeMode, SIGNAL(requestProject(QString)), this, SLOT(loadProject(QString))); + } + + m_core->openFiles(arguments); + updateActions(); + +} + +void ProjectExplorerPlugin::loadSession(const QString &session) +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::loadSession" << session; + m_session->loadSession(session); +} + + +void ProjectExplorerPlugin::showContextMenu(const QPoint &globalPos, Node *node) +{ + QMenu *contextMenu = 0; + + updateContextMenuActions(); + + if (!node) + node = m_session->sessionNode(); + + if (node->nodeType() != SessionNodeType) { + Project *project = m_session->projectForNode(node); + setCurrentNode(node); + + emit aboutToShowContextMenu(project, node); + switch (node->nodeType()) { + case ProjectNodeType: + if (node->parentFolderNode() == m_session->sessionNode()) + contextMenu = m_projectMenu; + else + contextMenu = m_subProjectMenu; + break; + case FolderNodeType: + contextMenu = m_folderMenu; + break; + case FileNodeType: + contextMenu = m_fileMenu; + break; + default: + qWarning("ProjectExplorerPlugin::showContextMenu - Missing handler for node type"); + } + } else { // session item + emit aboutToShowContextMenu(0, node); + + contextMenu = m_sessionContextMenu; + } + + if (contextMenu && contextMenu->actions().count() > 0) { + contextMenu->popup(globalPos); + } +} + +BuildManager *ProjectExplorerPlugin::buildManager() const +{ + return m_buildManager; +} + +void ProjectExplorerPlugin::buildStateChanged(Project * pro) +{ + if (debug) { + qDebug() << "buildStateChanged"; + qDebug() << pro->file()->fileName() << "isBuilding()" << m_buildManager->isBuilding(pro); + } + Q_UNUSED(pro); + updateActions(); +} + +void ProjectExplorerPlugin::buildQueueFinished(bool success) +{ + if (debug) + qDebug() << "buildQueueFinished()" << success; + + updateActions(); + + if (success && m_delayedRunConfiguration) { + IRunConfigurationRunner *runner = findRunner(m_delayedRunConfiguration, m_runMode); + if (runner) { + emit aboutToExecuteProject(m_delayedRunConfiguration->project()); + + RunControl *control = runner->run(m_delayedRunConfiguration, m_runMode); + m_outputPane->createNewOutputWindow(control); + m_outputPane->popup(false); + m_outputPane->showTabFor(control); + + connect(control, SIGNAL(addToOutputWindow(RunControl *, const QString &)), + this, SLOT(addToApplicationOutputWindow(RunControl *, const QString &))); + connect(control, SIGNAL(error(RunControl *, const QString &)), + this, SLOT(addErrorToApplicationOutputWindow(RunControl *, const QString &))); + connect(control, SIGNAL(finished()), + this, SLOT(runControlFinished())); + control->start(); + + + if (m_runMode == ProjectExplorer::Constants::DEBUGMODE) + m_debuggingRunControl = control; + + updateRunAction(); + } + } else { + if (m_buildManager->tasksAvailable()) + m_buildManager->showTaskWindow(); + } + + m_delayedRunConfiguration = QSharedPointer<RunConfiguration>(0); + m_runMode = QString::null; +} + +void ProjectExplorerPlugin::updateTaskActions() +{ + m_taskAction->setEnabled(m_buildManager->tasksAvailable()); +} + +void ProjectExplorerPlugin::setCurrent(Project *project, QString filePath, Node *node) +{ + if (debug) + qDebug() << "ProjectExplorer - setting path to " << (node ? node->path() : filePath) + << " and project to " << (project ? project->name() : "0"); + + if (node) + filePath = node->path(); + else + node = m_session->nodeForFile(filePath); + + bool projectChanged = false; + if (m_currentProject != project) { + int oldContext = -1; + int newContext = -1; + int oldLanguageID = -1; + int newLanguageID = -1; + if (m_currentProject) { + oldContext = m_currentProject->projectManager()->projectContext(); + oldLanguageID = m_currentProject->projectManager()->projectLanguage(); + } + if (project) { + newContext = project->projectManager()->projectContext(); + newLanguageID = project->projectManager()->projectLanguage(); + } + m_core->removeAdditionalContext(oldContext); + m_core->removeAdditionalContext(oldLanguageID); + m_core->addAdditionalContext(newContext); + m_core->addAdditionalContext(newLanguageID); + m_core->updateContext(); + + m_currentProject = project; + + projectChanged = true; + } + + if (projectChanged || m_currentNode != node) { + m_currentNode = node; + if (debug) + qDebug() << "ProjectExplorer - currentNodeChanged(" << node->path() << ", " << (project ? project->name() : "0") << ")"; + emit currentNodeChanged(m_currentNode, project); + } + if (projectChanged) { + if (debug) + qDebug() << "ProjectExplorer - currentProjectChanged(" << (project ? project->name() : "0") << ")"; + emit currentProjectChanged(project); + updateActions(); + } + + m_core->fileManager()->setCurrentFile(filePath); +} + +void ProjectExplorerPlugin::updateActions() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::updateActions"; + + bool enableBuildActions = m_currentProject && ! (m_buildManager->isBuilding(m_currentProject)); + bool hasProjects = !m_session->projects().isEmpty(); + bool building = m_buildManager->isBuilding(); + + if (debug) + qDebug()<<"BuildManager::isBuilding()"<<building; + + m_unloadAction->setEnabled(m_currentProject != 0); + if (m_currentProject == 0) { + m_unloadAction->setText(tr("Unload Project")); + m_buildAction->setText(tr("Build Project")); + } else { + m_unloadAction->setText(tr("Unload Project \"%1\"").arg(m_currentProject->name())); + m_buildAction->setText(tr("Build Project \"%1\"").arg(m_currentProject->name())); + } + + m_buildAction->setEnabled(enableBuildActions); + m_rebuildAction->setEnabled(enableBuildActions); + m_cleanAction->setEnabled(enableBuildActions); + m_clearSession->setEnabled(hasProjects && !building); + m_buildSessionAction->setEnabled(hasProjects && !building); + m_rebuildSessionAction->setEnabled(hasProjects && !building); + m_cleanSessionAction->setEnabled(hasProjects && !building); + m_cancelBuildAction->setEnabled(building); + m_dependenciesAction->setEnabled(hasProjects && !building); + + updateRunAction(); + + updateTaskActions(); +} + +// NBS TODO check projectOrder() +// what we want here is all the projects pro depends on +QStringList ProjectExplorerPlugin::allFilesWithDependencies(Project *pro) +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::allFilesWithDependencies(" << pro->file()->fileName() << ")"; + + QStringList filesToSave; + foreach(Project *p, m_session->projectOrder(pro)) { + FindAllFilesVisitor filesVisitor; + p->rootProjectNode()->accept(&filesVisitor); + filesToSave << filesVisitor.filePaths(); + } + qSort(filesToSave); + return filesToSave; +} + +bool ProjectExplorerPlugin::saveModifiedFiles(const QList<Project *> & projects) +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::saveModifiedFiles"; + + QList<Core::IFile *> modifiedFi = m_core->fileManager()->modifiedFiles(); + QMap<QString, Core::IFile *> modified; + + QStringList allFiles; + foreach (Project *pro, projects) + allFiles << allFilesWithDependencies(pro); + + foreach (Core::IFile * fi, modifiedFi) + modified.insert(fi->fileName(), fi); + + QList<Core::IFile *> filesToSave; + + QMap<QString, Core::IFile *>::const_iterator mit = modified.constBegin(); + QStringList::const_iterator ait = allFiles.constBegin(); + QMap<QString, Core::IFile *>::const_iterator mend = modified.constEnd(); + QStringList::const_iterator aend = allFiles.constEnd(); + + while (mit != mend && ait != aend) { + if (mit.key() < *ait) + ++mit; + else if (*ait < mit.key()) + ++ait; + else { + filesToSave.append(mit.value()); + ++ait; + ++mit; + } + } + + if (!filesToSave.isEmpty()) { + bool cancelled; + m_core->fileManager()->saveModifiedFiles(filesToSave, &cancelled, + tr("The following dependencies are modified, do you want to save them?")); + if (cancelled) { + return false; + } + } + return true; +} + +//NBS handle case where there is no activeBuildConfiguration +// because someone delete all build configurations + +void ProjectExplorerPlugin::buildProject() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::buildProject"; + + if (saveModifiedFiles(QList<Project *>() << m_currentProject)) + buildManager()->buildProject(m_currentProject, m_currentProject->activeBuildConfiguration()); +} + +void ProjectExplorerPlugin::buildSession() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::buildSession"; + + const QList<Project *> & projects = m_session->projectOrder(); + if (saveModifiedFiles(projects)) { + QStringList configurations; + foreach (const Project * pro, projects) + configurations << pro->activeBuildConfiguration(); + + m_buildManager->buildProjects(projects, configurations); + } +} + +void ProjectExplorerPlugin::rebuildProject() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::rebuildProject"; + + if (saveModifiedFiles(QList<Project *>() << m_currentProject)) { + m_buildManager->cleanProject(m_currentProject, m_currentProject->activeBuildConfiguration()); + m_buildManager->buildProject(m_currentProject, m_currentProject->activeBuildConfiguration()); + } +} + +void ProjectExplorerPlugin::rebuildSession() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::rebuildSession"; + + const QList<Project *> & projects = m_session->projectOrder(); + if (saveModifiedFiles(projects)) { + QStringList configurations; + foreach (const Project * pro, projects) + configurations << pro->activeBuildConfiguration(); + + m_buildManager->cleanProjects(projects, configurations); + m_buildManager->buildProjects(projects, configurations); + } +} + +void ProjectExplorerPlugin::cleanProject() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::cleanProject"; + + if (saveModifiedFiles(QList<Project *>() << m_currentProject)) + m_buildManager->cleanProject(m_currentProject, m_currentProject->activeBuildConfiguration()); +} + +void ProjectExplorerPlugin::cleanSession() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::cleanSession"; + + const QList<Project *> & projects = m_session->projectOrder(); + if (saveModifiedFiles(projects)) { + QStringList configurations; + foreach (const Project * pro, projects) + configurations << pro->activeBuildConfiguration(); + + m_buildManager->cleanProjects(projects, configurations); + } +} + +void ProjectExplorerPlugin::runProject() +{ + Project *pro = startupProject(); + if (!pro) + return; + + if (saveModifiedFiles(QList<Project *>() << pro)) { + m_runMode = ProjectExplorer::Constants::RUNMODE; + + m_delayedRunConfiguration = pro->activeRunConfiguration(); + //NBS TODO make the build project step take into account project dependencies + m_buildManager->buildProject(pro, pro->activeBuildConfiguration()); + } +} + +void ProjectExplorerPlugin::debugProject() +{ + Project *pro = startupProject(); + if (!pro || m_debuggingRunControl) + return; + + if (saveModifiedFiles(QList<Project *>() << pro)) { + m_runMode = ProjectExplorer::Constants::DEBUGMODE; + m_delayedRunConfiguration = pro->activeRunConfiguration(); + //NBS TODO make the build project step take into account project dependencies + m_buildManager->buildProject(pro, pro->activeBuildConfiguration()); + } +} + +void ProjectExplorerPlugin::addToApplicationOutputWindow(RunControl *rc, const QString &line) +{ + m_outputPane->appendOutput(rc, line); +} + +void ProjectExplorerPlugin::addErrorToApplicationOutputWindow(RunControl *rc, const QString &error) +{ + m_outputPane->appendOutput(rc, error); +} + +void ProjectExplorerPlugin::runControlFinished() +{ + if (sender() == m_debuggingRunControl) + m_debuggingRunControl = 0; + + updateRunAction(); +} + +void ProjectExplorerPlugin::startupProjectChanged() +{ + static QPointer<Project> previousStartupProject = 0; + Project *project = startupProject(); + if (project == previousStartupProject) + return; + + if (previousStartupProject) { + disconnect(previousStartupProject, SIGNAL(activeRunConfigurationChanged()), + this, SLOT(updateRunAction())); + } + + previousStartupProject = project; + + if (project) { + connect(project, SIGNAL(activeRunConfigurationChanged()), + this, SLOT(updateRunAction())); + } + + updateRunAction(); +} + +// NBS TODO implement more than one runner +IRunConfigurationRunner *ProjectExplorerPlugin::findRunner(QSharedPointer<RunConfiguration> config, const QString &mode) +{ + const QList<IRunConfigurationRunner *> runners = m_core->pluginManager()->getObjects<IRunConfigurationRunner>(); + foreach (IRunConfigurationRunner *runner, runners) + if (runner->canRun(config, mode)) + return runner; + return 0; +} + +void ProjectExplorerPlugin::updateRunAction() +{ + const Project *project = startupProject(); + const bool canRun = project && findRunner(project->activeRunConfiguration(), ProjectExplorer::Constants::RUNMODE); + const bool canDebug = project && !m_debuggingRunControl && findRunner(project->activeRunConfiguration(), ProjectExplorer::Constants::DEBUGMODE); + const bool building = m_buildManager->isBuilding(); + m_runAction->setEnabled(canRun && !building); + m_debugAction->setEnabled(canDebug && !building); +} + +void ProjectExplorerPlugin::cancelBuild() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::cancelBuild"; + + if (m_buildManager->isBuilding()) + m_buildManager->cancel(); +} + +void ProjectExplorerPlugin::editDependencies() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::editDependencies"; + + m_session->editDependencies(); +} + +void ProjectExplorerPlugin::addToRecentProjects(const QString &fileName) +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::addToRecentProjects(" << fileName << ")"; + + if (fileName.isEmpty()) + return; + QString prettyFileName(QDir::toNativeSeparators(fileName)); + m_recentProjects.removeAll(prettyFileName); + if (m_recentProjects.count() > m_maxRecentProjects) + m_recentProjects.removeLast(); + m_recentProjects.prepend(prettyFileName); + QFileInfo fi(prettyFileName); + m_lastOpenDirectory = fi.absolutePath(); +} + +void ProjectExplorerPlugin::updateRecentProjectMenu() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::updateRecentProjectMenu"; + + Core::IActionContainer *aci = + m_core->actionManager()->actionContainer(Constants::M_RECENTPROJECTS); + QMenu *menu = aci->menu(); + menu->clear(); + m_recentProjectsActions.clear(); + + menu->setEnabled(!m_recentProjects.isEmpty()); + + //projects (ignore sessions, they used to be in this list) + foreach (const QString &s, m_recentProjects) { + if (s.endsWith(".qws")) + continue; + QAction *action = menu->addAction(s); + m_recentProjectsActions.insert(action, s); + connect(action, SIGNAL(triggered()), this, SLOT(openRecentProject())); + } +} + +void ProjectExplorerPlugin::openRecentProject() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::openRecentProject()"; + + QAction *a = qobject_cast<QAction*>(sender()); + if (m_recentProjectsActions.contains(a)) { + const QString fileName = m_recentProjectsActions.value(a); + if (ProjectFileFactory *pf = findProjectFileFactory(fileName)) + pf->open(fileName); + } +} + +void ProjectExplorerPlugin::invalidateProject(Project *project) +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::invalidateProject" << project->name(); + if (m_currentProject == project) { + // + // Workaround for a bug in QItemSelectionModel + // - currentChanged etc are not emitted if the + // item is removed from the underlying data model + // + setCurrent(0, QString(), 0); + } + + disconnect(project, SIGNAL(fileListChanged()), this, SIGNAL(fileListChanged())); +} + +void ProjectExplorerPlugin::goToTaskWindow() +{ + m_buildManager->gotoTaskWindow(); +} + +void ProjectExplorerPlugin::updateContextMenuActions() +{ + if (ProjectNode *projectNode = qobject_cast<ProjectNode*>(m_currentNode)) { + const bool addFilesEnabled = projectNode->supportedActions().contains(ProjectNode::AddFile); + m_addExistingFilesAction->setEnabled(addFilesEnabled); + m_addNewFileAction->setEnabled(addFilesEnabled); + } else if (FileNode *fileNode = qobject_cast<FileNode*>(m_currentNode)) { + const bool removeFileEnabled = fileNode->projectNode()->supportedActions().contains(ProjectNode::RemoveFile); + m_removeFileAction->setEnabled(removeFileEnabled); + } +} + +void ProjectExplorerPlugin::addNewFile() +{ + Q_ASSERT(m_currentNode && m_currentNode->nodeType() == ProjectNodeType); + const QString location = QFileInfo(m_currentNode->path()).dir().absolutePath(); + m_core->showNewItemDialog(tr("New File", "Title of dialog"), + Core::BaseFileWizard::findWizardsOfKind(Core::IWizard::FileWizard) + + Core::BaseFileWizard::findWizardsOfKind(Core::IWizard::ClassWizard), + location); +} + +void ProjectExplorerPlugin::addExistingFiles() +{ + Q_ASSERT(m_currentNode && m_currentNode->nodeType() == ProjectNodeType); + ProjectNode *projectNode = qobject_cast<ProjectNode*>(m_currentNode); + const QString dir = QFileInfo(m_currentNode->path()).dir().absolutePath(); + QStringList fileNames = QFileDialog::getOpenFileNames(m_core->mainWindow(), tr("Add Existing Files"), dir); + if (fileNames.isEmpty()) + return; + + QHash<FileType, QString> fileTypeToFiles; + foreach (const QString &fileName, fileNames) { + FileType fileType = typeForFileName(m_core->mimeDatabase(), QFileInfo(fileName)); + fileTypeToFiles.insertMulti(fileType, fileName); + } + + QStringList notAdded; + foreach (const FileType type, fileTypeToFiles.uniqueKeys()) { + projectNode->addFiles(type, fileTypeToFiles.values(type), ¬Added); + } + if (!notAdded.isEmpty()) { + QString message = tr("Could not add following files to project %1:\n").arg(projectNode->name()); + QString files = notAdded.join("\n"); + QMessageBox::warning(m_core->mainWindow(), tr("Add files to project failed"), + message + files); + foreach (const QString &file, notAdded) + fileNames.removeOne(file); + } + + if (Core::IVersionControl *vcManager = m_core->vcsManager()->findVersionControlForDirectory(dir)) { + const QString files = fileNames.join("\n"); + QMessageBox::StandardButton button = + QMessageBox::question(m_core->mainWindow(), tr("Add to Version Control"), + tr("Add files\n%1\nto version control?").arg(files), + QMessageBox::Yes | QMessageBox::No); + if (button == QMessageBox::Yes) { + QStringList notAddedToVc; + foreach (const QString file, fileNames) { + if (!vcManager->vcsAdd(file)) + notAddedToVc << file; + } + + if (!notAddedToVc.isEmpty()) { + const QString message = tr("Could not add following files to version control\n"); + const QString filesNotAdded = notAddedToVc.join("\n"); + QMessageBox::warning(m_core->mainWindow(), tr("Add files to version control failed"), + message + filesNotAdded); + } + } + } +} + +void ProjectExplorerPlugin::openFile() +{ + Q_ASSERT(m_currentNode); + m_core->editorManager()->openEditor(m_currentNode->path()); + m_core->editorManager()->ensureEditorManagerVisible(); +} + +void ProjectExplorerPlugin::removeFile() +{ + Q_ASSERT(m_currentNode && m_currentNode->nodeType() == FileNodeType); + FileNode *fileNode = qobject_cast<FileNode*>(m_currentNode); + + const QString filePath = m_currentNode->path(); + const QString fileDir = QFileInfo(filePath).dir().absolutePath(); + RemoveFileDialog removeFileDialog(filePath, m_core->mainWindow()); + + if (removeFileDialog.exec() == QDialog::Accepted) { + const bool deleteFile = removeFileDialog.isDeleteFileChecked(); + + // remove from project + ProjectNode *projectNode = fileNode->projectNode(); + Q_ASSERT(projectNode); + + if (!projectNode->removeFiles(fileNode->fileType(), QStringList(filePath))) { + QMessageBox::warning(m_core->mainWindow(), tr("Remove file failed"), + tr("Could not remove file %1 from project %2.").arg(filePath).arg(projectNode->name())); + return; + } + + // remove from version control + m_core->vcsManager()->showDeleteDialog(filePath); + + // remove from file system + if (deleteFile) { + QFile file(filePath); + + if (file.exists()) { + // could have been deleted by vc + if (!file.remove()) + QMessageBox::warning(m_core->mainWindow(), tr("Delete file failed"), + tr("Could not delete file %1.").arg(filePath)); + } + } + } +} + +void ProjectExplorerPlugin::renameFile() +{ + QWidget *focusWidget = QApplication::focusWidget(); + while (focusWidget) { + ProjectTreeWidget *treeWidget = qobject_cast<ProjectTreeWidget*>(focusWidget); + if (treeWidget) { + treeWidget->editCurrentItem(); + break; + } + focusWidget = focusWidget->parentWidget(); + } +} + +void ProjectExplorerPlugin::populateBuildConfigurationMenu() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::populateBuildConfigurationMenu"; + + // delete the old actiongroup and all actions that are children of it + delete m_buildConfigurationActionGroup; + m_buildConfigurationActionGroup = new QActionGroup(m_buildConfigurationMenu); + m_buildConfigurationMenu->clear(); + if (Project *pro = m_currentProject) { + const QString &activeBuildConfiguration = pro->activeBuildConfiguration(); + foreach (const QString &buildConfiguration, pro->buildConfigurations()) { + QString displayName = pro->displayNameFor(buildConfiguration); + QAction *act = new QAction(displayName, m_buildConfigurationActionGroup); + if (debug) + qDebug() << "BuildConfiguration " << buildConfiguration << "active: " << activeBuildConfiguration; + act->setCheckable(true); + act->setChecked(buildConfiguration == activeBuildConfiguration); + act->setData(buildConfiguration); + m_buildConfigurationMenu->addAction(act); + } + m_buildConfigurationMenu->setEnabled(true); + } else { + m_buildConfigurationMenu->setEnabled(false); + } +} + +void ProjectExplorerPlugin::buildConfigurationMenuTriggered(QAction *action) +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::buildConfigurationMenuTriggered"; + + m_currentProject->setActiveBuildConfiguration(action->data().toString()); +} + +void ProjectExplorerPlugin::populateRunConfigurationMenu() +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::populateRunConfigurationMenu"; + + delete m_runConfigurationActionGroup; + m_runConfigurationActionGroup = new QActionGroup(m_runConfigurationMenu); + m_runConfigurationMenu->clear(); + + const Project *startupProject = m_session->startupProject(); + QSharedPointer<RunConfiguration> activeRunConfiguration + = (startupProject) ? startupProject->activeRunConfiguration() : QSharedPointer<RunConfiguration>(0); + + foreach (const Project *pro, m_session->projects()) { + foreach (QSharedPointer<RunConfiguration> runConfiguration, pro->runConfigurations()) { + const QString title = QString("%1 (%2)").arg(pro->name(), runConfiguration->name()); + QAction *act = new QAction(title, m_runConfigurationActionGroup); + act->setCheckable(true); + act->setData(qVariantFromValue(runConfiguration)); + act->setChecked(runConfiguration == activeRunConfiguration); + m_runConfigurationMenu->addAction(act); + if (debug) + qDebug() << "RunConfiguration" << runConfiguration << "project:" << pro->name() + << "active:" << (runConfiguration == activeRunConfiguration); + } + } + + m_runConfigurationMenu->setDisabled(m_runConfigurationMenu->actions().isEmpty()); +} + +void ProjectExplorerPlugin::runConfigurationMenuTriggered(QAction *action) +{ + if (debug) + qDebug() << "ProjectExplorerPlugin::runConfigurationMenuTriggered" << action; + + QSharedPointer<RunConfiguration> runConfiguration = qVariantValue<QSharedPointer<RunConfiguration> >(action->data()); + runConfiguration->project()->setActiveRunConfiguration(runConfiguration); + setStartupProject(runConfiguration->project()); +} + +void ProjectExplorerPlugin::populateOpenWithMenu() +{ + typedef QList<Core::IEditorFactory*> EditorFactoryList; + + m_openWithMenu->clear(); + + bool anyMatches = false; + const QString fileName = currentNode()->path(); + + if (const Core::MimeType mt = m_core->mimeDatabase()->findByFile(QFileInfo(fileName))) { + const EditorFactoryList factories = m_core->editorManager()->editorFactories(mt, false); + anyMatches = !factories.empty(); + if (anyMatches) { + const QList<Core::IEditor *> editorsOpenForFile = m_core->editorManager()->editorsForFileName(fileName); + // Add all suitable editors + foreach (Core::IEditorFactory *editorFactory, factories) { + // Add action to open with this very editor factory + QString const actionTitle(editorFactory->kind()); + QAction * const action = m_openWithMenu->addAction(actionTitle); + action->setData(qVariantFromValue(editorFactory)); + // File already open in an editor -> only enable that entry since + // we currently do not support opening a file in two editors at once + if (!editorsOpenForFile.isEmpty()) { + bool enabled = false; + foreach (Core::IEditor * const openEditor, editorsOpenForFile) { + if (editorFactory->kind() == QLatin1String(openEditor->kind())) + enabled = true; + break; + } + action->setEnabled(enabled); + } + } + } + } + m_openWithMenu->setEnabled(anyMatches); +} + +void ProjectExplorerPlugin::openWithMenuTriggered(QAction *action) +{ + Q_ASSERT(action != NULL); + Core::IEditorFactory * const editorFactory = qVariantValue<Core::IEditorFactory *>(action->data()); + Q_ASSERT(m_core != NULL); + Q_ASSERT(m_core->editorManager() != NULL); + Q_ASSERT(editorFactory != NULL); + m_core->editorManager()->openEditor(currentNode()->path(), editorFactory->kind()); + m_core->editorManager()->ensureEditorManagerVisible(); +} + +void ProjectExplorerPlugin::updateSessionMenu() +{ + m_sessionMenu->clear(); + const QString &activeSession = m_session->activeSession(); + foreach(const QString &session, m_session->sessions()) { + QAction *act = m_sessionMenu->addAction(session, this, SLOT(setSession())); + act->setCheckable(true); + if (session == activeSession) + act->setChecked(true); + } + m_sessionMenu->addSeparator(); + m_sessionMenu->addAction(m_sessionManagerAction); + + m_sessionMenu->setEnabled(true); +} + +void ProjectExplorerPlugin::setSession() +{ + QString session = static_cast<QAction *>(sender())->text(); + if (session != m_session->activeSession()) + m_session->loadSession(session); +} + +Q_EXPORT_PLUGIN(ProjectExplorerPlugin) diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h new file mode 100644 index 00000000000..4f956fc8f42 --- /dev/null +++ b/src/plugins/projectexplorer/projectexplorer.h @@ -0,0 +1,280 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROJECTEXPLORER_H +#define PROJECTEXPLORER_H + +#include "project.h" +#include "session.h" +#include "projectexplorer_export.h" + +#include <extensionsystem/iplugin.h> +#include <coreplugin/icorelistener.h> + +#include <QtCore/QObject> +#include <QtCore/QMap> +#include <QtCore/QSharedPointer> +#include <QtCore/QList> +#include <QtCore/QQueue> +#include <QtCore/QModelIndex> +#include <QtGui/QMenu> +#include <QtGui/QTreeWidget> +#include <QtGui/QTreeWidgetItem> + + +namespace Core { +class IContext; +class ICore; +class IMode; +class IFileFactory; + namespace Internal { + class WelcomeMode; + } +} + +namespace ProjectExplorer { + +class BuildManager; +class PersistentSettings; +class RunConfiguration; +class RunControl; +class SessionManager; +class IRunConfigurationRunner; + +namespace Internal { +class ApplicationOutput; +class OutputPane; +class ProjectWindow; +class ProjectFileFactory; + +} // namespace Internal + +class PROJECTEXPLORER_EXPORT ProjectExplorerPlugin + : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + ProjectExplorerPlugin(); + ~ProjectExplorerPlugin(); + + static ProjectExplorerPlugin *instance(); + + bool openProject(const QString &fileName); + bool openProjects(const QStringList &fileNames); + + SessionManager *session() const; + + Project *currentProject() const; + Node *currentNode() const; + + void setCurrentFile(Project *project, const QString &file); + void setCurrentNode(Node *node); + + Project *startupProject() const; + void setStartupProject(ProjectExplorer::Project *project = 0); + + BuildManager *buildManager() const; + + bool saveModifiedFiles(const QList<Project *> & projects); + + void showContextMenu(const QPoint &globalPos, Node *node); + + //PluginInterface + bool initialize(const QStringList &arguments, QString *error_message); + void extensionsInitialized(); + void shutdown(); + +signals: + void aboutToShowContextMenu(ProjectExplorer::Project *project, + ProjectExplorer::Node *node); + + // Is emitted when a project has been added/removed, + // or the file list of a specific project has changed. + void fileListChanged(); + + void currentProjectChanged(ProjectExplorer::Project *project); + void currentNodeChanged(ProjectExplorer::Node *node, ProjectExplorer::Project *project); + void aboutToExecuteProject(ProjectExplorer::Project *project); + +private slots: + void buildStateChanged(ProjectExplorer::Project * pro); + void buildQueueFinished(bool success); + void buildProject(); + void buildSession(); + void rebuildProject(); + void rebuildSession(); + void cleanProject(); + void cleanSession(); + void cancelBuild(); + void debugProject(); + void editDependencies(); + bool saveAction(ProjectExplorer::Project *pro = 0); + void loadAction(); + void unloadProject(); + void clearSession(); + void newProject(); + void sessionManager(); + void populateBuildConfigurationMenu(); + void buildConfigurationMenuTriggered(QAction *); + void populateRunConfigurationMenu(); + void runConfigurationMenuTriggered(QAction *); + void populateOpenWithMenu(); + void openWithMenuTriggered(QAction *action); + void updateSessionMenu(); + void setSession(); + + void restoreSession(); + void loadSession(const QString &session); + void runProject(); + void savePersistentSettings(); + void goToTaskWindow(); + + void updateContextMenuActions(); + void addNewFile(); + void addExistingFiles(); + void openFile(); + void removeFile(); + void renameFile(); + + void updateRecentProjectMenu(); + void openRecentProject(); + + void invalidateProject(ProjectExplorer::Project *project); + + void setCurrentFile(const QString &filePath); + + // RunControl + void runControlFinished(); + + void startupProjectChanged(); // Calls updateRunAction + void updateRunAction(); + + void addToApplicationOutputWindow(RunControl *, const QString &line); + void addErrorToApplicationOutputWindow(RunControl *, const QString &error); + void updateTaskActions(); + + void loadProject(const QString &project) { openProject(project); } + void currentModeChanged(Core::IMode *mode); + +private: + void setCurrent(Project *project, QString filePath, Node *node); + + QStringList allFilesWithDependencies(Project *pro); + IRunConfigurationRunner *findRunner(QSharedPointer<RunConfiguration> config, const QString &mode); + + void updateActions(); + void addToRecentProjects(const QString &fileName); + void updateWelcomePage(Core::Internal::WelcomeMode *welcomeMode); + Internal::ProjectFileFactory *findProjectFileFactory(const QString &filename) const; + + static ProjectExplorerPlugin *m_instance; + + QMenu *m_sessionContextMenu; + QMenu *m_sessionMenu; + QMenu *m_projectMenu; + QMenu *m_subProjectMenu; + QMenu *m_folderMenu; + QMenu *m_fileMenu; + QMenu *m_openWithMenu; + + QMultiMap<int, QObject*> m_actionMap; + QAction *m_sessionManagerAction; + QAction *m_newAction; +#if 0 + QAction *m_loadAction; +#endif + QAction *m_unloadAction; + QAction *m_clearSession; + QAction *m_buildAction; + QAction *m_buildSessionAction; + QAction *m_rebuildAction; + QAction *m_rebuildSessionAction; + QAction *m_cleanAction; + QAction *m_cleanSessionAction; + QAction *m_runAction; + QAction *m_cancelBuildAction; + QAction *m_debugAction; + QAction *m_dependenciesAction; + QAction *m_taskAction; + QAction *m_addNewFileAction; + QAction *m_addExistingFilesAction; + QAction *m_openFileAction; + QAction *m_removeFileAction; + QAction *m_renameFileAction; + + QMenu *m_buildConfigurationMenu; + QActionGroup *m_buildConfigurationActionGroup; + QMenu *m_runConfigurationMenu; + QActionGroup *m_runConfigurationActionGroup; + + Core::ICore *m_core; + Internal::ProjectWindow *m_proWindow; + SessionManager *m_session; + + Project *m_currentProject; + Node *m_currentNode; + + BuildManager *m_buildManager; + + QList<Internal::ProjectFileFactory*> m_fileFactories; + QStringList m_profileMimeTypes; + Internal::OutputPane *m_outputPane; + + QStringList m_recentProjects; + static const int m_maxRecentProjects = 7; + QMap<QAction*, QString> m_recentProjectsActions; + + QString m_lastOpenDirectory; + QSharedPointer<RunConfiguration> m_delayedRunConfiguration; + RunControl *m_debuggingRunControl; + QString m_runMode; + QString m_projectFilterString; +}; + +namespace Internal { +class CoreListenerCheckingForRunningBuild : public Core::ICoreListener +{ + Q_OBJECT +public: + CoreListenerCheckingForRunningBuild(BuildManager *manager); + + bool coreAboutToClose(); + +private: + BuildManager *m_manager; +}; +} + +} // namespace ProjectExplorer + +#endif // PROJECTEXPLORER_H diff --git a/src/plugins/projectexplorer/projectexplorer.pri b/src/plugins/projectexplorer/projectexplorer.pri new file mode 100644 index 00000000000..45d1e96374c --- /dev/null +++ b/src/plugins/projectexplorer/projectexplorer.pri @@ -0,0 +1,3 @@ +include(projectexplorer_dependencies.pri) + +LIBS *= -l$$qtLibraryTarget(ProjectExplorer) diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro new file mode 100644 index 00000000000..bbb8a4c74d9 --- /dev/null +++ b/src/plugins/projectexplorer/projectexplorer.pro @@ -0,0 +1,115 @@ +TEMPLATE = lib +TARGET = ProjectExplorer +QT += xml \ + script +include(../../qworkbenchplugin.pri) +include(projectexplorer_dependencies.pri) +include(../../../shared/scriptwrapper/scriptwrapper.pri) +HEADERS += projectexplorer.h \ + projectexplorer_export.h \ + projectwindow.h \ + buildmanager.h \ + compileoutputwindow.h \ + taskwindow.h \ + outputwindow.h \ + persistentsettings.h \ + projectfilewizardextension.h \ + session.h \ + dependenciesdialog.h \ + allprojectsfilter.h \ + buildparserinterface.h \ + projectexplorerconstants.h \ + project.h \ + pluginfilefactory.h \ + iprojectmanager.h \ + currentprojectfilter.h \ + scriptwrappers.h \ + allprojectsfind.h \ + buildstep.h \ + buildconfiguration.h \ + environment.h \ + iprojectproperties.h \ + buildsettingspropertiespage.h \ + environmenteditmodel.h \ + processstep.h \ + abstractprocessstep.h \ + editorconfiguration.h \ + editorsettingspropertiespage.h \ + runconfiguration.h \ + applicationlauncher.h \ + consoleprocess.h \ + abstractprocess.h \ + applicationrunconfiguration.h \ + runsettingspropertiespage.h \ + projecttreewidget.h \ + foldernavigationwidget.h \ + customexecutablerunconfiguration.h \ + buildprogress.h \ + projectnodes.h \ + sessiondialog.h \ + projectwizardpage.h \ + buildstepspage.h \ + removefiledialog.h \ + nodesvisitor.h \ + projectmodels.h \ + currentprojectfind.h +SOURCES += projectexplorer.cpp \ + projectwindow.cpp \ + buildmanager.cpp \ + compileoutputwindow.cpp \ + taskwindow.cpp \ + outputwindow.cpp \ + persistentsettings.cpp \ + projectfilewizardextension.cpp \ + session.cpp \ + dependenciesdialog.cpp \ + allprojectsfilter.cpp \ + currentprojectfilter.cpp \ + scriptwrappers.cpp \ + allprojectsfind.cpp \ + project.cpp \ + pluginfilefactory.cpp \ + buildstep.cpp \ + buildconfiguration.cpp \ + buildparserinterface.cpp \ + environment.cpp \ + buildsettingspropertiespage.cpp \ + environmenteditmodel.cpp \ + processstep.cpp \ + abstractprocessstep.cpp \ + editorconfiguration.cpp \ + editorsettingspropertiespage.cpp \ + runconfiguration.cpp \ + applicationrunconfiguration.cpp \ + runsettingspropertiespage.cpp \ + projecttreewidget.cpp \ + foldernavigationwidget.cpp \ + customexecutablerunconfiguration.cpp \ + buildprogress.cpp \ + projectnodes.cpp \ + sessiondialog.cpp \ + projectwizardpage.cpp \ + buildstepspage.cpp \ + removefiledialog.cpp \ + nodesvisitor.cpp \ + projectmodels.cpp \ + currentprojectfind.cpp +FORMS += dependenciesdialog.ui \ + buildsettingspropertiespage.ui \ + processstep.ui \ + editorsettingspropertiespage.ui \ + runsettingspropertiespage.ui \ + sessiondialog.ui \ + projectwizardpage.ui \ + buildstepspage.ui \ + removefiledialog.ui +win32 { + SOURCES += consoleprocess_win.cpp \ + applicationlauncher_win.cpp \ + winguiprocess.cpp + HEADERS += winguiprocess.h +} +else:unix:SOURCES += consoleprocess_unix.cpp \ + applicationlauncher_x11.cpp +RESOURCES += projectexplorer.qrc +DEFINES += PROJECTEXPLORER_LIBRARY diff --git a/src/plugins/projectexplorer/projectexplorer.qrc b/src/plugins/projectexplorer/projectexplorer.qrc new file mode 100644 index 00000000000..a9e1b3488ef --- /dev/null +++ b/src/plugins/projectexplorer/projectexplorer.qrc @@ -0,0 +1,23 @@ +<RCC> + <qresource prefix="/projectexplorer" > + <file>images/build.png</file> + <file>images/build_small.png</file> + <file>images/clean.png</file> + <file>images/clean_small.png</file> + <file>images/closetab.png</file> + <file>images/compile_error.png</file> + <file>images/compile_unspecified.png</file> + <file>images/compile_warning.png</file> + <file>images/debugger_start.png</file> + <file>images/debugger_start_small.png</file> + <file>images/filtericon.png</file> + <file>images/insert_line_small.png</file> + <file>images/projectexplorer.png</file> + <file>images/rebuild.png</file> + <file>images/rebuild_small.png</file> + <file>images/run.png</file> + <file>images/run_small.png</file> + <file>images/session.png</file> + <file>images/stop.png</file> + </qresource> +</RCC> diff --git a/src/plugins/projectexplorer/projectexplorer_dependencies.pri b/src/plugins/projectexplorer/projectexplorer_dependencies.pri new file mode 100644 index 00000000000..674c8bbb362 --- /dev/null +++ b/src/plugins/projectexplorer/projectexplorer_dependencies.pri @@ -0,0 +1,5 @@ +include(../../libs/utils/utils.pri) +include(../../plugins/quickopen/quickopen.pri) +include(../../plugins/find/find.pri) +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/texteditor/texteditor.pri) diff --git a/src/plugins/projectexplorer/projectexplorer_export.h b/src/plugins/projectexplorer/projectexplorer_export.h new file mode 100644 index 00000000000..e554e40ab07 --- /dev/null +++ b/src/plugins/projectexplorer/projectexplorer_export.h @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/**************************************************************************** +** +** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef PROJECTEXPLORER_EXPORT_H +#define PROJECTEXPLORER_EXPORT_H + +#include <QtCore/qglobal.h> + +#if defined(PROJECTEXPLORER_LIBRARY) +# define PROJECTEXPLORER_EXPORT Q_DECL_EXPORT +#else +# define PROJECTEXPLORER_EXPORT Q_DECL_IMPORT +#endif + +#endif // PROJECTEXPLORER_EXPORT_H diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h new file mode 100644 index 00000000000..04fc727b3f5 --- /dev/null +++ b/src/plugins/projectexplorer/projectexplorerconstants.h @@ -0,0 +1,180 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROJECTEXPLORERCONSTANTS_H +#define PROJECTEXPLORERCONSTANTS_H + +namespace ProjectExplorer { +namespace Constants { + +// modes and their priorities +const char * const MODE_SESSION = "ProjectExplorer.Mode.Session"; +const int P_MODE_SESSION = 85; + +// actions +const char * const NEWSESSION = "ProjectExplorer.NewSession"; +const char * const NEWPROJECT = "ProjectExplorer.NewProject"; +const char * const LOAD = "ProjectExplorer.Load"; +const char * const UNLOAD = "ProjectExplorer.Unload"; +const char * const CLEARSESSION = "ProjectExplorer.ClearSession"; +const char * const BUILD = "ProjectExplorer.Build"; +const char * const BUILDSESSION = "ProjectExplorer.BuildSession"; +const char * const REBUILD = "ProjectExplorer.Rebuild"; +const char * const REBUILDSESSION = "ProjectExplorer.RebuildSession"; +const char * const CLEAN = "ProjectExplorer.Clean"; +const char * const CLEANSESSION = "ProjectExplorer.CleanSession"; +const char * const BUILDCONFIGURATIONMENU = "ProjectExplorer.BuildConfigurationMenu"; +const char * const CANCELBUILD = "ProjectExplorer.CancelBuild"; +const char * const RUNCONFIGURATIONMENU = "ProjectExplorer.RunConfigurationMenu"; +const char * const DEBUG = "ProjectExplorer.Debug"; +const char * const DEPENDENCIES = "ProjectExplorer.Dependencies"; +const char * const FINDINALLPROJECTS = "ProjectExplorer.FindInAllProjects"; +const char * const GOTOTASKWINDOW = "ProjectExplorer.GoToTaskWindow"; +const char * const SHOWPROPERTIES = "ProjectExplorer.ShowProperties"; +const char * const ADDNEWFILE = "ProjectExplorer.AddNewFile"; +const char * const ADDEXISTINGFILES = "ProjectExplorer.AddExistingFiles"; +const char * const OPENFILE = "ProjectExplorer.OpenFile"; +const char * const REMOVEFILE = "ProjectExplorer.RemoveFile"; +const char * const RENAMEFILE = "ProjectExplorer.RenameFile"; + +//Run modes +const char * const RUNMODE = "ProjectExplorer.RunMode"; +const char * const DEBUGMODE = "ProjectExplorer.DebugMode"; + +// action priorities +const int P_ACTION_RUN = 100; +const int P_ACTION_DEBUG = 90; +const int P_ACTION_BUILDSESSION = 80; + +const char * const RUN = "ProjectExplorer.Run"; + +// context +const char * const C_PROJECTEXPLORER = "Project Explorer"; + +// languages +const char * const LANG_CXX = "CXX"; + +// menus +const char * const M_RECENTPROJECTS = "ProjectExplorer.Menu.Recent"; +const char * const M_BUILDPROJECT = "ProjectExplorer.Menu.Build"; +const char * const M_DEBUG = "ProjectExplorer.Menu.Debug"; +const char * const M_SESSION = "ProjectExplorer.Menu.Session"; + +// toolbars +const char * const T_BUILDPROJECT = "ProjectExplorer.ToolBar.Build"; + +// menu groups +const char * const G_BUILD_SESSION = "ProjectExplorer.Group.BuildSession"; +const char * const G_BUILD_PROJECT = "ProjectExplorer.Group.Build"; +const char * const G_BUILD_OTHER = "ProjectExplorer.Group.Other"; +const char * const G_BUILD_RUN = "ProjectExplorer.Group.Run"; +const char * const G_BUILD_TASK = "ProjectExplorer.Group.BuildTask"; +const char * const G_BUILD_CANCEL = "ProjectExplorer.Group.BuildCancel"; + +// toolbar groups +const char * const G_TOOLBAR_CUSTOM = "ProjectExplorer.ToolBarGroup.Custom"; +const char * const G_TOOLBAR_BUILD = "ProjectExplorer.ToolBarGroup.Build"; +const char * const G_TOOLBAR_RUN = "ProjectExplorer.ToolBarGroup.Run"; +const char * const G_TOOLBAR_OTHER = "ProjectExplorer.ToolBarGroup.Other"; + +// context menus +const char * const M_SESSIONCONTEXT = "Project.Menu.Session"; +const char * const M_PROJECTCONTEXT = "Project.Menu.Project"; +const char * const M_SUBPROJECTCONTEXT = "Project.Menu.SubProject"; +const char * const M_FOLDERCONTEXT = "Project.Menu.Folder"; +const char * const M_FILECONTEXT = "Project.Menu.File"; +const char * const M_OPENFILEWITHCONTEXT = "Project.Menu.File.OpenWith"; + +// context menu groups +const char * const G_SESSION_BUILD = "Session.Group.Build"; +const char * const G_SESSION_FILES = "Session.Group.Files"; +const char * const G_SESSION_OTHER = "Session.Group.Other"; +const char * const G_SESSION_CONFIG = "Session.Group.Config"; + +const char * const G_PROJECT_OPEN = "Project.Group.Open"; +const char * const G_PROJECT_NEW = "Project.Group.New"; +const char * const G_PROJECT_FILES = "Project.Group.Files"; +const char * const G_PROJECT_BUILD = "Project.Group.Build"; +const char * const G_PROJECT_OTHER = "Project.Group.Other"; +const char * const G_PROJECT_RUN = "Project.Group.Run"; +const char * const G_PROJECT_CONFIG = "Project.Group.Config"; + +const char * const G_FOLDER_FILES = "ProjectFolder.Group.Files"; +const char * const G_FOLDER_OTHER = "ProjectFolder.Group.Other"; +const char * const G_FOLDER_CONFIG = "ProjectFolder.Group.Config"; + +const char * const G_FILE_OPEN = "ProjectFile.Group.Open"; +const char * const G_FILE_OTHER = "ProjectFile.Group.Other"; +const char * const G_FILE_CONFIG = "ProjectFile.Group.Config"; + +// file kind +const char * const FILE_FACTORY_KIND = "ProjectExplorer.FileFactoryKind"; + +// wizard kind +const char * const WIZARD_TYPE_PROJECT = "ProjectExplorer.WizardType.Project"; + +// icons +const char * const ICON_BUILD = ":/projectexplorer/images/build.png"; +const char * const ICON_BUILD_SMALL = ":/projectexplorer/images/build_small.png"; +const char * const ICON_CLEAN = ":/projectexplorer/images/clean.png"; +const char * const ICON_CLEAN_SMALL = ":/projectexplorer/images/clean_small.png"; +const char * const ICON_REBUILD = ":/projectexplorer/images/rebuild.png"; +const char * const ICON_REBUILD_SMALL = ":/projectexplorer/images/rebuild_small.png"; +const char * const ICON_RUN = ":/projectexplorer/images/run.png"; +const char * const ICON_RUN_SMALL = ":/projectexplorer/images/run_small.png"; +const char * const ICON_SESSION = ":/projectexplorer/images/session.png"; +const char * const ICON_INSERT_LINE = ":/projectexplorer/images/insert_line_small.png"; +const char * const ICON_DEBUG = ":/projectexplorer/images/debugger_start.png"; +const char * const ICON_DEBUG_SMALL = ":/projectexplorer/images/debugger_start_small.png"; +const char * const ICON_CLOSETAB = ":/projectexplorer/images/closetab.png"; +const char * const ICON_STOP = ":/projectexplorer/images/stop.png"; + +// find filters +const char * const FIND_CUR_PROJECT = "ProjectExplorer.FindFilter.CurrentProject"; +const char * const FIND_ALL_PROJECTS = "ProjectExplorer.FindFilter.AllProjects"; + +const char * const TASK_BUILD = "ProjectExplorer.Task.Build"; +const char * const SESSIONFILE_MIMETYPE = "application/vnd.nokia.xml.qt.creator.session"; + + +const char * const PROFILE_MIMETYPE = "application/vnd.nokia.qt.qmakeprofile"; +const char * const C_SOURCE_MIMETYPE = "text/x-csrc"; +const char * const C_HEADER_MIMETYPE = "text/x-chdr"; +const char * const CPP_SOURCE_MIMETYPE = "text/x-c++src"; +const char * const CPP_HEADER_MIMETYPE = "text/x-c++hdr"; +const char * const FORM_MIMETYPE = "application/x-designer"; +const char * const RESOURCE_MIMETYPE = "application/vnd.nokia.xml.qt.resource"; + +} +} + +#endif //PROJECTEXPLORERCONSTANTS_H diff --git a/src/plugins/projectexplorer/projectfilewizardextension.cpp b/src/plugins/projectexplorer/projectfilewizardextension.cpp new file mode 100644 index 00000000000..c00a969e7e1 --- /dev/null +++ b/src/plugins/projectexplorer/projectfilewizardextension.cpp @@ -0,0 +1,199 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "projectfilewizardextension.h" +#include "projectexplorer.h" +#include "projectnodes.h" +#include "nodesvisitor.h" +#include "projectwizardpage.h" + +#include <coreplugin/basefilewizard.h> +#include <coreplugin/dialogs/iwizard.h> +#include <coreplugin/filemanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/iversioncontrol.h> +#include <coreplugin/vcsmanager.h> + +#include <QtCore/QVariant> +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtCore/QMultiMap> + +enum { debugExtension = 0 }; + +namespace ProjectExplorer { + +typedef QList<ProjectNode *> ProjectNodeList; + +namespace Internal { + +// --------- AllProjectNodesVisitor. Figure out all projects. +// No sooner said then done. +class AllProjectNodesVisitor : public NodesVisitor +{ + AllProjectNodesVisitor(ProjectNodeList &l) : m_projectNodes(l) {} +public: + + static ProjectNodeList allProjects(const ProjectExplorerPlugin *pe); + + virtual void visitProjectNode(ProjectNode *node); + +private: + ProjectNodeList &m_projectNodes; +}; + +ProjectNodeList AllProjectNodesVisitor::allProjects(const ProjectExplorerPlugin *pe) +{ + ProjectNodeList rc; + AllProjectNodesVisitor visitor(rc); + pe->session()->sessionNode()->accept(&visitor); + return rc; +} + +void AllProjectNodesVisitor::visitProjectNode(ProjectNode *node) +{ + if (node->supportedActions().contains(ProjectNode::AddFile)) + m_projectNodes << node; +} + +// --------- ProjectWizardContext +struct ProjectWizardContext { + Core::IVersionControl *versionControl; + ProjectNodeList projects; + ProjectWizardPage *page; +}; + +// ---- ProjectFileWizardExtension +ProjectFileWizardExtension::ProjectFileWizardExtension(Core::ICore *core) : + m_core(core), + m_context(0) +{ +} + +ProjectFileWizardExtension::~ProjectFileWizardExtension() +{ + delete m_context; +} + +void ProjectFileWizardExtension::firstExtensionPageShown(const QList<Core::GeneratedFile> &files) +{ + if (debugExtension) + qDebug() << Q_FUNC_INFO << files.size(); + // Setup files display and version control depending on path + QStringList fileNames; + foreach (const Core::GeneratedFile &f, files) + fileNames.push_back(f.path()); + + const QString directory = QFileInfo(fileNames.front()).absolutePath(); + m_context->versionControl = m_core->vcsManager()->findVersionControlForDirectory(directory); + + m_context->page->setFilesDisplay(fileNames); + m_context->page->setAddToVersionControlEnabled(m_context->versionControl != 0); +} + +static ProjectNode *currentProject() +{ + if (Node *currentNode = ProjectExplorerPlugin::instance()->currentNode()) + if (ProjectNode *currentProjectNode = qobject_cast<ProjectNode *>(currentNode)) + return currentProjectNode; + return 0; +} + +QList<QWizardPage *> ProjectFileWizardExtension::extensionPages(const Core::IWizard *wizard) +{ + if (!m_context) + m_context = new ProjectWizardContext; + // Init context with page and projects + m_context->page = new ProjectWizardPage; + m_context->versionControl = 0; + m_context->projects = AllProjectNodesVisitor::allProjects(ProjectExplorerPlugin::instance()); + // Set up project list which remains the same over duration of wizard execution + // Disable "add project to project" + const bool hasProjects = !m_context->projects.empty(); + if (hasProjects) { + // Compile list of names and find current project if there is one + QStringList projectNames; + ProjectNode *current = currentProject(); + int currentIndex = -1; + const int count = m_context->projects.size(); + for (int i = 0; i < count; i++) { + ProjectNode *pn = m_context->projects.at(i); + projectNames.push_back(pn->name()); + if (current == pn) + currentIndex = i; + } + m_context->page->setProjects(projectNames); + if (currentIndex != -1) + m_context->page->setCurrentProjectIndex(currentIndex); + } + m_context->page->setAddToProjectEnabled(hasProjects && wizard->kind() != Core::IWizard::ProjectWizard); + + return QList<QWizardPage *>() << m_context->page; +} + +bool ProjectFileWizardExtension::process(const QList<Core::GeneratedFile> &files, QString *errorMessage) +{ + typedef QMultiMap<FileType, QString> TypeFileMap; + // Add files to project && version control + if (m_context->page->addToProject()) { + ProjectNode *project = m_context->projects.at(m_context->page->currentProjectIndex()); + // Split into lists by file type and add + TypeFileMap typeFileMap; + foreach (const Core::GeneratedFile &generatedFile, files) { + const QString path = generatedFile.path(); + typeFileMap.insert(typeForFileName(m_core->mimeDatabase(), path), path); + } + foreach (FileType type, typeFileMap.uniqueKeys()) { + const QStringList files = typeFileMap.values(type); + if (!project->addFiles(type, files)) { + *errorMessage = tr("Failed to add one or more files to project\n'%1' (%2)."). + arg(project->path(), files.join(QLatin1String(","))); + return false; + } + } + } + // Add files to version control + if (m_context->page->addToVersionControl()) { + foreach (const Core::GeneratedFile &generatedFile, files) { + if (!m_context->versionControl->vcsAdd(generatedFile.path())) { + *errorMessage = tr("Failed to add '%1' to the version control system.").arg(generatedFile.path()); + return false; + } + } + } + + return true; +} + + +} +} diff --git a/src/plugins/projectexplorer/projectfilewizardextension.h b/src/plugins/projectexplorer/projectfilewizardextension.h new file mode 100644 index 00000000000..aa58d5a232b --- /dev/null +++ b/src/plugins/projectexplorer/projectfilewizardextension.h @@ -0,0 +1,73 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROJECTFILEWIZARDEXTENSION2_H +#define PROJECTFILEWIZARDEXTENSION2_H + +#include <coreplugin/ifilewizardextension.h> + +namespace Core { + class ICore; +} + +namespace ProjectExplorer { + +namespace Internal { + +struct ProjectWizardContext; + +/* + Adds final page allowing the user to add files to project && version control + to BaseFileWizard. + */ +class ProjectFileWizardExtension : public Core::IFileWizardExtension +{ + Q_OBJECT +public: + explicit ProjectFileWizardExtension(Core::ICore *core); + virtual ~ProjectFileWizardExtension(); + + virtual QList<QWizardPage *> extensionPages(const Core::IWizard *wizard); + virtual bool process(const QList<Core::GeneratedFile> &files, QString *errorMessage); + +public slots: + virtual void firstExtensionPageShown(const QList<Core::GeneratedFile> &); + +private: + Core::ICore *m_core; + ProjectWizardContext *m_context; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // PROJECTFILEWIZARDEXTENSION2_H diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp new file mode 100644 index 00000000000..f15ad466423 --- /dev/null +++ b/src/plugins/projectexplorer/projectmodels.cpp @@ -0,0 +1,1180 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "project.h" +#include "projectmodels.h" +#include "projectexplorerconstants.h" + +#include <coreplugin/fileiconprovider.h> + +#include <QtGui/QApplication> +#include <QtGui/QIcon> +#include <QtGui/QStyle> +#include <QtGui/QMessageBox> +#include <QtGui/QSortFilterProxyModel> +#include <QtCore/QFileInfo> +#include <QtCore/QDebug> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; +using Core::FileIconProvider; + +namespace { + // sorting helper function + bool sortNodes(Node *n1, Node *n2) { + // Ordering is: project files, project, folder, file + + const NodeType n1Type = n1->nodeType(); + const NodeType n2Type = n2->nodeType(); + + // project files + FileNode *file1 = qobject_cast<FileNode*>(n1); + FileNode *file2 = qobject_cast<FileNode*>(n2); + if (file1 && file1->fileType() == ProjectFileType) { + if (file2 && file2->fileType() == ProjectFileType) { + const QString fileName1 = QFileInfo(file1->path()).fileName(); + const QString fileName2 = QFileInfo(file2->path()).fileName(); + + if (fileName1 != fileName2) + return fileName1 < fileName2; + else + return file1 < file2; + } else { + return true; // project file is before everything else + } + } else { + if (file2 && file2->fileType() == ProjectFileType) { + return false; + } + } + + // projects + if (n1Type == ProjectNodeType) { + if (n2Type == ProjectNodeType) { + ProjectNode *project1 = static_cast<ProjectNode*>(n1); + ProjectNode *project2 = static_cast<ProjectNode*>(n2); + + if (project1->name() != project2->name()) + return project1->name() < project2->name(); // sort by name + else + return project1 < project2; // sort by pointer value + } else { + return true; // project is before folder & file + } + } + if (n2Type == ProjectNodeType) + return false; + + if (n1Type == FolderNodeType) { + if (n2Type == FolderNodeType) { + FolderNode *folder1 = static_cast<FolderNode*>(n1); + FolderNode *folder2 = static_cast<FolderNode*>(n2); + + if (folder1->name() != folder2->name()) + return folder1->name() < folder2->name(); + else + return folder1 < folder2; + } else { + return true; // folder is before file + } + } + if (n2Type == FolderNodeType) + return false; + + // must be file nodes + { + const QString filePath1 = n1->path(); + const QString filePath2 = n2->path(); + + const QString fileName1 = QFileInfo(filePath1).fileName(); + const QString fileName2 = QFileInfo(filePath2).fileName(); + + if (fileName1 != fileName2) { + return fileName1 < fileName2; // sort by file names + } else { + if (filePath1 != filePath2) { + return filePath1 < filePath2; // sort by path names + } else { + return n1 < n2; // sort by pointer value + } + } + } + return false; + } +} + +/*! + \class DetailedModel + + A 1:1 mapping of the internal node tree. + + The detailed model shows the complete project file hierarchy and all containing files. + */ + +DetailedModel::DetailedModel(SessionNode *rootNode, QObject *parent) + : QAbstractItemModel(parent), + m_rootNode(rootNode), + //m_startupProject(0), + m_folderToAddTo(0) +{ + NodesWatcher *watcher = new NodesWatcher(this); + m_rootNode->registerWatcher(watcher); + connect(watcher, SIGNAL(foldersAboutToBeAdded(FolderNode*, const QList<FolderNode*> &)), + this, SLOT(foldersAboutToBeAdded(FolderNode*, const QList<FolderNode*> &))); + connect(watcher, SIGNAL(foldersAdded()), + this, SLOT(foldersAdded())); + connect(watcher, SIGNAL(foldersAboutToBeRemoved(FolderNode*, const QList<FolderNode*> &)), + this, SLOT(foldersAboutToBeRemoved(FolderNode*, const QList<FolderNode*> &))); + connect(watcher, SIGNAL(filesAboutToBeAdded(FolderNode*, const QList<FileNode*> &)), + this, SLOT(filesAboutToBeAdded(FolderNode*, const QList<FileNode*> &))); + connect(watcher, SIGNAL(filesAdded()), + this, SLOT(filesAdded())); + connect(watcher, SIGNAL(filesAboutToBeRemoved(FolderNode*, const QList<FileNode*> &)), + this, SLOT(filesAboutToBeRemoved(FolderNode*, const QList<FileNode*> &))); +} + +QModelIndex DetailedModel::index(int row, int column, const QModelIndex &parent) const +{ + QModelIndex result; + if (!parent.isValid() && row == 0 && column == 0) { + result = createIndex(0, 0, m_rootNode); + } else if (column == 0) { + FolderNode *parentNode = qobject_cast<FolderNode*>(nodeForIndex(parent)); + Q_ASSERT(parentNode); + result = createIndex(row, 0, m_childNodes.value(parentNode).at(row)); + } + return result; +} + +QModelIndex DetailedModel::parent(const QModelIndex &index) const +{ + QModelIndex parentIndex; + + if (Node *node = nodeForIndex(index)) { + if (FolderNode *parentFolderNode = node->parentFolderNode()) { + if (FolderNode *grandParentFolderNode = parentFolderNode->parentFolderNode()) { + Q_ASSERT(m_childNodes.contains(grandParentFolderNode)); + int row = m_childNodes.value(grandParentFolderNode).indexOf(parentFolderNode); + parentIndex = createIndex(row, 0, parentFolderNode); + } else { + parentIndex = createIndex(0, 0, parentFolderNode); + } + } + } + + return parentIndex; +} + +Qt::ItemFlags DetailedModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags result; + if (index.isValid()) { + result = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + + if (Node *node = nodeForIndex(index)) { + if (node->nodeType() == FileNodeType) + result |= Qt::ItemIsEditable; + } + } + return result; +} + +QVariant DetailedModel::data(const QModelIndex &index, int role) const +{ + QVariant result; + + if (Node *node = nodeForIndex(index)) { + FolderNode *folderNode = qobject_cast<FolderNode*>(node); + switch (role) { + case Qt::DisplayRole: + case Qt::EditRole: { + if (folderNode) + result = folderNode->name(); + else + result = QFileInfo(node->path()).fileName(); //TODO cache that? + break; + } + case Qt::ToolTipRole: { + if (folderNode && (folderNode->nodeType() != ProjectNodeType)) + result = tr("%1 of project %2").arg(folderNode->name()).arg(folderNode->projectNode()->name()); + else + result = node->path(); + break; + } + case Qt::DecorationRole: { + if (folderNode) + result = folderNode->icon(); + else + result = FileIconProvider::instance()->icon(QFileInfo(node->path())); + break; + } + case Qt::FontRole: { + QFont font; + if (qobject_cast<ProjectNode*>(folderNode)) { + if (index == this->index(0,0) && m_isStartupProject) + font.setBold(true); + } + result = font; + break; + } + case ProjectExplorer::Project::FilePathRole: { + result = node->path(); + break; + } + } + } + + return result; +} + +bool DetailedModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + bool result = false; + + if (Node *node = nodeForIndex(index)) { + FileNode *fileNode = qobject_cast<FileNode*>(node); + if (fileNode && role == Qt::EditRole && !value.toString().isEmpty()) { + ProjectNode *projectNode = node->projectNode(); + QString newPath = QFileInfo(fileNode->path()).dir().absoluteFilePath(value.toString()); + if (!projectNode->renameFile(fileNode->fileType(), fileNode->path(), newPath)) + QMessageBox::warning(0, tr("Could not rename file"), + tr("Renaming file %1 to %2 failed.").arg(fileNode->path()) + .arg(value.toString())); + } + } + + return result; +} + +int DetailedModel::rowCount(const QModelIndex & parent) const +{ + int count = 0; + + if (!parent.isValid()) { // root item + count = 1; + } else { + // we can be sure that internal cache + // has been updated by fetchMore() + FolderNode *folderNode = qobject_cast<FolderNode*>(nodeForIndex(parent)); + if (folderNode && m_childNodes.contains(folderNode)) + count = m_childNodes.value(folderNode).size(); + } + return count; +} + +int DetailedModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 1; +} + +bool DetailedModel::hasChildren(const QModelIndex &parent) const +{ + bool hasChilds = false; + + if (!parent.isValid()) {// root index + hasChilds = true; + } else { + if (FolderNode *folderNode = qobject_cast<FolderNode*>(nodeForIndex(parent))) { + if (m_childNodes.contains(folderNode)) // internal cache + hasChilds = !m_childNodes.value(folderNode).isEmpty(); + else { + if (!folderNode->subFolderNodes().isEmpty() + || !folderNode->fileNodes().isEmpty()) // underlying data + hasChilds = true; + else { + // Make sure add/remove signals are emitted + // even for empty nodes (i.e. where canFetchMore + // returns false) + m_childNodes.insert(folderNode, QList<Node*>()); + } + } + } + } + + return hasChilds; +} + +/*! + Returns true if internal cache has not been built up yet + */ +bool DetailedModel::canFetchMore(const QModelIndex & parent) const +{ + bool canFetch = false; + if (parent.isValid()) { + if (FolderNode *folderNode = qobject_cast<FolderNode*>(nodeForIndex(parent))) { + canFetch = !m_childNodes.contains(folderNode); + } + } + return canFetch; +} + +/*! + Updates internal cache + */ +void DetailedModel::fetchMore(const QModelIndex & parent) +{ + FolderNode *folderNode = qobject_cast<FolderNode*>(nodeForIndex(parent)); + Q_ASSERT(folderNode); + Q_ASSERT(!m_childNodes.contains(folderNode)); + + m_childNodes.insert(folderNode, childNodeList(folderNode)); +} + +void DetailedModel::reset() +{ + // todo:How to implement this correctly????? + m_childNodes.clear(); + QAbstractItemModel::reset(); +} + +void DetailedModel::foldersAboutToBeAdded(FolderNode *parentFolder, + const QList<FolderNode*> &newFolders) +{ + Q_UNUSED(newFolders); + Q_ASSERT(parentFolder); + + if (m_childNodes.contains(parentFolder)) + m_folderToAddTo = parentFolder; +} + +void DetailedModel::foldersAdded() +{ + if (m_folderToAddTo) { + QList<Node*> newChildNodes = childNodeList(m_folderToAddTo); + addToChildNodes(m_folderToAddTo, newChildNodes); + m_folderToAddTo = 0; + } +} + +void DetailedModel::foldersAboutToBeRemoved(FolderNode *parentFolder, + const QList<FolderNode*> &staleFolders) +{ + Q_ASSERT(parentFolder); + + if (m_childNodes.contains(parentFolder)) { + QList<Node*> newChildNodes = m_childNodes.value(parentFolder); + QList<FolderNode*> nodesToRemove = staleFolders; + qSort(nodesToRemove.begin(), nodesToRemove.end(), sortNodes); + + QList<Node*>::iterator newListIter = newChildNodes.begin(); + QList<FolderNode*>::const_iterator toRemoveIter = nodesToRemove.constBegin(); + for (; toRemoveIter != nodesToRemove.constEnd(); ++toRemoveIter) { + while (*newListIter != *toRemoveIter) + ++newListIter; + newListIter = newChildNodes.erase(newListIter); + } + + removeFromChildNodes(parentFolder, newChildNodes); + + // Clear cache for all folders beneath the current folder + foreach (FolderNode *folder, staleFolders) { + foreach (FolderNode *subFolder, recursiveSubFolders(folder)) { + m_childNodes.remove(subFolder); + } + } + } +} + +void DetailedModel::filesAboutToBeAdded(FolderNode *parentFolder, + const QList<FileNode*> &newFiles) +{ + Q_UNUSED(newFiles); + Q_ASSERT(parentFolder); + + if (m_childNodes.contains(parentFolder)) + m_folderToAddTo = parentFolder; +} + +void DetailedModel::filesAdded() +{ + if (m_folderToAddTo) { + QList<Node*> newChildNodes = childNodeList(m_folderToAddTo); + addToChildNodes(m_folderToAddTo, newChildNodes); + m_folderToAddTo = 0; + } +} + +void DetailedModel::filesAboutToBeRemoved(FolderNode *parentFolder, + const QList<FileNode*> &staleFiles) +{ + Q_ASSERT(parentFolder); + + if (m_childNodes.contains(parentFolder)) { + QList<Node*> newChildNodes = m_childNodes.value(parentFolder); + QList<FileNode*> nodesToRemove = staleFiles; + qSort(nodesToRemove.begin(), nodesToRemove.end(), sortNodes); + + QList<Node*>::iterator newListIter = newChildNodes.begin(); + QList<FileNode*>::const_iterator toRemoveIter = nodesToRemove.constBegin(); + for (; toRemoveIter != nodesToRemove.constEnd(); ++toRemoveIter) { + while (*newListIter != *toRemoveIter) + ++newListIter; + newListIter = newChildNodes.erase(newListIter); + } + + removeFromChildNodes(parentFolder, newChildNodes); + } +} + +Node *DetailedModel::nodeForIndex(const QModelIndex &index) const +{ + return (Node*)index.internalPointer(); +} + +/*! + Returns the index corresponding to a node. + */ +QModelIndex DetailedModel::indexForNode(const Node *node) +{ + if (!node) + return QModelIndex(); + + if (FolderNode *parentFolder = node->parentFolderNode()) { + // iterate recursively + QModelIndex parentIndex = indexForNode(parentFolder); + + // update internal cache + if (canFetchMore(parentIndex)) + fetchMore(parentIndex); + Q_ASSERT(m_childNodes.contains(parentFolder)); + + int row = m_childNodes.value(parentFolder).indexOf(const_cast<Node*>(node)); + if (row >= 0) + return index(row, 0, parentIndex); + else + return QModelIndex(); + } else { + // root node + return index(0, 0); + } +} + +QList<Node*> DetailedModel::childNodeList(FolderNode *folderNode) const +{ + QList<FolderNode*> folderNodes = folderNode->subFolderNodes(); + QList<FileNode*> fileNodes = folderNode->fileNodes(); + + QList<Node*> nodes; + foreach (FolderNode *folderNode, folderNodes) + nodes << folderNode; + foreach (FileNode *fileNode, fileNodes) + nodes << fileNode; + + qSort(nodes.begin(), nodes.end(), sortNodes); + + return nodes; +} + +void DetailedModel::addToChildNodes(FolderNode *parentFolder, QList<Node*> newChildNodes) +{ + QList<Node*> childNodes = m_childNodes.value(parentFolder); + QModelIndex parentIndex = indexForNode(parentFolder); + Q_ASSERT(parentIndex.isValid()); + + // position -> nodes, with positions in decreasing order + QList<QPair<int, QList<Node*> > > insertions; + + // add nodes that should be added at the end or in between + int newIndex = newChildNodes.size() - 1; + for (int oldIndex = childNodes.size() - 1; + oldIndex >= 0; --oldIndex, --newIndex) { + QList<Node*> nodesPerIndex; + Node* oldNode = childNodes.at(oldIndex); + while (newChildNodes.at(newIndex) != oldNode) { + nodesPerIndex.append(newChildNodes.at(newIndex)); + --newIndex; + } + if (!nodesPerIndex.isEmpty()) + insertions.append(QPair<int, QList<Node*> >(oldIndex + 1, nodesPerIndex)); + } + { // add nodes that should be added to the beginning + QList<Node*> insertAtStart; + while (newIndex >= 0) { + insertAtStart.append(newChildNodes.at(newIndex--)); + } + if (!insertAtStart.isEmpty()) + insertions.append(QPair<int, QList<Node*> >(0, insertAtStart)); + } + + for (QList<QPair<int, QList<Node*> > >::const_iterator iter = insertions.constBegin(); + iter != insertions.constEnd(); ++iter) { + + const int key = iter->first; + const QList<Node*> nodesToInsert = iter->second; + + beginInsertRows(parentIndex, key, key + nodesToInsert.size() - 1); + + // update internal cache + for (QList<Node*>::const_iterator nodeIterator = nodesToInsert.constBegin(); + nodeIterator != nodesToInsert.constEnd(); ++nodeIterator) + childNodes.insert(key, *nodeIterator); + m_childNodes.insert(parentFolder, childNodes); + + endInsertRows(); + } + + Q_ASSERT(childNodes == newChildNodes); +} + +void DetailedModel::removeFromChildNodes(FolderNode *parentFolder, QList<Node*> newChildNodes) +{ + QList<Node*> childNodes = m_childNodes.value(parentFolder); + QModelIndex parentIndex = indexForNode(parentFolder); + Q_ASSERT(parentIndex.isValid()); + + // position -> nodes, with positions in decreasing order + QList<QPair<int, QList<Node*> > > deletions; + + // add nodes that should be removed at the end or in between + int oldIndex = childNodes.size() - 1; + for (int newIndex = newChildNodes.size() - 1; + newIndex >= 0; --oldIndex, --newIndex) { + QList<Node*> nodesPerIndex; + Node* newNode = newChildNodes.at(newIndex); + while (childNodes.at(oldIndex) != newNode) { + nodesPerIndex.append(childNodes.at(oldIndex)); + --oldIndex; + } + if (!nodesPerIndex.isEmpty()) + deletions.append(QPair<int, QList<Node*> >(oldIndex + 1, nodesPerIndex)); + } + { // add nodes that should be removed to the beginning + QList<Node*> deleteAtStart; + while (oldIndex >= 0) { + deleteAtStart.append(childNodes.at(oldIndex--)); + } + if (!deleteAtStart.isEmpty()) + deletions.append(QPair<int, QList<Node*> >(0, deleteAtStart)); + } + + for (QList<QPair<int, QList<Node*> > >::const_iterator iter = deletions.constBegin(); + iter != deletions.constEnd(); ++iter) { + + const int key = iter->first; + const QList<Node*> nodes = iter->second; + + beginRemoveRows(parentIndex, key, key + nodes.size() - 1); + + // update internal cache + for (int i = nodes.size(); i > 0; --i) + childNodes.removeAt(key); + m_childNodes.insert(parentFolder, childNodes); + + endRemoveRows(); + } + + Q_ASSERT(childNodes == newChildNodes); +} + +QList<FolderNode*> DetailedModel::recursiveSubFolders(FolderNode *parentFolder) +{ + QList<FolderNode*> subFolders; + foreach (FolderNode *subFolder, parentFolder->subFolderNodes()) { + subFolders.append(subFolder); + subFolders != recursiveSubFolders(subFolder); + } + return subFolders; +} + + +/*! + \class FlatModel + + The flat model shows only application/library projects. + + + Shows all application/library projects directly unter the root project + This results in a "flattened" view (max 3 hierachies). + */ + +FlatModel::FlatModel(SessionNode *rootNode, QObject *parent) + : QAbstractItemModel(parent), + m_filterProjects(true), + m_filterGeneratedFiles(true), + m_rootNode(rootNode), + m_startupProject(0), + m_parentFolderForChange(0) +{ + NodesWatcher *watcher = new NodesWatcher(this); + m_rootNode->registerWatcher(watcher); + + connect(watcher, SIGNAL(foldersAboutToBeAdded(FolderNode *, const QList<FolderNode*> &)), + this, SLOT(foldersAboutToBeAdded(FolderNode *, const QList<FolderNode*> &))); + connect(watcher, SIGNAL(foldersAdded()), + this, SLOT(foldersAdded())); + + connect(watcher, SIGNAL(foldersAboutToBeRemoved(FolderNode *, const QList<FolderNode*> &)), + this, SLOT(foldersAboutToBeRemoved(FolderNode *, const QList<FolderNode*> &))); + connect(watcher, SIGNAL(foldersRemoved()), + this, SLOT(foldersRemoved())); + + connect(watcher, SIGNAL(filesAboutToBeAdded(FolderNode *,const QList<FileNode*> &)), + this, SLOT(filesAboutToBeAdded(FolderNode *, const QList<FileNode *> &))); + connect(watcher, SIGNAL(filesAdded()), + this, SLOT(filesAdded())); + + connect(watcher, SIGNAL(filesAboutToBeRemoved(FolderNode *, const QList<FileNode*> &)), + this, SLOT(filesAboutToBeRemoved(FolderNode *, const QList<FileNode*> &))); + connect(watcher, SIGNAL(filesRemoved()), + this, SLOT(filesRemoved())); +} + +QModelIndex FlatModel::index(int row, int column, const QModelIndex &parent) const +{ + QModelIndex result; + if (!parent.isValid() && row == 0 && column == 0) { // session + result = createIndex(0, 0, m_rootNode); + } else if (parent.isValid() && column == 0) { + FolderNode *parentNode = qobject_cast<FolderNode*>(nodeForIndex(parent)); + Q_ASSERT(parentNode); + QHash<FolderNode*, QList<Node*> >::const_iterator it = m_childNodes.constFind(parentNode); + if (it == m_childNodes.constEnd()) { + fetchMore(parentNode); + it = m_childNodes.constFind(parentNode); + } + + if (row < it.value().size()) + result = createIndex(row, 0, it.value().at(row)); + } +// qDebug() << "index of " << row << column << parent.data(Project::FilePathRole) << " is " << result.data(Project::FilePathRole); + return result; +} + +QModelIndex FlatModel::parent(const QModelIndex &idx) const +{ + QModelIndex parentIndex; + if (Node *node = nodeForIndex(idx)) { + FolderNode *parentNode = visibleFolderNode(node->parentFolderNode()); + if (parentNode) { + FolderNode *grandParentNode = visibleFolderNode(parentNode->parentFolderNode()); + if (grandParentNode) { + QHash<FolderNode*, QList<Node*> >::const_iterator it = m_childNodes.constFind(grandParentNode); + if (it == m_childNodes.constEnd()) { + fetchMore(grandParentNode); + it = m_childNodes.constFind(grandParentNode); + } + Q_ASSERT(it != m_childNodes.constEnd()); + const int row = it.value().indexOf(parentNode); + Q_ASSERT(row >= 0); + parentIndex = createIndex(row, 0, parentNode); + } else { + // top level node, parent is session + parentIndex = index(0, 0, QModelIndex()); + } + } + } + +// qDebug() << "parent of " << idx.data(Project::FilePathRole) << " is " << parentIndex.data(Project::FilePathRole); + + return parentIndex; +} + +QVariant FlatModel::data(const QModelIndex &index, int role) const +{ + QVariant result; + + if (Node *node = nodeForIndex(index)) { + FolderNode *folderNode = qobject_cast<FolderNode*>(node); + switch (role) { + case Qt::DisplayRole: + case Qt::EditRole: { + if (folderNode) + result = folderNode->name(); + else + result = QFileInfo(node->path()).fileName(); //TODO cache that? + break; + } + case Qt::ToolTipRole: { + result = node->path(); + break; + } + case Qt::DecorationRole: { + if (folderNode) + result = folderNode->icon(); + else + result = FileIconProvider::instance()->icon(QFileInfo(node->path())); + break; + } + case Qt::FontRole: { + QFont font; + if (node == m_startupProject) + font.setBold(true); + result = font; + break; + } + case ProjectExplorer::Project::FilePathRole: { + result = node->path(); + break; + } + } + } + + return result; +} + +int FlatModel::rowCount(const QModelIndex &parent) const +{ + int rows = 0; + if (!parent.isValid()) { + rows = 1; + } else { + FolderNode *folderNode = qobject_cast<FolderNode*>(nodeForIndex(parent)); + if (folderNode && m_childNodes.contains(folderNode)) + rows = m_childNodes.value(folderNode).size(); + } + return rows; +} + +int FlatModel::columnCount(const QModelIndex &/*parent*/) const +{ + return 1; +} + +bool FlatModel::hasChildren(const QModelIndex &parent) const +{ + if (!parent.isValid()) + return true; + + FolderNode *folderNode = qobject_cast<FolderNode*>(nodeForIndex(parent)); + if (!folderNode) + return false; + + QHash<FolderNode*, QList<Node*> >::const_iterator it = m_childNodes.constFind(folderNode); + if (it == m_childNodes.constEnd()) { + fetchMore(folderNode); + it = m_childNodes.constFind(folderNode); + } + return !it.value().isEmpty(); +} + +bool FlatModel::canFetchMore(const QModelIndex & parent) const +{ + if (!parent.isValid()) { + return false; + } else { + if (FolderNode *folderNode = qobject_cast<FolderNode*>(nodeForIndex(parent))) + return !m_childNodes.contains(folderNode); + else + return false; + } +} + +void FlatModel::recursiveAddFolderNodes(FolderNode *startNode, QList<Node *> *list, const QSet<Node *> &blackList) const +{ + foreach (FolderNode *folderNode, startNode->subFolderNodes()) { + if (folderNode && !blackList.contains(folderNode)) + recursiveAddFolderNodesImpl(folderNode, list, blackList); + } +} + +void FlatModel::recursiveAddFolderNodesImpl(FolderNode *startNode, QList<Node *> *list, const QSet<Node *> &blackList) const +{ + if (!filter(startNode)) { + if (!blackList.contains(startNode)) + list->append(startNode); + } else { + foreach (FolderNode *folderNode, startNode->subFolderNodes()) { + if (folderNode && !blackList.contains(folderNode)) + recursiveAddFolderNodesImpl(folderNode, list, blackList); + } + } +} + +void FlatModel::recursiveAddFileNodes(FolderNode *startNode, QList<Node *> *list, const QSet<Node *> &blackList) const +{ + foreach (FolderNode *subFolderNode, startNode->subFolderNodes()) { + if (!blackList.contains(subFolderNode)) + recursiveAddFileNodes(subFolderNode, list, blackList); + } + foreach (Node *node, startNode->fileNodes()) { + if (!blackList.contains(node) && !filter(node)) + list->append(node); + } +} + +QList<Node*> FlatModel::childNodes(FolderNode *parentNode, const QSet<Node*> &blackList) const +{ + QList<Node*> nodeList; + + if (parentNode->nodeType() == SessionNodeType) { + SessionNode *sessionNode = static_cast<SessionNode*>(parentNode); + QList<ProjectNode*> projectList = sessionNode->projectNodes(); + for (int i = 0; i < projectList.size(); ++i) { + if (!blackList.contains(projectList.at(i))) + nodeList << projectList.at(i); + } + } else { + recursiveAddFolderNodes(parentNode, &nodeList, blackList); + recursiveAddFileNodes(parentNode, &nodeList, blackList + nodeList.toSet()); + } + qSort(nodeList.begin(), nodeList.end(), sortNodes); + return nodeList; +} + +void FlatModel::fetchMore(FolderNode *folderNode) const +{ + Q_ASSERT(folderNode); + Q_ASSERT(!m_childNodes.contains(folderNode)); + + QList<Node*> nodeList = childNodes(folderNode); + m_childNodes.insert(folderNode, nodeList); +} + +void FlatModel::fetchMore(const QModelIndex &parent) +{ + FolderNode *folderNode = qobject_cast<FolderNode*>(nodeForIndex(parent)); + Q_ASSERT(folderNode); + + fetchMore(folderNode); +} + +void FlatModel::setStartupProject(ProjectNode *projectNode) +{ + if (m_startupProject != projectNode) { + QModelIndex oldIndex = (m_startupProject ? indexForNode(m_startupProject) : QModelIndex()); + QModelIndex newIndex = (projectNode ? indexForNode(projectNode) : QModelIndex()); + m_startupProject = projectNode; + if (oldIndex.isValid()) + emit dataChanged(oldIndex, oldIndex); + if (newIndex.isValid()) + emit dataChanged(newIndex, newIndex); + } +} + +void FlatModel::reset() +{ + m_childNodes.clear(); + QAbstractItemModel::reset(); +} + +QModelIndex FlatModel::indexForNode(const Node *node_) +{ + // We assume that we are only called for nodes that are represented + + // we use non-const pointers internally + Node *node = const_cast<Node*>(node_); + if (!node) + return QModelIndex(); + + if (node == m_rootNode) + return createIndex(0, 0, m_rootNode); + + FolderNode *parentNode = visibleFolderNode(node->parentFolderNode()); + + // Do we have the parent mapped? + QHash<FolderNode*, QList<Node*> >::const_iterator it = m_childNodes.constFind(parentNode); + if (it == m_childNodes.constEnd()) { + fetchMore(parentNode); + it = m_childNodes.constFind(parentNode); + } + if (it != m_childNodes.constEnd()) { + const int row = it.value().indexOf(node); + if (row != -1) + return createIndex(row, 0, node); + } + return QModelIndex(); +} + +void FlatModel::setProjectFilterEnabled(bool filter) +{ + m_filterProjects = filter; + reset(); +} + +void FlatModel::setGeneratedFilesFilterEnabled(bool filter) +{ + m_filterGeneratedFiles = filter; + reset(); +} + +Node *FlatModel::nodeForIndex(const QModelIndex &index) const +{ + if (index.isValid()) + return (Node*)index.internalPointer(); + return 0; +} + +/* + Returns the first folder node in the ancestors + for the given node that is not filtered + out by the Flat Model. +*/ +FolderNode *FlatModel::visibleFolderNode(FolderNode *node) const +{ + if (!node) + return 0; + + for (FolderNode *folderNode = node; + folderNode; + folderNode = folderNode->parentFolderNode()) { + if (!filter(folderNode)) + return folderNode; + } + return 0; +} + +bool FlatModel::filter(Node *node) const +{ + bool isHidden = false; + if (ProjectNode *projectNode = qobject_cast<ProjectNode*>(node)) { + if (m_filterProjects && projectNode->parentFolderNode() != m_rootNode) + isHidden = !projectNode->hasTargets(); + } + if (FileNode *fileNode = qobject_cast<FileNode*>(node)) { + if (m_filterGeneratedFiles) + isHidden = fileNode->isGenerated(); + } + + return isHidden; +} + +/// slots and all the fun +void FlatModel::added(FolderNode* parentNode, const QList<Node*> &newNodeList) +{ + QModelIndex parentIndex = indexForNode(parentNode); + // Old list + QHash<FolderNode*, QList<Node*> >::const_iterator it = m_childNodes.constFind(parentNode); + if (it == m_childNodes.constEnd()) + return; + QList<Node *> oldNodeList = it.value(); + + // Compare lists and emit signals, and modify m_childNodes on the fly + QList<Node *>::const_iterator oldIter = oldNodeList.constBegin(); + QList<Node *>::const_iterator newIter = newNodeList.constBegin(); + + // optimization, check for old list is empty + if (oldIter == oldNodeList.constEnd()) { + // New Node List is empty, nothing added which intrest us + if (newIter == newNodeList.constEnd()) + return; + // So all we need to do is easy + beginInsertRows(parentIndex, 0, newNodeList.size() - 1); + m_childNodes.insert(parentNode, newNodeList); + endInsertRows(); + return; + } + + while(true) + { + // Skip all that are the same + while(*oldIter == *newIter) { + ++oldIter; + ++newIter; + if (oldIter == oldNodeList.constEnd()) { + // At end of oldNodeList, sweep up rest of newNodeList + QList<Node *>::const_iterator startOfBlock = newIter; + newIter = newNodeList.constEnd(); + int pos = oldIter - oldNodeList.constBegin(); + int count = newIter - startOfBlock; + if (count > 0) { + beginInsertRows(parentIndex, pos, pos+count-1); + while(startOfBlock != newIter) { + oldNodeList.insert(pos, *startOfBlock); + ++pos; + ++startOfBlock; + } + m_childNodes.insert(parentNode, oldNodeList); + endInsertRows(); + } + return; // Done with the lists, leave the function + } + } + + QList<Node *>::const_iterator startOfBlock = newIter; + while(*oldIter != *newIter) + ++newIter; + // startOfBlock is the first that was diffrent + // newIter points to the new position of oldIter + // newIter - startOfBlock is number of new items + // oldIter is the position where those are... + int pos = oldIter - oldNodeList.constBegin(); + int count = newIter - startOfBlock; + beginInsertRows(parentIndex, pos, pos + count - 1); + while(startOfBlock != newIter) { + oldNodeList.insert(pos, *startOfBlock); + ++pos; + ++startOfBlock; + } + m_childNodes.insert(parentNode, oldNodeList); + endInsertRows(); + oldIter = oldNodeList.constBegin() + pos; + } +} + +void FlatModel::removed(FolderNode* parentNode, const QList<Node*> &newNodeList) +{ + QModelIndex parentIndex = indexForNode(parentNode); + // Old list + QHash<FolderNode*, QList<Node*> >::const_iterator it = m_childNodes.constFind(parentNode); + if (it == m_childNodes.constEnd()) + return; + QList<Node *> oldNodeList = it.value(); + // Compare lists and emit signals, and modify m_childNodes on the fly + QList<Node *>::const_iterator oldIter = oldNodeList.constBegin(); + QList<Node *>::const_iterator newIter = newNodeList.constBegin(); + + // optimization, check for new list is empty + if (newIter == newNodeList.constEnd()) { + // New Node List is empty, everything removed + if (oldIter == oldNodeList.constEnd()) + return; + // So all we need to do is easy + beginRemoveRows(parentIndex, 0, oldNodeList.size() - 1); + m_childNodes.insert(parentNode, newNodeList); + endRemoveRows(); + return; + } + + while(true) + { + // Skip all that are the same + while(*oldIter == *newIter) { + ++oldIter; + ++newIter; + if (newIter == newNodeList.constEnd()) { + // At end of newNodeList, sweep up rest of oldNodeList + QList<Node *>::const_iterator startOfBlock = oldIter; + oldIter = oldNodeList.constEnd(); + int pos = startOfBlock - oldNodeList.constBegin(); + int count = oldIter - startOfBlock; + if (count > 0) { + beginRemoveRows(parentIndex, pos, pos+count-1); + while(startOfBlock != oldIter) { + ++startOfBlock; + oldNodeList.removeAt(pos); + } + + m_childNodes.insert(parentNode, oldNodeList); + endRemoveRows(); + } + return; // Done with the lists, leave the function + } + } + + QList<Node *>::const_iterator startOfBlock = oldIter; + while(*oldIter != *newIter) + ++oldIter; + // startOfBlock is the first that was diffrent + // oldIter points to the new position of newIter + // oldIter - startOfBlock is number of new items + // newIter is the position where those are... + int pos = startOfBlock - oldNodeList.constBegin(); + int count = oldIter - startOfBlock; + beginRemoveRows(parentIndex, pos, pos + count - 1); + while(startOfBlock != oldIter) { + ++startOfBlock; + oldNodeList.removeAt(pos); + } + m_childNodes.insert(parentNode, oldNodeList); + endRemoveRows(); + oldIter = oldNodeList.constBegin() + pos; + } +} + +void FlatModel::foldersAboutToBeAdded(FolderNode *parentFolder, const QList<FolderNode*> &newFolders) +{ + Q_UNUSED(newFolders) + m_parentFolderForChange = parentFolder; +} + +void FlatModel::foldersAdded() +{ + // First found out what the folder is that we are adding the files to + FolderNode *folderNode = visibleFolderNode(m_parentFolderForChange); + + // Now get the new list for that folder + QList<Node *> newNodeList = childNodes(folderNode); + + added(folderNode, newNodeList); +} + +void FlatModel::foldersAboutToBeRemoved(FolderNode *parentFolder, const QList<FolderNode*> &staleFolders) +{ + QSet<Node *> blackList; + foreach(FolderNode * node, staleFolders) + blackList.insert(node); + + FolderNode *folderNode = visibleFolderNode(parentFolder); + QList<Node *> newNodeList = childNodes(folderNode, blackList); + + removed(parentFolder, newNodeList); + removeFromCache(staleFolders); +} + +void FlatModel::removeFromCache(QList<FolderNode *> list) +{ + foreach(FolderNode * fn, list) { + removeFromCache(fn->subFolderNodes()); + m_childNodes.remove(fn); + } +} + +void FlatModel::foldersRemoved() +{ + // Do nothing +} + +void FlatModel::filesAboutToBeAdded(FolderNode *folder, const QList<FileNode*> &newFiles) +{ + Q_UNUSED(newFiles) + m_parentFolderForChange = folder; +} + +void FlatModel::filesAdded() +{ + // First find out what the folder is that we are adding the files to + FolderNode *folderNode = visibleFolderNode(m_parentFolderForChange); + + // Now get the new List for that folder + QList<Node *> newNodeList = childNodes(folderNode); + added(folderNode, newNodeList); +} + +void FlatModel::filesAboutToBeRemoved(FolderNode *folder, const QList<FileNode*> &staleFiles) +{ + // First found out what the folder (that is the project) is that we are adding the files to + FolderNode *folderNode = visibleFolderNode(folder); + + QSet<Node *> blackList; + foreach(Node* node, staleFiles) + blackList.insert(node); + + // Now get the new List for that folder + QList<Node *> newNodeList = childNodes(folderNode, blackList); + removed(folderNode, newNodeList); +} + +void FlatModel::filesRemoved() +{ + // Do nothing +} diff --git a/src/plugins/projectexplorer/projectmodels.h b/src/plugins/projectexplorer/projectmodels.h new file mode 100644 index 00000000000..ecccefe188b --- /dev/null +++ b/src/plugins/projectexplorer/projectmodels.h @@ -0,0 +1,186 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROJECTMODELS_H +#define PROJECTMODELS_H + +#include <QtCore/QAbstractItemModel> +#include <QtCore/QSet> + +namespace ProjectExplorer { + +class Node; +class FileNode; +class FolderNode; +class ProjectNode; +class SessionNode; + +namespace Internal { + +// A 1:1 mapping of the internal data model +class DetailedModel : public QAbstractItemModel { + Q_OBJECT +public: + DetailedModel(SessionNode *rootNode, QObject *parent); + + // QAbstractItemModel + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + Qt::ItemFlags flags(const QModelIndex & index) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + int rowCount(const QModelIndex & parent = QModelIndex()) const; + int columnCount(const QModelIndex & parent = QModelIndex()) const; + bool hasChildren(const QModelIndex & parent = QModelIndex()) const; + + bool canFetchMore(const QModelIndex & parent) const; + void fetchMore(const QModelIndex & parent); + + void reset(); + + void setStartupProject(ProjectNode * /* projectNode */) {} + + Node *nodeForIndex(const QModelIndex &index) const; + QModelIndex indexForNode(const Node *node); + + public slots: + void setProjectFilterEnabled(bool /* filter */) {} + +private slots: + void foldersAboutToBeAdded(FolderNode *parentFolder, + const QList<FolderNode*> &newFolders); + void foldersAdded(); + + void foldersAboutToBeRemoved(FolderNode *parentFolder, + const QList<FolderNode*> &staleFolders); + + void filesAboutToBeAdded(FolderNode *folder, + const QList<FileNode*> &newFiles); + void filesAdded(); + + void filesAboutToBeRemoved(FolderNode *folder, + const QList<FileNode*> &staleFiles); + +private: + QList<Node*> childNodeList(FolderNode *folderNode) const; + + void connectProject(ProjectNode *project); + void addToChildNodes(FolderNode *parentFolder, QList<Node*> newList); + void removeFromChildNodes(FolderNode *parentFolder, QList<Node*> newList); + QList<FolderNode*> recursiveSubFolders(FolderNode *parentFolder); + + SessionNode *m_rootNode; + mutable QHash<FolderNode*, QList<Node*> > m_childNodes; + bool m_isStartupProject; + + FolderNode *m_folderToAddTo; + + friend class DetailedModelManager; +}; + + +// displays "flattened" view without pri files, subdirs pro files & virtual folders +// This view is optimized to be used in the edit mode sidebar +class FlatModel : public QAbstractItemModel { + Q_OBJECT +public: + FlatModel(SessionNode *rootNode, QObject *parent); + + // QAbstractItemModel + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + int rowCount(const QModelIndex & parent = QModelIndex()) const; + int columnCount(const QModelIndex & parent = QModelIndex()) const; + bool hasChildren(const QModelIndex & parent = QModelIndex()) const; + + bool canFetchMore(const QModelIndex & parent) const; + void fetchMore(const QModelIndex & parent); + + void reset(); + + void setStartupProject(ProjectNode *projectNode); + + ProjectExplorer::Node *nodeForIndex(const QModelIndex &index) const; + QModelIndex indexForNode(const Node *node); + +public slots: + void setProjectFilterEnabled(bool filter); + void setGeneratedFilesFilterEnabled(bool filter); + +private slots: + void foldersAboutToBeAdded(FolderNode *parentFolder, const QList<FolderNode*> &newFolders); + void foldersAdded(); + + void foldersAboutToBeRemoved(FolderNode *parentFolder, const QList<FolderNode*> &staleFolders); + void foldersRemoved(); + + // files + void filesAboutToBeAdded(FolderNode *folder, const QList<FileNode*> &newFiles); + void filesAdded(); + + void filesAboutToBeRemoved(FolderNode *folder, const QList<FileNode*> &staleFiles); + void filesRemoved(); + +private: + void added(FolderNode* folderNode, const QList<Node*> &newNodeList); + void removed(FolderNode* parentNode, const QList<Node*> &newNodeList); + void removeFromCache(QList<FolderNode *> list); + void fetchMore(FolderNode *foldernode) const; + + void recursiveAddFolderNodes(FolderNode *startNode, QList<Node *> *list, const QSet<Node *> &blackList = QSet<Node*>()) const; + void recursiveAddFolderNodesImpl(FolderNode *startNode, QList<Node *> *list, const QSet<Node *> &blackList = QSet<Node*>()) const; + void recursiveAddFileNodes(FolderNode *startNode, QList<Node *> *list, const QSet<Node *> &blackList = QSet<Node*>()) const; + QList<Node*> childNodes(FolderNode *parentNode, const QSet<Node*> &blackList = QSet<Node*>()) const; + + FolderNode *visibleFolderNode(FolderNode *node) const; + bool filter(Node *node) const; + + bool m_filterProjects; + bool m_filterGeneratedFiles; + + SessionNode *m_rootNode; + mutable QHash<FolderNode*, QList<Node*> > m_childNodes; + ProjectNode *m_startupProject; + + FolderNode *m_parentFolderForChange; + + friend class FlatModelManager; +}; + +} // namespace Internal +} // namespace ProjectExplorer + + +#endif // PROJECTMODELS_H diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp new file mode 100644 index 00000000000..39ccf9e7109 --- /dev/null +++ b/src/plugins/projectexplorer/projectnodes.cpp @@ -0,0 +1,685 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "nodesvisitor.h" +#include "projectnodes.h" +#include "projectexplorerconstants.h" + +#include <coreplugin/mimedatabase.h> + +#include <QtCore/QFileInfo> +#include <QtGui/QApplication> +#include <QtGui/QIcon> +#include <QtGui/QStyle> + +using namespace ProjectExplorer; + +/*! + \class FileNode + + Base class of all nodes in the node hierarchy. + + \see FileNode + \see FolderNode + \see ProjectNode +*/ +Node::Node(NodeType nodeType, + const QString &filePath) + : QObject(), + m_nodeType(nodeType), + m_projectNode(0), + m_folderNode(0), + m_path(filePath) +{ +} + +NodeType Node::nodeType() const +{ + return m_nodeType; +} + +/*! + Project that owns & manages the node. It's the first project in list of ancestors. + */ +ProjectNode *Node::projectNode() const +{ + return m_projectNode; +} + +/*! + Parent in node hierarchy. + */ +FolderNode *Node::parentFolderNode() const +{ + return m_folderNode; +} + +/*! + Path of file or folder in the filesystem the node represents. + */ +QString Node::path() const +{ + return m_path; +} + +void Node::setNodeType(NodeType type) +{ + m_nodeType = type; +} + +void Node::setProjectNode(ProjectNode *project) +{ + m_projectNode = project; +} + +void Node::setParentFolderNode(FolderNode *parentFolder) +{ + m_folderNode = parentFolder; +} + +void Node::setPath(const QString &path) +{ + m_path = path; +} + +/*! + \class FileNode + + In-memory presentation of a file. All FileNode's are leaf nodes. + + \see FolderNode + \see ProjectNode +*/ + +FileNode::FileNode(const QString &filePath, + const FileType fileType, + bool generated) + : Node(FileNodeType, filePath), + m_fileType(fileType), + m_generated(generated) +{ +} + +FileType FileNode::fileType() const +{ + return m_fileType; +} + +/*! + Returns true if the file is automatically generated by a compile step. + */ +bool FileNode::isGenerated() const +{ + return m_generated; +} + +/*! + \class FolderNode + + In-memory presentation of a folder. Note that the node itself + all children (files and folders) are "managed" by the owning project. + + \see FileNode + \see ProjectNode +*/ +FolderNode::FolderNode(const QString &folderPath) + : Node(FolderNodeType, folderPath), + m_folderName(folderPath) +{ + m_icon = QApplication::style()->standardIcon(QStyle::SP_DirIcon); +} + +FolderNode::~FolderNode() +{ + qDeleteAll(m_subFolderNodes); + qDeleteAll(m_fileNodes); +} + +/* + The display name that should be used in a view. + + + \see setFolderName() + */ +QString FolderNode::name() const +{ + return m_folderName; +} + +/* + The icon that should be used in a view. Default is the directory icon (QStyle::S_PDirIcon). + \see setIcon() + */ +QIcon FolderNode::icon() const +{ + return m_icon; +} + +QList<FileNode*> FolderNode::fileNodes() const +{ + return m_fileNodes; +} + +QList<FolderNode*> FolderNode::subFolderNodes() const +{ + return m_subFolderNodes; +} + +void FolderNode::accept(NodesVisitor *visitor) +{ + visitor->visitFolderNode(this); + foreach (FolderNode *subFolder, m_subFolderNodes) + subFolder->accept(visitor); +} + +void FolderNode::setFolderName(const QString &name) +{ + m_folderName = name; +} + +void FolderNode::setIcon(const QIcon &icon) +{ + m_icon = icon; +} + +/*! + \class ProjectNode + + In-memory presentation of a Project. + A concrete subclass must implement the "persistent" stuff + + \see FileNode + \see FolderNode +*/ + +/* + Creates uninitialized ProjectNode object. + */ +ProjectNode::ProjectNode(const QString &projectFilePath) + : FolderNode(projectFilePath) +{ + setNodeType(ProjectNodeType); + // project node "manages" itself + setProjectNode(this); + setFolderName(QFileInfo(m_folderName).fileName()); +} + +QList<ProjectNode*> ProjectNode::subProjectNodes() const +{ + return m_subProjectNodes; +} + +/*! + \function bool ProjectNode::addSubProjects(const QStringList &) + */ + +/*! + \function bool ProjectNode::removeSubProjects(const QStringList &) + */ + +/*! + \function bool ProjectNode::addFiles(const FileType, const QStringList &, QStringList *) + */ + +/*! + \function bool ProjectNode::removeFiles(const FileType, const QStringList &, QStringList *) + */ + +/*! + \function bool ProjectNode::renameFile(const FileType, const QString &, const QString &) + */ + +QList<NodesWatcher*> ProjectNode::watchers() const +{ + return m_watchers; +} + +/* + Registers a watcher for the current project & all sub projects + It does not take ownership of the watcher. + */ +void ProjectNode::registerWatcher(NodesWatcher *watcher) +{ + if (!watcher) + return; + connect(watcher, SIGNAL(destroyed(QObject *)), + this, SLOT(watcherDestroyed(QObject *))); + m_watchers.append(watcher); + foreach (ProjectNode *subProject, m_subProjectNodes) + subProject->registerWatcher(watcher); +} + +/* + Removes a watcher for the current project & all sub projects. +*/ +void ProjectNode::unregisterWatcher(NodesWatcher *watcher) +{ + if (!watcher) + return; + m_watchers.removeOne(watcher); + foreach (ProjectNode *subProject, m_subProjectNodes) + subProject->unregisterWatcher(watcher); +} + +void ProjectNode::accept(NodesVisitor *visitor) +{ + visitor->visitProjectNode(this); + + foreach (FolderNode *folder, m_subFolderNodes) + folder->accept(visitor); +} + +/*! + Adds project nodes to the hierarchy and emits the corresponding signals. + */ +void ProjectNode::addProjectNodes(const QList<ProjectNode*> &subProjects) +{ + if (!subProjects.isEmpty()) { + QList<FolderNode*> folderNodes; + foreach (ProjectNode *projectNode, subProjects) + folderNodes << projectNode; + + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->foldersAboutToBeAdded(this, folderNodes); + + foreach (ProjectNode *project, subProjects) { + Q_ASSERT_X(!project->parentFolderNode(), "addProjectNodes", + "Project node has already a parent"); + project->setParentFolderNode(this); + foreach (NodesWatcher *watcher, m_watchers) + project->registerWatcher(watcher); + m_subFolderNodes.append(project); + m_subProjectNodes.append(project); + } + qSort(m_subFolderNodes.begin(), m_subFolderNodes.end(), + sortNodesByPath); + qSort(m_subProjectNodes.begin(), m_subProjectNodes.end(), + sortNodesByPath); + + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->foldersAdded(); + } +} + +/*! + Remove project nodes from the hierarchy and emits the corresponding signals. + All objects in the argument list are deleted. + */ +void ProjectNode::removeProjectNodes(const QList<ProjectNode*> &subProjects) +{ + if (!subProjects.isEmpty()) { + QList<FolderNode*> toRemove; + foreach (ProjectNode *projectNode, subProjects) + toRemove << projectNode; + qSort(toRemove.begin(), toRemove.end(), sortNodesByPath); + + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->foldersAboutToBeRemoved(this, toRemove); + + QList<FolderNode*>::const_iterator toRemoveIter = toRemove.constBegin(); + QList<FolderNode*>::iterator folderIter = m_subFolderNodes.begin(); + QList<ProjectNode*>::iterator projectIter = m_subProjectNodes.begin(); + for (; toRemoveIter != toRemove.constEnd(); ++toRemoveIter) { + while ((*projectIter)->path() != (*toRemoveIter)->path()) { + ++projectIter; + Q_ASSERT_X(projectIter != m_subProjectNodes.end(), "removeProjectNodes", + "Project to remove is not part of specified folder!"); + } + while ((*folderIter)->path() != (*toRemoveIter)->path()) { + ++folderIter; + Q_ASSERT_X(folderIter != m_subFolderNodes.end(), "removeProjectNodes", + "Project to remove is not part of specified folder!"); + } + delete *projectIter; + projectIter = m_subProjectNodes.erase(projectIter); + folderIter = m_subFolderNodes.erase(folderIter); + } + + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->foldersRemoved(); + } +} + +/*! + Adds folder nodes to the hierarchy and emits the corresponding signals. + */ +void ProjectNode::addFolderNodes(const QList<FolderNode*> &subFolders, FolderNode *parentFolder) +{ + Q_ASSERT(parentFolder); + + if (!subFolders.isEmpty()) { + const bool emitSignals = (parentFolder->projectNode() == this); + + if (emitSignals) + foreach (NodesWatcher *watcher, m_watchers) + watcher->foldersAboutToBeAdded(parentFolder, subFolders); + + foreach (FolderNode *folder, subFolders) { + Q_ASSERT_X(!folder->parentFolderNode(), "addFolderNodes", + "Project node has already a parent folder"); + folder->setParentFolderNode(parentFolder); + folder->setProjectNode(this); + parentFolder->m_subFolderNodes.append(folder); + + // project nodes have to be added via addProjectNodes + Q_ASSERT_X(folder->nodeType() != ProjectNodeType, "addFolderNodes", + "project nodes have to be added via addProjectNodes"); + } + qSort(parentFolder->m_subFolderNodes.begin(), parentFolder->m_subFolderNodes.end(), + sortNodesByPath); + + if (emitSignals) + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->foldersAdded(); + } +} + +/*! + Remove file nodes from the hierarchy and emits the corresponding signals. + All objects in the subFolders list are deleted. + */ +void ProjectNode::removeFolderNodes(const QList<FolderNode*> &subFolders, + FolderNode *parentFolder) +{ + Q_ASSERT(parentFolder); + + if (!subFolders.isEmpty()) { + const bool emitSignals = (parentFolder->projectNode() == this); + + QList<FolderNode*> toRemove = subFolders; + qSort(toRemove.begin(), toRemove.end(), sortNodesByPath); + + if (emitSignals) + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->foldersAboutToBeRemoved(parentFolder, toRemove); + + QList<FolderNode*>::const_iterator toRemoveIter = toRemove.constBegin(); + QList<FolderNode*>::iterator folderIter = parentFolder->m_subFolderNodes.begin(); + for (; toRemoveIter != toRemove.constEnd(); ++toRemoveIter) { + Q_ASSERT_X(((*toRemoveIter)->nodeType() != ProjectNodeType), "removeFolderNodes", + "project nodes have to be removed via removeProjectNodes"); + while ((*folderIter)->path() != (*toRemoveIter)->path()) { + ++folderIter; + Q_ASSERT_X(folderIter != parentFolder->m_subFolderNodes.end(), "removeFileNodes", + "Folder to remove is not part of specified folder!"); + } + delete *folderIter; + folderIter = parentFolder->m_subFolderNodes.erase(folderIter); + } + + if (emitSignals) + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->foldersRemoved(); + } +} + +/*! + Adds file nodes to the internal list and emits the corresponding signals. + This method should be called within an implementation of the public method addFiles. + */ +void ProjectNode::addFileNodes(const QList<FileNode*> &files, FolderNode *folder) +{ + Q_ASSERT(folder); + + if (!files.isEmpty()) { + const bool emitSignals = (folder->projectNode() == this); + + if (emitSignals) + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->filesAboutToBeAdded(folder, files); + + foreach (FileNode *file, files) { + Q_ASSERT_X(!file->parentFolderNode(), "addFileNodes", + "File node has already a parent folder"); + + file->setParentFolderNode(folder); + file->setProjectNode(this); + folder->m_fileNodes.append(file); + } + qSort(folder->m_fileNodes.begin(), folder->m_fileNodes.end(), sortNodesByPath); + + if (emitSignals) + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->filesAdded(); + } +} + +/*! + Remove file nodes from the internal list and emits the corresponding signals. + All objects in the argument list are deleted. + This method should be called within an implementation of the public method removeFiles. + */ +void ProjectNode::removeFileNodes(const QList<FileNode*> &files, FolderNode *folder) +{ + Q_ASSERT(folder); + + if (!files.isEmpty()) { + const bool emitSignals = (folder->projectNode() == this); + + QList<FileNode*> toRemove = files; + qSort(toRemove.begin(), toRemove.end(), sortNodesByPath); + + if (emitSignals) + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->filesAboutToBeRemoved(folder, toRemove); + + QList<FileNode*>::const_iterator toRemoveIter = toRemove.constBegin(); + QList<FileNode*>::iterator filesIter = folder->m_fileNodes.begin(); + for (; toRemoveIter != toRemove.constEnd(); ++toRemoveIter) { + while ((*filesIter)->path() != (*toRemoveIter)->path()) { + ++filesIter; + Q_ASSERT_X(filesIter != folder->m_fileNodes.end(), "removeFileNodes", + "File to remove is not part of specified folder!"); + } + delete *filesIter; + filesIter = folder->m_fileNodes.erase(filesIter); + } + + if (emitSignals) + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->filesRemoved(); + } +} + +void ProjectNode::watcherDestroyed(QObject *watcher) +{ + // cannot use qobject_cast here + unregisterWatcher(static_cast<NodesWatcher*>(watcher)); +} + +/*! + Sort pointers to FileNodes + */ +bool ProjectNode::sortNodesByPath(Node *f1, Node *f2) { + return f1->path() < f2->path(); +} + +/*! + \class SessionNode +*/ + +SessionNode::SessionNode(const QString &sessionPath, QObject *parentObject) + : FolderNode(sessionPath) +{ + setParent(parentObject); + setNodeType(SessionNodeType); +} + +QList<NodesWatcher*> SessionNode::watchers() const +{ + return m_watchers; +} + +/* + Registers a watcher for the complete session tree. + It does not take ownership of the watcher. +*/ +void SessionNode::registerWatcher(NodesWatcher *watcher) +{ + if (!watcher) + return; + connect(watcher, SIGNAL(destroyed(QObject*)), + this, SLOT(watcherDestroyed(QObject*))); + m_watchers.append(watcher); + foreach (ProjectNode *project, m_projectNodes) + project->registerWatcher(watcher); +} + +/* + Removes a watcher from the complete session tree +*/ +void SessionNode::unregisterWatcher(NodesWatcher *watcher) +{ + if (!watcher) + return; + m_watchers.removeOne(watcher); + foreach (ProjectNode *project, m_projectNodes) + project->unregisterWatcher(watcher); +} + +void SessionNode::accept(NodesVisitor *visitor) +{ + visitor->visitSessionNode(this); + foreach (ProjectNode *project, m_projectNodes) + project->accept(visitor); +} + +QList<ProjectNode*> SessionNode::projectNodes() const +{ + return m_projectNodes; +} + +void SessionNode::addProjectNodes(const QList<ProjectNode*> &projectNodes) +{ + if (!projectNodes.isEmpty()) { + QList<FolderNode*> folderNodes; + foreach (ProjectNode *projectNode, projectNodes) + folderNodes << projectNode; + + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->foldersAboutToBeAdded(this, folderNodes); + + foreach (ProjectNode *project, projectNodes) { + Q_ASSERT_X(!project->parentFolderNode(), "addProjectNodes", + "Project node has already a parent folder"); + project->setParentFolderNode(this); + foreach (NodesWatcher *watcher, m_watchers) + project->registerWatcher(watcher); + m_subFolderNodes.append(project); + m_projectNodes.append(project); + } + + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->foldersAdded(); + } +} + +void SessionNode::removeProjectNodes(const QList<ProjectNode*> &projectNodes) +{ + if (!projectNodes.isEmpty()) { + QList<FolderNode*> toRemove; + foreach (ProjectNode *projectNode, projectNodes) + toRemove << projectNode; + + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->foldersAboutToBeRemoved(this, toRemove); + + QList<FolderNode*>::const_iterator toRemoveIter = toRemove.constBegin(); + QList<FolderNode*>::iterator folderIter = m_subFolderNodes.begin(); + QList<ProjectNode*>::iterator projectIter = m_projectNodes.begin(); + for (; toRemoveIter != toRemove.constEnd(); ++toRemoveIter) { + while ((*projectIter)->path() != (*toRemoveIter)->path()) { + ++projectIter; + Q_ASSERT_X(projectIter != m_projectNodes.end(), "removeProjectNodes", + "Project to remove is not part of specified folder!"); + } + while ((*folderIter)->path() != (*toRemoveIter)->path()) { + ++folderIter; + Q_ASSERT_X(folderIter != m_subFolderNodes.end(), "removeProjectNodes", + "Project to remove is not part of specified folder!"); + } + projectIter = m_projectNodes.erase(projectIter); + folderIter = m_subFolderNodes.erase(folderIter); + } + + foreach (NodesWatcher *watcher, m_watchers) + emit watcher->foldersRemoved(); + } +} + +void SessionNode::watcherDestroyed(QObject *watcher) +{ + // cannot use qobject_cast here + unregisterWatcher(static_cast<NodesWatcher*>(watcher)); +} + +/*! + \class NodesWatcher + + NodesWatcher let you keep track of changes in the tree. + + Add a watcher by calling ProjectNode::registerWatcher() or + SessionNode::registerWatcher(). Whenever the tree underneath the + ProectNode or SessionNode changes (e.g. nodes are added/removed), + the corresponding signals of the NodesWatcher are emitted. + Watchers can be removed from the complete tree or a subtree + by calling ProjectNode::unregisterWatcher and + SessionNode::unregisterWatcher(). + + The NodesWatcher is similar to the Observer in the + well-known Observer pattern (Booch et al). +*/ + +NodesWatcher::NodesWatcher(QObject *parent) + : QObject(parent) +{ +} + +// TODO Maybe put this in core, so that all can benefit +FileType typeForFileName(const Core::MimeDatabase *db, const QFileInfo &file) +{ + const Core::MimeType mt = db->findByFile(file); + if (!mt) + return UnknownFileType; + + const QString typeName = mt.type(); + if (typeName == QLatin1String(Constants::CPP_SOURCE_MIMETYPE) + || typeName == QLatin1String(Constants::C_SOURCE_MIMETYPE)) + return SourceType; + if (typeName == QLatin1String(Constants::CPP_HEADER_MIMETYPE) + || typeName == QLatin1String(Constants::C_HEADER_MIMETYPE)) + return HeaderType; + if (typeName == QLatin1String(Constants::RESOURCE_MIMETYPE)) + return ResourceType; + if (typeName == QLatin1String(Constants::FORM_MIMETYPE)) + return FormType; + return UnknownFileType; +} diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h new file mode 100644 index 00000000000..9eca475c09d --- /dev/null +++ b/src/plugins/projectexplorer/projectnodes.h @@ -0,0 +1,289 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROJECTNODES_H +#define PROJECTNODES_H + +#include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtGui/QIcon> + +#include "projectexplorer_export.h" + +QT_BEGIN_NAMESPACE +class QFileInfo; +QT_END_NAMESPACE + + +namespace Core { + class MimeDatabase; +} + +namespace ProjectExplorer { + +// +// = Node hierarchy = +// +// The nodes are arranged in a tree where leaves are FileNodes and non-leaves are FolderNodes +// A Project is a special Folder that manages the files and normal folders underneath it. +// +// The Watcher emits signals for structural changes in the hierarchy. +// A Visitor can be used to traverse all Projects and other Folders. +// + +enum NodeType { + FileNodeType = 1, + FolderNodeType, + ProjectNodeType, + SessionNodeType +}; + +// File types common for qt projects +enum FileType { + UnknownFileType = 0, + HeaderType, + SourceType, + FormType, + ResourceType, + ProjectFileType, + FileTypeSize +}; + +class Node; +class FileNode; +class FileContainerNode; +class FolderNode; +class ProjectNode; +class NodesWatcher; +class NodesVisitor; + +class PROJECTEXPLORER_EXPORT Node : public QObject { + Q_OBJECT +public: + NodeType nodeType() const; + ProjectNode *projectNode() const; // managing project + FolderNode *parentFolderNode() const; // parent folder or project + QString path() const; // file system path + +protected: + Node(NodeType nodeType, const QString &path); + + void setNodeType(NodeType type); + void setProjectNode(ProjectNode *project); + void setParentFolderNode(FolderNode *parentFolder); + void setPath(const QString &path); + +private: + NodeType m_nodeType; + ProjectNode *m_projectNode; + FolderNode *m_folderNode; + QString m_path; +}; + +class PROJECTEXPLORER_EXPORT FileNode : public Node { + Q_OBJECT +public: + FileNode(const QString &filePath, const FileType fileType, bool generated); + + FileType fileType() const; + bool isGenerated() const; + +private: + // managed by ProjectNode + friend class ProjectNode; + + FileType m_fileType; + bool m_generated; +}; + +class PROJECTEXPLORER_EXPORT FolderNode : public Node { + Q_OBJECT +public: + explicit FolderNode(const QString &folderPath); + virtual ~FolderNode(); + + QString name() const; + QIcon icon() const; + + QList<FileNode*> fileNodes() const; + QList<FolderNode*> subFolderNodes() const; + + virtual void accept(NodesVisitor *visitor); + +protected: + void setFolderName(const QString &name); + void setIcon(const QIcon &icon); + + QList<FolderNode*> m_subFolderNodes; + QList<FileNode*> m_fileNodes; + +private: + // managed by ProjectNode + friend class ProjectNode; + QString m_folderName; + QIcon m_icon; +}; + +class PROJECTEXPLORER_EXPORT ProjectNode : public FolderNode +{ + Q_OBJECT + +public: + enum ProjectAction { + AddSubProject, + RemoveSubProject, + AddFile, + RemoveFile + }; + + // all subFolders that are projects + QList<ProjectNode*> subProjectNodes() const; + + // determines if the project will be shown in the flat view + // TODO find a better name + virtual bool hasTargets() const = 0; + + virtual QList<ProjectAction> supportedActions() const = 0; + + virtual bool addSubProjects(const QStringList &proFilePaths) = 0; + + virtual bool removeSubProjects(const QStringList &proFilePaths) = 0; + + virtual bool addFiles(const FileType fileType, + const QStringList &filePaths, + QStringList *notAdded = 0) = 0; + // TODO: Maybe remove fileType, can be detected by project + virtual bool removeFiles(const FileType fileType, + const QStringList &filePaths, + QStringList *notRemoved = 0) = 0; + virtual bool renameFile(const FileType fileType, + const QString &filePath, + const QString &newFilePath) = 0; + + QList<NodesWatcher*> watchers() const; + void registerWatcher(NodesWatcher *watcher); + void unregisterWatcher(NodesWatcher *watcher); + + void accept(NodesVisitor *visitor); + + static bool sortNodesByPath(Node *n1, Node *n2); + +protected: + // this is just the in-memory representation, a subclass + // will add the persistent stuff + explicit ProjectNode(const QString &projectFilePath); + + // to be called in implementation of + // the corresponding public methods + void addProjectNodes(const QList<ProjectNode*> &subProjects); + void removeProjectNodes(const QList<ProjectNode*> &subProjects); + + void addFolderNodes(const QList<FolderNode*> &subFolders, FolderNode *parentFolder); + void removeFolderNodes(const QList<FolderNode*> &subFolders, FolderNode *parentFolder); + + void addFileNodes(const QList<FileNode*> &files, FolderNode *parentFolder); + void removeFileNodes(const QList<FileNode*> &files, FolderNode *parentFolder); + +private slots: + void watcherDestroyed(QObject *watcher); + +private: + QList<ProjectNode*> m_subProjectNodes; + QList<NodesWatcher*> m_watchers; + + // let SessionNode call setParentFolderNode + friend class SessionNode; +}; + +class PROJECTEXPLORER_EXPORT SessionNode : public FolderNode { + Q_OBJECT +public: + SessionNode(const QString &sessionFilePath, QObject *parentObject); + + QList<ProjectNode*> projectNodes() const; + + QList<NodesWatcher*> watchers() const; + void registerWatcher(NodesWatcher *watcher); + void unregisterWatcher(NodesWatcher *watcher); + + void accept(NodesVisitor *visitor); + +protected: + void addProjectNodes(const QList<ProjectNode*> &projectNodes); + void removeProjectNodes(const QList<ProjectNode*> &projectNodes); + +private slots: + void watcherDestroyed(QObject *watcher); + +private: + QList<ProjectNode*> m_projectNodes; + QList<NodesWatcher*> m_watchers; +}; + +class PROJECTEXPLORER_EXPORT NodesWatcher : public QObject { + Q_OBJECT +public: + explicit NodesWatcher(QObject *parent = 0); + +signals: + // folders & projects + void foldersAboutToBeAdded(FolderNode *parentFolder, + const QList<FolderNode*> &newFolders); + void foldersAdded(); + + void foldersAboutToBeRemoved(FolderNode *parentFolder, + const QList<FolderNode*> &staleFolders); + void foldersRemoved(); + + // files + void filesAboutToBeAdded(FolderNode *folder, + const QList<FileNode*> &newFiles); + void filesAdded(); + + void filesAboutToBeRemoved(FolderNode *folder, + const QList<FileNode*> &staleFiles); + void filesRemoved(); + +private: + + // let project & session emit signals + friend class ProjectNode; + friend class SessionNode; +}; + + +} // namespace ProjectExplorer + +// HACK: THERE SHOULD BE ONE PLACE TO MAKE THE FILE ENDING->FILE TYPE ASSOCIATION +ProjectExplorer::FileType typeForFileName(const Core::MimeDatabase *db, const QFileInfo &file); + +#endif // PROJECTNODES_H diff --git a/src/plugins/projectexplorer/projecttreewidget.cpp b/src/plugins/projectexplorer/projecttreewidget.cpp new file mode 100644 index 00000000000..108c8fd1aa0 --- /dev/null +++ b/src/plugins/projectexplorer/projecttreewidget.cpp @@ -0,0 +1,327 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "projecttreewidget.h" +#include "projectexplorer.h" +#include "projectexplorerconstants.h" +#include "projectmodels.h" + +#include <coreplugin/icore.h> +#include <coreplugin/editormanager/editormanager.h> + +#include <QtGui/QHeaderView> +#include <QtGui/QVBoxLayout> +#include <QtGui/QToolButton> +#include <QtGui/QFocusEvent> +#include <QtCore/QDebug> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +namespace { + bool debug = false; +} + +class ProjectTreeView : public QTreeView +{ +public: + ProjectTreeView() + { + setEditTriggers(QAbstractItemView::EditKeyPressed); + setFrameStyle(QFrame::NoFrame); + setIndentation(indentation() * 9/10); + { + QHeaderView *treeHeader = header(); + treeHeader->setVisible(false); + treeHeader->setResizeMode(QHeaderView::ResizeToContents); + treeHeader->setStretchLastSection(true); + } + setContextMenuPolicy(Qt::CustomContextMenu); + setUniformRowHeights(true); + setTextElideMode(Qt::ElideNone); +// setExpandsOnDoubleClick(false); + } + +protected: + // This is a workaround to stop Qt from redrawing the project tree every + // time the user opens or closes a menu when it has focus. Would be nicer to + // fix it in Qt. + void focusInEvent(QFocusEvent *event) + { + if (event->reason() != Qt::PopupFocusReason) + QTreeView::focusInEvent(event); + } + + void focusOutEvent(QFocusEvent *event) + { + if (event->reason() != Qt::PopupFocusReason) + QTreeView::focusOutEvent(event); + } +}; + +/*! + /class ProjectTreeWidget + + Shows the projects in form of a tree. + */ +ProjectTreeWidget::ProjectTreeWidget(Core::ICore *core, QWidget *parent) + : QWidget(parent), + m_core(core), + m_explorer(ProjectExplorerPlugin::instance()), + m_view(0), + m_model(0), + m_filterProjectsAction(0), + m_autoSync(false) +{ + m_model = new FlatModel(m_explorer->session()->sessionNode(), this); + + m_view = new ProjectTreeView; + m_view->setModel(m_model); + setFocusProxy(m_view); + initView(); + + QVBoxLayout *layout = new QVBoxLayout(); + layout->addWidget(m_view); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); + + m_filterProjectsAction = new QAction(tr("Simplify tree"), this); + m_filterProjectsAction->setCheckable(true); + m_filterProjectsAction->setChecked(false); // default is the traditional complex tree + connect(m_filterProjectsAction, SIGNAL(toggled(bool)), this, SLOT(setProjectFilter(bool))); + + m_filterGeneratedFilesAction = new QAction(tr("Hide generated files"), this); + m_filterGeneratedFilesAction->setCheckable(true); + m_filterGeneratedFilesAction->setChecked(true); + connect(m_filterGeneratedFilesAction, SIGNAL(toggled(bool)), this, SLOT(setGeneratedFilesFilter(bool))); + + // connections + connect(m_model, SIGNAL(modelReset()), + this, SLOT(initView())); + connect(m_view, SIGNAL(activated(const QModelIndex&)), + this, SLOT(openItem(const QModelIndex&))); + connect(m_view->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(handleCurrentItemChange(const QModelIndex&))); + connect(m_view, SIGNAL(customContextMenuRequested(const QPoint&)), + this, SLOT(showContextMenu(const QPoint&))); + connect(m_explorer->session(), SIGNAL(singleProjectAdded(ProjectExplorer::Project *)), + this, SLOT(handleProjectAdded(ProjectExplorer::Project *))); + connect(m_explorer->session(), SIGNAL(startupProjectChanged(ProjectExplorer::Project *)), + this, SLOT(startupProjectChanged(ProjectExplorer::Project *))); + + setAutoSynchronization(true); +} + +void ProjectTreeWidget::toggleAutoSynchronization() +{ + setAutoSynchronization(!m_autoSync); +} + +bool ProjectTreeWidget::autoSynchronization() const +{ + return m_autoSync; +} + +void ProjectTreeWidget::setAutoSynchronization(bool sync, bool syncNow) +{ + if (sync == m_autoSync) + return; + + m_autoSync = sync; + + if (debug) + qDebug() << (m_autoSync ? "Enabling auto synchronization" : "Disabling auto synchronization"); + if (m_autoSync) { + connect(m_explorer, SIGNAL(currentNodeChanged(ProjectExplorer::Node*, ProjectExplorer::Project*)), + this, SLOT(setCurrentItem(ProjectExplorer::Node*, ProjectExplorer::Project*))); + if (syncNow) + setCurrentItem(m_explorer->currentNode(), m_explorer->currentProject()); + } else { + disconnect(m_explorer, SIGNAL(currentNodeChanged(ProjectExplorer::Node*, ProjectExplorer::Project*)), + this, SLOT(setCurrentItem(ProjectExplorer::Node*, ProjectExplorer::Project*))); + } +} + +void ProjectTreeWidget::editCurrentItem() +{ + if (!m_view->selectionModel()->selectedIndexes().isEmpty()) + m_view->edit(m_view->selectionModel()->selectedIndexes().first()); +} + +void ProjectTreeWidget::setCurrentItem(Node *node, Project *project) +{ + if (debug) + qDebug() << "ProjectTreeWidget::setCurrentItem(" << (project ? project->name() : "0") + << ", " << (node ? node->path() : "0") << ")"; + if (!project) { + m_view->selectionModel()->reset(); + return; + } + + const QModelIndex mainIndex = m_model->indexForNode(node); + + if (!mainIndex.isValid()) { + if (debug) + qDebug() << "no main index, clear selection"; + m_view->selectionModel()->clearSelection(); + } else if (mainIndex != m_view->selectionModel()->currentIndex()) { + QItemSelectionModel *selections = m_view->selectionModel(); + if (debug) + qDebug() << "ProjectTreeWidget - changing selection"; + + selections->setCurrentIndex(mainIndex, QItemSelectionModel::SelectCurrent + | QItemSelectionModel::Clear); + m_view->scrollTo(mainIndex); + } +} + +void ProjectTreeWidget::handleCurrentItemChange(const QModelIndex ¤t) +{ + Node *node = m_model->nodeForIndex(current); + Q_ASSERT(node); + + bool autoSync = autoSynchronization(); + setAutoSynchronization(false); + m_explorer->setCurrentNode(node); + setAutoSynchronization(autoSync, false); +} + +void ProjectTreeWidget::showContextMenu(const QPoint &pos) +{ + QModelIndex index = m_view->indexAt(pos); + Node *node = m_model->nodeForIndex(index); + m_explorer->showContextMenu(m_view->mapToGlobal(pos), node); +} + +void ProjectTreeWidget::handleProjectAdded(ProjectExplorer::Project *project) +{ + Node *node = project->rootProjectNode(); + QModelIndex idx = m_model->indexForNode(node); + m_view->setExpanded(idx, true); +} + +void ProjectTreeWidget::startupProjectChanged(ProjectExplorer::Project *project) +{ + if (project) { + ProjectNode *node = project->rootProjectNode(); + m_model->setStartupProject(node); + } else { + m_model->setStartupProject(0); + } +} + +void ProjectTreeWidget::initView() +{ + QModelIndex sessionIndex = m_model->index(0, 0); + + // hide root folder + m_view->setRootIndex(sessionIndex); + + while (m_model->canFetchMore(sessionIndex)) + m_model->fetchMore(sessionIndex); + + // expand top level projects + for (int i = 0; i < m_model->rowCount(sessionIndex); ++i) { + m_view->expand(m_model->index(i, 0, sessionIndex)); + } + + setCurrentItem(m_explorer->currentNode(), m_explorer->currentProject()); +} + +void ProjectTreeWidget::openItem(const QModelIndex &mainIndex) +{ + Node *node = m_model->nodeForIndex(mainIndex); + if (node->nodeType() == FileNodeType) { + m_core->editorManager()->openEditor(node->path()); + m_core->editorManager()->ensureEditorManagerVisible(); + } +} + +void ProjectTreeWidget::setProjectFilter(bool filter) +{ + m_model->setProjectFilterEnabled(filter); + m_filterProjectsAction->setChecked(filter); +} + +void ProjectTreeWidget::setGeneratedFilesFilter(bool filter) +{ + m_model->setGeneratedFilesFilterEnabled(filter); + m_filterGeneratedFilesAction->setChecked(filter); +} + +ProjectTreeWidgetFactory::ProjectTreeWidgetFactory(Core::ICore *core) + : m_core(core) +{ +} + +ProjectTreeWidgetFactory::~ProjectTreeWidgetFactory() +{ +} + +QString ProjectTreeWidgetFactory::displayName() +{ + return tr("Projects"); +} + +QKeySequence ProjectTreeWidgetFactory::activationSequence() +{ + return QKeySequence(Qt::ALT + Qt::Key_X); +} + +Core::NavigationView ProjectTreeWidgetFactory::createWidget() +{ + Core::NavigationView n; + ProjectTreeWidget *ptw = new ProjectTreeWidget(m_core); + n.widget = ptw; + + QToolButton *filter = new QToolButton; + filter->setProperty("type", "dockbutton"); + filter->setIcon(QIcon(":/projectexplorer/images/filtericon.png")); + filter->setToolTip(tr("Filter tree")); + filter->setPopupMode(QToolButton::InstantPopup); + QMenu *filterMenu = new QMenu(filter); + filterMenu->addAction(ptw->m_filterProjectsAction); + filterMenu->addAction(ptw->m_filterGeneratedFilesAction); + filter->setMenu(filterMenu); + + QToolButton *toggleSync = new QToolButton; + toggleSync->setProperty("type", "dockbutton"); + toggleSync->setIcon(QIcon(":/qworkbench/images/linkicon.png")); + toggleSync->setCheckable(true); + toggleSync->setChecked(ptw->autoSynchronization()); + toggleSync->setToolTip(tr("Synchronize with Editor")); + connect(toggleSync, SIGNAL(clicked(bool)), ptw, SLOT(toggleAutoSynchronization())); + + n.doockToolBarWidgets << filter << toggleSync; + return n; +} + diff --git a/src/plugins/projectexplorer/projecttreewidget.h b/src/plugins/projectexplorer/projecttreewidget.h new file mode 100644 index 00000000000..a1ede3b8ab3 --- /dev/null +++ b/src/plugins/projectexplorer/projecttreewidget.h @@ -0,0 +1,109 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROJECTTREEWIDGET_H +#define PROJECTTREEWIDGET_H + +#include <coreplugin/inavigationwidgetfactory.h> + +#include <QtGui/QWidget> +#include <QtGui/QTreeView> + +namespace Core { +class ICore; +} + +namespace ProjectExplorer { + +class ProjectExplorerPlugin; +class Project; +class Node; + +namespace Internal { + +class FlatModel; + +class ProjectTreeWidget : public QWidget { + Q_OBJECT +public: + ProjectTreeWidget(Core::ICore *core, QWidget *parent = 0); + + bool autoSynchronization() const; + void setAutoSynchronization(bool sync, bool syncNow = true); + +public slots: + void toggleAutoSynchronization(); + void editCurrentItem(); + +private slots: + void setCurrentItem(ProjectExplorer::Node *node, ProjectExplorer::Project *project); + void setProjectFilter(bool filter); + void setGeneratedFilesFilter(bool filter); + + void handleCurrentItemChange(const QModelIndex ¤t); + void showContextMenu(const QPoint &pos); + void openItem(const QModelIndex &mainIndex); + void handleProjectAdded(ProjectExplorer::Project *project); + void startupProjectChanged(ProjectExplorer::Project *project); + void initView(); + +private: + Core::ICore *m_core; + ProjectExplorerPlugin *m_explorer; + QTreeView *m_view; + FlatModel *m_model; + QAction *m_filterProjectsAction; + QAction *m_filterGeneratedFilesAction; + + QModelIndex m_subIndex; + QString m_modelId; + bool m_autoSync; + + friend class ProjectTreeWidgetFactory; +}; + +class ProjectTreeWidgetFactory : public Core::INavigationWidgetFactory +{ +public: + ProjectTreeWidgetFactory(Core::ICore *core); + virtual ~ProjectTreeWidgetFactory(); + virtual QString displayName(); + virtual QKeySequence activationSequence(); + virtual Core::NavigationView createWidget(); +private: + Core::ICore *m_core; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // PROJECTTREEWIDGET_H diff --git a/src/plugins/projectexplorer/projectwindow.cpp b/src/plugins/projectexplorer/projectwindow.cpp new file mode 100644 index 00000000000..94532c41319 --- /dev/null +++ b/src/plugins/projectexplorer/projectwindow.cpp @@ -0,0 +1,273 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "project.h" +#include "projectwindow.h" +#include "projectexplorer.h" +#include "projectexplorerconstants.h" +#include "iprojectproperties.h" +#include "session.h" +#include "projecttreewidget.h" + +#include <coreplugin/minisplitter.h> +#include <coreplugin/fileiconprovider.h> +#include <coreplugin/icore.h> + +#include <QtCore/QDebug> +#include <QtGui/QBoxLayout> +#include <QtGui/QComboBox> +#include <QtGui/QTabWidget> +#include <QtGui/QToolBar> +#include <QtGui/QTreeWidget> +#include <QtGui/QHeaderView> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +namespace { +bool debug = false; +} + +ProjectWindow::ProjectWindow(Core::ICore *core, + QWidget *parent) : + QWidget(parent), + m_core(core) +{ + setWindowTitle(tr("Project Explorer")); + setWindowIcon(QIcon(":/projectexplorer/images/projectexplorer.png")); + + ExtensionSystem::PluginManager *pm = m_core->pluginManager(); + ProjectExplorerPlugin *projectExplorer = m_projectExplorer = pm->getObject<ProjectExplorerPlugin>(); + m_session = projectExplorer->session(); + + connect(m_session, SIGNAL(sessionLoaded()), this, SLOT(restoreStatus())); + connect(m_session, SIGNAL(aboutToSaveSession()), this, SLOT(saveStatus())); + + m_treeWidget = new QTreeWidget(this); + m_treeWidget->setFrameStyle(QFrame::NoFrame); + m_treeWidget->setRootIsDecorated(false); + m_treeWidget->header()->setResizeMode(QHeaderView::ResizeToContents); + m_treeWidget->setHeaderLabels(QStringList() + << tr("Projects") + << tr("Startup") + << tr("Path") + ); + + connect(m_treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), + this, SLOT(handleItem(QTreeWidgetItem*, int)), Qt::QueuedConnection); + connect(m_treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem *)), + this, SLOT(handleCurrentItemChanged(QTreeWidgetItem*))); + + QWidget *panelsWidget = new QWidget; + m_panelsTabWidget = new QTabWidget; + m_panelsTabWidget->setDocumentMode(true); + QVBoxLayout *panelsLayout = new QVBoxLayout(panelsWidget); + + panelsLayout->setSpacing(0); + panelsLayout->setContentsMargins(0, panelsLayout->margin(), 0, 0); + panelsLayout->addWidget(m_panelsTabWidget); + + QWidget *dummy = new QWidget; + QVBoxLayout *dummyLayout = new QVBoxLayout(dummy); + dummyLayout->setMargin(0); + dummyLayout->setSpacing(0); + dummyLayout->addWidget(new QToolBar(dummy)); + dummyLayout->addWidget(m_treeWidget); + + QSplitter *splitter = new Core::MiniSplitter; + splitter->setOrientation(Qt::Vertical); + splitter->addWidget(dummy); + splitter->addWidget(panelsWidget); + + + + // make sure that the tree treewidget has same size policy as qtabwidget + m_treeWidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred)); + const int treeWidgetMinSize = m_treeWidget->minimumSizeHint().height(); + splitter->setSizes(QList<int>() << treeWidgetMinSize << splitter->height() - treeWidgetMinSize); + + QVBoxLayout *topLayout = new QVBoxLayout(this); + topLayout->setMargin(0); + topLayout->setSpacing(0); + topLayout->addWidget(splitter); + + connect(m_session, SIGNAL(sessionLoaded()), this, SLOT(updateTreeWidget())); + connect(m_session, SIGNAL(startupProjectChanged(ProjectExplorer::Project*)), this, SLOT(updateTreeWidget())); + connect(m_session, SIGNAL(projectAdded(ProjectExplorer::Project*)), this, SLOT(updateTreeWidget())); + connect(m_session, SIGNAL(projectRemoved(ProjectExplorer::Project*)), this, SLOT(updateTreeWidget())); +} + +ProjectWindow::~ProjectWindow() +{ +} + +void ProjectWindow::restoreStatus() +{ + const QVariant lastPanel = m_session->value(QLatin1String("ProjectWindow/Panel")); + if (lastPanel.isValid()) { + const int index = lastPanel.toInt(); + if (index < m_panelsTabWidget->count()) + m_panelsTabWidget->setCurrentIndex(index); + } +} + +void ProjectWindow::saveStatus() +{ + m_session->setValue(QLatin1String("ProjectWindow/Panel"), m_panelsTabWidget->currentIndex()); +} + +void ProjectWindow::showProperties(ProjectExplorer::Project *project, const QModelIndex & /* subIndex */) +{ + if (debug) + qDebug() << "ProjectWindow - showProperties called"; + + // Remove the tabs from the tab widget first + while (m_panelsTabWidget->count() > 0) + m_panelsTabWidget->removeTab(0); + + while (m_panels.count()) { + PropertiesPanel *panel = m_panels.at(0); + m_panels.removeOne(panel); + delete panel; + } + + if (project) { + QList<IPanelFactory *> pages = + ExtensionSystem::PluginManager::instance()->getObjects<IPanelFactory>(); + foreach (IPanelFactory *panelFactory, pages) { + if (panelFactory->supports(project)) { + PropertiesPanel *panel = panelFactory->createPanel(project); + if (debug) + qDebug() << "ProjectWindow - setting up project properties tab " << panel->name(); + + m_panels.append(panel); + m_panelsTabWidget->addTab(panel->widget(), panel->name()); + } + } + } +} + +void ProjectWindow::updateTreeWidget() +{ + // This setFocus prevents a crash, which I (daniel) spend the better part of a day tracking down. + // To explain: Consider the case that a widget on either the build or run settings has Focus + // Us clearing the m_treewidget will emit a currentItemChanged(0) signal + // Which is connected to showProperties + // showProperties will now remove the widget that has focus from m_panelsTabWidget, so the treewidget + // gets focus, which will in focusIn select the first entry (due to QTreeWidget::clear() implementation, + // there are still items in the model) which emits another currentItemChanged() signal + // That one runs fully thorough and deletes all widgets, even that one that we are currently removing + // from m_panelsTabWidget. + // To prevent that, we simply prevent the focus switching.... + m_treeWidget->setFocus(); + m_treeWidget->clear(); + + foreach(Project *project, m_session->projects()) { + const QFileInfo fileInfo(project->file()->fileName()); + + QTreeWidgetItem *item = new QTreeWidgetItem(); + item->setText(0, fileInfo.baseName()); + item->setIcon(0, Core::FileIconProvider::instance()->icon(fileInfo)); + item->setText(2, fileInfo.filePath()); + + if (project->isApplication()) { + bool checked = (m_session->startupProject() == project); + item->setCheckState(1, checked ? Qt::Checked : Qt::Unchecked); + } + + m_treeWidget->addTopLevelItem(item); + + if (m_projectExplorer->currentProject() == project) { + m_treeWidget->setCurrentItem(item, 0, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + } + } + + + if (!m_treeWidget->currentItem()) { + if (m_treeWidget->topLevelItemCount() > 0) + m_treeWidget->setCurrentItem(m_treeWidget->topLevelItem(0), 0, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + else + handleCurrentItemChanged(0); + } + + + // Hack around Qt bug + m_treeWidget->viewport()->update(); +} + + +Project *ProjectWindow::findProject(const QString &path) const +{ + QList<Project*> projects = m_session->projects(); + foreach(Project* project, projects) { + if (project->file()->fileName() == path) { + return project; + } + } + return 0; + } + + +void ProjectWindow::handleCurrentItemChanged(QTreeWidgetItem *current) +{ + if (current) { + QString path = current->text(2); + if (Project *project = findProject(path)) { + + m_projectExplorer->setCurrentFile(project, path); + showProperties(project, QModelIndex()); + return; + } + } + + // we only get here if either current is zero or we didn't find a project for the path + m_projectExplorer->setCurrentFile(0, QString()); + showProperties(0, QModelIndex()); +} + + +void ProjectWindow::handleItem(QTreeWidgetItem *item, int column) +{ + if (!item || column != 1) // startup project + return; + + const QString path = item->text(2); + Project *project = findProject(path); + + if (project && project->isApplication()) { + if (!(item->checkState(1) == Qt::Checked)) { + item->setCheckState(1, Qt::Checked); // uncheck not supported + } else { + m_session->setStartupProject(project); + } + } +} diff --git a/src/plugins/projectexplorer/projectwindow.h b/src/plugins/projectexplorer/projectwindow.h new file mode 100644 index 00000000000..6f402008fba --- /dev/null +++ b/src/plugins/projectexplorer/projectwindow.h @@ -0,0 +1,92 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROJECTWINDOW_H +#define PROJECTWINDOW_H + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE +class QModelIndex; +class QTabWidget; +class QTreeWidget; +class QTreeWidgetItem; +QT_END_NAMESPACE + +namespace Core { +class ICore; +} + +namespace ProjectExplorer { + +class Project; +class PropertiesPanel; +class ProjectExplorerPlugin; +class SessionManager; + +namespace Internal { + + +class ProjectWindow : public QWidget +{ + Q_OBJECT + +public: + ProjectWindow(Core::ICore *core, QWidget *parent = 0); + ~ProjectWindow(); + +private slots: + void showProperties(ProjectExplorer::Project *project, const QModelIndex &subIndex); + void restoreStatus(); + void saveStatus(); + + void updateTreeWidget(); + void handleItem(QTreeWidgetItem *item, int column); + void handleCurrentItemChanged(QTreeWidgetItem *); + +private: + Core::ICore *m_core; + SessionManager *m_session; + ProjectExplorerPlugin *m_projectExplorer; + + QTreeWidget* m_treeWidget; + QTabWidget *m_panelsTabWidget; + + QList<PropertiesPanel*> m_panels; + + Project *findProject(const QString &path) const; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // PROJECTWINDOW_H diff --git a/src/plugins/projectexplorer/projectwizardpage.cpp b/src/plugins/projectexplorer/projectwizardpage.cpp new file mode 100644 index 00000000000..91cb17690d6 --- /dev/null +++ b/src/plugins/projectexplorer/projectwizardpage.cpp @@ -0,0 +1,121 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "projectwizardpage.h" +#include "ui_projectwizardpage.h" + +#include <QtCore/QDebug> +#include <QtCore/QTextStream> + +using namespace ProjectExplorer; +using namespace Internal; + +ProjectWizardPage::ProjectWizardPage(QWidget *parent) : + QWizardPage(parent), + m_ui(new Ui::WizardPage) +{ + m_ui->setupUi(this); + + connect(m_ui->addToProjectCheckBox, SIGNAL(toggled(bool)), + m_ui->projectComboBox, SLOT(setEnabled(bool))); +} + +ProjectWizardPage::~ProjectWizardPage() +{ + delete m_ui; +} + +void ProjectWizardPage::setProjects(const QStringList &l) +{ + m_ui->projectComboBox->clear(); + m_ui->projectComboBox->addItems(l); +} + +void ProjectWizardPage::setAddToProjectEnabled(bool b) +{ + m_ui->projectLabel->setEnabled(b); + m_ui->addToProjectLabel->setEnabled(b); + m_ui->addToProjectCheckBox->setChecked(b); + m_ui->addToProjectCheckBox->setEnabled(b); + m_ui->projectComboBox->setEnabled(b); +} + +int ProjectWizardPage::currentProjectIndex() const +{ + return m_ui->projectComboBox->currentIndex(); +} + +void ProjectWizardPage::setCurrentProjectIndex(int i) +{ + m_ui->projectComboBox->setCurrentIndex(i); +} + +bool ProjectWizardPage::addToProject() const +{ + return m_ui->addToProjectCheckBox->isChecked(); +} + +bool ProjectWizardPage::addToVersionControl() const +{ + return m_ui->addToVersionControlCheckBox->isChecked(); +} + +void ProjectWizardPage::setAddToVersionControlEnabled(bool b) +{ + m_ui->addToVersionControlLabel->setEnabled(b); + m_ui->addToVersionControlCheckBox->setChecked(b); + m_ui->addToVersionControlCheckBox->setEnabled(b); +} + +void ProjectWizardPage::changeEvent(QEvent *e) +{ + switch(e->type()) { + case QEvent::LanguageChange: + m_ui->retranslateUi(this); + break; + default: + break; + } +} + +void ProjectWizardPage::setFilesDisplay(const QStringList &files) +{ + QString fileMessage; { + QTextStream str(&fileMessage); + str << "<html>Files to be added:<pre>"; + const QStringList::const_iterator cend = files.constEnd(); + for (QStringList::const_iterator it = files.constBegin(); it != cend; ++it) + str << *it << '\n'; + str << "</pre>"; + } + m_ui->filesLabel->setText(fileMessage); +} diff --git a/src/plugins/projectexplorer/projectwizardpage.h b/src/plugins/projectexplorer/projectwizardpage.h new file mode 100644 index 00000000000..88e2d43d86e --- /dev/null +++ b/src/plugins/projectexplorer/projectwizardpage.h @@ -0,0 +1,83 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROJECTWIZARDPAGE_H +#define PROJECTWIZARDPAGE_H + +#include <QtGui/QWizardPage> + +namespace Core { + class IVersionControl; + class SCCManager; + class FileManager; +} + +namespace ProjectExplorer { + +class ProjectNode; + +namespace Internal { + +namespace Ui { +class WizardPage; +} + +class ProjectWizardPage : public QWizardPage { + Q_OBJECT + Q_DISABLE_COPY(ProjectWizardPage) +public: + explicit ProjectWizardPage(QWidget *parent = 0); + virtual ~ProjectWizardPage(); + + void setProjects(const QStringList &); + void setCurrentProjectIndex(int); + int currentProjectIndex() const; + + void setAddToProjectEnabled(bool b); + bool addToProject() const; + + bool addToVersionControl() const; + void setAddToVersionControlEnabled(bool b); + + void setFilesDisplay(const QStringList &files); + +protected: + virtual void changeEvent(QEvent *e); + +private: + Ui::WizardPage *m_ui; +}; + +} +} + +#endif // PROJECTWIZARDPAGE_H diff --git a/src/plugins/projectexplorer/projectwizardpage.ui b/src/plugins/projectexplorer/projectwizardpage.ui new file mode 100644 index 00000000000..bc31f8c49e1 --- /dev/null +++ b/src/plugins/projectexplorer/projectwizardpage.ui @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ProjectExplorer::Internal::WizardPage</class> + <widget class="QWizardPage" name="ProjectExplorer::Internal::WizardPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>619</width> + <height>414</height> + </rect> + </property> + <property name="windowTitle"> + <string>WizardPage</string> + </property> + <property name="title"> + <string>Project management</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="addToProjectLabel"> + <property name="text"> + <string>&Add to Project</string> + </property> + <property name="buddy"> + <cstring>addToProjectCheckBox</cstring> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QCheckBox" name="addToProjectCheckBox"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="projectLabel"> + <property name="text"> + <string>&Project</string> + </property> + <property name="buddy"> + <cstring>projectComboBox</cstring> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="projectComboBox"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="addToVersionControlLabel"> + <property name="text"> + <string>Add to &version control</string> + </property> + <property name="buddy"> + <cstring>addToVersionControlCheckBox</cstring> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QCheckBox" name="addToVersionControlCheckBox"/> + </item> + </layout> + </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> + <item> + <widget class="QScrollArea" name="scrollArea"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="verticalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="widgetResizable"> + <bool>true</bool> + </property> + <widget class="QWidget" name="scrollAreaWidgetContents"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>601</width> + <height>157</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="filesLabel"> + <property name="text"> + <string>The following files will be added: +f1 +f2 +</string> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/projectexplorer/removefiledialog.cpp b/src/plugins/projectexplorer/removefiledialog.cpp new file mode 100644 index 00000000000..b96a51190a1 --- /dev/null +++ b/src/plugins/projectexplorer/removefiledialog.cpp @@ -0,0 +1,68 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "removefiledialog.h" +#include "ui_removefiledialog.h" + +using namespace ProjectExplorer::Internal; + +RemoveFileDialog::RemoveFileDialog(const QString &filePath, QWidget *parent) : + QDialog(parent), + m_ui(new Ui::RemoveFileDialog) +{ + m_ui->setupUi(this); + m_ui->fileNameLabel->setText(filePath); + + // TODO + m_ui->removeVCCheckBox->setVisible(false); +} + +RemoveFileDialog::~RemoveFileDialog() +{ + delete m_ui; +} + +bool RemoveFileDialog::isDeleteFileChecked() const +{ + return m_ui->deleteFileCheckBox->isChecked(); +} + +void RemoveFileDialog::changeEvent(QEvent *e) +{ + switch(e->type()) { + case QEvent::LanguageChange: + m_ui->retranslateUi(this); + break; + default: + break; + } +} diff --git a/src/plugins/projectexplorer/removefiledialog.h b/src/plugins/projectexplorer/removefiledialog.h new file mode 100644 index 00000000000..a3b608f428f --- /dev/null +++ b/src/plugins/projectexplorer/removefiledialog.h @@ -0,0 +1,64 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef REMOVEFILEDIALOG_H +#define REMOVEFILEDIALOG_H + +#include <QtGui/QDialog> + +namespace ProjectExplorer { +namespace Internal { + +namespace Ui { + class RemoveFileDialog; +} + +class RemoveFileDialog : public QDialog { + Q_OBJECT + Q_DISABLE_COPY(RemoveFileDialog) +public: + explicit RemoveFileDialog(const QString &filePath, QWidget *parent = 0); + virtual ~RemoveFileDialog(); + + bool isDeleteFileChecked() const; + +protected: + virtual void changeEvent(QEvent *e); + +private: + Ui::RemoveFileDialog *m_ui; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // REMOVEFILEDIALOG_H diff --git a/src/plugins/projectexplorer/removefiledialog.ui b/src/plugins/projectexplorer/removefiledialog.ui new file mode 100644 index 00000000000..6f5ecee1d42 --- /dev/null +++ b/src/plugins/projectexplorer/removefiledialog.ui @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ProjectExplorer::Internal::RemoveFileDialog</class> + <widget class="QDialog" name="ProjectExplorer::Internal::RemoveFileDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>237</width> + <height>171</height> + </rect> + </property> + <property name="windowTitle"> + <string>Remove File</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="fileToDeleteLabel"> + <property name="text"> + <string>File to delete:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="fileNameLabel"> + <property name="font"> + <font> + <family>Courier New</family> + </font> + </property> + <property name="text"> + <string notr="true">placeholder</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>10</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QCheckBox" name="deleteFileCheckBox"> + <property name="text"> + <string>&Delete file permanently</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="removeVCCheckBox"> + <property name="text"> + <string>&Remove from Version Control</string> + </property> + </widget> + </item> + <item> + <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> + </layout> + </widget> + <resources> + <include location="../../libs/cplusplus/cplusplus.qrc"/> + <include location="../../libs/extensionsystem/pluginview.qrc"/> + <include location="../bookmarks/bookmarks.qrc"/> + <include location="../coreplugin/core.qrc"/> + <include location="../coreplugin/fancyactionbar.qrc"/> + <include location="../cppeditor/cppeditor.qrc"/> + <include location="../cpptools/cpptools.qrc"/> + <include location="../designer/designer.qrc"/> + <include location="../find/find.qrc"/> + <include location="../gdbdebugger/gdbdebugger.qrc"/> + <include location="../help/help.qrc"/> + <include location="../perforce/perforce.qrc"/> + <include location="projectexplorer.qrc"/> + <include location="../../../shared/proparser/proparser.qrc"/> + <include location="../qt4projectmanager/qt4projectmanager.qrc"/> + <include location="../qt4projectmanager/wizards/wizards.qrc"/> + <include location="../quickopen/quickopen.qrc"/> + <include location="../resourceeditor/resourceeditor.qrc"/> + <include location="../texteditor/texteditor.qrc"/> + </resources> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ProjectExplorer::Internal::RemoveFileDialog</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>ProjectExplorer::Internal::RemoveFileDialog</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/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp new file mode 100644 index 00000000000..612fc64bc38 --- /dev/null +++ b/src/plugins/projectexplorer/runconfiguration.cpp @@ -0,0 +1,138 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "runconfiguration.h" +#include "project.h" + +#include <QtCore/QTimer> + +#ifdef Q_OS_MAC +#include <Carbon/Carbon.h> +#endif + +#include <QtDebug> + +using namespace ProjectExplorer; + +// RunConfiguration +RunConfiguration::RunConfiguration(Project *project) + : m_project(project) +{ +} + +RunConfiguration::~RunConfiguration() +{ +} + +Project *RunConfiguration::project() const +{ + return m_project.data(); +} + +QString RunConfiguration::name() const +{ + return m_name; +} + +void RunConfiguration::setName(const QString &name) +{ + m_name = name; + emit nameChanged(); +} + +void RunConfiguration::save(PersistentSettingsWriter &writer) const +{ + writer.saveValue("RunConfiguration.name", m_name); +} + +void RunConfiguration::restore(const PersistentSettingsReader &reader) +{ + QVariant var = reader.restoreValue("RunConfiguration.name"); + if (var.isValid() && !var.toString().isEmpty()) + m_name = var.toString(); +} + + +IRunConfigurationFactory::IRunConfigurationFactory() +{ +} + +IRunConfigurationFactory::~IRunConfigurationFactory() +{ +} + +IRunConfigurationRunner::IRunConfigurationRunner() +{ +} + +IRunConfigurationRunner::~IRunConfigurationRunner() +{ +} + +RunControl::RunControl(QSharedPointer<RunConfiguration> runConfiguration) + : m_runConfiguration(runConfiguration) +{ +} + +QSharedPointer<RunConfiguration> RunControl::runConfiguration() +{ + return m_runConfiguration; +} + +RunControl::~RunControl() +{ +} + +void RunControl::bringApplicationToForeground(qint64 pid) +{ +#ifdef Q_OS_MAC + m_internalPid = pid; + m_foregroundCount = 0; + bringApplicationToForegroundInternal(); +#endif +} + +void RunControl::bringApplicationToForegroundInternal() +{ +#ifdef Q_OS_MAC + ProcessSerialNumber psn; + GetProcessForPID(m_internalPid, &psn); + if (SetFrontProcess(&psn) == procNotFound && m_foregroundCount < 15) { + // somehow the mac/carbon api says + // "-600 no eligible process with specified process id" + // if we call SetFrontProcess too early + ++m_foregroundCount; + QTimer::singleShot(200, this, SLOT(bringApplicationToForegroundInternal())); + return; + } +#endif +} diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h new file mode 100644 index 00000000000..67b9299ff05 --- /dev/null +++ b/src/plugins/projectexplorer/runconfiguration.h @@ -0,0 +1,168 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef RUNCONFIGURATION_H +#define RUNCONFIGURATION_H + +#include "projectexplorer_export.h" + +#include <QtCore/QObject> +#include <QtCore/QMetaType> +#include <QtCore/QPointer> +#include <QtCore/QSharedPointer> + +QT_BEGIN_NAMESPACE +class QString; +class QWidget; +QT_END_NAMESPACE + +namespace ProjectExplorer { + +class Project; +class PersistentSettingsReader; +class PersistentSettingsWriter; + +class RunControl; + +/* Base class for a run configuration. A run configuration specifies how a + * project should be run, while the runner (see below) does the actual running. + * + * Note that all RunControls and the project hold a shared pointer to the RunConfiguration. + * That is the lifetime of the RunConfiguration might exceed the life of the project. + * The user might still have a RunControl running (or output tab of that RunControl open) + * and yet unloaded the project. + * Also a RunConfiguration might be already removed from the list of RunConfigurations + * for a project, but stil be runnable via the output tab. + +*/ +class PROJECTEXPLORER_EXPORT RunConfiguration : public QObject +{ + Q_OBJECT +public: + RunConfiguration(Project *project); + virtual ~RunConfiguration(); + Project *project() const; + + // The type of this RunConfiguration, e.g. "ProjectExplorer.ApplicationRunConfiguration" + virtual QString type() const = 0; + // Name shown to the user + QString name() const; + void setName(const QString &name); + + // Returns the widget used to configure this run configuration. Ownership is transferred to the caller + virtual QWidget *configurationWidget() = 0; + + virtual void save(PersistentSettingsWriter &writer) const; + virtual void restore(const PersistentSettingsReader &reader); +signals: + void nameChanged(); +private: + QPointer<Project> m_project; + QString m_name; +}; + +/* The run configuration factory is used for restoring run configurations from + * settings. And used to create new runconfigurations in the "Run Settings" Dialog. + * For the first case bool canCreate(const QString &type) and + * QSharedPointer<RunConfiguration> create(Project *project, QString type) are used. + * For the second type the functions QStringList canCreate(Project *pro) and + * QString nameForType(const QString&) are used to generate a list of creatable + * RunConfigurations, and create(..) is used to create it. + */ +class PROJECTEXPLORER_EXPORT IRunConfigurationFactory : public QObject +{ + Q_OBJECT +public: + IRunConfigurationFactory(); + virtual ~IRunConfigurationFactory(); + // used to recreate the runConfigurations when restoring settings + virtual bool canCreate(const QString &type) const = 0; + // used to show the list of possible additons to a project, returns a list of types + virtual QStringList canCreate(Project *pro) const = 0; + // used to translate the types to names to display to the user + virtual QString nameForType(const QString &type) const = 0; + virtual QSharedPointer<RunConfiguration> create(Project *project, const QString &type) = 0; + +}; + +class PROJECTEXPLORER_EXPORT IRunConfigurationRunner : public QObject +{ + Q_OBJECT +public: + IRunConfigurationRunner(); + virtual ~IRunConfigurationRunner(); + virtual bool canRun(QSharedPointer<RunConfiguration> runConfiguration, const QString &mode) = 0; + virtual RunControl* run(QSharedPointer<RunConfiguration> runConfiguration, const QString &mode) = 0; + + virtual QString displayName() const = 0; + + // Returns the widget used to configure this runner. Ownership is transferred to the caller + virtual QWidget *configurationWidget(QSharedPointer<RunConfiguration> runConfiguration) = 0; +}; + +/* Each instance of this class represents one item that is run. + */ +class PROJECTEXPLORER_EXPORT RunControl : public QObject { + Q_OBJECT +public: + RunControl(QSharedPointer<RunConfiguration> runConfiguration); + virtual ~RunControl(); + virtual void start() = 0; + virtual void stop() = 0; + virtual bool isRunning() const = 0; + QSharedPointer<RunConfiguration> runConfiguration(); +signals: + void addToOutputWindow(RunControl *, const QString &line); + void error(RunControl *, const QString &error); + void started(); + void finished(); +public slots: + void bringApplicationToForeground(qint64 pid); +private slots: + void bringApplicationToForegroundInternal(); + +private: + QSharedPointer<RunConfiguration> m_runConfiguration; + +#ifdef Q_OS_MAC + //these two are used to bring apps in the foreground on Mac + qint64 m_internalPid; + int m_foregroundCount; +#endif +}; + +} // namespace ProjectExplorer + +// Allow a RunConfiguration to be stored in a QVariant +Q_DECLARE_METATYPE(ProjectExplorer::RunConfiguration*) + +#endif // RUNCONFIGURATION_H diff --git a/src/plugins/projectexplorer/runsettingspropertiespage.cpp b/src/plugins/projectexplorer/runsettingspropertiespage.cpp new file mode 100644 index 00000000000..607e8b8cb90 --- /dev/null +++ b/src/plugins/projectexplorer/runsettingspropertiespage.cpp @@ -0,0 +1,292 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "runsettingspropertiespage.h" +#include "runconfiguration.h" + +#include "ui_runsettingspropertiespage.h" + +#include <extensionsystem/pluginmanager.h> + +#include <QDebug> +#include <QtCore/QPair> + +namespace ProjectExplorer { +namespace Internal { +struct FactoryAndType +{ + ProjectExplorer::IRunConfigurationFactory *factory; + QString type; +}; +} // namespace +} // namespace + +Q_DECLARE_METATYPE(ProjectExplorer::Internal::FactoryAndType); + +namespace ProjectExplorer { +namespace Internal { + +/*! A model to represent the run configurations of a project. */ +class RunConfigurationsModel : public QAbstractListModel +{ +public: + RunConfigurationsModel(QObject *parent = 0) + : QAbstractListModel(parent) + {} + + 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 setRunConfigurations(const QList<QSharedPointer<RunConfiguration> > &runConfigurations); + void nameChanged(RunConfiguration *rc); + +private: + QList<QSharedPointer<RunConfiguration> > m_runConfigurations; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; +using ExtensionSystem::PluginManager; + +/// +/// RunSettingsPanelFactory +/// + +bool RunSettingsPanelFactory::supports(Project * /* project */) +{ + return true; +} + +PropertiesPanel *RunSettingsPanelFactory::createPanel(Project *project) +{ + return new RunSettingsPanel(project); +} + +/// +/// RunSettingsPanel +/// + +RunSettingsPanel::RunSettingsPanel(Project *project) + : PropertiesPanel(), + m_widget(new RunSettingsWidget(project)) +{ +} + +RunSettingsPanel::~RunSettingsPanel() +{ + delete m_widget; +} + +QString RunSettingsPanel::name() const +{ + return tr("Run Settings"); +} + +QWidget *RunSettingsPanel::widget() +{ + return m_widget; +} + +/// +/// RunConfigurationsModel +/// + +int RunConfigurationsModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_runConfigurations.size(); +} + +int RunConfigurationsModel::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : 1; +} + +void RunConfigurationsModel::nameChanged(RunConfiguration *rc) +{ + for (int i = 0; i<m_runConfigurations.size(); ++i) { + if (m_runConfigurations.at(i).data() == rc) { + emit dataChanged(index(i, 0), index(i,0)); + break; + } + } +} + +QVariant RunConfigurationsModel::data(const QModelIndex &index, int role) const +{ + if (role == Qt::DisplayRole) { + const int row = index.row(); + if (row < m_runConfigurations.size()) { + QSharedPointer<RunConfiguration> c = m_runConfigurations.at(row); + return c->name(); + } + } + + return QVariant(); +} + +void RunConfigurationsModel::setRunConfigurations(const QList<QSharedPointer<RunConfiguration> > &runConfigurations) +{ + m_runConfigurations = runConfigurations; + reset(); +} + + +/// +/// RunSettingsWidget +/// + +RunSettingsWidget::RunSettingsWidget(Project *project) + : m_project(project) + , m_runConfigurationsModel(new RunConfigurationsModel(this)) + , m_runConfigurationWidget(0) +{ + m_ui = new Ui::RunSettingsPropertiesPage; + m_ui->setupUi(this); + m_addMenu = new QMenu(m_ui->addToolButton); + m_ui->addToolButton->setIcon(QIcon(":/qworkbench/images/plus.png")); + m_ui->addToolButton->setMenu(m_addMenu); + m_ui->removeToolButton->setIcon(QIcon(":/qworkbench/images/minus.png")); + m_ui->runConfigurationCombo->setModel(m_runConfigurationsModel); + + connect(m_addMenu, SIGNAL(aboutToShow()), + this, SLOT(aboutToShowAddMenu())); + connect(m_ui->runConfigurationCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(activateRunConfiguration(int))); + connect(m_ui->removeToolButton, SIGNAL(clicked(bool)), + this, SLOT(removeRunConfiguration())); + + initRunConfigurationComboBox(); + const QList<QSharedPointer<RunConfiguration> > runConfigurations = m_project->runConfigurations(); + for (int i=0; i<runConfigurations.size(); ++i) { + connect(runConfigurations.at(i).data(), SIGNAL(nameChanged()), + this, SLOT(nameChanged())); + } + + // TODO: Add support for custom runner configuration widgets once we have some + /* + QList<IRunConfigurationRunner *> runners = PluginManager::instance()->getObjects<IRunConfigurationRunner>(); + foreach (IRunConfigurationRunner * runner, runners) { + if (runner->canRun(activeRunConfiguration)) + m_ui->layout->addWidget(runner->configurationWidget(activeRunConfiguration)); + } + */ +} + +RunSettingsWidget::~RunSettingsWidget() +{ + delete m_ui; +} + +void RunSettingsWidget::aboutToShowAddMenu() +{ + m_addMenu->clear(); + QList<IRunConfigurationFactory *> factories = ExtensionSystem::PluginManager::instance()->getObjects<IRunConfigurationFactory>(); + foreach (IRunConfigurationFactory * factory, factories) { + QStringList types = factory->canCreate(m_project); + foreach (const QString &type, types) { + QAction *action = m_addMenu->addAction(factory->nameForType(type));; + FactoryAndType fat; + fat.factory = factory; + fat.type = type; + QVariant v; + v.setValue(fat); + action->setData(v); + connect(action, SIGNAL(triggered()), + this, SLOT(addRunConfiguration())); + } + } +} + +void RunSettingsWidget::addRunConfiguration() +{ + QAction *act = qobject_cast<QAction *>(sender()); + if (!act) + return; + FactoryAndType fat = act->data().value<FactoryAndType>(); + QSharedPointer<RunConfiguration> newRC = fat.factory->create(m_project, fat.type); + if (!newRC) + return; + m_project->addRunConfiguration(newRC); + m_project->setActiveRunConfiguration(newRC); + initRunConfigurationComboBox(); + connect(newRC.data(), SIGNAL(nameChanged()), + this, SLOT(nameChanged())); +} + +void RunSettingsWidget::removeRunConfiguration() +{ + int index = m_ui->runConfigurationCombo->currentIndex(); + QSharedPointer<RunConfiguration> rc = m_project->runConfigurations().at(index); + disconnect(rc.data(), SIGNAL(nameChanged()), + this, SLOT(nameChanged())); + m_project->removeRunConfiguration(rc); + initRunConfigurationComboBox(); +} + +void RunSettingsWidget::initRunConfigurationComboBox() +{ + const QList<QSharedPointer<RunConfiguration> > runConfigurations = m_project->runConfigurations(); + QSharedPointer<RunConfiguration> activeRunConfiguration = m_project->activeRunConfiguration(); + m_runConfigurationsModel->setRunConfigurations(runConfigurations); + // Make sure the active run configuration is selected in the combo + for (int i = 0; i < runConfigurations.size(); ++i) { + if (runConfigurations.at(i) == activeRunConfiguration) + m_ui->runConfigurationCombo->setCurrentIndex(i); + } + m_ui->removeToolButton->setEnabled(runConfigurations.size() > 1); +} + +void RunSettingsWidget::activateRunConfiguration(int index) +{ + Q_ASSERT(m_project); + const QList<QSharedPointer<RunConfiguration> > runConfigurations = m_project->runConfigurations(); + Q_ASSERT(index < runConfigurations.size()); + QSharedPointer<RunConfiguration> selectedRunConfiguration = runConfigurations.at(index); + + // Change the active run configuration of the project + m_project->setActiveRunConfiguration(selectedRunConfiguration); + + // Update the run configuration configuration widget + delete m_runConfigurationWidget; + m_runConfigurationWidget = selectedRunConfiguration->configurationWidget(); + m_ui->groupBox->layout()->addWidget(m_runConfigurationWidget); +} + +void RunSettingsWidget::nameChanged() +{ + RunConfiguration *rc = qobject_cast<RunConfiguration *>(sender()); + m_runConfigurationsModel->nameChanged(rc); +} diff --git a/src/plugins/projectexplorer/runsettingspropertiespage.h b/src/plugins/projectexplorer/runsettingspropertiespage.h new file mode 100644 index 00000000000..50ed3f49788 --- /dev/null +++ b/src/plugins/projectexplorer/runsettingspropertiespage.h @@ -0,0 +1,100 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef RUNSETTINGSPROPERTIESPAGE_H +#define RUNSETTINGSPROPERTIESPAGE_H + +#include "iprojectproperties.h" +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + + +QT_END_NAMESPACE + +namespace ProjectExplorer { +namespace Internal { + +namespace Ui { +class RunSettingsPropertiesPage; +} + +class RunConfigurationsModel; +class RunSettingsWidget; + +class RunSettingsPanelFactory : public IPanelFactory +{ +public: + virtual bool supports(Project *project); + PropertiesPanel *createPanel(Project *project); +}; + +class RunSettingsPanel : public PropertiesPanel +{ + Q_OBJECT +public: + RunSettingsPanel(Project *project); + ~RunSettingsPanel(); + + QString name() const; + QWidget *widget(); +private: + RunSettingsWidget *m_widget; +}; + +class RunSettingsWidget : public QWidget +{ + Q_OBJECT +public: + RunSettingsWidget(Project *project); + ~RunSettingsWidget(); + +private slots: + void activateRunConfiguration(int index); + void aboutToShowAddMenu(); + void addRunConfiguration(); + void removeRunConfiguration(); + void nameChanged(); + +private: + void initRunConfigurationComboBox(); + Project *m_project; + RunConfigurationsModel *m_runConfigurationsModel; + Ui::RunSettingsPropertiesPage *m_ui; + QWidget *m_runConfigurationWidget; + QMenu *m_addMenu; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // RUNSETTINGSPROPERTIESPAGE_H diff --git a/src/plugins/projectexplorer/runsettingspropertiespage.ui b/src/plugins/projectexplorer/runsettingspropertiespage.ui new file mode 100644 index 00000000000..a973085873d --- /dev/null +++ b/src/plugins/projectexplorer/runsettingspropertiespage.ui @@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ProjectExplorer::Internal::RunSettingsPropertiesPage</class> + <widget class="QWidget" name="ProjectExplorer::Internal::RunSettingsPropertiesPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>521</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="layout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Run &configuration:</string> + </property> + <property name="buddy"> + <cstring>runConfigurationCombo</cstring> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="runConfigurationCombo"> + <property name="sizeAdjustPolicy"> + <enum>QComboBox::AdjustToMinimumContentsLength</enum> + </property> + <property name="minimumContentsLength"> + <number>30</number> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="addToolButton"> + <property name="text"> + <string>+</string> + </property> + <property name="popupMode"> + <enum>QToolButton::InstantPopup</enum> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="removeToolButton"> + <property name="text"> + <string>-</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> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Settings</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"/> + </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> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/projectexplorer/scriptwrappers.cpp b/src/plugins/projectexplorer/scriptwrappers.cpp new file mode 100644 index 00000000000..307fb6e650a --- /dev/null +++ b/src/plugins/projectexplorer/scriptwrappers.cpp @@ -0,0 +1,34 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "scriptwrappers.h" + diff --git a/src/plugins/projectexplorer/scriptwrappers.h b/src/plugins/projectexplorer/scriptwrappers.h new file mode 100644 index 00000000000..4eb59f6ec56 --- /dev/null +++ b/src/plugins/projectexplorer/scriptwrappers.h @@ -0,0 +1,36 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROJECTEXPLORER_SCRIPT_WRAPPER_H +#define PROJECTEXPLORER_SCRIPT_WRAPPER_H + +#endif diff --git a/src/plugins/projectexplorer/session.cpp b/src/plugins/projectexplorer/session.cpp new file mode 100644 index 00000000000..d0e27317e3a --- /dev/null +++ b/src/plugins/projectexplorer/session.cpp @@ -0,0 +1,1109 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "dependenciesdialog.h" +#include "project.h" +#include "projectexplorer.h" +#include "projectexplorerconstants.h" +#include "nodesvisitor.h" +#include "session.h" +#include "editorconfiguration.h" + +#include <coreplugin/icore.h> +#include <coreplugin/imode.h> +#include <coreplugin/filemanager.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/progressmanager/progressmanagerinterface.h> +#include <coreplugin/modemanager.h> +#include <utils/listutils.h> + +#include <texteditor/itexteditor.h> + + +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtCore/QSettings> +#include <QtCore/QFuture> +#include <QtGui/QMessageBox> +#include <QtGui/QMainWindow> + +namespace { + bool debug = false; +} + +/* SessionFile definitions */ +namespace ProjectExplorer { +namespace Internal { + +class SessionFile + : public Core::IFile +{ + Q_OBJECT + +public: + SessionFile(Core::ICore *core); + + bool load(const QString &fileName); + bool save(const QString &fileName = QString()); + + QString fileName() const; + void setFileName(const QString &fileName); + + QString defaultPath() const; + QString suggestedFileName() const; + virtual QString mimeType() const; + + bool isModified() const; + bool isReadOnly() const; + bool isSaveAsAllowed() const; + + void modified(Core::IFile::ReloadBehavior *behavior); + +public slots: + void sessionLoadingProgress(); + + +private: + const QString m_mimeType; + Core::ICore *m_core; + + QString m_fileName; + QList<Project *> m_projects; + Project *m_startupProject; + QMap<QString, QStringList> m_depMap; + + QMap<QString, QVariant> m_values; + + QFutureInterface<void> future; + friend class ProjectExplorer::SessionManager; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +using namespace ProjectExplorer; +using Internal::SessionFile; +using Internal::DependenciesDialog; + + +void SessionFile::sessionLoadingProgress() +{ + future.setProgressValue(future.progressValue() + 1); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); +} + +SessionFile::SessionFile(Core::ICore *core) : + m_mimeType(QLatin1String(ProjectExplorer::Constants::SESSIONFILE_MIMETYPE)), + m_core(core), + m_startupProject(0) +{ +} + +QString SessionFile::mimeType() const +{ + return m_mimeType; +} + +bool SessionFile::load(const QString &fileName) +{ + Q_ASSERT(!fileName.isEmpty()); + + if (debug) + qDebug() << "SessionFile::load " << fileName; + + + + m_fileName = fileName; + + // NPE: Load the session in the background? + // NPE: Let FileManager monitor filename + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + + + + PersistentSettingsReader reader; + if (!reader.load(m_fileName)) { + qWarning() << "SessionManager::load failed!" << fileName; + QApplication::restoreOverrideCursor(); + return false; + } + + m_core->progressManager()->addTask(future.future(), tr("Session"), + QLatin1String("ProjectExplorer.SessionFile.Load"), + Core::ProgressManagerInterface::CloseOnSuccess); + + const QStringList &keys = reader.restoreValue(QLatin1String("valueKeys")).toStringList(); + foreach (const QString &key, keys) { + QVariant value = reader.restoreValue("value-" + key); + m_values.insert(key, value); + } + + QStringList fileList = + reader.restoreValue(QLatin1String("ProjectList")).toStringList(); + QString configDir = QFileInfo(m_core->settings()->fileName()).path(); + QMutableStringListIterator it(fileList); + while (it.hasNext()) { + const QString &file = it.next(); + if (QFileInfo(file).isRelative()) { + // We used to write relative paths into the session file + // relative to the session files, and those were stored in the + // config dir + it.setValue(configDir + "/" + file); + } + } + + + int openEditorsCount = reader.restoreValue(QLatin1String("OpenEditors")).toInt(); + + future.setProgressRange(0, fileList.count() + openEditorsCount + 2); + future.setProgressValue(1); + + // indirectly adds projects to session + if (!fileList.isEmpty()) + ProjectExplorerPlugin::instance()->openProjects(fileList); + + sessionLoadingProgress(); + + // convert the relative paths in the dependency map to absolute paths + QMap<QString, QVariant> depMap = reader.restoreValue(QLatin1String("ProjectDependencies")).toMap(); + QMap<QString, QVariant>::const_iterator i = depMap.constBegin(); + while (i != depMap.constEnd()) { + const QString &key = i.key(); + QStringList values; + foreach (const QString &value, i.value().toStringList()) { + values << value; + } + m_depMap.insert(key, values); + ++i; + } + + // find and set the startup project + const QString startupProject = reader.restoreValue(QLatin1String("StartupProject")).toString(); + if (!startupProject.isEmpty()) { + const QString startupProjectPath = startupProject; + foreach (Project *pro, m_projects) { + if (QDir::cleanPath(pro->file()->fileName()) == startupProjectPath) { + m_startupProject = pro; + break; + } + } + if (!m_startupProject) + qWarning() << "Could not find startup project" << startupProjectPath; + } + + + const QVariant &editorsettings = reader.restoreValue(QLatin1String("EditorSettings")); + if (editorsettings.isValid()) { + connect(m_core->editorManager(), SIGNAL(editorOpened(Core::IEditor *)), + this, SLOT(sessionLoadingProgress())); + m_core->editorManager()->restoreState( + QByteArray::fromBase64(editorsettings.toByteArray())); + disconnect(m_core->editorManager(), SIGNAL(editorOpened(Core::IEditor *)), + this, SLOT(sessionLoadingProgress())); + } + + if (debug) + qDebug() << "SessionFile::load finished" << fileName; + + + future.reportFinished(); + QApplication::restoreOverrideCursor(); + return true; +} + +bool SessionFile::save(const QString &fileName) +{ + if (!fileName.isEmpty()) + m_fileName = fileName; + + Q_ASSERT(!m_fileName.isEmpty()); + + if (debug) + qDebug() << "SessionFile - saving " << m_fileName; + + PersistentSettingsWriter writer; + + // save the startup project + if (m_startupProject) { + writer.saveValue(QLatin1String("StartupProject"), m_startupProject->file()->fileName()); + } + + QStringList projectFiles; + foreach (Project *pro, m_projects) { + projectFiles << pro->file()->fileName(); + } + + writer.saveValue(QLatin1String("ProjectList"), projectFiles); + + QMap<QString, QVariant> depMap; + QMap<QString, QStringList>::const_iterator i = m_depMap.constBegin(); + while (i != m_depMap.constEnd()) { + QString key = i.key(); + QStringList values; + foreach (const QString &value, i.value()) { + values << value; + } + depMap.insert(key, values); + ++i; + } + writer.saveValue(QLatin1String("ProjectDependencies"), QVariant(depMap)); + + writer.saveValue(QLatin1String("OpenEditors"), + m_core->editorManager()->openedEditors().count()); + writer.saveValue(QLatin1String("EditorSettings"), + m_core->editorManager()->saveState().toBase64()); + + QMap<QString, QVariant>::const_iterator it, end; + end = m_values.constEnd(); + QStringList keys; + for (it = m_values.constBegin(); it != end; ++it) { + writer.saveValue("value-" + it.key(), it.value()); + keys << it.key(); + } + + writer.saveValue("valueKeys", keys); + + + if (writer.save(m_fileName, "QtCreatorSession")) + return true; + + return false; +} + +QString SessionFile::fileName() const +{ + return m_fileName; +} + +void SessionFile::setFileName(const QString &fileName) +{ + m_fileName = fileName; +} + +bool SessionFile::isModified() const +{ + return true; +} + +bool SessionFile::isReadOnly() const +{ + return false; +} + +bool SessionFile::isSaveAsAllowed() const +{ + return true; +} + +void SessionFile::modified(Core::IFile::ReloadBehavior *) +{ +} + +QString SessionFile::defaultPath() const +{ + if (!m_projects.isEmpty()) { + const QFileInfo fi(m_projects.first()->file()->fileName()); + return fi.absolutePath(); + } + return QString(); +} + +QString SessionFile::suggestedFileName() const +{ + if (m_startupProject) + return m_startupProject->name(); + + return tr("Untitled", "default file name to display"); +} + +Internal::SessionNodeImpl::SessionNodeImpl(SessionManager *manager) + : ProjectExplorer::SessionNode(manager->file()->fileName(), manager) +{ + setFileName("session"); +} + +void Internal::SessionNodeImpl::addProjectNode(ProjectNode *projectNode) +{ + addProjectNodes(QList<ProjectNode*>() << projectNode); +} + +void Internal::SessionNodeImpl::removeProjectNode(ProjectNode *projectNode) +{ + removeProjectNodes(QList<ProjectNode*>() << projectNode); +} + +void Internal::SessionNodeImpl::setFileName(const QString &fileName) +{ + setPath(fileName); + setFolderName(fileName); +} + +/* --------------------------------- */ + +SessionManager::SessionManager(Core::ICore *core, QObject *parent) + : QObject(parent), + m_core(core), + m_file(new SessionFile(core)), + m_sessionNode(new Internal::SessionNodeImpl(this)) +{ + // Create qtcreator dir if it doesn't yet exist + QString configDir = QFileInfo(m_core->settings()->fileName()).path(); + + QFileInfo fi(configDir + "/qtcreator/"); + if (!fi.exists()) { + QDir dir; + dir.mkpath(configDir + "/qtcreator"); + + // Move sessions to that directory + foreach(const QString &session, sessions()) { + QFile file(configDir + "/" + session + ".qws"); + if (file.exists()) + if(file.copy(configDir + "/qtcreator/" + session + ".qws")) + file.remove(); + } + } + + connect(m_core->modeManager(), SIGNAL(currentModeChanged(Core::IMode*)), + this, SLOT(saveActiveMode(Core::IMode*))); + connect(core->editorManager(), SIGNAL(editorCreated(Core::IEditor *, QString)), + this, SLOT(setEditorCodec(Core::IEditor *, QString))); + connect(ProjectExplorerPlugin::instance(), SIGNAL(currentProjectChanged(ProjectExplorer::Project *)), + this, SLOT(updateWindowTitle())); + + } + +SessionManager::~SessionManager() +{ + delete m_file; + emit sessionUnloaded(); +} + + +bool SessionManager::isDefaultVirgin() const +{ + return (isDefaultSession(m_sessionName) + && projects().isEmpty() + && m_core->editorManager()->openedEditors().isEmpty()); +} + + +bool SessionManager::isDefaultSession(const QString &session) const +{ + return (session == QLatin1String("default")); +} + + +void SessionManager::saveActiveMode(Core::IMode *mode) +{ + setValue(QLatin1String("ActiveMode"), mode->uniqueModeName()); +} + +void SessionManager::clearProjectFileCache() +{ + // If triggered by the fileListChanged signal of one project + // only invalidate cache for this project + Project *pro = qobject_cast<Project*>(sender()); + if (pro) + m_projectFileCache.remove(pro); + else + m_projectFileCache.clear(); +} + +bool SessionManager::recursiveDependencyCheck(const QString &newDep, const QString &checkDep) const +{ + if (newDep == checkDep) + return false; + + foreach (const QString &dependency, m_file->m_depMap.value(checkDep)) { + if (!recursiveDependencyCheck(newDep, dependency)) + return false; + } + + return true; +} + +bool SessionManager::hasDependency(Project *project, Project *depProject) const +{ + const QString &proName = project->file()->fileName(); + const QString &depName = depProject->file()->fileName(); + + const QStringList &proDeps = m_file->m_depMap.value(proName); + return proDeps.contains(depName); +} + +bool SessionManager::canAddDependency(Project *project, Project *depProject) const +{ + const QString &newDep = project->file()->fileName(); + const QString &checkDep = depProject->file()->fileName(); + + return recursiveDependencyCheck(newDep, checkDep); +} + +bool SessionManager::addDependency(Project *project, Project *depProject) +{ + const QString &proName = project->file()->fileName(); + const QString &depName = depProject->file()->fileName(); + + // check if this dependency is valid + if (!recursiveDependencyCheck(proName, depName)) + return false; + + QStringList proDeps = m_file->m_depMap.value(proName); + if (!proDeps.contains(depName)) { + proDeps.append(depName); + m_file->m_depMap[proName] = proDeps; + } + + return true; +} + +void SessionManager::setStartupProject(Project *startupProject) +{ + if (debug) + qDebug() << Q_FUNC_INFO << (startupProject ? startupProject->name() : "0"); + + if (startupProject) { + Q_ASSERT(m_file->m_projects.contains(startupProject)); + } + + m_file->m_startupProject = startupProject; + emit startupProjectChanged(startupProject); +} + +Project *SessionManager::startupProject() const +{ + return m_file->m_startupProject; +} + +void SessionManager::removeDependency(Project *project, + Project *depProject) +{ + const QString &proName = project->file()->fileName(); + const QString &depName = depProject->file()->fileName(); + + QStringList proDeps = m_file->m_depMap.value(proName); + proDeps.removeAll(depName); + if (proDeps.isEmpty()) { + m_file->m_depMap.remove(proName); + } else { + m_file->m_depMap[proName] = proDeps; + } +} + +void SessionManager::addProject(Project *project) +{ + addProjects(QList<Project*>() << project); +} + +void SessionManager::addProjects(const QList<Project*> &projects) +{ + QList<Project*> clearedList; + foreach (Project *pro, projects) { + if (!m_file->m_projects.contains(pro)) { + clearedList.append(pro); + m_file->m_projects.append(pro); + m_sessionNode->addProjectNode(pro->rootProjectNode()); + + connect(pro, SIGNAL(fileListChanged()), + this, SLOT(clearProjectFileCache())); + + if (debug) + qDebug() << "SessionManager - adding project " << pro->name(); + } + } + + foreach (Project *pro, clearedList) { + emit projectAdded(pro); + } + + if (clearedList.count() == 1) + emit singleProjectAdded(clearedList.first()); + + // maybe we have a new startup project? + if (!startupProject()) + if (Project *newStartupProject = defaultStartupProject()) + setStartupProject(newStartupProject); +} + +void SessionManager::removeProject(Project *project) +{ + if (project == 0) { + qDebug() << "SessionManager::removeProject(0) ... THIS SHOULD NOT HAPPEN"; + return; + } + removeProjects(QList<Project*>() << project); +} + +bool SessionManager::createImpl(const QString &fileName) +{ + Q_ASSERT(!fileName.isEmpty()); + + if (debug) + qDebug() << "SessionManager - creating new session " << fileName << " ..."; + + bool success = true; + + if (!m_file->fileName().isEmpty()) { + if (!save() || !clear()) { + success = false; + } + } + + if (success) { + delete m_file; + emit sessionUnloaded(); + m_file = new SessionFile(m_core); + m_file->setFileName(fileName); + setStartupProject(defaultStartupProject()); + } + + if (debug) + qDebug() << "SessionManager - creating new session returns " << success; + + if (success) + emit sessionLoaded(); + + return success; +} + +bool SessionManager::loadImpl(const QString &fileName) +{ + Q_ASSERT(!fileName.isEmpty()); + + if (debug) + qDebug() << "SessionManager - loading session " << fileName << " ..."; + + bool success = true; + + if (!m_file->fileName().isEmpty()) { + + if (isDefaultVirgin()) { + // do not save initial and virgin default session + } else if (!save() || !clear()) { + success = false; + } + } + + if (success) { + delete m_file; + emit sessionUnloaded(); + m_file = new SessionFile(m_core); + if (!m_file->load(fileName)) { + QMessageBox::warning(0, tr("Error while loading session"), \ + tr("Could not load session %1").arg(fileName)); + success = false; + } + setStartupProject(m_file->m_startupProject); + } + + if (success) { + // restore the active mode + const QString &modeIdentifier = value(QLatin1String("ActiveMode")).toString(); + if (!modeIdentifier.isEmpty()) + m_core->modeManager()->activateMode(modeIdentifier); + } + + if (debug) + qDebug() << "SessionManager - loading session returned " << success; + + if (success) + emit sessionLoaded(); + + return success; +} + +bool SessionManager::save() +{ + if (debug) + qDebug() << "SessionManager - saving session" << m_sessionName; + + emit aboutToSaveSession(); + + bool result = m_file->save(); + + if (!result) { + QMessageBox::warning(0, tr("Error while saving session"), + tr("Could not save session to file %1").arg(m_file->fileName())); + } + + if (debug) + qDebug() << "SessionManager - saving session returned " << result; + + return result; +} + + +/*! + \fn bool SessionManager::clear() + + Returns whether the clear operation has been not cancelled & all projects could be closed. + */ +bool SessionManager::clear() +{ + if (debug) + qDebug() << "SessionManager - clearing session ..."; + + bool cancelled; + QList<Project *> notClosed = requestCloseOfAllFiles(&cancelled); + + bool success = !cancelled; + + if (success) { + if (debug) + qDebug() << "SessionManager - Removing projects ..."; + + QList<Project *> toClose; + foreach (Project *pro, projects()) { + if (!notClosed.contains(pro)) + toClose << pro; + } + + setStartupProject(0); + removeProjects(toClose); + } + + if (!notClosed.isEmpty()) + success = false; + + if (debug) + qDebug() << "SessionManager - clearing session result is " << success; + + return success; +} + +void SessionManager::editDependencies() +{ + DependenciesDialog dlg(0, this); + dlg.exec(); +} + +QList<Project *> SessionManager::projects() const +{ + return m_file->m_projects; +} + +QStringList SessionManager::dependencies(const QString &proName) const +{ + QStringList result; + foreach (const QString &dep, m_file->m_depMap.value(proName)) + result += dependencies(dep); + + result << proName; + return result; +} + +QStringList SessionManager::dependenciesOrder() const +{ + QList<QPair<QString, QStringList> > unordered; + QStringList ordered; + + // copy the map to a temporary list + foreach (Project *pro, projects()) { + const QString &proName = pro->file()->fileName(); + unordered << QPair<QString, QStringList> + (proName, m_file->m_depMap.value(proName)); + } + + while (!unordered.isEmpty()) { + for (int i=(unordered.count()-1); i>=0; --i) { + if (unordered.at(i).second.isEmpty()) { + ordered << unordered.at(i).first; + unordered.removeAt(i); + } + } + + // remove the handled projects from the dependency lists + // of the remaining unordered projects + for (int i = 0; i < unordered.count(); ++i) { + foreach (const QString &pro, ordered) { + QStringList depList = unordered.at(i).second; + depList.removeAll(pro); + unordered[i].second = depList; + } + } + } + + return ordered; +} + +Project *SessionManager::defaultStartupProject() const +{ + // Just take first one + foreach (Project *p, m_file->m_projects) { + if (p->isApplication()) + return p; + } + return 0; +} + +QList<Project *> SessionManager::projectOrder(Project *project) const +{ + QList<Project *> result; + + QStringList pros; + if (project) { + pros = dependencies(project->file()->fileName()); + } else { + pros = dependenciesOrder(); + } + + foreach (const QString &proFile, pros) { + foreach (Project *pro, projects()) { + if (pro->file()->fileName() == proFile) { + result << pro; + break; + } + } + } + + return result; +} + +Project *SessionManager::projectForNode(Node *node) const +{ + if (!node) + return 0; + + Project *project = 0; + + FolderNode *rootProjectNode = qobject_cast<FolderNode*>(node); + if (!rootProjectNode) + rootProjectNode = node->parentFolderNode(); + while (rootProjectNode && rootProjectNode->parentFolderNode() != m_sessionNode) + rootProjectNode = rootProjectNode->parentFolderNode(); + + Q_ASSERT(rootProjectNode); + + QList<Project *> projectList = projects(); + foreach (Project *p, projectList) { + if (p->rootProjectNode() == rootProjectNode) { + project = p; + break; + } + } + + return project; +} + +Node *SessionManager::nodeForFile(const QString &fileName) const +{ + Node *node = 0; + if (Project *project = projectForFile(fileName)) { + FindNodesForFileVisitor findNodes(fileName); + project->rootProjectNode()->accept(&findNodes); + + foreach (Node *n, findNodes.nodes()) { + // prefer file nodes + if (!node || (node->nodeType() != FileNodeType && n->nodeType() == FileNodeType)) + node = n; + } + } + + return node; +} + +Project *SessionManager::projectForFile(const QString &fileName) const +{ + if (debug) + qDebug() << "SessionManager::projectForFile(" << fileName << ")"; + + Project *project = 0; + + QList<Project *> projectList = projects(); + + // Always check current project first + if (Project *currentProject = ProjectExplorerPlugin::instance()->currentProject()) { + projectList.removeOne(currentProject); + projectList.insert(0, currentProject); + } + + foreach (Project *p, projectList) { + if (!m_projectFileCache.contains(p)) { + m_projectFileCache.insert(p, p->files(Project::AllFiles)); + } + if (m_projectFileCache.value(p).contains(fileName)) { + project = p; + break; + } + } + return project; +} + +void SessionManager::setEditorCodec(Core::IEditor *editor, const QString &fileName) +{ + if (TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor)) + if (Project *project = projectForFile(fileName)) + textEditor->setTextCodec(project->editorConfiguration()->defaultTextCodec()); +} + + +QList<Project *> SessionManager::requestCloseOfAllFiles(bool *cancelled) +{ + *cancelled = false; + QList<Core::IFile*> filesToClose; + foreach (Project *pro, projects()) + filesToClose << pro->file(); + foreach (Core::IEditor *editor, m_core->editorManager()->openedEditors()) + filesToClose << editor->file(); + QList<Core::IFile*> notClosed; + if (!filesToClose.isEmpty()) + notClosed = m_core->fileManager()->saveModifiedFiles(filesToClose, cancelled); + // close editors here by hand + if (!*cancelled) { + QList<Core::IEditor*> editorsToClose; + foreach (Core::IEditor *editor, m_core->editorManager()->openedEditors()) + if (!notClosed.contains(editor->file())) + editorsToClose << editor; + m_core->editorManager()->closeEditors(editorsToClose, false); + // project files are closed/removed later (in this::clear) + } + return Core::Utils::qwConvertList<Project*>(notClosed); +} + +Core::IFile *SessionManager::file() const +{ + return m_file; +} + +void SessionManager::updateWindowTitle() +{ + QString windowTitle = tr("Qt Creator"); + if (!isDefaultSession(m_sessionName)) { + QString sessionName = m_sessionName; + if (sessionName.isEmpty()) + sessionName = tr("Untitled"); + windowTitle.prepend(sessionName + " - "); + } else { + if (Project *currentProject = ProjectExplorerPlugin::instance()->currentProject()) + windowTitle.prepend(currentProject->name() + " - "); + } + m_core->mainWindow()->setWindowTitle(windowTitle); +} + +void SessionManager::updateName(const QString &session) +{ + m_sessionName = session; + QString sessionName = m_sessionName; + + if (sessionName.isEmpty()) + sessionName = tr("Untitled"); + + m_displayName = tr("Session (\'%1\')").arg(sessionName); + updateWindowTitle(); +} + + +void SessionManager::removeProjects(QList<Project *> remove) +{ + QMap<QString, QStringList> resMap; + + foreach (Project *pro, remove) { + if (debug) + qDebug() << "SessionManager - emitting aboutToRemoveProject(" << pro->name() << ")"; + emit aboutToRemoveProject(pro); + } + + // TODO: Clear m_modelProjectHash + + // Delete projects + foreach (Project *pro, remove) { + m_file->m_projects.removeOne(pro); + + if (pro == m_file->m_startupProject) + setStartupProject(0); + + disconnect(pro, SIGNAL(fileListChanged()), + this, SLOT(clearProjectFileCache())); + m_projectFileCache.remove(pro); + + if (debug) + qDebug() << "SessionManager - emitting projectRemoved(" << pro->name() << ")"; + m_sessionNode->removeProjectNode(pro->rootProjectNode()); + emit projectRemoved(pro); + delete pro; + } + + // Refresh dependencies + QSet<QString> projectFiles; + foreach (Project *pro, projects()) { + if (!remove.contains(pro)) + projectFiles.insert(pro->file()->fileName()); + } + + QSet<QString>::const_iterator i = projectFiles.begin(); + while (i != projectFiles.end()) { + QStringList dependencies; + foreach (const QString &dependency, m_file->m_depMap.value(*i)) { + if (projectFiles.contains(dependency)) + dependencies << dependency; + } + if (!dependencies.isEmpty()) + resMap.insert(*i, dependencies); + ++i; + } + + m_file->m_depMap = resMap; + + if (startupProject() == 0) + if (Project *newStartupProject = defaultStartupProject()) + setStartupProject(newStartupProject); +} + +void SessionManager::setValue(const QString &name, const QVariant &value) +{ + m_file->m_values.insert(name, value); +} + +QVariant SessionManager::value(const QString &name) +{ + QMap<QString, QVariant>::const_iterator it = m_file->m_values.find(name); + if (it != m_file->m_values.constEnd()) + return *it; + else + return QVariant(); +} + +QString SessionManager::activeSession() const +{ + return m_sessionName; +} + +QStringList SessionManager::sessions() const +{ + QStringList result = m_core->settings()->value("Sessions").toStringList(); + + if (!result.contains("default")) + result.prepend("default"); + + return result; +} + +QString SessionManager::sessionNameToFileName(const QString &session) +{ + QString sn = session; + return QFileInfo(m_core->settings()->fileName()).path() + "/qtcreator/" + sn + ".qws"; +} + +void SessionManager::createAndLoadNewDefaultSession() +{ + updateName("default"); + createImpl(sessionNameToFileName(m_sessionName)); +} + +bool SessionManager::createSession(const QString &session) +{ + if (sessions().contains(session)) + return false; + QStringList list = m_core->settings()->value("Sessions").toStringList(); + list.append(session); + m_core->settings()->setValue("Sessions", list); + return true; +} + +bool SessionManager::deleteSession(const QString &session) +{ + QStringList list = m_core->settings()->value("Sessions").toStringList(); + if (!list.contains(session)) + return false; + list.removeOne(session); + m_core->settings()->setValue("Sessions", list); + QFile fi(sessionNameToFileName(session)); + if (fi.exists()) + return fi.remove(); + return false; +} + +bool SessionManager::cloneSession(const QString &original, const QString &clone) +{ + QStringList list = m_core->settings()->value("Sessions").toStringList(); + list.append(clone); + + if (!sessions().contains(original)) + return false; + + QFile fi(sessionNameToFileName(original)); + + // If the file does not exist, we can still clone + if (!fi.exists() || fi.copy(sessionNameToFileName(clone))) { + m_core->settings()->setValue("Sessions", list); + return true; + } + return false; +} + +bool SessionManager::loadSession(const QString &session) +{ + // Do nothing if we have that session already loaded, + // exception if the session is the default virgin session + // we still want to be able to load the default session + if (session == m_sessionName && !isDefaultVirgin()) + return true; + + if (!sessions().contains(session)) + return false; + QString fileName = sessionNameToFileName(session); + if (QFileInfo(fileName).exists()) { + if (loadImpl(fileName)) { + updateName(session); + return true; + } + } else { + // Create a new session with that name + if(!createImpl(sessionNameToFileName(session))) + return false; + updateName(session); + return true; + } + return false; +} + +QString SessionManager::lastSession() const +{ + QSettings *settings = m_core->settings(); + QString fileName = settings->value("ProjectExplorer/StartupSession").toString(); + return QFileInfo(fileName).baseName(); +} + +SessionNode *SessionManager::sessionNode() const +{ + return m_sessionNode; +} + +void SessionManager::reportProjectLoadingProgress() +{ + m_file->sessionLoadingProgress(); +} + + +#include "session.moc" diff --git a/src/plugins/projectexplorer/session.h b/src/plugins/projectexplorer/session.h new file mode 100644 index 00000000000..95cd21c937d --- /dev/null +++ b/src/plugins/projectexplorer/session.h @@ -0,0 +1,207 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SESSION_H +#define SESSION_H + +#include "projectexplorer_export.h" +#include "projectnodes.h" + +#include <coreplugin/ifile.h> + +#include <QtCore/QObject> +#include <QtCore/QAbstractItemModel> +#include <QtCore/QHash> +#include <QtCore/QList> +#include <QtCore/QString> +#include <QtCore/QStringList> + +namespace Core { +class ICore; +class IMode; +class IEditor; +} + +namespace ProjectExplorer { + +class Project; +class Node; +class SessionNode; +class SessionManager; + +namespace Internal { + +class SessionFile; + +// Must be in header as otherwise moc has issues +// with ProjectExplorer::SessionNode on msvc2005 +class SessionNodeImpl + : public ProjectExplorer::SessionNode +{ + Q_OBJECT +public: + SessionNodeImpl(SessionManager *manager); + + void addProjectNode(ProjectNode *projectNode); + void removeProjectNode(ProjectNode *projectNode); + + void setFileName(const QString &fileName); +}; + +} // namespace Internal + +// TODO the interface of this class is not really great + +// The implementation suffers that all the functions from the +// public interface just wrap around functions which do the actual work + +// This could be improved. +class PROJECTEXPLORER_EXPORT SessionManager + : public QObject +{ + Q_OBJECT + +public: + SessionManager(Core::ICore *core, QObject *parent = 0); + ~SessionManager(); + + // higher level session management + QString activeSession() const; + QString lastSession() const; + QStringList sessions() const; + + // creates a new default session and switches to it + void createAndLoadNewDefaultSession(); + + // Just creates a new session (Does not actually create the file) + bool createSession(const QString &session); + + // delete session name from session list + // delete file from disk + bool deleteSession(const QString &session); + + bool cloneSession(const QString &original, const QString &clone); + + // loads a session, takes a session name (not filename) + bool loadSession(const QString &session); + + bool save(); + bool clear(); + + void addProject(Project *project); + void addProjects(const QList<Project*> &projects); + void removeProject(Project *project); + void removeProjects(QList<Project *> remove); + + void editDependencies(); + void setStartupProject(Project *startupProject); + + // NBS think about dependency management again. + // Probably kill these here + bool canAddDependency(Project *project, Project *depProject) const; + bool hasDependency(Project *project, Project *depProject) const; + // adds the 'requiredProject' as a dependency to 'project' + bool addDependency(Project *project, Project *depProject); + void removeDependency(Project *project, Project *depProject); + + Core::IFile *file() const; + Project *startupProject() const; + + QList<Project *> projects() const; + + bool isDefaultVirgin() const; + bool isDefaultSession(const QString &session) const; + + // Let other plugins store persistent values within the session file + void setValue(const QString &name, const QVariant &value); + QVariant value(const QString &name); + + // NBS rewrite projectOrder (dependency management) + QList<Project *> projectOrder(Project *project = 0) const; + QAbstractItemModel *model(const QString &modelId) const; + + SessionNode *sessionNode() const; + + Project *projectForNode(ProjectExplorer::Node *node) const; + Node *nodeForFile(const QString &fileName) const; + Project *projectForFile(const QString &fileName) const; + + + void reportProjectLoadingProgress(); + +signals: + void projectAdded(ProjectExplorer::Project *project); + void singleProjectAdded(ProjectExplorer::Project *project); + void aboutToRemoveProject(ProjectExplorer::Project *project); + + void projectRemoved(ProjectExplorer::Project *project); + + void startupProjectChanged(ProjectExplorer::Project *project); + + void sessionUnloaded(); + void sessionLoaded(); + void aboutToSaveSession(); + +private slots: + void saveActiveMode(Core::IMode *mode); + void clearProjectFileCache(); + void setEditorCodec(Core::IEditor *editor, const QString &fileName); + void updateWindowTitle(); + +private: + bool loadImpl(const QString &fileName); + bool createImpl(const QString &fileName); + QString sessionNameToFileName(const QString &session); + + bool recursiveDependencyCheck(const QString &newDep, const QString &checkDep) const; + QStringList dependencies(const QString &proName) const; + QStringList dependenciesOrder() const; + Project *defaultStartupProject() const; + + QList<Project *> requestCloseOfAllFiles(bool *cancelled); + + void updateName(const QString &session); + + Core::ICore *m_core; + + Internal::SessionFile *m_file; + Internal::SessionNodeImpl *m_sessionNode; + QString m_displayName; + QString m_sessionName; + + mutable + QHash<Project *, QStringList> m_projectFileCache; +}; + +} // namespace ProjectExplorer + +#endif // SESSION_H diff --git a/src/plugins/projectexplorer/sessiondialog.cpp b/src/plugins/projectexplorer/sessiondialog.cpp new file mode 100644 index 00000000000..d674c047d7d --- /dev/null +++ b/src/plugins/projectexplorer/sessiondialog.cpp @@ -0,0 +1,194 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "sessiondialog.h" +#include "session.h" + +#include <QtGui/QInputDialog> + +#include <QtGui/QValidator> + +using namespace ProjectExplorer; +using namespace ProjectExplorer::Internal; + +namespace ProjectExplorer { +namespace Internal { + +class SessionValidator : public QValidator +{ +public: + SessionValidator(QObject *parent, QStringList sessions); + void fixup(QString & input) const; + QValidator::State validate(QString & input, int & pos) const; +private: + QStringList m_sessions; +}; + +SessionValidator::SessionValidator(QObject *parent, QStringList sessions) + : QValidator(parent), m_sessions(sessions) +{ +} + +QValidator::State SessionValidator::validate(QString &input, int &pos) const +{ + Q_UNUSED(pos); + if (m_sessions.contains(input)) + return QValidator::Intermediate; + else + return QValidator::Acceptable; +} + +void SessionValidator::fixup(QString &input) const +{ + int i = 2; + QString copy; + do { + copy = input + QString(" (%1)").arg(i); + ++i; + } while (m_sessions.contains(copy)); + input = copy; +} + +class NewSessionInputDialog : public QDialog +{ +public: + NewSessionInputDialog(QStringList sessions); + QString value(); +private: + QLineEdit *m_newSessionLineEdit; +}; + +NewSessionInputDialog::NewSessionInputDialog(QStringList sessions) +{ + setWindowTitle("New session name"); + QVBoxLayout *hlayout = new QVBoxLayout(this); + QLabel *label = new QLabel("Enter the name of the new session:", this); + hlayout->addWidget(label); + m_newSessionLineEdit = new QLineEdit(this); + m_newSessionLineEdit->setValidator(new SessionValidator(this, sessions)); + hlayout->addWidget(m_newSessionLineEdit); + QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); + connect(buttons, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttons, SIGNAL(rejected()), this, SLOT(reject())); + hlayout->addWidget(buttons); + setLayout(hlayout); +} + +QString NewSessionInputDialog::value() +{ + return m_newSessionLineEdit->text(); +} + +SessionDialog::SessionDialog(SessionManager *sessionManager, const QString &lastSession, bool startup) + : m_sessionManager(sessionManager), m_startup(startup) +{ + m_ui.setupUi(this); + + connect(m_ui.btCreateNew, SIGNAL(clicked()), + this, SLOT(createNew())); + + connect(m_ui.btClone, SIGNAL(clicked()), + this, SLOT(clone())); + connect(m_ui.btDelete, SIGNAL(clicked()), + this, SLOT(remove())); + + connect(m_ui.sessionList, SIGNAL(itemDoubleClicked ( QListWidgetItem *)), + this, SLOT(accept())); + + connect(m_ui.sessionList, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), + this, SLOT(updateActions())); + + QStringList sessions = sessionManager->sessions(); + foreach(const QString &session, sessions) { + m_ui.sessionList->addItem(session); + if (session == lastSession) + m_ui.sessionList->setCurrentRow(m_ui.sessionList->count() - 1); + } +} + +void SessionDialog::updateActions() +{ + bool enableDelete = false; + + if (m_ui.sessionList->currentItem()) + enableDelete = (m_ui.sessionList->currentItem()->text() != m_sessionManager->activeSession() + && m_ui.sessionList->currentItem()->text() != "default"); + m_ui.btDelete->setEnabled(enableDelete); +} + +void SessionDialog::accept() +{ + if (m_ui.sessionList->currentItem()) { + m_sessionManager->loadSession(m_ui.sessionList->currentItem()->text()); + } + QDialog::accept(); +} + +void SessionDialog::reject() +{ + // clear list + QDialog::reject(); +} + +void SessionDialog::createNew() +{ + NewSessionInputDialog newSessionInputDialog(m_sessionManager->sessions()); + if (newSessionInputDialog.exec() == QDialog::Accepted) { + QString newSession = newSessionInputDialog.value(); + if (newSession.isEmpty() || m_sessionManager->sessions().contains(newSession)) + return; + + m_sessionManager->createSession(newSession); + m_ui.sessionList->addItem(newSession); + m_ui.sessionList->setCurrentRow(m_ui.sessionList->count() - 1); + } +} + +void SessionDialog::clone() +{ + NewSessionInputDialog newSessionInputDialog(m_sessionManager->sessions()); + if (newSessionInputDialog.exec() == QDialog::Accepted) { + QString newSession = newSessionInputDialog.value(); + if (m_sessionManager->cloneSession(m_ui.sessionList->currentItem()->text(), newSession)) + m_ui.sessionList->addItem(newSession); + } +} + +void SessionDialog::remove() +{ + m_sessionManager->deleteSession(m_ui.sessionList->currentItem()->text()); + m_ui.sessionList->clear(); + m_ui.sessionList->addItems(m_sessionManager->sessions()); +} + +} +} diff --git a/src/plugins/projectexplorer/sessiondialog.h b/src/plugins/projectexplorer/sessiondialog.h new file mode 100644 index 00000000000..cae3e22028e --- /dev/null +++ b/src/plugins/projectexplorer/sessiondialog.h @@ -0,0 +1,73 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SESSIONDIALOG_H +#define SESSIONDIALOG_H + +#include <QtCore/QString> +#include <QtGui/QDialog> + +#include "ui_sessiondialog.h" + +namespace ProjectExplorer { + +class SessionManager; + +namespace Internal { + +class SessionDialog : public QDialog +{ + Q_OBJECT +public: + SessionDialog(SessionManager *sessionManager, const QString &lastSession, bool startup); + + void accept(); + void reject(); + +private slots: + void createNew(); + void clone(); + void remove(); + + void updateActions(); + +private: + Ui::SessionDialog m_ui; + SessionManager *m_sessionManager; + bool m_startup; +}; + +} +} + + +#endif // SESSIONDIALOG_H diff --git a/src/plugins/projectexplorer/sessiondialog.ui b/src/plugins/projectexplorer/sessiondialog.ui new file mode 100644 index 00000000000..942116e35e8 --- /dev/null +++ b/src/plugins/projectexplorer/sessiondialog.ui @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ProjectExplorer::Internal::SessionDialog</class> + <widget class="QDialog" name="ProjectExplorer::Internal::SessionDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>370</width> + <height>260</height> + </rect> + </property> + <property name="windowTitle"> + <string>Session Manager</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Choose your session</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <widget class="QListWidget" name="sessionList"/> + </item> + <item row="1" column="2"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QPushButton" name="btCreateNew"> + <property name="text"> + <string>Create New Session</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btClone"> + <property name="text"> + <string>Clone Session</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btDelete"> + <property name="text"> + <string>Delete Session</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="2" column="1" colspan="2"> + <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>ProjectExplorer::Internal::SessionDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>246</x> + <y>237</y> + </hint> + <hint type="destinationlabel"> + <x>78</x> + <y>216</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ProjectExplorer::Internal::SessionDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>191</x> + <y>244</y> + </hint> + <hint type="destinationlabel"> + <x>114</x> + <y>237</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/projectexplorer/taskwindow.cpp b/src/plugins/projectexplorer/taskwindow.cpp new file mode 100644 index 00000000000..ae3f42a7da1 --- /dev/null +++ b/src/plugins/projectexplorer/taskwindow.cpp @@ -0,0 +1,534 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "taskwindow.h" + +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/actionmanager/actionmanager.h> +#include <coreplugin/coreconstants.h> +#include <texteditor/itexteditor.h> +#include <texteditor/basetexteditor.h> +#include <projectexplorerconstants.h> + +#include <QtCore/QDir> +#include <QtGui/QKeyEvent> +#include <QtGui/QHeaderView> +#include <QtGui/QListView> +#include <QtGui/QPainter> +#include <QtCore/QAbstractItemModel> +#include <QtGui/QFont> +#include <QtGui/QFontMetrics> +#include <QtGui/QTextLayout> + +using namespace ProjectExplorer::Internal; + +// Internal Struct for TaskModel +struct TaskItem +{ + QString description; + QString file; + int line; + bool fileNotFound; + ProjectExplorer::BuildParserInterface::PatternType type; +}; + +class ProjectExplorer::Internal::TaskModel : public QAbstractItemModel +{ +public: + // Model stuff + TaskModel(); + 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; + void clear(); + void addTask(ProjectExplorer::BuildParserInterface::PatternType type, + const QString &description, const QString &file, int line); + int sizeOfFile(); + int sizeOfLineNumber(); + QModelIndex firstError() const; + void setFileNotFound(const QModelIndex &index, bool b); + + enum Roles { File = Qt::UserRole, Line, Description, FileNotFound }; +private: + QList<TaskItem> m_items; + int m_maxSizeOfFileName; +}; + +//// +// TaskView +//// + +TaskView::TaskView(QWidget *parent) + : QListView(parent) +{ + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +} + +TaskView::~TaskView() +{ + +} + +void TaskView::resizeEvent(QResizeEvent *e) +{ + Q_UNUSED(e); + static_cast<TaskDelegate *>(itemDelegate())->emitSizeHintChanged(selectionModel()->currentIndex()); +} + +void TaskView::keyPressEvent(QKeyEvent *e) +{ + if (!e->modifiers() && e->key() == Qt::Key_Return) { + emit activated(currentIndex()); + e->accept(); + return; + } + QListView::keyPressEvent(e); +} + +///// +// TaskModel +///// + +TaskModel::TaskModel() +{ + m_maxSizeOfFileName = 0; +} + +void TaskModel::addTask(ProjectExplorer::BuildParserInterface::PatternType type, const QString &description, const QString &file, int line) +{ + TaskItem task; + task.description = description; + task.file = file; + task.line = line; + task.type = type; + task.fileNotFound = false; + + beginInsertRows(QModelIndex(), m_items.size(), m_items.size()); + m_items.append(task); + endInsertRows(); + + QFont font; + QFontMetrics fm(font); + QString filename = task.file; + int pos = filename.lastIndexOf("/"); + if (pos != -1) + filename = file.mid(pos +1); + m_maxSizeOfFileName = qMax(m_maxSizeOfFileName, fm.width(filename)); +} + +void TaskModel::clear() +{ + if (m_items.isEmpty()) + return; + beginRemoveRows(QModelIndex(), 0, m_items.size() -1); + m_items.clear(); + endRemoveRows(); + m_maxSizeOfFileName = 0; +} + + +QModelIndex TaskModel::index(int row, int column, const QModelIndex &parent) const +{ + if (parent.isValid()) + return QModelIndex(); + return createIndex(row, column, 0); +} + +QModelIndex TaskModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +int TaskModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_items.count(); +} + +int TaskModel::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : 1; +} + +QVariant TaskModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= m_items.size() || index.column() != 0) + return QVariant(); + + if (role == TaskModel::File) + return m_items.at(index.row()).file; + else if (role == TaskModel::Line) + return m_items.at(index.row()).line; + else if (role == TaskModel::Description) + return m_items.at(index.row()).description; + else if (role == TaskModel::FileNotFound) + return m_items.at(index.row()).fileNotFound; + else if (role == Qt::DecorationRole) { + if (m_items.at(index.row()).type == ProjectExplorer::BuildParserInterface::Error) { + return QIcon(":/projectexplorer/images/compile_error.png"); + } else if (m_items.at(index.row()).type == ProjectExplorer::BuildParserInterface::Warning) { + return QIcon(":/projectexplorer/images/compile_warning.png"); + } else { + return QIcon(":/projectexplorer/images/compile_unspecified.png"); + } + } + return QVariant(); +} + +int TaskModel::sizeOfFile() +{ + return m_maxSizeOfFileName; +} + +int TaskModel::sizeOfLineNumber() +{ + QFont font; + QFontMetrics fm(font); + return fm.width("8888"); +} + +QModelIndex TaskModel::firstError() const +{ + int size = m_items.size(); + for (int i=0; i<size; ++i) { + if (m_items.at(i).type == ProjectExplorer::BuildParserInterface::Error) { + return index(i, 0); + } + } + return QModelIndex(); +} + +void TaskModel::setFileNotFound(const QModelIndex &idx, bool b) +{ + if (idx.isValid() && idx.row() < m_items.size()) { + m_items[idx.row()].fileNotFound = b; + emit dataChanged(idx, idx); + } +} + +///// +// TaskWindow +///// + +TaskWindow::TaskWindow() +{ + m_coreIFace = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + + m_model = new TaskModel; + m_listview = new TaskView; + + m_listview->setModel(m_model); + m_listview->setFrameStyle(QFrame::NoFrame); + m_listview->setWindowTitle(tr("Problems")); + m_listview->setSelectionMode(QAbstractItemView::SingleSelection); + TaskDelegate *tld = new TaskDelegate(this); + m_listview->setItemDelegate(tld); + m_listview->setWindowIcon(QIcon(":/qt4projectmanager/images/window.png")); + + connect(m_listview->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), + tld, SLOT(currentChanged(const QModelIndex &, const QModelIndex &))); + + connect(m_listview, SIGNAL(activated(const QModelIndex &)), + this, SLOT(showTaskInFile(const QModelIndex &))); + connect(m_listview, SIGNAL(clicked(const QModelIndex &)), + this, SLOT(showTaskInFile(const QModelIndex &))); + + m_errorCount = 0; + m_currentTask = -1; +} + +TaskWindow::~TaskWindow() +{ + delete m_listview; + delete m_model; +} + +QList<QWidget*> TaskWindow::toolBarWidgets() const +{ + return QList<QWidget*>(); +} + +QWidget *TaskWindow::outputWidget(QWidget *) +{ + return m_listview; +} + +void TaskWindow::clearContents() +{ + m_errorCount = 0; + m_currentTask = -1; + m_model->clear(); + emit tasksChanged(); +} + +void TaskWindow::visibilityChanged(bool /* b */) +{ + +} + +void TaskWindow::addItem(ProjectExplorer::BuildParserInterface::PatternType type, + const QString &description, const QString &file, int line) +{ + m_model->addTask(type, description, file, line); + if (type == ProjectExplorer::BuildParserInterface::Error) + ++m_errorCount; + emit tasksChanged(); +} + +void TaskWindow::showTaskInFile(const QModelIndex &index) +{ + if (!index.isValid()) + return; + QString file = index.data(TaskModel::File).toString(); + int line = index.data(TaskModel::Line).toInt(); + if (file.isEmpty() || line == -1) + return; + + if (QFileInfo(file).exists()) + TextEditor::BaseTextEditor::openEditorAt(file, line); + else + m_model->setFileNotFound(index, true); + m_listview->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select); + m_listview->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); +} + +int TaskWindow::numberOfTasks() const +{ + return m_model->rowCount(QModelIndex()); +} + +int TaskWindow::numberOfErrors() const +{ + return m_errorCount; +} + +int TaskWindow::priorityInStatusBar() const +{ + return 90; +} + +void TaskWindow::gotoFirstError() +{ + QModelIndex idx = m_model->firstError(); + if (idx.isValid()) + showTaskInFile(idx); +} + +bool TaskWindow::hasFocus() +{ + return m_listview->hasFocus(); +} + +bool TaskWindow::canFocus() +{ + return m_model->rowCount(); +} + +#include <QDebug> + +void TaskWindow::setFocus() +{ + if (m_model->rowCount()) { + m_listview->setFocus(); + if (m_listview->currentIndex() == QModelIndex()) { + m_listview->setCurrentIndex(m_model->index(0,0, QModelIndex())); + } + } +} + +///// +// Delegate +///// + +TaskDelegate::TaskDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ +} + +TaskDelegate::~TaskDelegate() +{ +} + +QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + + + QFontMetrics fm(option.font); + QSize s; + s.setWidth(option.rect.width()); + const QAbstractItemView * view = qobject_cast<const QAbstractItemView *>(opt.widget); + TaskModel *model = static_cast<TaskModel *>(view->model()); + int width = opt.rect.width() - model->sizeOfFile() - model->sizeOfLineNumber() - 12 - 22; + if (view->selectionModel()->currentIndex() == index) { + QString description = index.data(TaskModel::Description).toString(); + // Layout the description + int leading = fm.leading(); + int height = 0; + QTextLayout tl(description); + tl.beginLayout(); + while(true) { + QTextLine line = tl.createLine(); + if (!line.isValid()) + break; + line.setLineWidth(width); + height += leading; + line.setPosition(QPoint(0, height)); + height += static_cast<int>(line.height()); + } + tl.endLayout(); + + s.setHeight(height + leading + fm.height() + 3); + } else { + s.setHeight(fm.height() + 3); + } + return s; +} + +void TaskDelegate::emitSizeHintChanged(const QModelIndex &index) +{ + emit sizeHintChanged(index); +} + +void TaskDelegate::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + emit sizeHintChanged(current); + emit sizeHintChanged(previous); +} + +void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + painter->save(); + + QFontMetrics fm(opt.font); + QColor backgroundColor; + QColor textColor; + + const QAbstractItemView * view = qobject_cast<const QAbstractItemView *>(opt.widget); + bool selected = view->selectionModel()->currentIndex() == index; + + if (selected) { + painter->setBrush(opt.palette.highlight().color()); + backgroundColor = opt.palette.highlight().color(); + } else { + painter->setBrush(opt.palette.background().color()); + backgroundColor = opt.palette.background().color(); + } + painter->setPen(Qt::NoPen); + painter->drawRect(opt.rect); + + // Set Text Color + if (selected) + textColor = opt.palette.highlightedText().color(); + else + textColor = opt.palette.text().color(); + + painter->setPen(textColor); + + QIcon icon = index.data(Qt::DecorationRole).value<QIcon>(); + painter->drawPixmap(2, opt.rect.top() + 2, icon.pixmap(16, 16)); + + TaskModel *model = static_cast<TaskModel *>(view->model()); + int width = opt.rect.width() - model->sizeOfFile() - model->sizeOfLineNumber() - 12 - 22; + if (!selected) { + // in small mode we lay out differently + QString bottom = index.data(TaskModel::Description).toString(); + painter->drawText(22, 2 + opt.rect.top() + fm.ascent(), bottom); + if (fm.width(bottom) > width) { + // draw a gradient to mask the text + int gwidth = opt.rect.right() - width; + QLinearGradient lg(QPoint(width, 0), QPoint(width+gwidth, 0)); + QColor c = backgroundColor; + c.setAlpha(0); + lg.setColorAt(0, c); + lg.setColorAt(20.0/gwidth, backgroundColor); + painter->fillRect(width, 2 + opt.rect.top(), gwidth, fm.height() + 1, lg); + } + } else { + // Descritption + QString description = index.data(TaskModel::Description).toString(); + // Layout the description + int leading = fm.leading(); + int height = 0; + QTextLayout tl(description); + tl.beginLayout(); + while(true) { + QTextLine line = tl.createLine(); + if (!line.isValid()) + break; + line.setLineWidth(width); + height += leading; + line.setPosition(QPoint(0, height)); + height += static_cast<int>(line.height()); + } + tl.endLayout(); + tl.draw(painter, QPoint(22, 2 + opt.rect.top())); + //painter->drawText(22, 2 + opt.rect.top() + fm.ascent(), description); + + QColor mix; + mix.setRgb( static_cast<int>(0.7 * textColor.red() + 0.3 * backgroundColor.red()), + static_cast<int>(0.7 * textColor.green() + 0.3 * backgroundColor.green()), + static_cast<int>(0.7 * textColor.blue() + 0.3 * backgroundColor.blue())); + painter->setPen(mix); + + QString directory = index.data(TaskModel::File).toString(); + int secondBaseLine = 2 + fm.ascent() + opt.rect.top() + height + leading; //opt.rect.top() + fm.ascent() + fm.height() + 6; + if (index.data(TaskModel::FileNotFound).toBool()) { + QString fileNotFound = tr("File not found: %1").arg(directory); + painter->setPen(Qt::red); + painter->drawText(22, secondBaseLine, fileNotFound); + } else { + painter->drawText(22, secondBaseLine, directory); + } + } + + painter->setPen(textColor); + // Assemble string for the right side + // just filename + linenumer + QString file = index.data(TaskModel::File).toString(); + int pos = file.lastIndexOf("/"); + if (pos != -1) + file = file.mid(pos +1); + painter->drawText(width + 22 + 4, 2 + opt.rect.top() + fm.ascent(), file); + + QString topRight = index.data(TaskModel::Line).toString(); + painter->drawText(opt.rect.right() - fm.width(topRight) - 6 , 2 + opt.rect.top() + fm.ascent(), topRight); + // Separator lines + painter->setPen(QColor::fromRgb(150,150,150)); + painter->drawLine(0, opt.rect.bottom(), opt.rect.right(), opt.rect.bottom()); + painter->restore(); +} diff --git a/src/plugins/projectexplorer/taskwindow.h b/src/plugins/projectexplorer/taskwindow.h new file mode 100644 index 00000000000..68ae3481a1b --- /dev/null +++ b/src/plugins/projectexplorer/taskwindow.h @@ -0,0 +1,127 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef TASKWINDOW_H +#define TASKWINDOW_H + +#include "buildparserinterface.h" + +#include <coreplugin/ioutputpane.h> +#include <coreplugin/icore.h> + +#include <QtGui/QTreeWidget> +#include <QtGui/QStyledItemDelegate> +#include <QtGui/QListView> +#include <QtGui/QToolButton> + +namespace ProjectExplorer { +namespace Internal { + +class TaskModel; +class TaskView; + +class TaskWindow : public Core::IOutputPane +{ + Q_OBJECT + +public: + TaskWindow(); + ~TaskWindow(); + + QWidget *outputWidget(QWidget *); + QList<QWidget*> toolBarWidgets(void) const; + + QString name() const { return tr("Problems"); } + int priorityInStatusBar() const; + void clearContents(); + void visibilityChanged(bool visible); + + void addItem(BuildParserInterface::PatternType type, + const QString &description, const QString &file, int line); + + int numberOfTasks() const; + int numberOfErrors() const; + + void gotoFirstError(); + bool canFocus(); + bool hasFocus(); + void setFocus(); + +signals: + void tasksChanged(); + +private slots: + void showTaskInFile(const QModelIndex &index); + +private: + int sizeHintForColumn(int column) const; + + Core::ICore *m_coreIFace; + int m_errorCount; + int m_currentTask; + + TaskModel *m_model; + TaskView *m_listview; +}; + +class TaskView : public QListView +{ +public: + TaskView(QWidget *parent = 0); + ~TaskView(); + void resizeEvent(QResizeEvent *e); + void keyPressEvent(QKeyEvent *e); +}; + +class TaskDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + TaskDelegate(QObject * parent = 0); + ~TaskDelegate(); + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + + // TaskView uses this method if the size of the taskview changes + void emitSizeHintChanged(const QModelIndex &index); + +public slots: + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); + +private: + void generateGradientPixmap(int width, int height, QColor color, bool selected) const; +}; + +} //namespace Internal +} //namespace ProjectExplorer + +#endif diff --git a/src/plugins/projectexplorer/winguiprocess.cpp b/src/plugins/projectexplorer/winguiprocess.cpp new file mode 100644 index 00000000000..5179ea01cff --- /dev/null +++ b/src/plugins/projectexplorer/winguiprocess.cpp @@ -0,0 +1,187 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtCore/QDir> + +#include "winguiprocess.h" +#include "consoleprocess.h" + +using namespace ProjectExplorer::Internal; + +WinGuiProcess::WinGuiProcess(QObject *parent) + : QThread(parent) +{ + m_pid = 0; + m_exitCode = 0; +} + +WinGuiProcess::~WinGuiProcess() +{ + stop(); +} + +bool WinGuiProcess::start(const QString &program, const QStringList &args) +{ + m_program = program; + m_args = args; + + if (!isRunning()) { + QThread::start(QThread::NormalPriority); + return true; + } + return false; +} + +void WinGuiProcess::stop() +{ + if (m_pid) + TerminateProcess(m_pid->hProcess, 1); + wait(); +} + +bool WinGuiProcess::isRunning() const +{ + return QThread::isRunning(); +} + +bool WinGuiProcess::setupDebugInterface(HANDLE &bufferReadyEvent, HANDLE &dataReadyEvent, HANDLE &sharedFile, LPVOID &sharedMem) +{ + + bufferReadyEvent = CreateEvent(NULL, FALSE, FALSE, L"DBWIN_BUFFER_READY"); + if (!bufferReadyEvent || GetLastError() == ERROR_ALREADY_EXISTS) + return false; + dataReadyEvent = CreateEvent(NULL, FALSE, FALSE, L"DBWIN_DATA_READY"); + if (!dataReadyEvent || GetLastError() == ERROR_ALREADY_EXISTS) + return false; + sharedFile = CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, 0, 4096, L"DBWIN_BUFFER"); + if (!sharedFile || GetLastError() == ERROR_ALREADY_EXISTS) + return false; + sharedMem = MapViewOfFile(sharedFile, FILE_MAP_READ, 0, 0, 512); + if (!sharedMem) + return false; + return true; +} + +void WinGuiProcess::run() +{ + if (m_pid) + return; + + STARTUPINFO si; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + m_pid = new PROCESS_INFORMATION; + ZeroMemory(m_pid, sizeof(PROCESS_INFORMATION)); + + m_exitCode = 0; + + HANDLE bufferReadyEvent = NULL; + HANDLE dataReadyEvent = NULL; + HANDLE sharedFile = NULL; + LPVOID sharedMem = NULL; + + bool dbgInterface = setupDebugInterface(bufferReadyEvent, dataReadyEvent, sharedFile, sharedMem); + + QString cmdLine = ConsoleProcess::createCommandline(m_program, m_args); + bool success = CreateProcessW(0, (WCHAR*)cmdLine.utf16(), + 0, 0, TRUE, CREATE_UNICODE_ENVIRONMENT, + environment().isEmpty() ? 0 + : ConsoleProcess::createEnvironment(environment()).data(), + workingDirectory().isEmpty() ? 0 + : (WCHAR*)QDir::convertSeparators(workingDirectory()).utf16(), + &si, m_pid); + + if (!success) { + emit processError(tr("The process could not be started!")); + delete m_pid; + m_pid = 0; + return; + } + + if (!dbgInterface) { + emit receivedDebugOutput(tr("Cannot retrieve debugging output!")); + WaitForSingleObject(m_pid->hProcess, INFINITE); + } else { + LPSTR message; + LPDWORD processId; + HANDLE toWaitFor[2]; + + message = reinterpret_cast<LPSTR>(sharedMem) + sizeof(DWORD); + processId = reinterpret_cast<LPDWORD>(sharedMem); + + SetEvent(bufferReadyEvent); + + toWaitFor[0] = dataReadyEvent; + toWaitFor[1] = m_pid->hProcess; + + for (bool stop = false; !stop;) { + DWORD ret = WaitForMultipleObjects(2, toWaitFor, FALSE, INFINITE); + + switch (ret) { + case WAIT_OBJECT_0 + 0: + if (*processId == m_pid->dwProcessId) + emit receivedDebugOutput(QString::fromAscii(message)); + SetEvent(bufferReadyEvent); + break; + case WAIT_OBJECT_0 + 1: + stop = true; + break; + } + } + } + + GetExitCodeProcess(m_pid->hProcess, &m_exitCode); + emit processFinished(static_cast<int>(m_exitCode)); + + UnmapViewOfFile(sharedMem); + CloseHandle(sharedFile); + CloseHandle(bufferReadyEvent); + CloseHandle(dataReadyEvent); + CloseHandle(m_pid->hProcess); + CloseHandle(m_pid->hThread); + + delete m_pid; + m_pid = 0; +} + +qint64 WinGuiProcess::applicationPID() const +{ + if (m_pid) + return m_pid->dwProcessId; + return 0; +} + +int WinGuiProcess::exitCode() const +{ + return static_cast<int>(m_exitCode); +} diff --git a/src/plugins/projectexplorer/winguiprocess.h b/src/plugins/projectexplorer/winguiprocess.h new file mode 100644 index 00000000000..b6e9b488ea4 --- /dev/null +++ b/src/plugins/projectexplorer/winguiprocess.h @@ -0,0 +1,78 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef WINGUIPROCESS_H +#define WINGUIPROCESS_H + +#include <QtCore/QThread> +#include <QtCore/QStringList> +#include <windows.h> + +#include "abstractprocess.h" + +namespace ProjectExplorer { +namespace Internal { + +class WinGuiProcess : public QThread, public AbstractProcess +{ + Q_OBJECT + +public: + WinGuiProcess(QObject *parent); + ~WinGuiProcess(); + + bool start(const QString &program, const QStringList &args); + void stop(); + + bool isRunning() const; + qint64 applicationPID() const; + int exitCode() const; + +signals: + void processError(const QString &error); + void receivedDebugOutput(const QString &output); + void processFinished(int exitCode); + +private: + bool setupDebugInterface(HANDLE &bufferReadyEvent, HANDLE &dataReadyEvent, HANDLE &sharedFile, LPVOID &sharedMem); + void run(); + + PROCESS_INFORMATION *m_pid; + QString m_program; + QStringList m_args; + unsigned long m_exitCode; +}; + +} //namespace Internal +} //namespace ProjectExplorer + +#endif diff --git a/src/plugins/qhelpproject/qhelpproject.cpp b/src/plugins/qhelpproject/qhelpproject.cpp new file mode 100644 index 00000000000..ddd2fa8f35f --- /dev/null +++ b/src/plugins/qhelpproject/qhelpproject.cpp @@ -0,0 +1,201 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtCore/QFileInfo> +#include <QtCore/QDir> + +#include <qhelpsystem.h> + +#include "qhelpproject.h" +#include "qhelpprojectmanager.h" +#include "qhelpprojectitems.h" + +using namespace ProjectExplorer; +using namespace QHelpProjectPlugin::Internal; + +QHelpProject::QHelpProject(QHelpProjectManager *manager) +{ + m_manager = manager; + + ProjectExplorerInterface *projectExplorer = m_manager->projectExplorer(); + projectExplorer->addProjectItem(this); + + m_filesFolder = new QHelpProjectFolder(); + m_filesFolder->setName(QObject::tr("Files")); + projectExplorer->addProjectItem(m_filesFolder, this); +} + +QHelpProject::~QHelpProject() +{ + +} + +bool QHelpProject::open(const QString &fileName) +{ + m_projectFile = fileName; + + QHelpProjectParser parser; + if (!parser.parse(m_projectFile)) + return false; + + m_name = parser.namespaceURI(); + m_files = parser.files(); + + QFileInfo fi(fileName); + QString dir = fi.absolutePath() + QDir::separator(); + + ProjectExplorerInterface *projectExplorer = m_manager->projectExplorer(); + QHelpProjectFile *file; + foreach (QString f, m_files) { + file = new QHelpProjectFile(dir + f); + projectExplorer->addProjectItem(file, m_filesFolder); + } + + m_manager->projectExplorer()->updateItem(this); + + return true; +} + +bool QHelpProject::save(const QString &fileName) +{ + return true; +} + +QString QHelpProject::fileName() const +{ + return m_projectFile; +} + +QString QHelpProject::defaultPath() const +{ + return QString(); +} + +QString QHelpProject::suggestedFileName() const +{ + return QString(); +} + +QString QHelpProject::fileFilter() const +{ + return QString(); +} + +QString QHelpProject::fileExtension() const +{ + return QString(); +} + +bool QHelpProject::isModified() const +{ + return false; +} + +bool QHelpProject::isReadOnly() const +{ + return false; +} + +bool QHelpProject::isSaveAsAllowed() const +{ + return true; +} + +void QHelpProject::modified(QWorkbench::FileInterface::ReloadBehavior *behavior) +{ + +} + +IProjectManager *QHelpProject::projectManager() const +{ + return m_manager; +} + +QVariant QHelpProject::projectProperty(const QString &key) const +{ + return QVariant(); +} + +void QHelpProject::setProjectProperty(const QString &key, const QVariant &value) const +{ + +} + +void QHelpProject::executeProjectCommand(int cmd) +{ + +} + +bool QHelpProject::supportsProjectCommand(int cmd) +{ + return false; +} + +bool QHelpProject::hasProjectProperty(ProjectProperty property) const +{ + return false; +} + +QList<QWorkbench::FileInterface*> QHelpProject::dependencies() +{ + return QList<QWorkbench::FileInterface*>(); +} + +void QHelpProject::setExtraApplicationRunArguments(const QStringList &args) +{ + +} + +void QHelpProject::setCustomApplicationOutputHandler(QObject *handler) +{ + +} + +QStringList QHelpProject::files(const QList<QRegExp> &fileMatcher) +{ + return m_files; +} + +ProjectItemInterface::ProjectItemKind QHelpProject::kind() const +{ + return ProjectItemInterface::Project; +} + +QString QHelpProject::name() const +{ + return m_name; +} + +QIcon QHelpProject::icon() const +{ + return QIcon(); +} diff --git a/src/plugins/qhelpproject/qhelpproject.h b/src/plugins/qhelpproject/qhelpproject.h new file mode 100644 index 00000000000..fb895682561 --- /dev/null +++ b/src/plugins/qhelpproject/qhelpproject.h @@ -0,0 +1,115 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QHELPPROJECT_H +#define QHELPPROJECT_H + +#include <QtCore/QObject> + +#include <projectexplorer/ProjectExplorerInterfaces> + +namespace QHelpProjectPlugin { +namespace Internal { + +class QHelpProjectManager; +class QHelpProjectFolder; + +class QHelpProject : public QObject, + public ProjectExplorer::ProjectInterface +{ + Q_OBJECT + Q_INTERFACES(ProjectExplorer::ProjectInterface + ProjectExplorer::ProjectItemInterface + QWorkbench::FileInterface) + +public: + QHelpProject(QHelpProjectManager *manager); + ~QHelpProject(); + + bool open(const QString &fileName); + + //QWorkbench::FileInterface + bool save(const QString &fileName = QString()); + QString fileName() const; + + QString defaultPath() const; + QString suggestedFileName() const; + QString fileFilter() const; + QString fileExtension() const; + + bool isModified() const; + bool isReadOnly() const; + bool isSaveAsAllowed() const; + + void modified(QWorkbench::FileInterface::ReloadBehavior *behavior); + + //ProjectExplorer::ProjectInterface + ProjectExplorer::IProjectManager *projectManager() const; + + QVariant projectProperty(const QString &key) const; + void setProjectProperty(const QString &key, const QVariant &value) const; + + void executeProjectCommand(int cmd); + bool supportsProjectCommand(int cmd); + + bool hasProjectProperty(ProjectProperty property) const; + QList<QWorkbench::FileInterface *> dependencies(); + + void setExtraApplicationRunArguments(const QStringList &args); + void setCustomApplicationOutputHandler(QObject *handler); + + QStringList files(const QList<QRegExp> &fileMatcher); + + + //ProjectExplorer::ProjectItemInterface + ProjectItemKind kind() const; + + QString name() const; + QIcon icon() const; + +signals: + void buildFinished(const QString &error); + void changed(); + +private: + QHelpProjectManager *m_manager; + QHelpProjectFolder *m_filesFolder; + QString m_projectFile; + QString m_name; + QStringList m_files; +}; + +} +} +} + +#endif diff --git a/src/plugins/qhelpproject/qhelpproject.pro b/src/plugins/qhelpproject/qhelpproject.pro new file mode 100644 index 00000000000..667da0009bf --- /dev/null +++ b/src/plugins/qhelpproject/qhelpproject.pro @@ -0,0 +1,16 @@ +TEMPLATE = lib +TARGET = QHelpProject + +include(../../qworkbenchplugin.pri) +include(../../../../helpsystem/qhelpsystem.pri) +include(../../plugins/coreplugin/coreplugin.pri) + +SOURCES += qhelpprojectmanager.cpp \ + qhelpproject.cpp \ + qhelpprojectitems.cpp + + +HEADERS += qhelpprojectmanager.h \ + qhelpproject.h \ + qhelpprojectitems.h + diff --git a/src/plugins/qhelpproject/qhelpprojectitems.cpp b/src/plugins/qhelpproject/qhelpprojectitems.cpp new file mode 100644 index 00000000000..9c0d98c2efd --- /dev/null +++ b/src/plugins/qhelpproject/qhelpprojectitems.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtGui/QFileIconProvider> + +#include "qhelpprojectitems.h" + +using namespace ProjectExplorer; +using namespace QHelpProjectPlugin::Internal; + +QIcon QHelpProjectFile::m_icon = QIcon(); +QIcon QHelpProjectFolder::m_icon = QIcon(); + +QHelpProjectFile::QHelpProjectFile(const QString &fileName) +{ + m_file = fileName; + + if (m_icon.isNull()) { + QFileIconProvider iconProvider; + m_icon = iconProvider.icon(QFileIconProvider::File); + } +} + +QString QHelpProjectFile::fullName() const +{ + return m_file; +} + +ProjectItemInterface::ProjectItemKind QHelpProjectFile::kind() const +{ + return ProjectItemInterface::ProjectFile; +} + +QString QHelpProjectFile::name() const +{ + QFileInfo fi(m_file); + return fi.fileName(); +} + +QIcon QHelpProjectFile::icon() const +{ + return m_icon; +} + + + +QHelpProjectFolder::QHelpProjectFolder() +{ + if (m_icon.isNull()) { + QFileIconProvider iconProvider; + m_icon = iconProvider.icon(QFileIconProvider::Folder); + } +} + +QHelpProjectFolder::~QHelpProjectFolder() +{ +} + +void QHelpProjectFolder::setName(const QString &name) +{ + m_name = name; +} + +ProjectItemInterface::ProjectItemKind QHelpProjectFolder::kind() const +{ + return ProjectItemInterface::ProjectFolder; +} + +QString QHelpProjectFolder::name() const +{ + return m_name; +} + +QIcon QHelpProjectFolder::icon() const +{ + return m_icon; +} diff --git a/src/plugins/qhelpproject/qhelpprojectitems.h b/src/plugins/qhelpproject/qhelpprojectitems.h new file mode 100644 index 00000000000..dc32c990ae6 --- /dev/null +++ b/src/plugins/qhelpproject/qhelpprojectitems.h @@ -0,0 +1,91 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QHELPPROJECTITEMS_H +#define QHELPPROJECTITEMS_H + +#include <QtGui/QIcon> + +#include <projectexplorer/ProjectExplorerInterfaces> + +namespace QHelpProjectPlugin { +namespace Internal { + +class QHelpProjectFile : public QObject, + public ProjectExplorer::ProjectFileInterface +{ + Q_OBJECT + Q_INTERFACES(ProjectExplorer::ProjectFileInterface ProjectExplorer::ProjectItemInterface) + +public: + QHelpProjectFile(const QString &fileName); + QString fullName() const; + + //ProjectExplorer::ProjectItemInterface + ProjectItemKind kind() const; + + QString name() const; + QIcon icon() const; + +private: + QString m_file; + static QIcon m_icon; +}; + +class QHelpProjectFolder : public QObject, + public ProjectExplorer::ProjectFolderInterface +{ + Q_OBJECT + Q_INTERFACES(ProjectExplorer::ProjectFolderInterface ProjectExplorer::ProjectItemInterface) + +public: + QHelpProjectFolder(); + ~QHelpProjectFolder(); + + void setName(const QString &name); + + //ProjectExplorer::ProjectItemInterface + ProjectItemKind kind() const; + + QString name() const; + QIcon icon() const; + +private: + QString m_name; + static QIcon m_icon; +}; + +} +} +} + +#endif diff --git a/src/plugins/qhelpproject/qhelpprojectmanager.cpp b/src/plugins/qhelpproject/qhelpprojectmanager.cpp new file mode 100644 index 00000000000..4878c7cdda6 --- /dev/null +++ b/src/plugins/qhelpproject/qhelpprojectmanager.cpp @@ -0,0 +1,121 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qhelpprojectmanager.h" +#include "qhelpproject.h" + +#include <QtCore/qplugin.h> +#include <QtCore/QFileInfo> +#include <QtCore/QDebug> + +using namespace QHelpProjectPlugin::Internal; + +QHelpProjectManager::QHelpProjectManager() +{ +} + +QHelpProjectManager::~QHelpProjectManager() +{ +} + +bool QHelpProjectManager::init(ExtensionSystem::PluginManager *pm, QString *error_message) +{ + m_pm = pm; + m_core = m_pm->interface<QWorkbench::ICore>(); + QWorkbench::ActionManagerInterface *am = m_core->actionManager(); + + m_projectContext = m_core->uniqueIDManager()-> + uniqueIdentifier(QLatin1String("QHelpProject")); + + m_languageId = m_core->uniqueIDManager()-> + uniqueIdentifier(QLatin1String("QHelpLanguage")); + + m_projectExplorer = m_pm->interface<ProjectExplorer::ProjectExplorerInterface>(); + + return true; +} + +void QHelpProjectManager::extensionsInitialized() +{ +} + +void QHelpProjectManager::cleanup() +{ +} + +int QHelpProjectManager::projectContext() const +{ + return m_projectContext; +} + +int QHelpProjectManager::projectLanguage() const +{ + return m_languageId; +} + +bool QHelpProjectManager::canOpen(const QString &fileName) +{ + qDebug() << "can open " << fileName; + QFileInfo fi(fileName); + if (fi.suffix() == QLatin1String("qthp")) + return true; + return false; +} + +QList<ProjectExplorer::Project*> QHelpProjectManager::open(const QString &fileName) +{ + QList<ProjectExplorer::Project*> lst; + QHelpProject *pro = new QHelpProject(this); + if (pro->open(fileName)) { + lst.append(pro); + } else { + delete pro; + } + return lst; +} + +QString QHelpProjectManager::fileFilter() const +{ + return tr("Qt Help Project File (*.qthp)"); +} + +QVariant QHelpProjectManager::editorProperty(const QString &key) const +{ + return QVariant(); +} + +ProjectExplorer::ProjectExplorerInterface *QHelpProjectManager::projectExplorer() const +{ + return m_projectExplorer; +} + +Q_EXPORT_PLUGIN(QHelpProjectManager) diff --git a/src/plugins/qhelpproject/qhelpprojectmanager.h b/src/plugins/qhelpproject/qhelpprojectmanager.h new file mode 100644 index 00000000000..b3eed263dc1 --- /dev/null +++ b/src/plugins/qhelpproject/qhelpprojectmanager.h @@ -0,0 +1,89 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QHELPPROJECTMANAGER_H +#define QHELPPROJECTMANAGER_H + +#include <QtCore/QObject> +#include <QtCore/QMap> + +#include <extensionsystem/plugininterface.h> +#include <projectexplorer/ProjectExplorerInterfaces> + +namespace Core { +class ICore; +} + +namespace QHelpProjectPlugin { +namespace Internal { + +class QHelpProjectManager : public QObject, + public ExtensionSystem::PluginInterface, + public ProjectExplorer::IProjectManager +{ + Q_OBJECT + Q_CLASSINFO("RequiredPlugin", "ProjectExplorer") + Q_INTERFACES(ExtensionSystem::PluginInterface + ProjectExplorer::IProjectManager) + +public: + QHelpProjectManager(); + ~QHelpProjectManager(); + + bool init(ExtensionSystem::PluginManager *pm, QString *error_message = 0); + void extensionsInitialized(); + void cleanup(); + + int projectContext() const; + int projectLanguage() const; + + bool canOpen(const QString &fileName); + QList<ProjectExplorer::ProjectInterface*> open(const QString &fileName); + QString fileFilter() const; + + QVariant editorProperty(const QString &key) const; + + ProjectExplorer::ProjectExplorerInterface *projectExplorer() const; + +private: + ExtensionSystem::PluginManager *m_pm; + QWorkbench::ICore *m_core; + ProjectExplorer::ProjectExplorerInterface *m_projectExplorer; + + int m_projectContext; + int m_languageId; +}; + +} // namespace Internal +} // namespace QHelpProjectPlugin + +#endif // QHELPPROJECTMANAGER_H diff --git a/src/plugins/qt4projectmanager/Qt4ProjectManager.mimetypes.xml b/src/plugins/qt4projectmanager/Qt4ProjectManager.mimetypes.xml new file mode 100644 index 00000000000..7fa148c8812 --- /dev/null +++ b/src/plugins/qt4projectmanager/Qt4ProjectManager.mimetypes.xml @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="application/vnd.nokia.qt.qmakeprofile"> + <sub-class-of type="text/plain"/> + <comment>Qt Project file</comment> + <glob pattern="*.pro"/> + </mime-type> + <mime-type type="application/vnd.nokia.qt.qmakeproincludefile"> + <sub-class-of type="text/plain"/> + <comment>Qt Project include file</comment> + <glob pattern="*.pri"/> + </mime-type> +</mime-info> diff --git a/src/plugins/qt4projectmanager/Qt4ProjectManager.pluginspec b/src/plugins/qt4projectmanager/Qt4ProjectManager.pluginspec new file mode 100644 index 00000000000..8e166238867 --- /dev/null +++ b/src/plugins/qt4projectmanager/Qt4ProjectManager.pluginspec @@ -0,0 +1,14 @@ +<plugin name="Qt4ProjectManager" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Provides project type for Qt 4 pro files and tools.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="TextEditor" version="0.9.1"/> + <dependency name="ProjectExplorer" version="0.9.1"/> + <dependency name="CppTools" version="0.9.1"/> + <dependency name="CppEditor" version="0.9.1"/> + <dependency name="Help" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/qt4projectmanager/applicationlauncher.h b/src/plugins/qt4projectmanager/applicationlauncher.h new file mode 100644 index 00000000000..e48bcb99a71 --- /dev/null +++ b/src/plugins/qt4projectmanager/applicationlauncher.h @@ -0,0 +1,88 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef APPLICATIONLAUNCHER_H +#define APPLICATIONLAUNCHER_H + +#include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtCore/QProcess> + +namespace Qt4ProjectManager { +namespace Internal { + +class ConsoleProcess; +class WinGuiProcess; + +class ApplicationLauncher : public QObject +{ + Q_OBJECT + +public: + enum Mode {Console, Gui}; + ApplicationLauncher(QObject *parent); + void setWorkingDirectory(const QString &dir); + void setEnvironment(const QStringList &env); + + void start(Mode mode, const QString &program, + const QStringList &args = QStringList()); + bool isRunning() const; + qint64 applicationPID() const; + +signals: + void applicationError(const QString &error); + void appendOutput(const QString &error); + void processExited(int exitCode); + +private slots: + void processStopped(); +#ifdef Q_OS_WIN + void readWinDebugOutput(const QString &output); + void processFinished(int exitCode); +#else + void guiProcessError(); + void readStandardOutput(); + void processDone(int, QProcess::ExitStatus); +#endif + +private: + QProcess *m_guiProcess; + ConsoleProcess *m_consoleProcess; + Mode m_currentMode; + + WinGuiProcess *m_winGuiProcess; +}; + +} //namespace Internal +} //namespace Qt4ProjectManager + +#endif // APPLICATIONLAUNCHER_H diff --git a/src/plugins/qt4projectmanager/buildoptionspage.cpp b/src/plugins/qt4projectmanager/buildoptionspage.cpp new file mode 100644 index 00000000000..1dcc0961510 --- /dev/null +++ b/src/plugins/qt4projectmanager/buildoptionspage.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtCore/QSettings> +#include <QtGui/QLineEdit> + +#include "buildoptionspage.h" + +BuildOptionsPage::BuildOptionsPage(QWorkbench::PluginManager *app) +{ + core = app; + QWorkbench::ICore *coreIFace = core->interface<QWorkbench::ICore>(); + if (coreIFace && coreIFace->settings()) { + QSettings *s = coreIFace->settings(); + s->beginGroup("BuildOptions"); + m_qmakeCmd = s->value("QMake", "qmake").toString(); + m_makeCmd = s->value("Make", "make").toString(); + m_makeCleanCmd = s->value("MakeClean", "make clean").toString(); + s->endGroup(); + } +} + +QString BuildOptionsPage::name() const +{ + return tr("Commands"); +} + +QString BuildOptionsPage::category() const +{ + return "Qt4|Build"; +} + +QString BuildOptionsPage::trCategory() const +{ + return tr("Qt4|Build"); +} + +QWidget *BuildOptionsPage::createPage(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + m_ui.setupUi(w); + m_ui.qmakeLineEdit->setText(m_qmakeCmd); + m_ui.makeLineEdit->setText(m_makeCmd); + m_ui.makeCleanLineEdit->setText(m_makeCleanCmd); + + return w; +} + +void BuildOptionsPage::finished(bool accepted) +{ + if (!accepted) + return; + + m_qmakeCmd = m_ui.qmakeLineEdit->text(); + m_makeCmd = m_ui.makeLineEdit->text(); + m_makeCleanCmd = m_ui.makeCleanLineEdit->text(); + QWorkbench::ICore *coreIFace = core->interface<QWorkbench::ICore>(); + if (coreIFace && coreIFace->settings()) { + QSettings *s = coreIFace->settings(); + s->beginGroup("BuildOptions"); + s->setValue("QMake", m_qmakeCmd); + s->setValue("Make", m_makeCmd); + s->setValue("MakeClean", m_makeCleanCmd); + s->endGroup(); + } +} + +QWorkbench::Plugin *BuildOptionsPage::plugin() +{ + return 0; +} + +QObject *BuildOptionsPage::qObject() +{ + return this; +} diff --git a/src/plugins/qt4projectmanager/buildparserfactory.cpp b/src/plugins/qt4projectmanager/buildparserfactory.cpp new file mode 100644 index 00000000000..7b839b6606b --- /dev/null +++ b/src/plugins/qt4projectmanager/buildparserfactory.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "buildparserfactory.h" +#include "qt4projectmanagerconstants.h" +#include "gccparser.h" +#include "msvcparser.h" + +using namespace Qt4ProjectManager::Internal; + +GccParserFactory::~GccParserFactory() +{ + +} + +bool GccParserFactory::canCreate(const QString & name) const +{ + return (name == Constants::BUILD_PARSER_GCC); +} + +ProjectExplorer::BuildParserInterface * GccParserFactory::create(const QString & name) const +{ + Q_UNUSED(name); + return new GccParser(); +} + +MsvcParserFactory::~MsvcParserFactory() +{ + +} + +bool MsvcParserFactory::canCreate(const QString & name) const +{ + return (name == Constants::BUILD_PARSER_MSVC); +} + +ProjectExplorer::BuildParserInterface * MsvcParserFactory::create(const QString & name) const +{ + Q_UNUSED(name); + return new MsvcParser(); +} diff --git a/src/plugins/qt4projectmanager/buildparserfactory.h b/src/plugins/qt4projectmanager/buildparserfactory.h new file mode 100644 index 00000000000..685e86fbaba --- /dev/null +++ b/src/plugins/qt4projectmanager/buildparserfactory.h @@ -0,0 +1,68 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BUILDPARSERFACTORY_H +#define BUILDPARSERFACTORY_H + +#include <projectexplorer/ProjectExplorerInterfaces> + + +namespace ProjectExplorer { +class BuildParserInterface; +} +namespace Qt4ProjectManager { +namespace Internal { + +class GccParserFactory : public ProjectExplorer::IBuildParserFactory +{ + Q_OBJECT +public: + GccParserFactory() {}; + virtual ~GccParserFactory(); + virtual bool canCreate(const QString & name) const; + virtual ProjectExplorer::BuildParserInterface * create(const QString & name) const; +}; + +class MsvcParserFactory : public ProjectExplorer::IBuildParserFactory +{ + Q_OBJECT +public: + MsvcParserFactory() {}; + virtual ~MsvcParserFactory(); + virtual bool canCreate(const QString & name) const; + virtual ProjectExplorer::BuildParserInterface * create(const QString & name) const; +}; + +} +} + +#endif diff --git a/src/plugins/qt4projectmanager/cesdkhandler.cpp b/src/plugins/qt4projectmanager/cesdkhandler.cpp new file mode 100644 index 00000000000..de42c5d2f05 --- /dev/null +++ b/src/plugins/qt4projectmanager/cesdkhandler.cpp @@ -0,0 +1,153 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cesdkhandler.h" +#include <QtCore/QFile> +#include <QtCore/QDebug> +#include <QtCore/QXmlStreamReader> + +using namespace Qt4ProjectManager::Internal; +using ProjectExplorer::Environment; + +CeSdkInfo::CeSdkInfo() : m_major(0) , m_minor(0) +{ +} + +Environment CeSdkInfo::addToEnvironment(const Environment &env) +{ + qDebug()<<"adding "<<name()<< "to Environment"; + Environment e(env); + e.set("INCLUDE", m_include); + e.set("LIB", m_lib); + e.prependOrSetPath(m_bin); + qDebug()<<e.toStringList(); + return e; +} + +CeSdkHandler::CeSdkHandler() +{ +} + +QString CeSdkHandler::platformName(const QString &qtpath) +{ + QString platformName; + QString CE_SDK; + QString CE_ARCH; + QFile f(qtpath); + if(f.exists() && f.open(QIODevice::ReadOnly)) { + while(!f.atEnd()) { + QByteArray line = f.readLine(); + if(line.startsWith("CE_SDK")) { + int index = line.indexOf('='); + if(index >= 0) { + CE_SDK = line.mid(index + 1).trimmed(); + } + } else if(line.startsWith("CE_ARCH")) { + int index = line.indexOf('='); + if(index >= 0) { + CE_ARCH = line.mid(index + 1).trimmed(); + } + } + if(!CE_SDK.isEmpty() && !CE_ARCH.isEmpty()) { + platformName = CE_SDK + " (" + CE_ARCH + ")"; + break; + } + } + } + return platformName; +} + +bool CeSdkHandler::parse(const QString &vsdir) +{ + // look at the file at %VCInstallDir%/vcpackages/WCE.VCPlatform.config + // and scan through all installed sdks... + m_list.clear(); + + VCInstallDir = vsdir + "/VC/"; + VSInstallDir = vsdir; + + QDir vStudioDir(VCInstallDir); + if (!vStudioDir.cd("vcpackages")) + return false; + + QFile configFile(vStudioDir.absoluteFilePath(QLatin1String("WCE.VCPlatform.config"))); + qDebug()<<"##"; + if (!configFile.exists() || !configFile.open(QIODevice::ReadOnly)) + return false; + + qDebug()<<"parsing"; + + QString currentElement; + CeSdkInfo currentItem; + QXmlStreamReader xml(&configFile); + while (!xml.atEnd()) { + xml.readNext(); + if (xml.isStartElement()) { + currentElement = xml.name().toString(); + if (currentElement == QLatin1String("Platform")) + currentItem = CeSdkInfo(); + else if (currentElement == QLatin1String("Directories")) { + QXmlStreamAttributes attr = xml.attributes(); + currentItem.m_include = fixPaths(attr.value(QLatin1String("Include")).toString()); + currentItem.m_lib = fixPaths(attr.value(QLatin1String("Library")).toString()); + currentItem.m_bin = fixPaths(attr.value(QLatin1String("Path")).toString()); + } + } else if (xml.isEndElement()) { + if (xml.name().toString() == QLatin1String("Platform")) + m_list.append(currentItem); + } else if (xml.isCharacters() && !xml.isWhitespace()) { + if (currentElement == QLatin1String("PlatformName")) + currentItem.m_name = xml.text().toString(); + else if (currentElement == QLatin1String("OSMajorVersion")) + currentItem.m_major = xml.text().toString().toInt(); + else if (currentElement == QLatin1String("OSMinorVersion")) + currentItem.m_minor = xml.text().toString().toInt(); + } + } + + if (xml.error() && xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) { + qWarning() << "XML ERROR:" << xml.lineNumber() << ": " << xml.errorString(); + return false; + } + return m_list.size() > 0 ? true : false; +} + +CeSdkInfo CeSdkHandler::find(const QString &name) +{ + qDebug()<<"looking for platform "<<name; + for (QList<CeSdkInfo>::iterator it = m_list.begin(); it != m_list.end(); ++it) { + qDebug()<<"...."<<it->name(); + if (it->name() == name) + return *it; + } + return CeSdkInfo(); +} diff --git a/src/plugins/qt4projectmanager/cesdkhandler.h b/src/plugins/qt4projectmanager/cesdkhandler.h new file mode 100644 index 00000000000..88d3bdb4701 --- /dev/null +++ b/src/plugins/qt4projectmanager/cesdkhandler.h @@ -0,0 +1,107 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CE_SDK_HANDLER_INCL +#define CE_SDK_HANDLER_INCL + +#include <projectexplorer/ProjectExplorerInterfaces> + +#include <QStringList> +#include <QDir> + +#define VCINSTALL_MACRO "$(VCInstallDir)" +#define VSINSTALL_MACRO "$(VSInstallDir)" + +namespace Qt4ProjectManager { +namespace Internal { + +class CeSdkInfo +{ +public: + CeSdkInfo(); + inline QString name(); + inline QString binPath(); + inline QString includePath(); + inline QString libPath(); + ProjectExplorer::Environment addToEnvironment(const ProjectExplorer::Environment &env); + inline bool isValid(); + inline int majorVersion(); + inline int minorVersion(); + inline bool isSupported(); +private: + friend class CeSdkHandler; + QString m_name; + QString m_bin; + QString m_include; + QString m_lib; + int m_major; + int m_minor; +}; + +inline QString CeSdkInfo::name(){ return m_name; } +inline QString CeSdkInfo::binPath(){ return m_bin; } +inline QString CeSdkInfo::includePath(){ return m_include; } +inline QString CeSdkInfo::libPath(){ return m_lib; } +inline bool CeSdkInfo::isValid(){ return !m_name.isEmpty() && !m_bin.isEmpty() && !m_include.isEmpty() && !m_lib.isEmpty(); } +inline int CeSdkInfo::majorVersion(){ return m_major; } +inline int CeSdkInfo::minorVersion(){ return m_minor; } +inline bool CeSdkInfo::isSupported() { return m_major >= 5; } + +class CeSdkHandler +{ +public: + CeSdkHandler(); + bool parse(const QString &path); + inline QList<CeSdkInfo> listAll() const; + CeSdkInfo find(const QString &name); + static QString platformName(const QString &qtpath); +private: + inline QString fixPaths(QString path) const; + QList<CeSdkInfo> m_list; + QString VCInstallDir; + QString VSInstallDir; +}; + +inline QList<CeSdkInfo> CeSdkHandler::listAll() const +{ + return m_list; +} + +inline QString CeSdkHandler::fixPaths(QString path) const +{ + return QDir::toNativeSeparators(QDir::cleanPath(path.replace(VCINSTALL_MACRO, VCInstallDir).replace(VSINSTALL_MACRO, VSInstallDir).replace(QLatin1String(";$(PATH)"), QLatin1String("")))); +} + +} +} + +#endif diff --git a/src/plugins/qt4projectmanager/deployhelper.cpp b/src/plugins/qt4projectmanager/deployhelper.cpp new file mode 100644 index 00000000000..bf1fdf271d6 --- /dev/null +++ b/src/plugins/qt4projectmanager/deployhelper.cpp @@ -0,0 +1,196 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "deployhelper.h" +#include "qt4project.h" + +#include <QDebug> +#include <QDir> +#include <QEventLoop> + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; +DeployHelperRunStep::DeployHelperRunStep(Qt4Project *pro) + : BuildStep(pro), m_started(false), m_pro(pro) +{ + QDir workbenchDir = QCoreApplication::applicationDirPath(); + workbenchDir.cdUp(); + m_binary = QDir::convertSeparators( workbenchDir.absolutePath() + QLatin1String("/qtembeddedtools/qemudeployer")); + m_id = "id"; + +}; + +bool DeployHelperRunStep::init(const QString &configuration) +{ + Q_UNUSED(configuration); + m_qtdir = m_pro->qtDir(configuration); + QFileInfo fi(m_pro->file()->fileName()); + m_appdir = fi.absolutePath(); + //find target + m_exec = ""; + QStringList targets = QStringList(); //TODO fix m_pro->qmakeTarget(); + foreach(const QString& target, targets) { + QFileInfo fi(m_appdir + QLatin1Char('/') + target); + if(fi.exists()) + m_exec = target; + break; + } + m_skin = m_pro->value("VNCSkin").toString(); + return true; +} + +void DeployHelperRunStep::run(QFutureInterface<bool> & fi) +{ + if (m_id.isNull() || m_binary.isNull()) { + fi.reportResult(false); + return; + } + if (m_started) + stop(); + + QStringList args; + args << "start" << "-id"<<m_id<<"-qtdir"<<m_qtdir<<"-appdir"<<m_appdir<<"-exec"<<m_exec; + if (!m_skin.isEmpty()) + args<<"-skin"<<m_skin; + + for (int i=0; i<m_extraargs.count(); ++i) + args.append(m_extraargs.at(i)); + + QProcess proc; + connect(&proc, SIGNAL(finished (int,QProcess::ExitStatus)), + this, SLOT(processFinished()), Qt::DirectConnection); + connect(&proc, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection); + + QStringList env = QProcess::systemEnvironment(); + env.replaceInStrings(QRegExp("^PATH=(.*)", Qt::CaseInsensitive), "PATH="+QApplication::applicationDirPath()+";\\1"); + proc.setEnvironment(env); + proc.setProcessChannelMode(QProcess::MergedChannels); + proc.start(m_binary, args); + proc.waitForStarted(); + m_started = true; + + m_eventLoop = new QEventLoop; + m_eventLoop->exec(); + delete m_eventLoop; + m_eventLoop = 0; + fi.reportResult(true); + return; +} + +DeployHelperRunStep::~DeployHelperRunStep() +{ + cleanup(); +} + +QString DeployHelperRunStep::binary() +{ + return m_binary; +} + +QString DeployHelperRunStep::id() +{ + return m_id; +} + +bool DeployHelperRunStep::started() +{ + return m_started; +} + +void DeployHelperRunStep::processFinished() +{ + m_eventLoop->exit(0); +} + +void DeployHelperRunStep::stop() +{ + if (m_id.isNull() || m_binary.isNull() || !m_started) + return; + + QStringList env = QProcess::systemEnvironment(); + env.replaceInStrings(QRegExp("^PATH=(.*)", Qt::CaseInsensitive), "PATH="+QApplication::applicationDirPath()+";\\1"); + + QStringList args; + args<<"stop"<<"-id"<<m_id; + QProcess proc; + proc.setEnvironment(env); + proc.start(m_binary, args); + proc.waitForFinished(); + m_started = false; +} + +void DeployHelperRunStep::cleanup() +{ + if (m_id.isNull() || m_binary.isNull() || !m_started) + return; + + QStringList env = QProcess::systemEnvironment(); + env.replaceInStrings(QRegExp("^PATH=(.*)", Qt::CaseInsensitive), "PATH="+QApplication::applicationDirPath()+";\\1"); + + QStringList args; + args<<"cleanup"<<"-id"<<m_id; + QProcess proc; + proc.setEnvironment(env); + proc.start(m_binary, args); + proc.waitForFinished(); + m_started = false; +} + +void DeployHelperRunStep::readyRead() +{ + // TODO Unbreak the application output (this whole thing should be moved to a IRunConfigurationRunner) + QProcess * proc = qobject_cast<QProcess *>(sender()); + while (proc->canReadLine()) { + QString line = proc->readLine().trimmed(); + if (line.startsWith("L:") || line.startsWith("A:")) { + //emit addToApplicationOutputWindow(line.mid(2)); + } else { + //emit addToApplicationOutputWindow(line); + } + } +} + +QString DeployHelperRunStep::name() +{ + return "trolltech.qt4projectmanager.deployhelperrunstep"; +} + +QString DeployHelperRunStep::displayName() +{ + return "Linux emulator"; +} + +ProjectExplorer::BuildStepConfigWidget * DeployHelperRunStep::configWidget() +{ + return 0; +} diff --git a/src/plugins/qt4projectmanager/deployhelper.h b/src/plugins/qt4projectmanager/deployhelper.h new file mode 100644 index 00000000000..a132ba34864 --- /dev/null +++ b/src/plugins/qt4projectmanager/deployhelper.h @@ -0,0 +1,95 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DEPLOYHELPER_H +#define DEPLOYHELPER_H + +#include <projectexplorer/ProjectExplorerInterfaces> + +#include <QtCore/QString> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE +class QEventLoop; +QT_END_NAMESPACE + +namespace Qt4ProjectManager { + +class Qt4Project; + +namespace Internal { + +class DeployHelperRunStep : public ProjectExplorer::BuildStep +{ + Q_OBJECT +public: + DeployHelperRunStep(Qt4Project * pro); + ~DeployHelperRunStep(); + + virtual bool init(const QString &configuration); + virtual void run(QFutureInterface<bool> &); + + virtual QString name(); + virtual QString displayName(); + + virtual QString binary(); + virtual QString id(); + + virtual ProjectExplorer::BuildStepConfigWidget *configWidget(); + +private: + bool started(); + + void stop(); + void cleanup(); + +private slots: + void readyRead(); + void processFinished(); + +private: + QString m_qtdir; + QString m_appdir; + QString m_exec; + QString m_skin; + QString m_binary; + QStringList m_extraargs; + QString m_id; + bool m_started; + Qt4Project *m_pro; + QEventLoop *m_eventLoop; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // DEPLOYHELPER_H diff --git a/src/plugins/qt4projectmanager/directorywatcher.cpp b/src/plugins/qt4projectmanager/directorywatcher.cpp new file mode 100644 index 00000000000..61860856a32 --- /dev/null +++ b/src/plugins/qt4projectmanager/directorywatcher.cpp @@ -0,0 +1,206 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "directorywatcher.h" + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QFileSystemWatcher> +#include <QtCore/QTimer> + +enum { debugWatcher = 0 }; + +namespace Qt4ProjectManager { +namespace Internal { + +int DirectoryWatcher::m_objectCount = 0; +QHash<QString,int> DirectoryWatcher::m_directoryCount; +QFileSystemWatcher *DirectoryWatcher::m_watcher = 0; + +/* + \class DirectoryWatcher + + A wrapper for QFileSystemWatcher that collects + consecutive changes to a registered directory and emits directoryChanged() and fileChanged(). + + Note that files added are only monitored if the parent directory is added, too. + + All instances of DirectoryWatcher share one QFileSystemWatcher object. + That's because every QFileSystemWatcher object consumes a file descriptor, + even if no files are watched. +*/ +DirectoryWatcher::DirectoryWatcher(QObject *parent) : + QObject(parent), + m_timer(0) +{ + if (!m_watcher) + m_watcher = new QFileSystemWatcher(); + ++m_objectCount; + connect(m_watcher, SIGNAL(directoryChanged(QString)), + this, SLOT(slotDirectoryChanged(QString))); +} + +DirectoryWatcher::~DirectoryWatcher() +{ + foreach (const QString &dir, m_directories) + removeDirectory(dir); + if (--m_objectCount == 0) { + delete m_watcher; + m_watcher = 0; + } +} + +QStringList DirectoryWatcher::directories() const +{ + if (debugWatcher) + qDebug() << Q_FUNC_INFO << m_directories; + return m_directories; +} + +void DirectoryWatcher::addDirectory(const QString &dir) +{ + if (debugWatcher) + qDebug() << Q_FUNC_INFO << dir; + if (m_directories.contains(dir)) + return; + m_directories += dir; + if (m_directoryCount[dir] == 0) + m_watcher->addPath(dir); + m_directoryCount[dir] += 1; +} + +void DirectoryWatcher::removeDirectory(const QString &dir) +{ + if (debugWatcher) + qDebug() << Q_FUNC_INFO << dir; + m_directories.removeOne(dir); + m_directoryCount[dir] -= 1; + if (m_directoryCount[dir] == 0) + m_watcher->removePath(dir); +} + +QStringList DirectoryWatcher::files() const +{ + if (debugWatcher) + qDebug() << Q_FUNC_INFO << m_files.keys(); + return m_files.keys(); +} + +void DirectoryWatcher::addFile(const QString &filePath) +{ + addFiles(QStringList() << filePath); +} + +void DirectoryWatcher::addFiles(const QStringList &filePaths) +{ + foreach (const QString filePath, filePaths) { + QFileInfo file(filePath); + m_files.insert(file.absoluteFilePath(),file.lastModified()); + } +} + +void DirectoryWatcher::removeFile(const QString &filePath) +{ + m_files.remove(filePath); +} + +void DirectoryWatcher::slotDirectoryChanged(const QString &path) +{ + if (debugWatcher) + qDebug() << Q_FUNC_INFO << path; + if (!m_directories.contains(path) + || m_pendingDirectories.contains(path)) + return; + + if (!m_timer) { + m_timer = new QTimer(this); + m_timer->setSingleShot(true); + m_timer->setInterval(500); // delay for 0.5 sec + connect(m_timer, SIGNAL(timeout()), this, SLOT(slotDelayedDirectoriesChanged())); + } + if (!m_timer->isActive()) + m_timer->start(); + m_pendingDirectories.push_back(path); +} + +void DirectoryWatcher::slotDelayedDirectoriesChanged() +{ + if (debugWatcher) + qDebug() << Q_FUNC_INFO << " emitting " << m_pendingDirectories; + const QStringList::const_iterator cend = m_pendingDirectories.constEnd(); + for (QStringList::const_iterator it = m_pendingDirectories.constBegin(); it != cend; ++it) { + const QString dir = *it; + if (!QFileInfo(dir).exists()) + removeDirectory(*it); + emit directoryChanged(*it); + updateFileList(*it); + } + m_pendingDirectories.clear(); +} + +void DirectoryWatcher::updateFileList(const QString &dir) +{ + const QStringList monitoredFiles = m_files.keys(); + QStringList removedFiles = monitoredFiles; + if (QFileInfo(dir).exists()) { + // Compare directory contents and emit signals + QFileInfoList entryInfoList + = QDir(dir).entryInfoList(QDir::Files|QDir::CaseSensitive); + // Loop over directory creating the new map of file->time, removing + // the existing entries from the old map + const QFileInfoList::const_iterator cend = entryInfoList.constEnd(); + for (QFileInfoList::const_iterator filIt = entryInfoList.constBegin(); + filIt != cend; ++filIt) { + const QString path = filIt->absoluteFilePath(); + FileModificationTimeMap::iterator mapIt = m_files.find(path); + if (mapIt != m_files.end()) { + const QDateTime lastModified = filIt->lastModified(); + if (lastModified > mapIt.value()) { + if (debugWatcher) + qDebug() << Q_FUNC_INFO << "emitting file changed" << path; + emit fileChanged(path); + m_files[path] = lastModified; + } + removedFiles.removeOne(path); + } + } + } + + if (!removedFiles.isEmpty()) { + foreach (const QString &file, removedFiles) + removeFile(file); + } +} + +} // namespace Internal +} // namespace Qt4ProjectManager diff --git a/src/plugins/qt4projectmanager/directorywatcher.h b/src/plugins/qt4projectmanager/directorywatcher.h new file mode 100644 index 00000000000..bde27794fe8 --- /dev/null +++ b/src/plugins/qt4projectmanager/directorywatcher.h @@ -0,0 +1,93 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DIRECTORYWATCHER +#define DIRECTORYWATCHER + +#include <QtCore/QDateTime> +#include <QtCore/QHash> +#include <QtCore/QObject> +#include <QtCore/QStringList> + + +QT_BEGIN_NAMESPACE +class QTimer; +class QFileSystemWatcher; +QT_END_NAMESPACE + +namespace Qt4ProjectManager { +namespace Internal { + +class DirectoryWatcher : public QObject +{ + Q_DISABLE_COPY(DirectoryWatcher) + Q_OBJECT +public: + explicit DirectoryWatcher(QObject *parent = 0); + virtual ~DirectoryWatcher(); + + QStringList directories() const; + void addDirectory(const QString &dir); + void removeDirectory(const QString &dir); + + QStringList files() const; + void addFile(const QString &filePath); + void addFiles(const QStringList &filePaths); + void removeFile(const QString &filePath); + +signals: + void directoryChanged(const QString &path); + void fileChanged(const QString &path); + +private slots: + void slotDirectoryChanged(const QString &); + void slotDelayedDirectoriesChanged(); + +private: + void updateFileList(const QString &dir); + + static int m_objectCount; + static QHash<QString,int> m_directoryCount; + static QFileSystemWatcher *m_watcher; + + QTimer *m_timer; + QStringList m_directories; + QStringList m_pendingDirectories; + + typedef QHash<QString, QDateTime> FileModificationTimeMap; + FileModificationTimeMap m_files; +}; + +} +} //namespace Qt4ProjectManager + +#endif diff --git a/src/plugins/qt4projectmanager/embeddedpropertiespage.cpp b/src/plugins/qt4projectmanager/embeddedpropertiespage.cpp new file mode 100644 index 00000000000..9a0d16264a0 --- /dev/null +++ b/src/plugins/qt4projectmanager/embeddedpropertiespage.cpp @@ -0,0 +1,140 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "embeddedpropertiespage.h" +#include "qt4project.h" + +#include <QFileInfo> +#include <QDir> + +using namespace ProjectExplorer; +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; + +/// +/// EmbeddedPropertiesPanelFactory +/// + +bool EmbeddedPropertiesPanelFactory::supports(Project *project) +{ +#ifdef Q_OS_WIN + Qt4Project *pro = qobject_cast<Qt4Project *>(project); + if (pro) { + return true; + } +#else + Q_UNUSED(project); +#endif + return false; +} + +ProjectExplorer::PropertiesPanel *EmbeddedPropertiesPanelFactory::createPanel( + ProjectExplorer::Project *project) +{ + return new EmbeddedPropertiesPanel(project); +} + +/// +/// EmbeddedPropertiesPanel +/// + +EmbeddedPropertiesPanel::EmbeddedPropertiesPanel(ProjectExplorer::Project *project) + : PropertiesPanel(), + m_widget(new EmbeddedPropertiesWidget(project)) +{ +} + +EmbeddedPropertiesPanel::~EmbeddedPropertiesPanel() +{ + delete m_widget; +} + +QString EmbeddedPropertiesPanel::name() const +{ + return tr("Embedded Linux"); + +} + +QWidget *EmbeddedPropertiesPanel::widget() +{ + return m_widget; +} + +/// +/// EmbeddedPropertiesWidget +/// + +EmbeddedPropertiesWidget::EmbeddedPropertiesWidget(ProjectExplorer::Project *project) + : QWidget() +{ + m_ui.setupUi(this); + +#ifdef Q_OS_WIN + m_ui.virtualBoxCheckbox->setChecked(project->value("useVBOX").toBool()); + + // Find all skins + QString skin = QFileInfo(project->value("VNCSkin").toString()).fileName(); + QStringList skins; + + QDir skinDir = QApplication::applicationDirPath(); + skinDir.cdUp(); + if (skinDir.cd("qtembeddedtools") && skinDir.cd("qsimplevnc")) { + skins = skinDir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ); + } + m_ui.skinComboBox->clear(); + m_ui.skinComboBox->addItems(skins); + if (!skin.isEmpty()) { + int index = m_ui.skinComboBox->findText(skin); + if (index != -1) + m_ui.skinComboBox->setCurrentIndex(index); + } +#else + Q_UNUSED(project) +#endif + //TODO readd finish code + /* + project->setValue("useVBOX", m_ui.virtualBoxCheckbox->isChecked()); + + //Skin + QDir skinDir = QApplication::applicationDirPath(); + skinDir.cdUp(); + skinDir.cd("qtembeddedtools"); + skinDir.cd("qsimplevnc"); + project->setValue("VNCSkin", skinDir.absolutePath() + "/" + m_ui.skinComboBox->currentText() + "/" + m_ui.skinComboBox->currentText()); + + */ +} + +EmbeddedPropertiesWidget::~EmbeddedPropertiesWidget() +{ +} + diff --git a/src/plugins/qt4projectmanager/embeddedpropertiespage.h b/src/plugins/qt4projectmanager/embeddedpropertiespage.h new file mode 100644 index 00000000000..47b3721654e --- /dev/null +++ b/src/plugins/qt4projectmanager/embeddedpropertiespage.h @@ -0,0 +1,85 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef EMBEDDEDPROPERTIESPAGE_H +#define EMBEDDEDPROPERTIESPAGE_H + +#include "ui_embeddedpropertiespage.h" +#include <projectexplorer/iprojectproperties.h> +#include <QtCore/QModelIndex> + +namespace ProjectExplorer { +class Project; +} + +namespace Qt4ProjectManager { + +namespace Internal { + +class EmbeddedPropertiesWidget; + +class EmbeddedPropertiesPanelFactory : public ProjectExplorer::IPanelFactory +{ +public: + virtual bool supports(ProjectExplorer::Project *project); + ProjectExplorer::PropertiesPanel *createPanel(ProjectExplorer::Project *project); +}; + +class EmbeddedPropertiesPanel : public ProjectExplorer::PropertiesPanel +{ + Q_OBJECT +public: + EmbeddedPropertiesPanel(ProjectExplorer::Project *project); + ~EmbeddedPropertiesPanel(); + + QString name() const; + QWidget *widget(); + +private: + EmbeddedPropertiesWidget *m_widget; +}; + +class EmbeddedPropertiesWidget : public QWidget +{ + Q_OBJECT +public: + EmbeddedPropertiesWidget(ProjectExplorer::Project *project); + virtual ~EmbeddedPropertiesWidget(); + private: + Ui_EmbeddedPropertiesPage m_ui; + ProjectExplorer::Project *m_pro; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // EMBEDDEDPROPERTIESPAGE_H diff --git a/src/plugins/qt4projectmanager/embeddedpropertiespage.ui b/src/plugins/qt4projectmanager/embeddedpropertiespage.ui new file mode 100644 index 00000000000..5f521ed1a3a --- /dev/null +++ b/src/plugins/qt4projectmanager/embeddedpropertiespage.ui @@ -0,0 +1,49 @@ +<ui version="4.0" > + <class>EmbeddedPropertiesPage</class> + <widget class="QWidget" name="EmbeddedPropertiesPage" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>649</width> + <height>302</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout" > + <item> + <layout class="QFormLayout" name="formLayout" > + <property name="fieldGrowthPolicy" > + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + <item row="0" column="1" > + <widget class="QCheckBox" name="virtualBoxCheckbox" > + <property name="text" > + <string>Use Virtual Box
 +Note: This adds the toolchain to the build environment and runs the program inside a virtual machine.
 +It also automatically sets the correct qt version.</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="skinLabel" > + <property name="text" > + <string>Skin:</string> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="skinComboBox" /> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/qt4projectmanager/enveditdialog.ui b/src/plugins/qt4projectmanager/enveditdialog.ui new file mode 100644 index 00000000000..acda028c179 --- /dev/null +++ b/src/plugins/qt4projectmanager/enveditdialog.ui @@ -0,0 +1,236 @@ +<ui version="4.0" > + <class>Qt4ProjectManager::Internal::EnvEditDialog</class> + <widget class="QDialog" name="Qt4ProjectManager::Internal::EnvEditDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>398</width> + <height>298</height> + </rect> + </property> + <property name="windowTitle" > + <string>Build Environment</string> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <layout class="QGridLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="0" column="1" > + <widget class="QLineEdit" name="envNameEdit" /> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_4" > + <property name="text" > + <string>Make Command:</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLineEdit" name="makeEdit" /> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Build Environment:</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_5" > + <property name="text" > + <string>mkspec:</string> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QComboBox" name="mkspecCombo" /> + </item> + </layout> + </item> + <item> + <layout class="QGridLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="2" column="1" > + <widget class="QLineEdit" name="varEdit" /> + </item> + <item row="1" column="2" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>91</height> + </size> + </property> + </spacer> + </item> + <item rowspan="2" row="0" column="0" colspan="2" > + <widget class="QTreeWidget" name="varList" > + <property name="uniformRowHeights" > + <bool>true</bool> + </property> + <property name="itemsExpandable" > + <bool>false</bool> + </property> + <property name="columnCount" > + <number>2</number> + </property> + <column> + <property name="text" > + <string>0</string> + </property> + </column> + <column> + <property name="text" > + <string>1</string> + </property> + </column> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLineEdit" name="valEdit" /> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Values:</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Variable:</string> + </property> + </widget> + </item> + <item row="2" column="2" > + <widget class="QToolButton" name="addButton" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="0" column="2" > + <widget class="QToolButton" name="delButton" > + <property name="text" > + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QPushButton" name="importButton" > + <property name="text" > + <string>Import</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>91</width> + <height>31</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="okButton" > + <property name="text" > + <string>OK</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cancelButton" > + <property name="text" > + <string>Cancel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <tabstops> + <tabstop>envNameEdit</tabstop> + <tabstop>makeEdit</tabstop> + <tabstop>mkspecCombo</tabstop> + <tabstop>varEdit</tabstop> + <tabstop>valEdit</tabstop> + <tabstop>delButton</tabstop> + <tabstop>addButton</tabstop> + <tabstop>varList</tabstop> + <tabstop>importButton</tabstop> + <tabstop>okButton</tabstop> + <tabstop>cancelButton</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>okButton</sender> + <signal>clicked()</signal> + <receiver>Qt4ProjectManager::Internal::EnvEditDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>278</x> + <y>253</y> + </hint> + <hint type="destinationlabel" > + <x>96</x> + <y>254</y> + </hint> + </hints> + </connection> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>Qt4ProjectManager::Internal::EnvEditDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>369</x> + <y>253</y> + </hint> + <hint type="destinationlabel" > + <x>179</x> + <y>282</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/qt4projectmanager/envvariablespage.ui b/src/plugins/qt4projectmanager/envvariablespage.ui new file mode 100644 index 00000000000..7d5262b21c5 --- /dev/null +++ b/src/plugins/qt4projectmanager/envvariablespage.ui @@ -0,0 +1,121 @@ +<ui version="4.0" > + <class>Qt4ProjectManager::Internal::EnvVariablesPage</class> + <widget class="QWidget" name="Qt4ProjectManager::Internal::EnvVariablesPage" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>398</width> + <height>336</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox" > + <property name="enabled" > + <bool>true</bool> + </property> + <property name="title" > + <string>Build Environments</string> + </property> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QListWidget" name="listView" /> + </item> + <item> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <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="delButton" > + <property name="text" > + <string>Delete</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QGridLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="1" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Default mkspec:</string> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Default make command:</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLineEdit" name="mkspecEdit" /> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="defaultCmdEdit" /> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/qt4projectmanager/gccparser.cpp b/src/plugins/qt4projectmanager/gccparser.cpp new file mode 100644 index 00000000000..d54d0daa9b0 --- /dev/null +++ b/src/plugins/qt4projectmanager/gccparser.cpp @@ -0,0 +1,131 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gccparser.h" +#include "qt4projectmanagerconstants.h" + +#include <QDebug> + +using namespace Qt4ProjectManager; + +GccParser::GccParser() +{ + m_regExp.setPattern("^([^\\(\\)]+[^\\d]):(\\d+):(\\d+:)*(\\s(warning|error):)?(.+)$"); + m_regExp.setMinimal(true); + + m_regExpIncluded.setPattern("^.*from\\s([^:]+):(\\d+)(,|:)$"); + m_regExpIncluded.setMinimal(true); + + m_regExpLinker.setPattern("^(\\S+)\\(\\S+\\):(.+)$"); + m_regExpLinker.setMinimal(true); + + //make[4]: Entering directory `/home/kkoehne/dev/ide-explorer/src/plugins/qtscripteditor' + m_makeDir.setPattern("^make.*: (\\w+) directory .(.+).$"); + m_makeDir.setMinimal(true); + + m_linkIndent = false; +} + +QString GccParser::name() const +{ + return QLatin1String(Qt4ProjectManager::Constants::BUILD_PARSER_GCC); +} + +void GccParser::stdOutput(const QString & line) +{ + QString lne = line.trimmed(); + + if (m_makeDir.indexIn(lne) > -1) { + if (m_makeDir.cap(1) == "Leaving") + emit leaveDirectory(m_makeDir.cap(2)); + else + emit enterDirectory(m_makeDir.cap(2)); + } +} + +void GccParser::stdError(const QString & line) +{ + QString lne = line.trimmed(); + if (m_regExp.indexIn(lne) > -1) { + ProjectExplorer::BuildParserInterface::PatternType type; + if (m_regExp.cap(5) == "warning") + type = ProjectExplorer::BuildParserInterface::Warning; + else if (m_regExp.cap(5) == "error") + type = ProjectExplorer::BuildParserInterface::Error; + else + type = ProjectExplorer::BuildParserInterface::Unknown; + + QString description = m_regExp.cap(6); + if (m_linkIndent) + description.prepend(QLatin1String("-> ")); + + //qDebug()<<m_regExp.cap(1)<<m_regExp.cap(2)<<m_regExp.cap(3)<<m_regExp.cap(4); + + emit addToTaskWindow( + m_regExp.cap(1), //filename + type, + m_regExp.cap(2).toInt(), //line number + description); + + } else if (m_regExpIncluded.indexIn(lne) > -1) { + emit addToTaskWindow( + m_regExpIncluded.cap(1), //filename + ProjectExplorer::BuildParserInterface::Unknown, + m_regExpIncluded.cap(2).toInt(), //linenumber + lne //description + ); + } else if (m_regExpLinker.indexIn(lne) > -1) { + ProjectExplorer::BuildParserInterface::PatternType type = ProjectExplorer::BuildParserInterface::Error; + QString description = m_regExpLinker.cap(2); + if (lne.endsWith(QLatin1Char(':'))) { + m_linkIndent = true; + } else if (m_linkIndent) { + description.prepend(QLatin1String("-> ")); + type = ProjectExplorer::BuildParserInterface::Unknown; + } + emit addToTaskWindow( + m_regExpLinker.cap(1), //filename + type, + -1, //linenumber + description); + } else if (lne.startsWith(QLatin1String("collect2:"))) { + emit addToTaskWindow( + "", + ProjectExplorer::BuildParserInterface::Error, + -1, + lne //description + ); + m_linkIndent = false; + } else { + m_linkIndent = false; + } +} diff --git a/src/plugins/qt4projectmanager/gccparser.h b/src/plugins/qt4projectmanager/gccparser.h new file mode 100644 index 00000000000..fea60f7d481 --- /dev/null +++ b/src/plugins/qt4projectmanager/gccparser.h @@ -0,0 +1,60 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GCCPARSER +#define GCCPARSER + +#include <QtCore/QRegExp> +#include <projectexplorer/ProjectExplorerInterfaces> + +namespace Qt4ProjectManager { + +class GccParser : public ProjectExplorer::BuildParserInterface +{ + Q_OBJECT + +public: + GccParser(); + QString name() const; + virtual void stdOutput(const QString & line); + virtual void stdError(const QString & line); +private: + QRegExp m_regExp; + QRegExp m_regExpIncluded; + QRegExp m_regExpLinker; + QRegExp m_makeDir; + bool m_linkIndent; +}; + +} //namespace ProjectExplorer + +#endif diff --git a/src/plugins/qt4projectmanager/gccpreprocessor.cpp b/src/plugins/qt4projectmanager/gccpreprocessor.cpp new file mode 100644 index 00000000000..81bc4641767 --- /dev/null +++ b/src/plugins/qt4projectmanager/gccpreprocessor.cpp @@ -0,0 +1,127 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "gccpreprocessor.h" +#include <QProcess> +#include <QString> +#include <QFile> +#include <QtDebug> + +using namespace Qt4ProjectManager::Internal; + +GCCPreprocessor::GCCPreprocessor(const QString &gcc) + : m_gcc(gcc) +{ } + +GCCPreprocessor::~GCCPreprocessor() +{ } + +void GCCPreprocessor::setGcc(const QString &gcc) +{ + if (m_gcc == gcc) + return; + m_gcc = gcc; + m_predefinedMacros.clear();; + m_systemHeaderPaths.clear();; +} + +QByteArray GCCPreprocessor::predefinedMacros() +{ + if (m_predefinedMacros.isEmpty()) { + QStringList arguments; + arguments << QLatin1String("-xc++") + << QLatin1String("-E") + << QLatin1String("-dM") + << QLatin1String("-"); + + QProcess cpp; + cpp.start(m_gcc, arguments); + cpp.closeWriteChannel(); + cpp.waitForFinished(); + m_predefinedMacros = cpp.readAllStandardOutput(); + } + return m_predefinedMacros; +} + +QList<HeaderPath> GCCPreprocessor::systemHeaderPaths() +{ + if (m_systemHeaderPaths.isEmpty()) { + QStringList arguments; + arguments << QLatin1String("-xc++") + << QLatin1String("-E") + << QLatin1String("-v") + << QLatin1String("-"); + + QProcess cpp; + cpp.setReadChannelMode(QProcess::MergedChannels); + cpp.start(m_gcc, arguments); + cpp.closeWriteChannel(); + cpp.waitForFinished(); + + QByteArray line; + while (cpp.canReadLine()) { + line = cpp.readLine(); + if (line.startsWith("#include")) + break; + } + + if (! line.isEmpty() && line.startsWith("#include")) { + HeaderPath::Kind kind = HeaderPath::UserHeaderPath; + while (cpp.canReadLine()) { + line = cpp.readLine(); + if (line.startsWith("#include")) { + kind = HeaderPath::GlobalHeaderPath; + } else if (! line.isEmpty() && QChar(line.at(0)).isSpace()) { + HeaderPath::Kind thisHeaderKind = kind; + + line = line.trimmed(); + if (line.endsWith('\n')) + line.chop(1); + + int index = line.indexOf(" (framework directory)"); + if (index != -1) { + line = line.left(index); + thisHeaderKind = HeaderPath::FrameworkHeaderPath; + } + + m_systemHeaderPaths.append(HeaderPath(QFile::decodeName(line), thisHeaderKind)); + } else if (line.startsWith("End of search list.")) { + break; + } else { + qWarning() << "ignore line:" << line; + } + } + } + } + return m_systemHeaderPaths; +} diff --git a/src/plugins/qt4projectmanager/gccpreprocessor.h b/src/plugins/qt4projectmanager/gccpreprocessor.h new file mode 100644 index 00000000000..7a2bfb0921c --- /dev/null +++ b/src/plugins/qt4projectmanager/gccpreprocessor.h @@ -0,0 +1,62 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GCCPREPROCESSOR_H +#define GCCPREPROCESSOR_H + +#include "headerpath.h" +#include <QByteArray> +#include <QList> + +namespace Qt4ProjectManager { +namespace Internal { + +class GCCPreprocessor +{ +public: + GCCPreprocessor(const QString &gcc = QLatin1String("g++")); + ~GCCPreprocessor(); + + void setGcc(const QString &gcc); + QByteArray predefinedMacros(); + QList<HeaderPath> systemHeaderPaths(); + +private: + QString m_gcc; + QByteArray m_predefinedMacros; + QList<HeaderPath> m_systemHeaderPaths; +}; + +} // end of namespace Internal +} // end of namespace Qt4ProjectManager + +#endif // GCCPREPROCESSOR_H diff --git a/src/plugins/qt4projectmanager/gdbmacrosbuildstep.cpp b/src/plugins/qt4projectmanager/gdbmacrosbuildstep.cpp new file mode 100644 index 00000000000..ab5dcaeb9a4 --- /dev/null +++ b/src/plugins/qt4projectmanager/gdbmacrosbuildstep.cpp @@ -0,0 +1,192 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gdbmacrosbuildstep.h" +#include "qt4projectmanagerconstants.h" +#include "qt4project.h" +#include "qmakestep.h" +#include "makestep.h" + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; + +GdbMacrosBuildStep::GdbMacrosBuildStep(Qt4Project *project) + : BuildStep(project), m_project(project), m_configWidget(new GdbMacrosBuildStepConfigWidget) +{ + +} + +GdbMacrosBuildStep::~GdbMacrosBuildStep() +{ +} + +bool GdbMacrosBuildStep::init(const QString &buildConfiguration) +{ + m_buildDirectory = m_project->buildDirectory(buildConfiguration); + m_qmake = m_project->qtVersion(buildConfiguration)->qmakeCommand(); + m_buildConfiguration = buildConfiguration; + qDebug()<<"m_buildDirectory "<<m_buildDirectory; + qDebug()<<"m_qmake "<<m_qmake; + return true; +} + +void GdbMacrosBuildStep::run(QFutureInterface<bool> & fi) +{ + // TODO CONFIG handling + + QString dumperPath = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>() + ->resourcePath() + "/gdbmacros/"; + QStringList files; + files << "gdbmacros.cpp" + << "gdbmacros.pro"; + + + QString destDir = m_buildDirectory + "/qtc-gdbmacros/"; + QDir dir; + dir.mkpath(destDir); + foreach(const QString &file, files) { + QFile destination(destDir + file); + if (destination.exists()) + destination.remove(); + QFile::copy(dumperPath + file, destDir + file); + qDebug()<<"copying"<<(dumperPath + file)<<" to "<< (destDir + file); + } + + Qt4Project *qt4Project = static_cast<Qt4Project *>(project()); + + QProcess qmake; + qmake.setEnvironment(qt4Project->environment(m_buildConfiguration).toStringList()); + qmake.setWorkingDirectory(destDir); + QStringList configarguments; + QStringList makeArguments; + + // Find qmake step... + QMakeStep *qmakeStep = qt4Project->qmakeStep(); + // Find out which configuration is used in this build configuration + // and what kind of CONFIG we need to pass to qmake for that + if (qmakeStep->value(m_buildConfiguration, "buildConfiguration").isValid()) { + QtVersion::QmakeBuildConfig defaultBuildConfiguration = qt4Project->qtVersion(m_buildConfiguration)->defaultBuildConfig(); + QtVersion::QmakeBuildConfig projectBuildConfiguration = QtVersion::QmakeBuildConfig(qmakeStep->value(m_buildConfiguration, "buildConfiguration").toInt()); + if ((defaultBuildConfiguration & QtVersion::BuildAll) && !(projectBuildConfiguration & QtVersion::BuildAll)) + configarguments << "CONFIG-=debug_and_release"; + if (!(defaultBuildConfiguration & QtVersion::BuildAll) && (projectBuildConfiguration & QtVersion::BuildAll)) + configarguments << "CONFIG+=debug_and_release"; + if ((defaultBuildConfiguration & QtVersion::DebugBuild) && !(projectBuildConfiguration & QtVersion::DebugBuild)) + configarguments << "CONFIG+=release"; + if (!(defaultBuildConfiguration & QtVersion::DebugBuild) && (projectBuildConfiguration & QtVersion::DebugBuild)) + configarguments << "CONFIG+=debug"; + if (projectBuildConfiguration & QtVersion::BuildAll) + makeArguments << (projectBuildConfiguration & QtVersion::DebugBuild ? "debug" : "release"); + + } else { + // Old style with CONFIG+=debug_and_release + qDebug() << "OLD STYLE CONF"; + configarguments << "CONFIG+=debug_and_release"; + const MakeStep *ms = qt4Project->makeStep(); + QStringList makeargs = ms->value(m_buildConfiguration, "makeargs").toStringList(); + if (makeargs.contains("debug")) { + makeArguments << "debug"; + } else if(makeargs.contains("release")) { + makeArguments << "release"; + } + } + + QString mkspec = qt4Project->qtVersion(m_buildConfiguration)->mkspec(); + qmake.start(m_qmake, QStringList()<<"-spec"<<mkspec<<configarguments<<"gdbmacros.pro"); + qmake.waitForFinished(); + qDebug()<<qmake.readAllStandardError()<<qmake.readAllStandardOutput(); + + qmake.start(qt4Project->qtVersion(m_buildConfiguration)->makeCommand(), makeArguments); + qmake.waitForFinished(); + qDebug()<<qmake.readAllStandardError()<<qmake.readAllStandardOutput(); + + fi.reportResult(true); +} + +QString GdbMacrosBuildStep::name() +{ + return Constants::GDBMACROSBUILDSTEP; +} + +QString GdbMacrosBuildStep::displayName() +{ + return "Gdb Macros Build"; +} + +ProjectExplorer::BuildStepConfigWidget *GdbMacrosBuildStep::createConfigWidget() +{ + return new GdbMacrosBuildStepConfigWidget; +} + +bool GdbMacrosBuildStep::immutable() const +{ + return false; +} + +bool GdbMacrosBuildStepFactory::canCreate(const QString &name) const +{ + return name == Constants::GDBMACROSBUILDSTEP; +} + +ProjectExplorer::BuildStep *GdbMacrosBuildStepFactory::create(ProjectExplorer::Project *pro, const QString &name) const +{ + Q_ASSERT(name == Constants::GDBMACROSBUILDSTEP); + Qt4Project *qt4project = qobject_cast<Qt4Project *>(pro); + Q_ASSERT(qt4project); + return new GdbMacrosBuildStep(qt4project); +} + +QStringList GdbMacrosBuildStepFactory::canCreateForProject(ProjectExplorer::Project *pro) const +{ + QStringList results; + if (qobject_cast<QObject *>(pro)) + results << Constants::GDBMACROSBUILDSTEP; + return results; +} + +QString GdbMacrosBuildStepFactory::displayNameForName(const QString &name) const +{ + if (name == Constants::GDBMACROSBUILDSTEP) + return "Gdb Macros Build"; + else + return QString::null; +} + +QString GdbMacrosBuildStepConfigWidget::displayName() const +{ + return "Gdb Macros Build"; +} + +void GdbMacrosBuildStepConfigWidget::init(const QString & /*buildConfiguration*/) +{ + // TODO +} diff --git a/src/plugins/qt4projectmanager/gdbmacrosbuildstep.h b/src/plugins/qt4projectmanager/gdbmacrosbuildstep.h new file mode 100644 index 00000000000..d08f0c395b8 --- /dev/null +++ b/src/plugins/qt4projectmanager/gdbmacrosbuildstep.h @@ -0,0 +1,82 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GDBMACROSBUILDSTEP_H +#define GDBMACROSBUILDSTEP_H + +#include <projectexplorer/buildstep.h> + +namespace Qt4ProjectManager { +class Qt4Project; +namespace Internal { +class GdbMacrosBuildStepConfigWidget; + +class GdbMacrosBuildStep : public ProjectExplorer::BuildStep +{ + Q_OBJECT +public: + GdbMacrosBuildStep(Qt4Project * project); + virtual ~GdbMacrosBuildStep(); + virtual bool init(const QString &buildConfiguration); + virtual void run(QFutureInterface<bool> &); + virtual QString name(); + virtual QString displayName(); + virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); + virtual bool immutable() const; +private: + Qt4Project *m_project; + GdbMacrosBuildStepConfigWidget* m_configWidget; + QString m_buildDirectory; + QString m_qmake; + QString m_buildConfiguration; +}; + +class GdbMacrosBuildStepFactory : public ProjectExplorer::IBuildStepFactory +{ + Q_OBJECT +public: + virtual bool canCreate(const QString &name) const; + virtual ProjectExplorer::BuildStep *create(ProjectExplorer::Project *pro, const QString &name) const; + virtual QStringList canCreateForProject(ProjectExplorer::Project *pro) const; + virtual QString displayNameForName(const QString &name) const; + +}; + +class GdbMacrosBuildStepConfigWidget : public ProjectExplorer::BuildStepConfigWidget +{ + virtual QString displayName() const; + virtual void init(const QString &buildConfiguration); +}; +} +} + +#endif // GDBMACROSBUILDSTEP_H diff --git a/src/plugins/qt4projectmanager/headerpath.h b/src/plugins/qt4projectmanager/headerpath.h new file mode 100644 index 00000000000..e85f89f1573 --- /dev/null +++ b/src/plugins/qt4projectmanager/headerpath.h @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef HEADERPATH_H +#define HEADERPATH_H + +#include <QString> + +namespace Qt4ProjectManager { +namespace Internal { + +class HeaderPath +{ +public: + enum Kind { + GlobalHeaderPath, + UserHeaderPath, + FrameworkHeaderPath + }; + + HeaderPath() + : _kind(GlobalHeaderPath) + { } + + HeaderPath(const QString &path, Kind kind) + : _path(path), _kind(kind) + { } + + QString path() const { return _path; } + Kind kind() const { return _kind; } + +private: + QString _path; + Kind _kind; +}; + +} // end of namespace Internal +} // end of namespace Qt4ProjectManager + +#endif // HEADERPATH_H diff --git a/src/plugins/qt4projectmanager/images/qt_project.png b/src/plugins/qt4projectmanager/images/qt_project.png Binary files differnew file mode 100644 index 00000000000..25133f5f476 --- /dev/null +++ b/src/plugins/qt4projectmanager/images/qt_project.png diff --git a/src/plugins/qt4projectmanager/images/run_qmake.png b/src/plugins/qt4projectmanager/images/run_qmake.png Binary files differnew file mode 100644 index 00000000000..b5aa4345c3c --- /dev/null +++ b/src/plugins/qt4projectmanager/images/run_qmake.png diff --git a/src/plugins/qt4projectmanager/images/run_qmake_small.png b/src/plugins/qt4projectmanager/images/run_qmake_small.png Binary files differnew file mode 100644 index 00000000000..3d732520b17 --- /dev/null +++ b/src/plugins/qt4projectmanager/images/run_qmake_small.png diff --git a/src/plugins/qt4projectmanager/images/window.png b/src/plugins/qt4projectmanager/images/window.png Binary files differnew file mode 100644 index 00000000000..9493e3b1cea --- /dev/null +++ b/src/plugins/qt4projectmanager/images/window.png diff --git a/src/plugins/qt4projectmanager/makestep.cpp b/src/plugins/qt4projectmanager/makestep.cpp new file mode 100644 index 00000000000..e347a591066 --- /dev/null +++ b/src/plugins/qt4projectmanager/makestep.cpp @@ -0,0 +1,315 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "makestep.h" +#include "qt4project.h" +#include "qt4projectmanagerconstants.h" + +#include <QFileInfo> +#include <QDir> +#include <extensionsystem/ExtensionSystemInterfaces> + +using ProjectExplorer::IBuildParserFactory; +using ProjectExplorer::BuildParserInterface; +using ProjectExplorer::Environment; +using ExtensionSystem::PluginManager; +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; + +namespace { +bool debug = false; +} + +MakeStep::MakeStep(Qt4Project * project) + : AbstractProcessStep(project), + m_project(project), + m_buildParser(0) +{ +} + +MakeStep::~MakeStep() +{ + delete m_buildParser; + m_buildParser = 0; +} + +ProjectExplorer::BuildParserInterface *MakeStep::buildParser(const QtVersion * const version) +{ + QString buildParser; + QtVersion::ToolchainType type = version->toolchainType(); + if( type == QtVersion::MSVC || type == QtVersion::WINCE) + buildParser = Constants::BUILD_PARSER_MSVC; + else + buildParser = Constants::BUILD_PARSER_GCC; + + QList<IBuildParserFactory *> buildParserFactories = + ExtensionSystem::PluginManager::instance()->getObjects<ProjectExplorer::IBuildParserFactory>(); + + foreach (IBuildParserFactory * factory, buildParserFactories) + if (factory->canCreate(buildParser)) + return factory->create(buildParser); + return 0; +} + +bool MakeStep::init(const QString &name) +{ + m_buildConfiguration = name; + Environment environment = project()->environment(name); + setEnvironment(name, environment); + + QString workingDirectory; + if (project()->value(name, "useShadowBuild").toBool()) + workingDirectory = project()->value(name, "buildDirectory").toString(); + if(workingDirectory.isEmpty()) + workingDirectory = QFileInfo(project()->file()->fileName()).absolutePath(); + setWorkingDirectory(name, workingDirectory); + + //NBS only dependency on Qt4Project, we probably simply need a MakeProject from which Qt4Project derives + QString makeCmd = qobject_cast<Qt4Project *>(project())->qtVersion(name)->makeCommand(); + if(!value(name, "makeCmd").toString().isEmpty()) + makeCmd = value(name, "makeCmd").toString(); + if (!QFileInfo(makeCmd).isAbsolute()) { + // Try to detect command in environment + QString tmp = environment.searchInPath(makeCmd); + if(tmp == QString::null) { + emit addToOutputWindow(tr("<font color=\"#ff0000\">Could not find make command: %1 "\ + "in the build environment</font>").arg(makeCmd)); + return false; + } + makeCmd = tmp; + } + setCommand(name, makeCmd); + + bool skipMakeClean = false; + QStringList args; + if (value("clean").isValid() && value("clean").toBool()) { + args = QStringList() << "clean"; + if (!QDir(workingDirectory).exists(QLatin1String("Makefile"))) { + skipMakeClean = true; + } + } else { + args = value(name, "makeargs").toStringList(); + } + + // -w option enables "Enter"/"Leaving directory" messages, which we need for detecting the + // absolute file path + // FIXME doing this without the user having a way to override this is rather bad + // so we only do it for unix and if the user didn't override the make command + // but for now this is the least invasive change + QtVersion::ToolchainType t = qobject_cast<Qt4Project *>(project())->qtVersion(name)->toolchainType(); + if (t != QtVersion::MSVC && t != QtVersion::WINCE) { + if (value(name, "makeCmd").toString().isEmpty()) + args << "-w"; + } + + setEnabled(name, !skipMakeClean); + setArguments(name, args); + + m_openDirectories.clear(); + addDirectory(workingDirectory); + + delete m_buildParser; + m_buildParser = 0; + + m_buildParser = buildParser(qobject_cast<Qt4Project *>(project())->qtVersion(name)); + if (m_buildParser) { + connect(m_buildParser, SIGNAL(addToOutputWindow(const QString &)), + this, SIGNAL(addToOutputWindow(const QString &)), + Qt::DirectConnection); + connect(m_buildParser, SIGNAL(addToTaskWindow(const QString &, int, int, const QString &)), + this, SLOT(slotAddToTaskWindow(const QString &, int, int, const QString &)), + Qt::DirectConnection); + connect(m_buildParser, SIGNAL(enterDirectory(const QString &)), + this, SLOT(addDirectory(const QString &)), + Qt::DirectConnection); + connect(m_buildParser, SIGNAL(leaveDirectory(const QString &)), + this, SLOT(removeDirectory(const QString &)), + Qt::DirectConnection); + } + + return AbstractProcessStep::init(name); +} + +void MakeStep::slotAddToTaskWindow(const QString & fn, int type, int linenumber, const QString & description) +{ + QString filePath = fn; + if (!filePath.isEmpty() && !QDir::isAbsolutePath(filePath)) { + // We have no save way to decide which file in which subfolder + // is meant. Therefore we apply following heuristics: + // 1. Search for unique file in directories currently indicated as open by GNU make + // (Enter directory xxx, Leave directory xxx...) + current directory + // 3. Check if file is unique in whole project + // 4. Otherwise give up + + filePath = filePath.trimmed(); + + QList<QFileInfo> possibleFiles; + foreach (const QString &dir, m_openDirectories) { + QFileInfo candidate(dir + QLatin1Char('/') + filePath); + if (debug) + qDebug() << "Checking path " << candidate.filePath(); + if (candidate.exists() + && !possibleFiles.contains(candidate)) { + if (debug) + qDebug() << candidate.filePath() << "exists!"; + possibleFiles << candidate; + } + } + if (possibleFiles.count() == 0) { + if (debug) + qDebug() << "No success. Trying all files in project ..."; + QString fileName = QFileInfo(filePath).fileName(); + foreach (const QString &file, m_project->files(ProjectExplorer::Project::AllFiles)) { + QFileInfo candidate(file); + if (candidate.fileName() == fileName) { + if (debug) + qDebug() << "Found " << file; + possibleFiles << candidate; + } + } + } + if (possibleFiles.count() == 1) + filePath = possibleFiles.first().filePath(); + else + qWarning() << "Could not find absolute location of file " << filePath; + } + emit addToTaskWindow(filePath, type, linenumber, description); +} + +void MakeStep::addDirectory(const QString &dir) +{ + if (!m_openDirectories.contains(dir)) + m_openDirectories.insert(dir); +} + +void MakeStep::removeDirectory(const QString &dir) +{ + if (m_openDirectories.contains(dir)) + m_openDirectories.remove(dir); +} + +void MakeStep::run(QFutureInterface<bool> & fi) +{ + if (qobject_cast<Qt4Project *>(project())->rootProjectNode()->projectType() == ScriptTemplate) { + fi.reportResult(true); + return; + } + + if (!enabled(m_buildConfiguration)) { + emit addToOutputWindow(tr("<font color=\"#0000ff\"><b>No Makefile found, assuming project is clean.</b></font>")); + fi.reportResult(true); + return; + } + + AbstractProcessStep::run(fi); +} + +void MakeStep::stdOut(const QString &line) +{ + if (m_buildParser) + m_buildParser->stdOutput(line); + AbstractProcessStep::stdOut(line); +} + +void MakeStep::stdError(const QString &line) +{ + if (m_buildParser) + m_buildParser->stdError(line); + AbstractProcessStep::stdError(line); +} + +QString MakeStep::name() +{ + return Constants::MAKESTEP; +} + +QString MakeStep::displayName() +{ + return "Make"; +} + +bool MakeStep::immutable() const +{ + return true; +} + +ProjectExplorer::BuildStepConfigWidget *MakeStep::createConfigWidget() +{ + return new MakeStepConfigWidget(this); +} + +MakeStepConfigWidget::MakeStepConfigWidget(MakeStep *makeStep) + : BuildStepConfigWidget(), m_makeStep(makeStep) +{ + m_ui.setupUi(this); + connect(m_ui.makeLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(makeLineEditTextEdited())); + connect(m_ui.makeArgumentsLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(makeArgumentsLineEditTextEdited())); +} + +QString MakeStepConfigWidget::displayName() const +{ + return m_makeStep->displayName(); +} + +void MakeStepConfigWidget::init(const QString &buildConfiguration) +{ + m_buildConfiguration = buildConfiguration; + bool showPage0 = buildConfiguration.isNull(); + m_ui.stackedWidget->setCurrentIndex(showPage0? 0 : 1); + + if(!showPage0) { + Qt4Project *pro = qobject_cast<Qt4Project *>(m_makeStep->project()); + m_ui.makeLabel->setText(tr("Override %1:").arg(pro->qtVersion(buildConfiguration)->makeCommand())); + + const QString &makeCmd = m_makeStep->value(buildConfiguration, "makeCmd").toString(); + m_ui.makeLineEdit->setText(makeCmd); + + const QStringList &makeArguments = + m_makeStep->value(buildConfiguration, "makeargs").toStringList(); + m_ui.makeArgumentsLineEdit->setText(ProjectExplorer::Environment::joinArgumentList(makeArguments)); + } + +} + +void MakeStepConfigWidget::makeLineEditTextEdited() +{ + Q_ASSERT(!m_buildConfiguration.isNull()); + m_makeStep->setValue(m_buildConfiguration, "makeCmd", m_ui.makeLineEdit->text()); +} + +void MakeStepConfigWidget::makeArgumentsLineEditTextEdited() +{ + Q_ASSERT(!m_buildConfiguration.isNull()); + m_makeStep->setValue(m_buildConfiguration, "makeargs", ProjectExplorer::Environment::parseCombinedArgString(m_ui.makeArgumentsLineEdit->text())); +} diff --git a/src/plugins/qt4projectmanager/makestep.h b/src/plugins/qt4projectmanager/makestep.h new file mode 100644 index 00000000000..966e0f2a4b2 --- /dev/null +++ b/src/plugins/qt4projectmanager/makestep.h @@ -0,0 +1,95 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef MAKESTEP_H +#define MAKESTEP_H + +#include <projectexplorer/ProjectExplorerInterfaces> +#include <QDebug> + +#include "qtversionmanager.h" + +#include "ui_makestep.h" + +namespace Qt4ProjectManager { + +class Qt4Project; + + +// NBS move this class to an own plugin? So that there can be a make project at a future time +class MakeStep : public ProjectExplorer::AbstractProcessStep +{ + Q_OBJECT +public: + MakeStep(Qt4Project * project); + ~MakeStep(); + virtual bool init(const QString & name); + virtual void run(QFutureInterface<bool> &); + virtual QString name(); + virtual QString displayName(); + virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); + virtual bool immutable() const; +protected: + virtual void stdOut(const QString &line); + virtual void stdError(const QString &line); +private slots: + void slotAddToTaskWindow(const QString & fn, int type, int linenumber, const QString & description); + void addDirectory(const QString &dir); + void removeDirectory(const QString &dir); +private: + ProjectExplorer::BuildParserInterface *buildParser(const Internal::QtVersion * const version); + Qt4Project *m_project; + ProjectExplorer::BuildParserInterface *m_buildParser; + bool m_skipMakeClean; + QString m_buildConfiguration; + QSet<QString> m_openDirectories; +}; + +class MakeStepConfigWidget : public ProjectExplorer::BuildStepConfigWidget +{ + Q_OBJECT +public: + MakeStepConfigWidget(MakeStep *makeStep); + QString displayName() const; + void init(const QString &buildConfiguration); +private slots: + void makeLineEditTextEdited(); + void makeArgumentsLineEditTextEdited(); +private: + QString m_buildConfiguration; + Ui::MakeStep m_ui; + MakeStep *m_makeStep; +}; + +} // Qt4ProjectManager + +#endif // MAKESTEP_H diff --git a/src/plugins/qt4projectmanager/makestep.ui b/src/plugins/qt4projectmanager/makestep.ui new file mode 100644 index 00000000000..7ce4a4034b0 --- /dev/null +++ b/src/plugins/qt4projectmanager/makestep.ui @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MakeStep</class> + <widget class="QWidget" name="MakeStep"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>428</width> + <height>384</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QStackedWidget" name="stackedWidget"> + <property name="currentIndex"> + <number>1</number> + </property> + <widget class="QWidget" name="page_1"> + <layout class="QFormLayout" name="formLayout_2"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + </layout> + </widget> + <widget class="QWidget" name="page_2"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="makeLabel"> + <property name="text"> + <string>Override %1:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="makeLineEdit"/> + </item> + <item> + <widget class="QLabel" name="makeArgumentsLabel"> + <property name="text"> + <string>Make arguments:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="makeArgumentsLineEdit"/> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>255</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>makeLineEdit</tabstop> + <tabstop>makeArgumentsLineEdit</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/qt4projectmanager/msvcenvironment.cpp b/src/plugins/qt4projectmanager/msvcenvironment.cpp new file mode 100644 index 00000000000..9405ed7decb --- /dev/null +++ b/src/plugins/qt4projectmanager/msvcenvironment.cpp @@ -0,0 +1,156 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "msvcenvironment.h" + +#include <QSettings> +#include <QFile> +#include <QDebug> +#include <QStringList> +#include <QRegExp> +#include <QTemporaryFile> + +using namespace Qt4ProjectManager::Internal; +using ProjectExplorer::Environment; + +MSVCEnvironment::MSVCEnvironment(const QString &name, const QString &path) + : m_name(name), m_path(path), m_valuesSet(false) +{ + +} + +QString MSVCEnvironment::name() const +{ + return m_name; +} + +QString MSVCEnvironment::path() const +{ + return m_path; +} + +QList<MSVCEnvironment> MSVCEnvironment::availableVersions() +{ + QList<MSVCEnvironment> result; + + QSettings registry("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", + QSettings::NativeFormat); + QStringList versions = registry.allKeys(); + foreach(const QString &version, versions) { + QString dir = registry.value(version).toString(); + result << MSVCEnvironment(version, dir); + } + return result; +} + +QString MSVCEnvironment::description() const +{ + QString desc; + desc = "Microsoft Visual Studio " + m_name + " from " + m_path; + return desc; +} + +// This method is ugly as hell, but that is to be expected +// It crates a temporary batch file which first runs vsvars32.bat from MSVC +// and then runs *set* to get the environment +// We cache that information. +Environment MSVCEnvironment::addToEnvironment(const Environment &env) const +{ + Environment e(env); + if (!m_valuesSet) { + QString desc; + QString varsbat = m_path + "Common7\\Tools\\vsvars32.bat"; + if (QFileInfo(varsbat).exists()) { + QTemporaryFile tf(QDir::tempPath() + "\\XXXXXX.bat"); + if(!tf.open()) + return e; + QString filename = tf.fileName(); + tf.write("call \"" + varsbat.toLocal8Bit()+"\"\r\n"); + tf.write(("set > \"" + QDir::tempPath() + "\\qtcreator-msvc-environment.txt\"\r\n").toLocal8Bit()); + tf.flush(); + tf.waitForBytesWritten(30000); + + QProcess run; + QString cmdPath = e.searchInPath("cmd"); + run.start(cmdPath, QStringList()<<"/c"<<filename); + run.waitForFinished(); + tf.close(); + + QFile vars(QDir::tempPath() + "\\qtcreator-msvc-environment.txt"); + if(vars.exists() && vars.open(QIODevice::ReadOnly)) { + while(!vars.atEnd()) { + QByteArray line = vars.readLine(); + QString line2 = QString::fromLocal8Bit(line); + line2 = line2.trimmed(); + QRegExp regexp("(\\w*)=(.*)"); + if(regexp.exactMatch(line2)) { + QString variable = regexp.cap(1); + QString value = regexp.cap(2); + value.replace('%' + variable + '%', e.value(variable)); + m_values.append(QPair<QString, QString>(variable, value)); + + } + } + vars.close(); + vars.remove(); + } + } + m_valuesSet = true; + } + + QList< QPair<QString, QString> >::const_iterator it, end; + end = m_values.constEnd(); + for (it = m_values.constBegin(); it != end; ++it) { + e.set((*it).first, (*it).second); + qDebug()<<"variable:"<<(*it).first<<"value:"<<(*it).second; + } + + +// QFile varsbat(m_path + "Common7\\Tools\\vsvars32.bat"); +// if(varsbat.exists() && varsbat.open(QIODevice::ReadOnly)) { +// while(!varsbat.atEnd()) { +// QByteArray line = varsbat.readLine(); +// QString line2 = QString::fromLocal8Bit(line); +// line2 = line2.trimmed(); +// QRegExp regexp("\\s*@?(S|s)(E|e)(T|t)\\s*(\\w*)=(.*)"); +// if(regexp.exactMatch(line2)) { +// QString variable = regexp.cap(4); +// QString value = regexp.cap(5); +// value.replace('%' + variable + '%', e.value(variable)); +// e.set(variable, value); +// } +// } +// varsbat.close(); +// } + + return e; +} diff --git a/src/plugins/qt4projectmanager/msvcenvironment.h b/src/plugins/qt4projectmanager/msvcenvironment.h new file mode 100644 index 00000000000..eda0fcb2be3 --- /dev/null +++ b/src/plugins/qt4projectmanager/msvcenvironment.h @@ -0,0 +1,64 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef MSVCENVIRONMENT_H +#define MSVCENVIRONMENT_H + +#include <QString> +#include <QList> + +#include <projectexplorer/ProjectExplorerInterfaces> + +namespace Qt4ProjectManager { +namespace Internal { + +class MSVCEnvironment +{ +public: + QString name() const; + QString path() const; + ProjectExplorer::Environment addToEnvironment(const ProjectExplorer::Environment &env) const; + QString description() const; + + static QList<MSVCEnvironment> availableVersions(); +private: + + MSVCEnvironment(const QString &name, const QString &path); + QString m_name; + QString m_path; + mutable QList<QPair<QString, QString> > m_values; + mutable bool m_valuesSet; +}; + +} +} +#endif diff --git a/src/plugins/qt4projectmanager/msvcparser.cpp b/src/plugins/qt4projectmanager/msvcparser.cpp new file mode 100644 index 00000000000..055e7bf1d1a --- /dev/null +++ b/src/plugins/qt4projectmanager/msvcparser.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtCore/QStringList> + +#include "msvcparser.h" +#include "qt4projectmanagerconstants.h" + +using namespace Qt4ProjectManager; + +MsvcParser::MsvcParser() +{ + m_compileRegExp.setPattern("^([^\\(]+)\\((\\d+)\\)+\\s:[^:\\d]+(\\d+):(.*)$"); + m_compileRegExp.setMinimal(true); + m_linkRegExp.setPattern("^([^\\(]+)\\s:[^:\\d]+(\\d+):(.*)$"); + m_linkRegExp.setMinimal(true); +} + +QString MsvcParser::name() const +{ + return QLatin1String(Qt4ProjectManager::Constants::BUILD_PARSER_MSVC); +} + +void MsvcParser::stdError(const QString & line) +{ + Q_UNUSED(line); + //do nothing +} + +void MsvcParser::stdOutput(const QString & line) +{ + QString lne = line.trimmed(); + if (m_compileRegExp.indexIn(lne) > -1 && m_compileRegExp.numCaptures() == 4) { + emit addToTaskWindow( + m_compileRegExp.cap(1), //filename + toType(m_compileRegExp.cap(3).toInt()), // PatternType + m_compileRegExp.cap(2).toInt(), //linenumber + m_compileRegExp.cap(4) //description + ); + + } else if (m_linkRegExp.indexIn(lne) > -1 && m_linkRegExp.numCaptures() == 3) { + QString fileName = m_linkRegExp.cap(1); + if (fileName.contains(QLatin1String("LINK"), Qt::CaseSensitive)) + fileName.clear(); + + emit addToTaskWindow( + fileName, //filename + toType(m_linkRegExp.cap(2).toInt()), // pattern type + -1, // line number + m_linkRegExp.cap(3) // description + ); + } +} + +ProjectExplorer::BuildParserInterface::PatternType MsvcParser::toType(int number) +{ + if (number == 0) + return ProjectExplorer::BuildParserInterface::Unknown; + else if (number > 4000 && number < 5000) + return ProjectExplorer::BuildParserInterface::Warning; + else + return ProjectExplorer::BuildParserInterface::Error; +} diff --git a/src/plugins/qt4projectmanager/msvcparser.h b/src/plugins/qt4projectmanager/msvcparser.h new file mode 100644 index 00000000000..2bb9b043ca1 --- /dev/null +++ b/src/plugins/qt4projectmanager/msvcparser.h @@ -0,0 +1,59 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef MSVCPARSER_H +#define MSVCPARSER_H + +#include <QtCore/QRegExp> +#include <projectexplorer/ProjectExplorerInterfaces> + +namespace Qt4ProjectManager { + +class MsvcParser : public ProjectExplorer::BuildParserInterface +{ + Q_OBJECT + +public: + MsvcParser(); + QString name() const; + + virtual void stdOutput(const QString & line); + virtual void stdError(const QString & line); +private: + ProjectExplorer::BuildParserInterface::PatternType toType(int number); + QRegExp m_compileRegExp; + QRegExp m_linkRegExp; +}; + +} //namespace ProjectExplorer + +#endif diff --git a/src/plugins/qt4projectmanager/proeditorcontainer.ui b/src/plugins/qt4projectmanager/proeditorcontainer.ui new file mode 100644 index 00000000000..ba9c371e6d6 --- /dev/null +++ b/src/plugins/qt4projectmanager/proeditorcontainer.ui @@ -0,0 +1,72 @@ +<ui version="4.0" > + <class>ProEditorContainer</class> + <widget class="QWidget" name="ProEditorContainer" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>503</width> + <height>372</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QHBoxLayout" > + <item> + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>6</number> + </property> + <property name="leftMargin" > + <number>0</number> + </property> + <property name="topMargin" > + <number>0</number> + </property> + <property name="rightMargin" > + <number>0</number> + </property> + <property name="bottomMargin" > + <number>0</number> + </property> + <item> + <widget class="QTreeView" name="m_scopeList" > + <property name="uniformRowHeights" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="Qt4ProjectManager::Internal::ProEditor" name="m_variableEdit" /> + </item> + <item> + <widget class="QCheckBox" name="m_advancedCheckBox" > + <property name="text" > + <string>Advanced Mode</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="Qt4ProjectManager::Internal::ValueEditor" native="1" name="m_valueEdit" /> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Qt4ProjectManager::Internal::ProEditor</class> + <extends>QFrame</extends> + <header>proparser/proeditor.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>Qt4ProjectManager::Internal::ValueEditor</class> + <extends>QWidget</extends> + <header>valueeditor.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/qt4projectmanager/profilecache.cpp b/src/plugins/qt4projectmanager/profilecache.cpp new file mode 100644 index 00000000000..022a037f13c --- /dev/null +++ b/src/plugins/qt4projectmanager/profilecache.cpp @@ -0,0 +1,362 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qt4project.h" +#include "qt4nodes.h" +#include "qt4projectmanager.h" +#include "profilecache.h" +#include "proparser/prowriter.h" +#include "proitems.h" +#include "qt4projectmanagerconstants.h" + +#include <coreplugin/filemanager.h> +#include <utils/reloadpromptutils.h> + +#include <QtCore/QFileInfo> +#include <QtCore/QDebug> +#include <QtGui/QMainWindow> +#include <QtCore/QDir> + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; + +namespace { +bool debug = false; +} + +namespace Qt4ProjectManager { +namespace Internal { + +class ManagedProjectFile + : public Core::IFile +{ + Q_OBJECT + +public: + ManagedProjectFile(ProFileCache *parent, ProFile *profile); + virtual ~ManagedProjectFile(); + + ProFile *profile(); + void setProfile(ProFile *profile); + bool isOutOfSync() { return m_outOfSync; } + void setOutOfSync(bool state) { m_outOfSync = state; } + + // Core::IFile + bool save(const QString &fileName = QString()); + QString fileName() const; + + QString defaultPath() const; + QString suggestedFileName() const; + + virtual QString mimeType() const; + + bool isModified() const; + bool isReadOnly() const; + bool isSaveAsAllowed() const; + + void modified(ReloadBehavior *behavior); + +signals: + void changed(); + +private: + const QString m_mimeType; + ProFile *m_profile; + ProFileCache *m_cache; + bool m_outOfSync; +}; + +} // namespace Internal +} // namespace Qt4ProFileNodemanager + +ManagedProjectFile::ManagedProjectFile(ProFileCache *parent, ProFile *profile) : + Core::IFile(parent), + m_mimeType(QLatin1String(Qt4ProjectManager::Constants::PROFILE_MIMETYPE)), + m_profile(profile), + m_cache(parent), + m_outOfSync(false) +{ + +} + +ManagedProjectFile::~ManagedProjectFile() +{ + delete m_profile; +} + +ProFile *ManagedProjectFile::profile() +{ + return m_profile; +} + +void ManagedProjectFile::setProfile(ProFile *profile) +{ + m_outOfSync = false; + if (profile == m_profile) + return; + delete m_profile; + m_profile = profile; +} + +bool ManagedProjectFile::save(const QString &) +{ + if (!m_profile) + return false; + + ProWriter pw; + bool ok = pw.write(m_profile, m_profile->fileName()); + m_profile->setModified(false); + m_cache->notifyModifiedChanged(m_profile); + + return ok; +} + +QString ManagedProjectFile::fileName() const +{ + return QDir::cleanPath(m_profile->fileName()); +} + +QString ManagedProjectFile::defaultPath() const +{ + QFileInfo fi(fileName()); + return fi.absolutePath(); +} + +QString ManagedProjectFile::suggestedFileName() const +{ + QFileInfo fi(fileName()); + return fi.fileName(); +} + +bool ManagedProjectFile::isModified() const +{ + return m_profile->isModified(); +} + +bool ManagedProjectFile::isReadOnly() const +{ + QFileInfo fi(fileName()); + return !fi.isWritable(); +} + +bool ManagedProjectFile::isSaveAsAllowed() const +{ + return false; +} + +void ManagedProjectFile::modified(ReloadBehavior *behavior) +{ + + switch (*behavior) { + case Core::IFile::ReloadNone: + case Core::IFile::ReloadPermissions: + return; + case Core::IFile::ReloadAll: + m_cache->notifyChanged(QSet<ProFile *>() << m_profile, true); + return; + case Core::IFile::AskForReload: + break; + } + + const QString prompt = tr("The project file %1 has changed outside Qt Creator. Do you want to reload it?").arg(m_profile->fileName()); + switch (const Core::Utils::ReloadPromptAnswer answer = Core::Utils::reloadPrompt(tr("File Changed"), prompt, m_cache->manager()->core()->mainWindow())) { + case Core::Utils::ReloadAll: + case Core::Utils::ReloadCurrent: + m_cache->notifyChanged(QSet<ProFile *>() << m_profile, true); + if (answer == Core::Utils::ReloadAll) + *behavior = Core::IFile::ReloadAll; + break; + case Core::Utils::ReloadSkipCurrent: + break; + case Core::Utils::ReloadNone: + *behavior = Core::IFile::ReloadNone; + break; + } +} + +QString ManagedProjectFile::mimeType() const +{ + return m_mimeType; +} + + +#include "profilecache.moc" + +ProFileCache::ProFileCache(Qt4Manager *manager) + : QObject(manager) +{ + m_manager = manager; +} + +ProFileCache::~ProFileCache() +{ + qDeleteAll(m_profiles.values()); + m_profiles.clear(); + m_projects.clear(); +} + +void ProFileCache::notifyModifiedChanged(ProFile *profile) +{ + QList<Qt4ProFileNode *> pros = m_projects.values(profile->fileName()); + for (int i=0; i<pros.count(); ++i) { + Qt4ProFileNode *pro = pros.at(i); + pro->update(); + } +} + +void ProFileCache::notifyChanged(const QSet<ProFile *> &profiles, bool external) +{ + QList<Qt4ProFileNode *> notifyProjects; + + foreach (ProFile *profile, profiles) { + QList<Qt4ProFileNode *> pros = m_projects.values(profile->fileName()); + + if (external) { + ManagedProjectFile *file = m_profiles.value(profile->fileName()); + if (file) + file->setOutOfSync(true); + } + + QList<Qt4ProFileNode *>::const_iterator i = pros.constBegin(); + for (; i != pros.constEnd(); ++i) { + if (!notifyProjects.contains(*i)) + notifyProjects << *i; + } + } + + QList<Qt4ProFileNode *>::const_iterator i = notifyProjects.constBegin(); + while (i != notifyProjects.constEnd()) { + (*i)->update(); + ++i; + } +} + +void ProFileCache::updateDependencies(const QSet<ProFile *> &files, Qt4ProFileNode *project) +{ + // just remove and add files that have changed + const QSet<QString> &oldFiles = m_projects.keys(project).toSet(); + QSet<QString> newFiles; + + QList<Core::IFile *> addedFiles; + foreach (ProFile *file, files) { + newFiles << file->fileName(); + if (!m_profiles.contains(file->fileName())) { + ManagedProjectFile *profile = new ManagedProjectFile(this, file); + m_profiles.insert(file->fileName(), profile); + if (debug) + qDebug() << "ProFileCache - inserting file " << file->fileName(); + addedFiles << profile; + } else { + ManagedProjectFile *profile = m_profiles.value(file->fileName()); + profile->setProfile(file); + } + } + m_manager->core()->fileManager()->addFiles(addedFiles); + + if (oldFiles.isEmpty()) { + connect(project, SIGNAL(destroyed(QObject *)), + this, SLOT(removeProject(QObject *))); + } + + foreach (const QString &profile, (oldFiles - newFiles).toList()) { + removeFile(profile, project); + } + + foreach (const QString &profile, (newFiles - oldFiles).toList()) { + m_projects.insertMulti(profile, project); + } +} + +QStringList ProFileCache::dependencies(Qt4ProFileNode *project) const +{ + return m_projects.keys(project); +} + +ProFile *ProFileCache::proFile(const QString &file) const +{ + QSet<ProFile *> profiles = proFiles(QStringList(file)); + if (profiles.isEmpty()) + return 0; + return profiles.toList().first(); +} + +QSet<ProFile *> ProFileCache::proFiles(const QStringList &files) const +{ + QSet<ProFile *> results; + foreach (const QString &file, files) { + ManagedProjectFile *managedFile = m_profiles.value(file, 0); + if (managedFile && !managedFile->isOutOfSync()) { + results << managedFile->profile(); + } + } + return results; +} + +QSet<ProFile *> ProFileCache::proFiles(Qt4ProFileNode *project) const +{ + return proFiles(m_projects.keys(project)); +} + +Core::IFile *ProFileCache::fileInterface(const QString &file) +{ + return m_profiles.value(file); +} + +void ProFileCache::removeFile(const QString &file, Qt4ProFileNode *proj) +{ + QList<Qt4ProFileNode*> projects = m_projects.values(file); + m_projects.remove(file); + projects.removeAll(proj); + if (!projects.isEmpty()) { + foreach (Qt4ProFileNode *p, projects) { + m_projects.insert(file, p); + } + } else { + ManagedProjectFile *mp = m_profiles.value(file, 0); + if (debug) + qDebug() << "ProFileCache - removing file " << file; + m_manager->core()->fileManager()->removeFile(mp); + m_profiles.remove(file); + delete mp; + } +} + +void ProFileCache::removeProject(QObject *obj) +{ + // Cannot use qobject_cast here because + // it is triggered by destroyed signal + Qt4ProFileNode *proj = static_cast<Qt4ProFileNode*>(obj); + QStringList files = m_projects.keys(proj); + foreach (const QString &file, files) { + removeFile(file, proj); + } +} diff --git a/src/plugins/qt4projectmanager/profilecache.h b/src/plugins/qt4projectmanager/profilecache.h new file mode 100644 index 00000000000..59e9a76eb5f --- /dev/null +++ b/src/plugins/qt4projectmanager/profilecache.h @@ -0,0 +1,97 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROFILECACHE_H +#define PROFILECACHE_H + +#include <QtCore/QObject> +#include <QtCore/QSet> +#include <QtCore/QStringList> +#include <QtCore/QMap> + +QT_BEGIN_NAMESPACE +class ProFile; +QT_END_NAMESPACE + +namespace Core { +class IFile; +} + +namespace Qt4ProjectManager { + +//class Qt4Project; +class Qt4Manager; + +namespace Internal { + +class Qt4ProFileNode; +class ManagedProjectFile; + +class ProFileCache : public QObject +{ + Q_OBJECT + +public: + ProFileCache(Qt4Manager *manager); + ~ProFileCache(); + + // does not result in a reparse or resolve + void notifyModifiedChanged(ProFile *profile); + + // if external is true we need to reparse, it it's false we only resolve + void notifyChanged(const QSet<ProFile *> &profiles, bool external = false); + + void updateDependencies(const QSet<ProFile *> &files, Qt4ProFileNode *projectNode); + QStringList dependencies(Qt4ProFileNode *projectNode) const; + + ProFile *proFile(const QString &filename) const; + QSet<ProFile *> proFiles(const QStringList &files) const; + QSet<ProFile *> proFiles(Qt4ProFileNode *projectNode) const; + + Core::IFile *fileInterface(const QString &filename); + inline Qt4Manager *manager() const { return m_manager; } + +protected slots: + void removeProject(QObject *obj); + +private: + void removeFile(const QString &profile, Qt4ProFileNode *projectNode); + + Qt4Manager *m_manager; + QMultiMap<QString, Qt4ProFileNode *> m_projects; + QMap<QString, ManagedProjectFile *> m_profiles; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // PROFILECACHE_H diff --git a/src/plugins/qt4projectmanager/profileeditor.cpp b/src/plugins/qt4projectmanager/profileeditor.cpp new file mode 100644 index 00000000000..fc4c6d5542b --- /dev/null +++ b/src/plugins/qt4projectmanager/profileeditor.cpp @@ -0,0 +1,169 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "profileeditor.h" +#include "profilehighlighter.h" +#include "qt4projectmanager.h" +#include "qt4projectmanagerconstants.h" +#include "profilecache.h" +#include "profileeditorfactory.h" +#include "proeditormodel.h" +#include "procommandmanager.h" + +#include <coreplugin/icore.h> +#include <coreplugin/uniqueidmanager.h> +#include <texteditor/fontsettings.h> +#include <texteditor/texteditoractionhandler.h> +#include <texteditor/texteditorconstants.h> +#include <texteditor/texteditorsettings.h> + +#include <QtCore/QFileInfo> +#include <QtGui/QTextEdit> +#include <QtGui/QHeaderView> + +using namespace ExtensionSystem; +using namespace Core; +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; +using namespace ProjectExplorer; + + +ProFileEditorEditable::ProFileEditorEditable(ProFileEditor *editor, Core::ICore *core) + :BaseTextEditorEditable(editor) +{ + m_context << core->uniqueIDManager()-> + uniqueIdentifier(Qt4ProjectManager::Constants::C_PROFILEEDITOR); + m_context << core->uniqueIDManager()-> + uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR); +// m_contexts << core->uniqueIDManager()-> +// uniqueIdentifier(Qt4ProjectManager::Constants::PROJECT_KIND); +} + +TextEditor::BaseTextEditorEditable *ProFileEditor::createEditableInterface() +{ + return new ProFileEditorEditable(this, m_core); +} + +ProFileEditor::ProFileEditor(QWidget *parent, ProFileEditorFactory *factory, TextEditor::TextEditorActionHandler *ah) + : BaseTextEditor(parent), m_factory(factory), m_ah(ah) +{ + Qt4Manager *manager = factory->qt4ProjectManager(); + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + + ProFileDocument *doc = new ProFileDocument(manager); + doc->setMimeType(QLatin1String(Qt4ProjectManager::Constants::PROFILE_MIMETYPE)); + setBaseTextDocument(doc); + + ah->setupActions(this); + + baseTextDocument()->setSyntaxHighlighter(new ProFileHighlighter); +} + +ProFileEditor::~ProFileEditor() +{ +} + +QList<int> ProFileEditorEditable::context() const +{ + return m_context; +} + +Core::IEditor *ProFileEditorEditable::duplicate(QWidget *parent) +{ + ProFileEditor *ret = new ProFileEditor(parent, qobject_cast<ProFileEditor*>(editor())->factory(), + qobject_cast<ProFileEditor*>(editor())->actionHandler()); + ret->duplicateFrom(editor()); + ret->initialize(); + return ret->editableInterface(); +} + +void ProFileEditor::initialize() +{ + TextEditor::TextEditorSettings *settings = TextEditor::TextEditorSettings::instance(); + + connect(settings, SIGNAL(fontSettingsChanged(const TextEditor::FontSettings&)), + this, SLOT(setFontSettings(const TextEditor::FontSettings&))); + + setFontSettings(settings->fontSettings()); +} + +const char *ProFileEditorEditable::kind() const +{ + return Qt4ProjectManager::Constants::PROFILE_EDITOR; +} + +void ProFileEditor::setFontSettings(const TextEditor::FontSettings &fs) +{ + TextEditor::BaseTextEditor::setFontSettings(fs); + ProFileHighlighter *highlighter = qobject_cast<ProFileHighlighter*>(baseTextDocument()->syntaxHighlighter()); + if (!highlighter) + return; + + static QVector<QString> categories; + if (categories.isEmpty()) { + categories << QLatin1String(TextEditor::Constants::C_VARIABLE) + << QLatin1String(TextEditor::Constants::C_FUNCTION) + << QLatin1String(TextEditor::Constants::C_COMMENT); + } + + const QVector<QTextCharFormat> formats = fs.toTextCharFormats(categories); + highlighter->setFormats(formats.constBegin(), formats.constEnd()); + highlighter->rehighlight(); +} + +ProFileDocument::ProFileDocument(Qt4Manager *manager) + : TextEditor::BaseTextDocument(), m_manager(manager) +{ +} + +bool ProFileDocument::save(const QString &name) +{ + if (BaseTextDocument::save(name)) { + ProFile *profile = m_manager->proFileCache()->proFile(name); + if (profile) + m_manager->proFileCache()->notifyChanged(QSet<ProFile*>() << profile, true); + return true; + } + return false; +} + +QString ProFileDocument::defaultPath() const +{ + QFileInfo fi(fileName()); + return fi.absolutePath(); +} + +QString ProFileDocument::suggestedFileName() const +{ + QFileInfo fi(fileName()); + return fi.fileName(); +} diff --git a/src/plugins/qt4projectmanager/profileeditor.h b/src/plugins/qt4projectmanager/profileeditor.h new file mode 100644 index 00000000000..44710bacfbd --- /dev/null +++ b/src/plugins/qt4projectmanager/profileeditor.h @@ -0,0 +1,121 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROFILEEDITOR +#define PROFILEEDITOR + +#include <texteditor/basetextdocument.h> +#include <texteditor/basetexteditor.h> +#include "ui_proeditorcontainer.h" + +namespace TextEditor { +class FontSettings; +class BaseEditorActionHandler; +} + +namespace Core { +class ICore; +class IFile; +} + +namespace Qt4ProjectManager { + +class Qt4Manager; +class Qt4Project; + +namespace Internal { + +class ProFileEditorFactory; +class ProItemInfoManager; +class ProEditorModel; +class ProFileHighlighter; + +class ProFileEditor; + +class ProFileEditorEditable : public TextEditor::BaseTextEditorEditable +{ +public: + ProFileEditorEditable(ProFileEditor *, Core::ICore *core); + QList<int> context() const; + + bool duplicateSupported() const { return true; } + Core::IEditor *duplicate(QWidget *parent); + const char *kind() const; +private: + QList<int> m_context; +}; + +class ProFileEditor : public TextEditor::BaseTextEditor +{ + Q_OBJECT + +public: + ProFileEditor(QWidget *parent, ProFileEditorFactory *factory, + TextEditor::TextEditorActionHandler *ah); + ~ProFileEditor(); + void initialize(); + + bool save(const QString &fileName = QString()); + + + ProFileEditorFactory *factory() { return m_factory; } + TextEditor::TextEditorActionHandler *actionHandler() const { return m_ah; } +protected: + TextEditor::BaseTextEditorEditable *createEditableInterface(); + +public slots: + virtual void setFontSettings(const TextEditor::FontSettings &); + +private: + Core::ICore *m_core; + ProFileEditorFactory *m_factory; + TextEditor::TextEditorActionHandler *m_ah; +}; + +class ProFileDocument : public TextEditor::BaseTextDocument +{ + Q_OBJECT + +public: + ProFileDocument(Qt4Manager *manager); + bool save(const QString &name); + QString defaultPath() const; + QString suggestedFileName() const; + +private: + Qt4Manager *m_manager; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // PROFILEEDITOR diff --git a/src/plugins/qt4projectmanager/profileeditorfactory.cpp b/src/plugins/qt4projectmanager/profileeditorfactory.cpp new file mode 100644 index 00000000000..6866193db87 --- /dev/null +++ b/src/plugins/qt4projectmanager/profileeditorfactory.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "profileeditorfactory.h" +#include "qt4projectmanager.h" +#include "qt4projectmanagerconstants.h" +#include "profileeditor.h" +#include "qt4projectmanagerenums.h" + +#include <coreplugin/icore.h> +#include <coreplugin/fileiconprovider.h> +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/texteditoractionhandler.h> + +#include <QtCore/QFileInfo> +#include <QtGui/QAction> +#include <QtGui/QMenu> + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; + +ProFileEditorFactory::ProFileEditorFactory(Qt4Manager *manager, TextEditor::TextEditorActionHandler *handler) : + m_kind(QLatin1String(Qt4ProjectManager::Constants::PROFILE_EDITOR)), + m_mimeTypes(QStringList() << QLatin1String(Qt4ProjectManager::Constants::PROFILE_MIMETYPE) + << QLatin1String(Qt4ProjectManager::Constants::PROINCLUDEFILE_MIMETYPE)), + m_manager(manager), + m_actionHandler(handler) +{ + Core::FileIconProvider *iconProvider = Core::FileIconProvider::instance(); + iconProvider->registerIconForSuffix(QIcon(":/qt4projectmanager/images/qt_project.png"), + QLatin1String("pro")); + iconProvider->registerIconForSuffix(QIcon(":/qt4projectmanager/images/qt_project.png"), + QLatin1String("pri")); +} + +ProFileEditorFactory::~ProFileEditorFactory() +{ +} + +QString ProFileEditorFactory::kind() const +{ + return m_kind; +} + +Core::IFile *ProFileEditorFactory::open(const QString &fileName) +{ + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + Core::IEditor *iface = core->editorManager()->openEditor(fileName, kind()); + return iface ? iface->file() : 0; +} + +Core::IEditor *ProFileEditorFactory::createEditor(QWidget *parent) +{ + ProFileEditor *rc = new ProFileEditor(parent, this, m_actionHandler); + rc->initialize(); + return rc->editableInterface(); +} + +QStringList ProFileEditorFactory::mimeTypes() const +{ + return m_mimeTypes; +} + +void ProFileEditorFactory::initializeActions() +{ + m_actionHandler->initializeActions(); +} diff --git a/src/plugins/qt4projectmanager/profileeditorfactory.h b/src/plugins/qt4projectmanager/profileeditorfactory.h new file mode 100644 index 00000000000..fb50fa0895f --- /dev/null +++ b/src/plugins/qt4projectmanager/profileeditorfactory.h @@ -0,0 +1,80 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROFILEEDITORFACTORY_H +#define PROFILEEDITORFACTORY_H + +#include <coreplugin/editormanager/ieditorfactory.h> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE +class QAction; +QT_END_NAMESPACE + +namespace TextEditor { +class TextEditorActionHandler; +} + +namespace Qt4ProjectManager { + +class Qt4Manager; + +namespace Internal { + +class ProFileEditorFactory : public Core::IEditorFactory +{ + Q_OBJECT + +public: + ProFileEditorFactory(Qt4Manager *parent, TextEditor::TextEditorActionHandler *handler); + ~ProFileEditorFactory(); + + virtual QStringList mimeTypes() const; + virtual QString kind() const; + Core::IFile *open(const QString &fileName); + Core::IEditor *createEditor(QWidget *parent); + + inline Qt4Manager *qt4ProjectManager() const { return m_manager; } + + void initializeActions(); + +private: + const QString m_kind; + const QStringList m_mimeTypes; + Qt4Manager *m_manager; + TextEditor::TextEditorActionHandler *m_actionHandler; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // PROFILEEDITORFACTORY_H diff --git a/src/plugins/qt4projectmanager/profilehighlighter.cpp b/src/plugins/qt4projectmanager/profilehighlighter.cpp new file mode 100644 index 00000000000..df4dbc45c9d --- /dev/null +++ b/src/plugins/qt4projectmanager/profilehighlighter.cpp @@ -0,0 +1,198 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "profilehighlighter.h" + +#include <QtCore/QRegExp> +#include <QtGui/QColor> +#include <QtGui/QTextDocument> +#include <QtGui/QTextEdit> + +using namespace Qt4ProjectManager::Internal; + +#define MAX_VARIABLES 48 +const char *const variables[MAX_VARIABLES] = { + "CONFIG", + "DEFINES", + "DEF_FILE", + "DEPENDPATH", + "DESTDIR", + "DESTDIR_TARGET", + "DISTFILES", + "DLLDESTDIR", + "FORMS", + "HEADERS", + "INCLUDEPATH", + "LEXSOURCES", + "LIBS", + "MAKEFILE", + "MOC_DIR", + "OBJECTS", + "OBJECTS_DIR", + "OBJMOC", + "POST_TARGETDEPS", + "PRECOMPILED_HEADER", + "PRE_TARGETDEPS", + "QMAKE", + "QMAKESPEC", + "QT", + "RCC_DIR", + "RC_FILE", + "REQUIRES", + "RESOURCES", + "RES_FILE", + "SOURCES", + "SRCMOC", + "SUBDIRS", + "TARGET", + "TARGET_EXT", + "TARGET_x", + "TARGET_x.y.z", + "TEMPLATE", + "TRANSLATIONS", + "UI_DIR", + "UI_HEADERS_DIR", + "UI_SOURCES_DIR", + "VER_MAJ", + "VER_MIN", + "VER_PAT", + "VERSION", + "VPATH", + "YACCSOURCES", + 0 +}; + +#define MAX_FUNCTIONS 22 +const char *const functions[MAX_FUNCTIONS] = { + "basename", + "CONFIG", + "contains", + "count", + "dirname", + "error", + "exists", + "find", + "for", + "include", + "infile", + "isEmpty", + "join", + "member", + "message", + "prompt", + "quote", + "sprintf", + "system", + "unique", + "warning", + 0 +}; + +struct KeywordHelper +{ + inline KeywordHelper(const QString &word) : needle(word) {} + const QString needle; +}; + +static bool operator<(const KeywordHelper &helper, const char *kw) +{ + return helper.needle < QLatin1String(kw); +} + +static bool operator<(const char *kw, const KeywordHelper &helper) +{ + return QLatin1String(kw) < helper.needle; +} + +static bool isVariable(const QString &word) +{ + const char *const *start = &variables[0]; + const char *const *end = &variables[MAX_VARIABLES - 1]; + const char *const *kw = qBinaryFind(start, end, KeywordHelper(word)); + return *kw != 0; +} + +static bool isFunction(const QString &word) +{ + const char *const *start = &functions[0]; + const char *const *end = &functions[MAX_FUNCTIONS - 1]; + const char *const *kw = qBinaryFind(start, end, KeywordHelper(word)); + return *kw != 0; +} + +ProFileHighlighter::ProFileHighlighter(QTextDocument *document) : + QSyntaxHighlighter(document) +{ +} + +void ProFileHighlighter::highlightBlock(const QString &text) +{ + if (text.isEmpty()) + return; + + QString buf; + bool inCommentMode = false; + + QTextCharFormat emptyFormat; + int i = 0; + for (;;) { + const QChar c = text.at(i); + if (inCommentMode) { + setFormat(i, 1, m_formats[ProfileCommentFormat]); + } else { + if (c.isLetter() || c == '_' || c == '.') { + buf += c; + setFormat(i - buf.length()+1, buf.length(), emptyFormat); + if (!buf.isEmpty() && isFunction(buf)) + setFormat(i - buf.length()+1, buf.length(), m_formats[ProfileFunctionFormat]); + else if (!buf.isEmpty() && isVariable(buf)) + setFormat(i - buf.length()+1, buf.length(), m_formats[ProfileVariableFormat]); + } else if (c == '(') { + if (!buf.isEmpty() && isFunction(buf)) + setFormat(i - buf.length(), buf.length(), m_formats[ProfileFunctionFormat]); + buf.clear(); + } else if (c == '#') { + inCommentMode = true; + setFormat(i, 1, m_formats[ProfileCommentFormat]); + buf.clear(); + } else { + if (!buf.isEmpty() && isVariable(buf)) + setFormat(i - buf.length(), buf.length(), m_formats[ProfileVariableFormat]); + buf.clear(); + } + } + i++; + if (i >= text.length()) + break; + } +} + diff --git a/src/plugins/qt4projectmanager/profilehighlighter.h b/src/plugins/qt4projectmanager/profilehighlighter.h new file mode 100644 index 00000000000..c15d584e099 --- /dev/null +++ b/src/plugins/qt4projectmanager/profilehighlighter.h @@ -0,0 +1,65 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROFILEHIGHLIGHTER_H +#define PROFILEHIGHLIGHTER_H + +#include "qt4projectmanagerenums.h" +#include <QtGui/QSyntaxHighlighter> +#include <QtGui/QTextCharFormat> +#include <QtCore/QtAlgorithms> + +namespace Qt4ProjectManager { +namespace Internal { + +class ProFileHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT +public: + ProFileHighlighter(QTextDocument *document = 0); + virtual void highlightBlock(const QString &text); + + // Set formats from a sequence of type QTextCharFormat + template <class InputIterator> + void setFormats(InputIterator begin, InputIterator end) { + qCopy(begin, end, m_formats); + } + +private: + QTextCharFormat m_formats[NumProfileFormats]; + +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // PROFILEHIGHLIGHTER_H diff --git a/src/plugins/qt4projectmanager/profilereader.cpp b/src/plugins/qt4projectmanager/profilereader.cpp new file mode 100644 index 00000000000..bba0393a619 --- /dev/null +++ b/src/plugins/qt4projectmanager/profilereader.cpp @@ -0,0 +1,192 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "profilereader.h" +#include "profilecache.h" + +#include <QtCore/QDir> +#include <QtCore/QDebug> + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; + +ProFileReader::ProFileReader(ProFileCache *cache) + : m_cache(cache) +{ +} + +void ProFileReader::setQtVersion(QtVersion *qtVersion) { + QHash<QString, QStringList> additionalVariables; + additionalVariables.insert(QString("QT_BUILD_TREE"), QStringList() << qtVersion->path()); + additionalVariables.insert(QString("QT_SOURCE_TREE"), QStringList() << qtVersion->sourcePath()); + addVariables(additionalVariables); + addProperties(qtVersion->versionInfo()); + // ### TODO override QT_VERSION property +} + +bool ProFileReader::readProFile(const QString &fileName) +{ + //disable caching -> list of include files is not updated otherwise +// ProFile *pro = proFileFromCache(fileName); +// if (!pro) { +// pro = new ProFile(fileName); +// if (!queryProFile(pro)) { +// delete pro; +// return false; +// } +// } + QString fn = QFileInfo(fileName).filePath(); + ProFile *pro = new ProFile(fn); + if (!queryProFile(pro)) { + delete pro; + return false; + } + m_includeFiles.insert(fn, pro); + return accept(pro); +} + +ProFile *ProFileReader::parsedProFile(const QString &fileName) +{ +// ProFile *pro = proFileFromCache(fileName); +// if (pro) +// return pro; + QString fn = QFileInfo(fileName).filePath(); + ProFile *pro = ProFileEvaluator::parsedProFile(fn); + if (pro) { + m_includeFiles.insert(fn, pro); + } + return pro; +} + +void ProFileReader::releaseParsedProFile(ProFile */*proFile*/) +{ + return; +} + +ProFile *ProFileReader::proFileFromCache(const QString &fileName) const +{ + QString fn = QFileInfo(fileName).filePath(); + ProFile *pro = 0; + if (m_includeFiles.contains(fn)) + pro = m_includeFiles.value(fn); + else + pro = m_cache->proFile(fn); // this can expand to null + return pro; +} + +QList<ProFile*> ProFileReader::includeFiles() const +{ + QString qmakeMkSpecDir = propertyValue("QMAKE_MKSPECS"); + QList<ProFile *> list; + QMap<QString, ProFile *>::const_iterator it, end; + end = m_includeFiles.constEnd(); + for (it = m_includeFiles.constBegin(); it != end; ++it) { + if (!(it.key().startsWith(qmakeMkSpecDir))) + list.append(it.value()); + } + return list; +} + +QString ProFileReader::value(const QString &variable) const +{ + QStringList vals = values(variable); + if (!vals.isEmpty()) + return vals.first(); + + return QString(); +} + + +// Construct QFileInfo from path value and base directory +// Truncate trailing slashs and make absolute +static inline QFileInfo infoFromPath(QString path, + const QString &baseDirectory) +{ + if (path.size() > 1 && path.endsWith(QLatin1Char('/'))) + path.truncate(path.size() - 1); + QFileInfo info(path); + if (info.isAbsolute()) + return info; + return QFileInfo(baseDirectory, path); +} + +/*! + Returns a list of absolute paths + */ +QStringList ProFileReader::absolutePathValues(const QString &variable, + const QString &baseDirectory, + PathValuesMode mode, + const ProFile *pro) const +{ + QStringList rawList; + if (!pro) + rawList = values(variable); + else + rawList = values(variable, pro); + + if (rawList.isEmpty()) + return QStringList(); + + // Normalize list of paths, kill trailing slashes, + // remove duplicates while maintaining order + const QChar slash = QLatin1Char('/'); + QStringList result; + const QStringList::const_iterator rcend = rawList.constEnd(); + for (QStringList::const_iterator it = rawList.constBegin(); it != rcend; ++it) { + const QFileInfo info = infoFromPath(*it, baseDirectory); + if (mode == AllPaths || info.exists()) { + if (mode != ExistingFilePaths || info.isFile()) { + const QString absPath = info.absoluteFilePath(); + if (!result.contains(absPath)) + result.push_back(absPath); + } + } + } + return result; +} + +void ProFileReader::fileMessage(const QString &message) +{ + Q_UNUSED(message); + // we ignore these... +} + +void ProFileReader::logMessage(const QString &message) +{ + Q_UNUSED(message); + // we ignore these... +} + +void ProFileReader::errorMessage(const QString &message) +{ + emit errorFound(message); +} diff --git a/src/plugins/qt4projectmanager/profilereader.h b/src/plugins/qt4projectmanager/profilereader.h new file mode 100644 index 00000000000..682793c236b --- /dev/null +++ b/src/plugins/qt4projectmanager/profilereader.h @@ -0,0 +1,86 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PROFILEREADER_H +#define PROFILEREADER_H + +#include "profileevaluator.h" +#include "qtversionmanager.h" + +#include <QtCore/QObject> +#include <QtCore/QMap> + +namespace Qt4ProjectManager { + +namespace Internal { + +class ProFileCache; + +class ProFileReader : public QObject, public ProFileEvaluator +{ + Q_OBJECT + +public: + ProFileReader(ProFileCache *cache); + + void setQtVersion(QtVersion *qtVersion); + bool readProFile(const QString &fileName); + QList<ProFile*> includeFiles() const; + + QString value(const QString &variable) const; + + enum PathValuesMode { AllPaths, ExistingPaths, ExistingFilePaths }; + QStringList absolutePathValues(const QString &variable, + const QString &baseDirectory, + PathValuesMode mode, + const ProFile *pro = 0) const; + +signals: + void errorFound(const QString &error); + +private: + virtual ProFile *parsedProFile(const QString &fileName); + virtual void releaseParsedProFile(ProFile *proFile); + virtual void logMessage(const QString &msg); + virtual void fileMessage(const QString &msg); + virtual void errorMessage(const QString &msg); + +private: + ProFile *proFileFromCache(const QString &fileName) const; + ProFileCache *m_cache; + QMap<QString, ProFile *> m_includeFiles; +}; + +} //namespace Internal +} //namespace Qt4ProjectManager + +#endif // PROFILEREADER_H diff --git a/src/plugins/qt4projectmanager/projectloadwizard.cpp b/src/plugins/qt4projectmanager/projectloadwizard.cpp new file mode 100644 index 00000000000..b53575c7861 --- /dev/null +++ b/src/plugins/qt4projectmanager/projectloadwizard.cpp @@ -0,0 +1,221 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "projectloadwizard.h" +#include "qt4project.h" +#include "qtversionmanager.h" +#include "qt4projectmanager.h" +#include "qmakestep.h" +#include "makestep.h" +#include <QtCore/QVariant> +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QButtonGroup> +#include <QtGui/QCheckBox> +#include <QtGui/QComboBox> +#include <QtGui/QFormLayout> +#include <QtGui/QHBoxLayout> +#include <QtGui/QHeaderView> +#include <QtGui/QLabel> +#include <QtGui/QLineEdit> +#include <QtGui/QListWidget> +#include <QtGui/QRadioButton> +#include <QtGui/QSpacerItem> +#include <QtGui/QToolButton> +#include <QtGui/QVBoxLayout> +#include <QtGui/QWizard> +#include <QtGui/QWizardPage> +#include <QFileDialog> + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; + +ProjectLoadWizard::ProjectLoadWizard(Qt4Project *project, QWidget *parent, Qt::WindowFlags flags) + : QWizard(parent, flags), m_project(project), m_importVersion(0), m_temporaryVersion(false) +{ + QtVersionManager * vm = project->qt4ProjectManager()->versionManager(); + QString directory = QFileInfo(project->file()->fileName()).absolutePath(); + QString importVersion = vm->findQtVersionFromMakefile(directory); + + if (!importVersion.isNull()) { + // This also means we have a build in there + // First get the qt version + m_importVersion = vm->qtVersionForDirectory(importVersion); + // Okay does not yet exist, create + if (!m_importVersion) { + m_importVersion = new QtVersion(QFileInfo(importVersion).baseName(), importVersion); + m_temporaryVersion = true; + } + + m_importBuildConfig = m_importVersion->defaultBuildConfig(); + m_importBuildConfig= vm->scanMakefileForQmakeConfig(directory, m_importBuildConfig); + } + + // So now we have the version and the configuration for that version + // If buildAll we create debug and release configurations, + // if not then just either debug or release + // The default buildConfiguration depends on QmakeBuildConfig::DebugBuild + // Also if the qt version is not yet in the Tools Options dialog we offer to add it there + + if (m_importVersion) + setupImportPage(m_importVersion, m_importBuildConfig); + + setOptions(options() | QWizard::NoCancelButton | QWizard::NoBackButtonOnLastPage); +} + +// We don't want to actually show the dialog if we don't show the import page +// We used to simply call ::exec() on the dialog +void ProjectLoadWizard::execDialog() +{ + if (m_importVersion) + exec(); + else + done(QDialog::Accepted); +} + +ProjectLoadWizard::~ProjectLoadWizard() +{ + +} + +void ProjectLoadWizard::addBuildConfiguration(QString name, QtVersion *qtversion, QtVersion::QmakeBuildConfig buildConfiguration) +{ + QMakeStep *qmakeStep = m_project->qmakeStep(); + MakeStep *makeStep = m_project->makeStep(); + + bool debug = buildConfiguration & QtVersion::DebugBuild; + // Check that bc.name is not already in use + if (m_project->buildConfigurations().contains(name)) { + int i =1; + do { + ++i; + } while (m_project->buildConfigurations().contains(name + " " + QString::number(i))); + name.append(" " + QString::number(i)); + } + + // Add the buildconfiguration + m_project->addBuildConfiguration(name); + // set some options for qmake and make + if (buildConfiguration & QtVersion::BuildAll) // debug_and_release => explicit targets + makeStep->setValue(name, "makeargs", QStringList() << (debug ? "debug" : "release")); + + qmakeStep->setValue(name, "buildConfiguration", int(buildConfiguration)); + + // Finally set the qt version + bool defaultQtVersion = (qtversion == 0); + if (defaultQtVersion) + m_project->setQtVersion(name, 0); + else + m_project->setQtVersion(name, qtversion->uniqueId()); + +} + +void ProjectLoadWizard::done(int result) +{ + QWizard::done(result); + // This normally happens on showing the final page, but since we + // don't show it anymore, do it here + + QString directory = QFileInfo(m_project->file()->fileName()).absolutePath(); + if (m_importVersion && importCheckbox->isChecked()) { + if (m_temporaryVersion) + m_project->qt4ProjectManager()->versionManager()->addVersion(m_importVersion); + // Import the existing stuff + // qDebug()<<"Creating m_buildconfiguration entry from imported stuff"; + // qDebug()<<((m_importBuildConfig& QtVersion::BuildAll)? "debug_and_release" : "")<<((m_importBuildConfig & QtVersion::DebugBuild)? "debug" : "release"); + bool debug = m_importBuildConfig & QtVersion::DebugBuild; + addBuildConfiguration(debug ? "Debug" : "Release", m_importVersion, m_importBuildConfig); + + if (m_importBuildConfig & QtVersion::BuildAll) { + // Also create the other configuration + QtVersion::QmakeBuildConfig otherBuildConfiguration = m_importBuildConfig; + if (debug) + otherBuildConfiguration = QtVersion::QmakeBuildConfig(otherBuildConfiguration & ~ QtVersion::DebugBuild); + else + otherBuildConfiguration = QtVersion::QmakeBuildConfig(otherBuildConfiguration | QtVersion::DebugBuild); + + addBuildConfiguration(debug ? "Release" : "Debug", m_importVersion, otherBuildConfiguration); + } + } else { + if (m_temporaryVersion) + delete m_importVersion; + // Create default + addBuildConfiguration("Debug", 0, QtVersion::QmakeBuildConfig(QtVersion::BuildAll | QtVersion::DebugBuild)); + addBuildConfiguration("Release", 0, QtVersion::BuildAll); + } + + if (!m_project->buildConfigurations().isEmpty()) + m_project->setActiveBuildConfiguration(m_project->buildConfigurations().at(0)); +} + +// This function used to do the commented stuff instead of having only one page +int ProjectLoadWizard::nextId() const +{ + return -1; +} + +void ProjectLoadWizard::setupImportPage(QtVersion *version, QtVersion::QmakeBuildConfig buildConfig) +{ + resize(605, 490); + // Import Page + importPage = new QWizardPage(this); + importPage->setTitle("Import existing settings"); + QVBoxLayout *importLayout = new QVBoxLayout(importPage); + importLabel = new QLabel(importPage); + + QString versionString = version->name() + " (" + version->path() + ")"; + QString buildConfigString = (buildConfig & QtVersion::BuildAll) ? QLatin1String("debug_and_release ") : QLatin1String(""); + buildConfigString.append((buildConfig & QtVersion::DebugBuild) ? QLatin1String("debug") : QLatin1String("release")); + importLabel->setTextFormat(Qt::RichText); + importLabel->setText(tr("Qt Creator has found an already existing build in the source directory.<br><br>" + "<b>Qt Version:</b> %1<br>" + "<b>Build configuration:</b> %2<br>") + .arg(versionString) + .arg(buildConfigString)); + + importLayout->addWidget(importLabel); + + + importCheckbox = new QCheckBox(importPage); + importCheckbox->setText("Import existing build settings."); + importCheckbox->setChecked(true); + importLayout->addWidget(importCheckbox); + import2Label = new QLabel(importPage); + import2Label->setTextFormat(Qt::RichText); + if (m_temporaryVersion) + import2Label->setText(QString("<b>Note:</b> Importing the settings will automatically add the Qt Version from:<br><b>%1</b> to the list of qt versions.") + .arg(m_importVersion->path())); + importLayout->addWidget(import2Label); + addPage(importPage); +} + diff --git a/src/plugins/qt4projectmanager/projectloadwizard.h b/src/plugins/qt4projectmanager/projectloadwizard.h new file mode 100644 index 00000000000..d75d4e69542 --- /dev/null +++ b/src/plugins/qt4projectmanager/projectloadwizard.h @@ -0,0 +1,102 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/******************************************************************************** +** Form generated from reading ui file 'projectloadwizard.ui' +** +** Created: Mon Nov 3 16:31:45 2008 +** by: Qt User Interface Compiler version 4.5.0 +** +** WARNING! All changes made in this file will be lost when recompiling ui file! +********************************************************************************/ + +#ifndef PROJECTLOADWIZARD_H +#define PROJECTLOADWIZARD_H + +#include "qt4project.h" +#include <QtGui/QWizard> + + +QT_BEGIN_NAMESPACE +class QWizardPage; +class QVBoxLayout; +class QHBoxLayout; +class QLabel; +class QCheckBox; +class QRadioButton; +class QListWidget; +class QLineEdit; +class QToolButton; +class QSpacerItem; +class QFormLayout; +class QComboBox; +QT_END_NAMESPACE + +namespace Qt4ProjectManager { +class Qt4Project; +namespace Internal { + +class ProjectLoadWizard : public QWizard +{ + Q_OBJECT +public: + ProjectLoadWizard(Qt4ProjectManager::Qt4Project *project, QWidget * parent = 0, Qt::WindowFlags flags = 0); + virtual ~ProjectLoadWizard(); + virtual int nextId() const; + virtual void done(int result); + void execDialog(); +private: + void addBuildConfiguration(QString name, QtVersion *qtversion, QtVersion::QmakeBuildConfig buildConfiguration); + void setupImportPage(QtVersion *version, QtVersion::QmakeBuildConfig buildConfig); + + Qt4Project *m_project; + + // Only used for imported stuff + QtVersion *m_importVersion; + QtVersion::QmakeBuildConfig m_importBuildConfig; + // Those that we might add + bool m_temporaryVersion; + + // This was a file autogenarated by Designer, before I found out you can't actually + // create non linear wizards in it + // So those variables should all be m_*, but that one has to wait for refactoring support :) + QWizardPage *importPage; + QLabel *importLabel; + QLabel *import2Label; + QCheckBox *importCheckbox; + + void setupUi(); +}; +} +} + +#endif // UI_PROJECTLOADWIZARD_H diff --git a/src/plugins/qt4projectmanager/qmakebuildstepfactory.cpp b/src/plugins/qt4projectmanager/qmakebuildstepfactory.cpp new file mode 100644 index 00000000000..fbf1a80952c --- /dev/null +++ b/src/plugins/qt4projectmanager/qmakebuildstepfactory.cpp @@ -0,0 +1,102 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qmakebuildstepfactory.h" +#include "qmakestep.h" +#include "makestep.h" +#include "qt4project.h" +#include "qt4projectmanagerconstants.h" + +using namespace Qt4ProjectManager::Internal; +using namespace ProjectExplorer; + +QMakeBuildStepFactory::QMakeBuildStepFactory() +{ +} + +QMakeBuildStepFactory::~QMakeBuildStepFactory() +{ +} + +bool QMakeBuildStepFactory::canCreate(const QString & name) const +{ + return (name == Constants::QMAKESTEP); +} + +ProjectExplorer::BuildStep * QMakeBuildStepFactory::create(ProjectExplorer::Project * pro, const QString & name) const +{ + Q_UNUSED(name); + return new QMakeStep(static_cast<Qt4Project *>(pro)); +} + +QStringList QMakeBuildStepFactory::canCreateForProject(ProjectExplorer::Project *pro) const +{ + Q_UNUSED(pro) + return QStringList(); +} + +QString QMakeBuildStepFactory::displayNameForName(const QString &name) const +{ + Q_UNUSED(name); + return QString(); +} + +MakeBuildStepFactory::MakeBuildStepFactory() +{ +} + +MakeBuildStepFactory::~MakeBuildStepFactory() +{ +} + +bool MakeBuildStepFactory::canCreate(const QString & name) const +{ + return (name == Constants::MAKESTEP); +} + +ProjectExplorer::BuildStep * MakeBuildStepFactory::create(ProjectExplorer::Project * pro, const QString & name) const +{ + Q_UNUSED(name); + return new MakeStep(static_cast<Qt4Project *>(pro)); +} + +QStringList MakeBuildStepFactory::canCreateForProject(ProjectExplorer::Project *pro) const +{ + Q_UNUSED(pro) + return QStringList(); +} + +QString MakeBuildStepFactory::displayNameForName(const QString &name) const +{ + Q_UNUSED(name); + return QString(); +} diff --git a/src/plugins/qt4projectmanager/qmakebuildstepfactory.h b/src/plugins/qt4projectmanager/qmakebuildstepfactory.h new file mode 100644 index 00000000000..c2292b832cb --- /dev/null +++ b/src/plugins/qt4projectmanager/qmakebuildstepfactory.h @@ -0,0 +1,75 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QMAKEBUILDSTEPFACTORY_H +#define QMAKEBUILDSTEPFACTORY_H + +#include <projectexplorer/buildstep.h> +#include <QString> + +namespace ProjectExplorer { +class BuildStep; +class IBuildStepFactory; +class Project; +} + +namespace Qt4ProjectManager { +namespace Internal { + +class QMakeBuildStepFactory : public ProjectExplorer::IBuildStepFactory +{ + Q_OBJECT +public: + QMakeBuildStepFactory(); + virtual ~QMakeBuildStepFactory(); + bool canCreate(const QString & name) const; + ProjectExplorer::BuildStep * create(ProjectExplorer::Project * pro, const QString & name) const; + QStringList canCreateForProject(ProjectExplorer::Project *pro) const; + QString displayNameForName(const QString &name) const; +}; + +class MakeBuildStepFactory : public ProjectExplorer::IBuildStepFactory +{ + Q_OBJECT +public: + MakeBuildStepFactory(); + virtual ~MakeBuildStepFactory(); + bool canCreate(const QString & name) const; + ProjectExplorer::BuildStep * create(ProjectExplorer::Project * pro, const QString & name) const; + QStringList canCreateForProject(ProjectExplorer::Project *pro) const; + QString displayNameForName(const QString &name) const; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // QMAKEBUILDSTEPFACTORY_H diff --git a/src/plugins/qt4projectmanager/qmakestep.cpp b/src/plugins/qt4projectmanager/qmakestep.cpp new file mode 100644 index 00000000000..47986909235 --- /dev/null +++ b/src/plugins/qt4projectmanager/qmakestep.cpp @@ -0,0 +1,272 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qmakestep.h" +#include "qt4project.h" +#include "qt4projectmanagerconstants.h" +#include "qt4projectmanager.h" +#include "makestep.h" +#include "qtversionmanager.h" + +#include <coreplugin/icore.h> + +#include <QFileDialog> +#include <QDir> +#include <QFile> +#include <QCoreApplication> + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; +using namespace ProjectExplorer; + +QMakeStep::QMakeStep(Qt4Project *project) + : AbstractProcessStep(project), m_pro(project), m_forced(false) +{ +} + +QMakeStep::~QMakeStep() +{ +} + +QStringList QMakeStep::arguments(const QString &buildConfiguration) +{ + QStringList additonalArguments = value(buildConfiguration, "qmakeArgs").toStringList(); + QStringList arguments; + arguments << project()->file()->fileName(); + if (!additonalArguments.contains("-spec")) { + if(m_pro->value("useVBOX").toBool()) { //NBS TODO don't special case VBOX like this + arguments << "-spec" << "linux-i686fb-g++"; + arguments << "-unix"; + } else { + arguments << "-spec" << m_pro->qtVersion(buildConfiguration)->mkspec(); + } + } + + arguments << "-r"; + + if (value(buildConfiguration, "buildConfiguration").isValid()) { + QStringList configarguments; + QtVersion::QmakeBuildConfig defaultBuildConfiguration = m_pro->qtVersion(buildConfiguration)->defaultBuildConfig(); + QtVersion::QmakeBuildConfig projectBuildConfiguration = QtVersion::QmakeBuildConfig(value(buildConfiguration, "buildConfiguration").toInt()); + if ((defaultBuildConfiguration & QtVersion::BuildAll) && !(projectBuildConfiguration & QtVersion::BuildAll)) + configarguments << "CONFIG-=debug_and_release"; + if (!(defaultBuildConfiguration & QtVersion::BuildAll) && (projectBuildConfiguration & QtVersion::BuildAll)) + configarguments << "CONFIG+=debug_and_release"; + if ((defaultBuildConfiguration & QtVersion::DebugBuild) && !(projectBuildConfiguration & QtVersion::DebugBuild)) + configarguments << "CONFIG+=release"; + if (!(defaultBuildConfiguration & QtVersion::DebugBuild) && (projectBuildConfiguration & QtVersion::DebugBuild)) + configarguments << "CONFIG+=debug"; + arguments << configarguments; + } else { + arguments << "CONFIG+=debug_and_release"; + } + + if (!additonalArguments.isEmpty()) + arguments << additonalArguments; + + return arguments; +} + +bool QMakeStep::init(const QString &name) +{ + m_buildConfiguration = name; + const QtVersion *qtVersion = m_pro->qtVersion(name); + + if (!qtVersion->isValid()) { +#if defined(Q_OS_MAC) + emit addToOutputWindow(tr("\n<font color=\"#ff0000\"><b>No valid Qt version set. Set one in Preferences </b></font>\n")); +#else + emit addToOutputWindow(tr("\n<font color=\"#ff0000\"><b>No valid Qt version set. Set one in Tools/Options </b></font>\n")); +#endif + return false; + } + + QStringList args = arguments(name); + QString workingDirectory = m_pro->buildDirectory(name); + + Environment environment = m_pro->environment(name); + if(!environment.value("QMAKESPEC").isEmpty() && environment.value("QMAKESPEC") != qtVersion->mkspec()) + emit addToOutputWindow(tr("QMAKESPEC set to ") + environment.value("QMAKESPEC") + + tr(" overrides mkspec of selected qt ")+qtVersion->mkspec()); + + QString program = qtVersion->qmakeCommand(); + + // Check wheter we need to run qmake + bool needToRunQMake = true; + if (QDir(workingDirectory).exists(QLatin1String("Makefile"))) + needToRunQMake = false; + + QStringList newEnv = environment.toStringList(); + newEnv.sort(); + + if (m_lastEnv != newEnv) { + m_lastEnv = newEnv; + needToRunQMake = true; + } + if (m_lastWorkingDirectory != workingDirectory) { + m_lastWorkingDirectory = workingDirectory; + needToRunQMake = true; + } + + if (m_lastArguments != args) { + m_lastArguments = args; + needToRunQMake = true; + } + + if (m_lastProgram != program) { + m_lastProgram = program; + needToRunQMake = true; + } + + if (m_forced) { + m_forced = false; + needToRunQMake = true; + } + + setEnabled(name, needToRunQMake); + setWorkingDirectory(name, workingDirectory); + setCommand(name, program); + setArguments(name, args); + setEnvironment(name, environment); + setWorkingDirectory(name, workingDirectory); + return AbstractProcessStep::init(name); +} + +void QMakeStep::run(QFutureInterface<bool> &fi) +{ + if (qobject_cast<Qt4Project *>(project())->rootProjectNode()->projectType() == ScriptTemplate) { + fi.reportResult(true); + return; + } + + if (!enabled(m_buildConfiguration)) { + emit addToOutputWindow(tr("<font color=\"#0000ff\">Configuration unchanged, skipping QMake step.</font>")); + fi.reportResult(true); + return; + } + AbstractProcessStep::run(fi); +} + +QString QMakeStep::name() +{ + return Constants::QMAKESTEP; +} + +QString QMakeStep::displayName() +{ + return "QMake"; +} + +void QMakeStep::setForced(bool b) +{ + m_forced = b; +} + +bool QMakeStep::forced() +{ + return m_forced; +} + +ProjectExplorer::BuildStepConfigWidget *QMakeStep::createConfigWidget() +{ + return new QMakeStepConfigWidget(this); +} + +bool QMakeStep::immutable() const +{ + return true; +} + + +void QMakeStep::processStartupFailed() +{ + m_forced = true; + AbstractProcessStep::processStartupFailed(); +} + +bool QMakeStep::processFinished(int exitCode, QProcess::ExitStatus status) +{ + bool result = AbstractProcessStep::processFinished(exitCode, status); + if (!result) + m_forced = true; + return result; +} + +QMakeStepConfigWidget::QMakeStepConfigWidget(QMakeStep *step) + : BuildStepConfigWidget(), m_step(step) +{ + m_ui.setupUi(this); + connect(m_ui.qmakeAdditonalArgumentsLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(qmakeArgumentsLineEditTextEdited())); + connect(m_ui.buildConfigurationComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(buildConfigurationChanged())); +} + +void QMakeStepConfigWidget::qmakeArgumentsLineEditTextEdited() +{ + Q_ASSERT(!m_buildConfiguration.isNull()); + m_step->setValue(m_buildConfiguration, "qmakeArgs", ProjectExplorer::Environment::parseCombinedArgString(m_ui.qmakeAdditonalArgumentsLineEdit->text())); + m_ui.qmakeArgumentsEdit->setPlainText(ProjectExplorer::Environment::joinArgumentList(m_step->arguments(m_buildConfiguration))); +} + +void QMakeStepConfigWidget::buildConfigurationChanged() +{ + QtVersion::QmakeBuildConfig buildConfiguration = QtVersion::QmakeBuildConfig(m_step->value(m_buildConfiguration, "buildConfiguration").toInt()); + if (m_ui.buildConfigurationComboBox->currentIndex() == 0) { + // debug + buildConfiguration = QtVersion::QmakeBuildConfig(buildConfiguration | QtVersion::DebugBuild); + } else { + buildConfiguration = QtVersion::QmakeBuildConfig(buildConfiguration & ~QtVersion::DebugBuild); + } + m_step->setValue(m_buildConfiguration, "buildConfiguration", int(buildConfiguration)); + m_ui.qmakeArgumentsEdit->setPlainText(ProjectExplorer::Environment::joinArgumentList(m_step->arguments(m_buildConfiguration))); +} + +QString QMakeStepConfigWidget::displayName() const +{ + return m_step->displayName(); +} + +void QMakeStepConfigWidget::init(const QString &buildConfiguration) +{ + m_buildConfiguration = buildConfiguration; + if (buildConfiguration.isEmpty()){ + m_ui.stackedWidget->setCurrentWidget(m_ui.page_2); + } else { + m_ui.stackedWidget->setCurrentWidget(m_ui.page_1); + QString qmakeArgs = ProjectExplorer::Environment::joinArgumentList(m_step->value(buildConfiguration, "qmakeArgs").toStringList()); + m_ui.qmakeAdditonalArgumentsLineEdit->setText(qmakeArgs); + m_ui.qmakeArgumentsEdit->setPlainText(ProjectExplorer::Environment::joinArgumentList(m_step->arguments(buildConfiguration))); + bool debug = QtVersion::QmakeBuildConfig(m_step->value(buildConfiguration, "buildConfiguration").toInt()) & QtVersion::DebugBuild; + m_ui.buildConfigurationComboBox->setCurrentIndex(debug? 0 : 1); + } +} + diff --git a/src/plugins/qt4projectmanager/qmakestep.h b/src/plugins/qt4projectmanager/qmakestep.h new file mode 100644 index 00000000000..55156033623 --- /dev/null +++ b/src/plugins/qt4projectmanager/qmakestep.h @@ -0,0 +1,94 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QMAKESTEP_H +#define QMAKESTEP_H + +#include <projectexplorer/ProjectExplorerInterfaces> +#include <QStringList> + +#include "ui_qmakestep.h" + +namespace Qt4ProjectManager { + +class Qt4Project; + +class QMakeStep : public ProjectExplorer::AbstractProcessStep +{ + Q_OBJECT +public: + QMakeStep(Qt4Project * project); + ~QMakeStep(); + virtual bool init(const QString & name); + virtual void run(QFutureInterface<bool> &); + virtual QString name(); + virtual QString displayName(); + virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); + virtual bool immutable() const; + QStringList arguments(const QString &buildConfiguration); + void setForced(bool b); + bool forced(); +protected: + virtual void processStartupFailed(); + virtual bool processFinished(int exitCode, QProcess::ExitStatus status); + +private: + Qt4Project *m_pro; + // last values + QString m_buildConfiguration; + QStringList m_lastEnv; + QString m_lastWorkingDirectory; + QStringList m_lastArguments; + QString m_lastProgram; + bool m_forced; +}; + + +class QMakeStepConfigWidget : public ProjectExplorer::BuildStepConfigWidget +{ + Q_OBJECT +public: + QMakeStepConfigWidget(QMakeStep *step); + QString displayName() const; + void init(const QString &buildConfiguration); +private slots: + void qmakeArgumentsLineEditTextEdited(); + void buildConfigurationChanged(); +private: + QString m_buildConfiguration; + Ui::QMakeStep m_ui; + QMakeStep *m_step; +}; + +} // namespace Qt4ProjectManager + +#endif // QMAKESTEP_H diff --git a/src/plugins/qt4projectmanager/qmakestep.ui b/src/plugins/qt4projectmanager/qmakestep.ui new file mode 100644 index 00000000000..df5fb98e393 --- /dev/null +++ b/src/plugins/qt4projectmanager/qmakestep.ui @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QMakeStep</class> + <widget class="QWidget" name="QMakeStep"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>414</width> + <height>442</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QStackedWidget" name="stackedWidget"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="page_1"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>QMake Build Configuration:</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QComboBox" name="buildConfigurationComboBox"> + <item> + <property name="text"> + <string>debug</string> + </property> + </item> + <item> + <property name="text"> + <string>release</string> + </property> + </item> + </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> + </item> + <item> + <widget class="QLabel" name="qmakeArgsLabel"> + <property name="text"> + <string>Additional arguments:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="qmakeAdditonalArgumentsLineEdit"/> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Arguments:</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + </widget> + </item> + <item> + <widget class="QPlainTextEdit" name="qmakeArgumentsEdit"> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>120</height> + </size> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>393</width> + <height>179</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="page_2"/> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/qt4projectmanager/qt4buildconfigwidget.cpp b/src/plugins/qt4projectmanager/qt4buildconfigwidget.cpp new file mode 100644 index 00000000000..5efb1086861 --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4buildconfigwidget.cpp @@ -0,0 +1,257 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qt4buildconfigwidget.h" +#include "ui_qt4buildconfigwidget.h" +#include "qt4project.h" +#include "qt4projectmanager.h" +#include "qmakestep.h" +#include "makestep.h" + +#include <QtGui/QFileDialog> + +namespace { +bool debug = false; +} + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; + +Qt4BuildConfigWidget::Qt4BuildConfigWidget(Qt4Project *project) + : BuildStepConfigWidget(), + m_pro(project) +{ + m_ui = new Ui::Qt4BuildConfigWidget(); + m_ui->setupUi(this); + m_ui->invalidQtWarningLabel->setVisible(false); + + connect(m_ui->nameLineEdit, SIGNAL(textEdited(QString)), + this, SLOT(changeConfigName(QString))); + + connect(m_ui->shadowBuildCheckBox, SIGNAL(clicked(bool)), + this, SLOT(shadowBuildCheckBoxClicked(bool))); + + connect(m_ui->shadowBuildButton, SIGNAL(clicked(bool)), + this, SLOT(shadowBuildButtonClicked())); + + connect(m_ui->shadowBuildLineEdit, SIGNAL(textEdited(QString)), + this, SLOT(shadowBuildLineEditTextChanged())); + + connect(m_ui->qtVersionComboBox, SIGNAL(currentIndexChanged(QString)), + this, SLOT(qtVersionComboBoxCurrentIndexChanged(QString))); + + connect(m_ui->importLabel, SIGNAL(linkActivated(QString)), + this, SLOT(importLabelClicked())); + + connect(m_pro->qt4ProjectManager()->versionManager(), SIGNAL(qtVersionsChanged()), + this, SLOT(setupQtVersionsComboBox())); + +} + +Qt4BuildConfigWidget::~Qt4BuildConfigWidget() +{ + delete m_ui; +} + +QString Qt4BuildConfigWidget::displayName() const +{ + return tr("General"); +} + +void Qt4BuildConfigWidget::init(const QString &buildConfiguration) +{ + if (debug) + qDebug() << "Qt4BuildConfigWidget::init()"; + + m_buildConfiguration = buildConfiguration; + + m_ui->nameLineEdit->setText(m_pro->displayNameFor(m_buildConfiguration)); + + setupQtVersionsComboBox(); + + bool shadowBuild = m_pro->value(buildConfiguration, "useShadowBuild").toBool(); + m_ui->shadowBuildCheckBox->setChecked(shadowBuild); + m_ui->shadowBuildLineEdit->setEnabled(shadowBuild); + m_ui->shadowBuildLineEdit->setText(m_pro->buildDirectory(buildConfiguration)); + shadowBuildLineEditTextChanged(); // Update the import label + m_ui->shadowBuildButton->setEnabled(shadowBuild); +} + +void Qt4BuildConfigWidget::changeConfigName(const QString &newName) +{ + m_pro->setDisplayNameFor(m_buildConfiguration, newName); +} + +void Qt4BuildConfigWidget::setupQtVersionsComboBox() +{ + if (m_buildConfiguration.isEmpty()) // not yet initialized + return; + + disconnect(m_ui->qtVersionComboBox, SIGNAL(currentIndexChanged(const QString &)), + this, SLOT(qtVersionComboBoxCurrentIndexChanged(const QString &))); + + m_ui->qtVersionComboBox->clear(); + m_ui->qtVersionComboBox->addItem(tr("Default Qt Version"), 0); + + if(m_pro->qtVersionId(m_buildConfiguration) == 0) { + m_ui->qtVersionComboBox->setCurrentIndex(0); + m_ui->invalidQtWarningLabel->setVisible(false); + } + // Add Qt Versions to the combo box + QtVersionManager *vm = m_pro->qt4ProjectManager()->versionManager(); + const QList<QtVersion *> &versions = vm->versions(); + for(int i=0; i<versions.size(); ++i) { + m_ui->qtVersionComboBox->addItem(versions.at(i)->name(), versions.at(i)->uniqueId()); + + if(versions.at(i)->uniqueId() == m_pro->qtVersionId(m_buildConfiguration)) { + m_ui->qtVersionComboBox->setCurrentIndex(i+1); + m_ui->invalidQtWarningLabel->setVisible(!versions.at(i)->isValid()); + } + } + + // And connect again + connect(m_ui->qtVersionComboBox, SIGNAL(currentIndexChanged(const QString &)), + this, SLOT(qtVersionComboBoxCurrentIndexChanged(const QString &))); +} + +void Qt4BuildConfigWidget::shadowBuildButtonClicked() +{ + QString initialDirectory = m_ui->shadowBuildLineEdit->text(); + if (initialDirectory.isEmpty()) + initialDirectory = QFileInfo(m_pro->file()->fileName()).absolutePath(); + + QString shadowBuildDirectory = + QFileDialog::getExistingDirectory(this, tr("Shadow Build Directory"), initialDirectory ); + + if (shadowBuildDirectory != QString::null) + m_ui->shadowBuildLineEdit->setText(shadowBuildDirectory); + shadowBuildLineEditTextChanged(); +} + +void Qt4BuildConfigWidget::shadowBuildCheckBoxClicked(bool checked) +{ + m_ui->shadowBuildLineEdit->setEnabled(checked); + m_ui->shadowBuildButton->setEnabled(checked); + bool b = m_ui->shadowBuildCheckBox->isChecked(); + m_pro->setValue(m_buildConfiguration, "useShadowBuild", b); + if (b) + m_pro->setValue(m_buildConfiguration, "buildDirectory", m_ui->shadowBuildLineEdit->text()); + else + m_pro->setValue(m_buildConfiguration, "buildDirectory", QVariant(QString::null)); +} + +void Qt4BuildConfigWidget::shadowBuildLineEditTextChanged() +{ + m_pro->setValue(m_buildConfiguration, "buildDirectory", m_ui->shadowBuildLineEdit->text()); + // if the directory already exists + // check if we have a build in there and + // offer to import it + m_ui->importLabel->setVisible(false); + if (m_ui->shadowBuildCheckBox->isChecked()) { + QString qtPath = m_pro->qt4ProjectManager()->versionManager()->findQtVersionFromMakefile(m_ui->shadowBuildLineEdit->text()); + if(!qtPath.isEmpty()) { + m_ui->importLabel->setVisible(true); + } + } + +// QFileInfo fi(m_ui->shadowBuildLineEdit->text()); +// if (fi.exists()) { +// m_ui->shadowBuildLineEdit->setStyleSheet(""); +// m_ui->shadowBuildLineEdit->setToolTip(""); +// } else { +// m_ui->shadowBuildLineEdit->setStyleSheet("background: red;"); +// m_ui->shadowBuildLineEdit->setToolTip(tr("Directory does not exist.")); +// } +} + +void Qt4BuildConfigWidget::importLabelClicked() +{ + if (m_ui->shadowBuildCheckBox->isChecked()) { + QString directory = m_ui->shadowBuildLineEdit->text(); + if (!directory.isEmpty()) { + QtVersionManager *vm = m_pro->qt4ProjectManager()->versionManager(); + QString qtPath = vm->findQtVersionFromMakefile(directory); + if (!qtPath.isEmpty()) { + QtVersion *version = vm->qtVersionForDirectory(qtPath); + if (!version) { + version = new QtVersion(QFileInfo(qtPath).baseName(), qtPath); + vm->addVersion(version); + } + QtVersion::QmakeBuildConfig qmakeBuildConfig = version->defaultBuildConfig(); + qmakeBuildConfig = vm->scanMakefileForQmakeConfig(directory, qmakeBuildConfig); + + // So we got all the information now apply it... + m_pro->setQtVersion(m_buildConfiguration, version->uniqueId()); + // Combo box will be updated at the end + + // Find qmakestep... + QMakeStep *qmakeStep = m_pro->qmakeStep(); + MakeStep *makeStep = m_pro->makeStep(); + + qmakeStep->setValue(m_buildConfiguration, "buildConfiguration", int(qmakeBuildConfig)); + // Adjust command line arguments, this is ugly as hell + // If we are switching to BuildAll we want "release" in there and no "debug" + // or "debug" in there and no "release" + // If we are switching to not BuildAl we want neither "release" nor "debug" in there + QStringList makeCmdArguments = makeStep->value(m_buildConfiguration, "makeargs").toStringList(); + bool debug = qmakeBuildConfig & QtVersion::DebugBuild; + if (qmakeBuildConfig & QtVersion::BuildAll) { + makeCmdArguments.removeAll(debug ? "release" : "debug"); + if (!makeCmdArguments.contains(debug ? "debug" : "release")) + makeCmdArguments.append(debug ? "debug" : "release"); + } else { + makeCmdArguments.removeAll("debug"); + makeCmdArguments.removeAll("remove"); + } + makeStep->setValue(m_buildConfiguration, "makeargs", makeCmdArguments); + } + } + } + setupQtVersionsComboBox(); +} + +void Qt4BuildConfigWidget::qtVersionComboBoxCurrentIndexChanged(const QString &) +{ + //Qt Version + int newQtVersion; + if (m_ui->qtVersionComboBox->currentIndex() == 0) { + newQtVersion = 0; + } else { + newQtVersion = m_ui->qtVersionComboBox->itemData(m_ui->qtVersionComboBox->currentIndex()).toInt(); + } + bool isValid = m_pro->qt4ProjectManager()->versionManager()->version(newQtVersion)->isValid(); + m_ui->invalidQtWarningLabel->setVisible(!isValid); + if(newQtVersion != m_pro->qtVersionId(m_buildConfiguration)) { + m_pro->setQtVersion(m_buildConfiguration, newQtVersion); + m_pro->update(); + } +} diff --git a/src/plugins/qt4projectmanager/qt4buildconfigwidget.h b/src/plugins/qt4projectmanager/qt4buildconfigwidget.h new file mode 100644 index 00000000000..725ed18bdcc --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4buildconfigwidget.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QT4BUILDCONFIGWIDGET_H +#define QT4BUILDCONFIGWIDGET_H + +#include <projectexplorer/buildstep.h> + +namespace Qt4ProjectManager { + +class Qt4Project; + +namespace Internal { + +namespace Ui { +class Qt4BuildConfigWidget; +} + +class Qt4BuildConfigWidget : public ProjectExplorer::BuildStepConfigWidget +{ + Q_OBJECT +public: + Qt4BuildConfigWidget(Qt4Project *project); + ~Qt4BuildConfigWidget(); + + QString displayName() const; + void init(const QString &buildConfiguration); + +private slots: + void changeConfigName(const QString &newName); + void setupQtVersionsComboBox(); + void shadowBuildCheckBoxClicked(bool checked); + void shadowBuildButtonClicked(); + void shadowBuildLineEditTextChanged(); + void importLabelClicked(); + void qtVersionComboBoxCurrentIndexChanged(const QString &); + +private: + Ui::Qt4BuildConfigWidget *m_ui; + Qt4Project *m_pro; + QString m_buildConfiguration; +}; + +} // Internal +} // Qt4ProjectManager + +#endif // QT4BUILDCONFIGWIDGET_H diff --git a/src/plugins/qt4projectmanager/qt4buildconfigwidget.ui b/src/plugins/qt4projectmanager/qt4buildconfigwidget.ui new file mode 100644 index 00000000000..f62d4a69b53 --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4buildconfigwidget.ui @@ -0,0 +1,191 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Qt4ProjectManager::Internal::Qt4BuildConfigWidget</class> + <widget class="QWidget" name="Qt4ProjectManager::Internal::Qt4BuildConfigWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>652</width> + <height>247</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="nameLabel"> + <property name="text"> + <string>Configuration Name</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="nameLineEdit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>100</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="qtVersionLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Qt Version</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QComboBox" name="qtVersionComboBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="invalidQtWarningLabel"> + <property name="text"> + <string>This Qt-Version is invalid.</string> + </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> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Shadow Build</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QCheckBox" name="shadowBuildCheckBox"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="buildDirLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Build Directory</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLineEdit" name="shadowBuildLineEdit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>100</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="shadowBuildButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="4" column="1"> + <widget class="QLabel" name="importLabel"> + <property name="text"> + <string><a href="import">Import existing build</a></string> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="1"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0"> + <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> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/qt4projectmanager/qt4buildenvironmentwidget.cpp b/src/plugins/qt4projectmanager/qt4buildenvironmentwidget.cpp new file mode 100644 index 00000000000..7c3a73781f9 --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4buildenvironmentwidget.cpp @@ -0,0 +1,182 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qt4buildenvironmentwidget.h" +#include "ui_qt4buildenvironmentwidget.h" + +#include "qt4project.h" +#include <projectexplorer/environmenteditmodel.h> + +namespace { +bool debug = false; +} + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; +using ProjectExplorer::EnvironmentModel; + +Qt4BuildEnvironmentWidget::Qt4BuildEnvironmentWidget(Qt4Project *project) + : BuildStepConfigWidget(), m_pro(project) +{ + m_ui = new Ui::Qt4BuildEnvironmentWidget(); + m_ui->setupUi(this); + + m_environmentModel = new EnvironmentModel(); + m_environmentModel->setMergedEnvironments(true); + m_ui->environmentTreeView->setModel(m_environmentModel); + m_ui->environmentTreeView->header()->resizeSection(0, 250); + + connect(m_environmentModel, SIGNAL(userChangesUpdated()), + this, SLOT(environmentModelUserChangesUpdated())); + + connect(m_environmentModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(updateButtonsEnabled())); + + connect(m_ui->editButton, SIGNAL(clicked(bool)), + this, SLOT(editEnvironmentButtonClicked())); + connect(m_ui->addButton, SIGNAL(clicked(bool)), + this, SLOT(addEnvironmentButtonClicked())); + connect(m_ui->removeButton, SIGNAL(clicked(bool)), + this, SLOT(removeEnvironmentButtonClicked())); + connect(m_ui->unsetButton, SIGNAL(clicked(bool)), + this, SLOT(unsetEnvironmentButtonClicked())); + connect(m_ui->environmentTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex &)), + this, SLOT(environmentCurrentIndexChanged(const QModelIndex &, const QModelIndex &))); + connect(m_ui->clearSystemEnvironmentCheckBox, SIGNAL(toggled(bool)), + this, SLOT(clearSystemEnvironmentCheckBoxClicked(bool))); +} + +Qt4BuildEnvironmentWidget::~Qt4BuildEnvironmentWidget() +{ + delete m_ui; + delete m_environmentModel; +} + +QString Qt4BuildEnvironmentWidget::displayName() const +{ + return tr("Build Environment"); +} + +void Qt4BuildEnvironmentWidget::init(const QString &buildConfiguration) +{ + if (debug) + qDebug() << "Qt4BuildConfigWidget::init()"; + + m_buildConfiguration = buildConfiguration; + m_ui->clearSystemEnvironmentCheckBox->setChecked(!m_pro->useSystemEnvironment(buildConfiguration)); + m_environmentModel->setBaseEnvironment(m_pro->baseEnvironment(buildConfiguration)); + m_environmentModel->setUserChanges(m_pro->userEnvironmentChanges(buildConfiguration)); + + updateButtonsEnabled(); +} + + +void Qt4BuildEnvironmentWidget::editEnvironmentButtonClicked() +{ + m_ui->environmentTreeView->edit(m_ui->environmentTreeView->currentIndex()); +} + +void Qt4BuildEnvironmentWidget::addEnvironmentButtonClicked() +{ + QModelIndex index = m_environmentModel->addVariable(); + m_ui->environmentTreeView->setCurrentIndex(index); + m_ui->environmentTreeView->edit(index); + updateButtonsEnabled(); +} + +void Qt4BuildEnvironmentWidget::removeEnvironmentButtonClicked() +{ + const QString &name = m_environmentModel->indexToVariable(m_ui->environmentTreeView->currentIndex()); + m_environmentModel->removeVariable(name); + updateButtonsEnabled(); +} +// unset in Merged Environment Mode means, unset if it comes from the base environment +// or remove when it is just a change we added +// unset in changes view, means just unset +void Qt4BuildEnvironmentWidget::unsetEnvironmentButtonClicked() +{ + const QString &name = m_environmentModel->indexToVariable(m_ui->environmentTreeView->currentIndex()); + if(!m_environmentModel->isInBaseEnvironment(name) && m_environmentModel->mergedEnvironments()) + m_environmentModel->removeVariable(name); + else + m_environmentModel->unset(name); + updateButtonsEnabled(); +} + +void Qt4BuildEnvironmentWidget::switchEnvironmentTab(int newTab) +{ + bool mergedEnvironments = (newTab == 0); + m_environmentModel->setMergedEnvironments(mergedEnvironments); + if(mergedEnvironments) { + m_ui->removeButton->setText(tr("Reset")); + } else { + m_ui->removeButton->setText(tr("Remove")); + } +} + +void Qt4BuildEnvironmentWidget::updateButtonsEnabled() +{ + environmentCurrentIndexChanged(m_ui->environmentTreeView->currentIndex(), QModelIndex()); +} + +void Qt4BuildEnvironmentWidget::environmentCurrentIndexChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + Q_UNUSED(previous) + if(current.isValid()) { + if(m_environmentModel->mergedEnvironments()) { + const QString &name = m_environmentModel->indexToVariable(current); + bool modified = m_environmentModel->isInBaseEnvironment(name) && m_environmentModel->changes(name); + bool unset = m_environmentModel->isUnset(name); + m_ui->removeButton->setEnabled(modified || unset); + m_ui->unsetButton->setEnabled(!unset); + return; + } else { + m_ui->removeButton->setEnabled(true); + m_ui->unsetButton->setEnabled(!m_environmentModel->isUnset(m_environmentModel->indexToVariable(current))); + return; + } + } + m_ui->editButton->setEnabled(current.isValid()); + m_ui->removeButton->setEnabled(false); + m_ui->unsetButton->setEnabled(false); +} + +void Qt4BuildEnvironmentWidget::clearSystemEnvironmentCheckBoxClicked(bool checked) +{ + m_pro->setUseSystemEnvironment(m_buildConfiguration, !checked); + m_environmentModel->setBaseEnvironment(m_pro->baseEnvironment(m_buildConfiguration)); +} + +void Qt4BuildEnvironmentWidget::environmentModelUserChangesUpdated() +{ + m_pro->setUserEnvironmentChanges(m_buildConfiguration, m_environmentModel->userChanges()); +} diff --git a/src/plugins/qt4projectmanager/qt4buildenvironmentwidget.h b/src/plugins/qt4projectmanager/qt4buildenvironmentwidget.h new file mode 100644 index 00000000000..fe134206ecf --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4buildenvironmentwidget.h @@ -0,0 +1,83 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QT4BUILDENVIRONMENTWIDGET_H +#define QT4BUILDENVIRONMENTWIDGET_H + +#include <projectexplorer/buildstep.h> + +namespace ProjectExplorer { +class EnvironmentModel; +} + +namespace Qt4ProjectManager { + +class Qt4Project; + +namespace Internal { + +namespace Ui { +class Qt4BuildEnvironmentWidget; +} + +class Qt4BuildEnvironmentWidget : public ProjectExplorer::BuildStepConfigWidget +{ + Q_OBJECT +public: + Qt4BuildEnvironmentWidget(Qt4Project *project); + ~Qt4BuildEnvironmentWidget(); + + QString displayName() const; + void init(const QString &buildConfiguration); + +private slots: + void environmentModelUserChangesUpdated(); + void editEnvironmentButtonClicked(); + void addEnvironmentButtonClicked(); + void removeEnvironmentButtonClicked(); + void unsetEnvironmentButtonClicked(); + void switchEnvironmentTab(int newTab); + void environmentCurrentIndexChanged(const QModelIndex ¤t, const QModelIndex &previous); + void clearSystemEnvironmentCheckBoxClicked(bool); + void updateButtonsEnabled(); + +private: + Ui::Qt4BuildEnvironmentWidget *m_ui; + Qt4Project *m_pro; + ProjectExplorer::EnvironmentModel *m_environmentModel; + QString m_buildConfiguration; +}; + +} // Internal +} // Qt4ProjectManager + +#endif // QT4BUILDENVIRONMENTWIDGET_H diff --git a/src/plugins/qt4projectmanager/qt4buildenvironmentwidget.ui b/src/plugins/qt4projectmanager/qt4buildenvironmentwidget.ui new file mode 100644 index 00000000000..354ac61e89b --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4buildenvironmentwidget.ui @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Qt4ProjectManager::Internal::Qt4BuildEnvironmentWidget</class> + <widget class="QWidget" name="Qt4ProjectManager::Internal::Qt4BuildEnvironmentWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QCheckBox" name="clearSystemEnvironmentCheckBox"> + <property name="text"> + <string>Clear system environment</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTreeView" name="environmentTreeView"> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <property name="headerHidden"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QPushButton" name="editButton"> + <property name="text"> + <string>&Edit</string> + </property> + </widget> + </item> + <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>&Reset</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="unsetButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&Unset</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> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/qt4projectmanager/qt4nodes.cpp b/src/plugins/qt4projectmanager/qt4nodes.cpp new file mode 100644 index 00000000000..847d6ae38f3 --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4nodes.cpp @@ -0,0 +1,975 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "proeditormodel.h" +#include "profilecache.h" +#include "profilereader.h" +#include "qt4nodes.h" +#include "qt4project.h" +#include "qt4projectmanager.h" +#include "directorywatcher.h" + +#include <projectexplorer/nodesvisitor.h> + +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/filemanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/iversioncontrol.h> +#include <coreplugin/vcsmanager.h> + +#include <cpptools/cppmodelmanagerinterface.h> + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QTimer> +#include <QtGui/QMainWindow> +#include <QtGui/QMessageBox> +#include <QtGui/QPushButton> + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; + +namespace { + bool debug = false; +} + +namespace { + // sorting helper function + bool sortProjectFilesByPath(ProFile *f1, ProFile *f2) + { + return f1->fileName() < f2->fileName(); + } +} + +/*! + \class Qt4PriFileNode + Implements abstract ProjectNode class + */ + +Qt4PriFileNode::Qt4PriFileNode(Qt4Project *project, + const QString &filePath) + : ProjectNode(filePath), + m_core(project->qt4ProjectManager()->core()), + m_project(project), + m_projectFilePath(filePath), + m_projectDir(QFileInfo(filePath).absolutePath()), + m_includeFile(0), + m_saveTimer(new QTimer(this)) +{ + Q_ASSERT(project); + setFolderName(QFileInfo(filePath).baseName()); + setIcon(QIcon(":/qt4projectmanager/images/qt_project.png")); + + // m_saveTimer is used for the delayed saving of the pro file + // so that multiple insert/remove calls in one event loop run + // trigger just one save call. + m_saveTimer->setSingleShot(true); + connect(m_saveTimer, SIGNAL(timeout()), this, SLOT(save())); +} + +void Qt4PriFileNode::update(ProFile *includeFile, ProFileReader *reader) +{ + Q_ASSERT(includeFile); + Q_ASSERT(reader); + + m_includeFile = includeFile; + + // add project file node + if (m_fileNodes.isEmpty()) + addFileNodes(QList<FileNode*>() << new FileNode(m_projectFilePath, ProjectFileType, false), this); + + static QList<FileType> fileTypes = + (QList<FileType>() << ProjectExplorer::HeaderType + << ProjectExplorer::SourceType + << ProjectExplorer::FormType + << ProjectExplorer::ResourceType + << ProjectExplorer::UnknownFileType); + + // update files + const QDir projectDir = QFileInfo(m_projectFilePath).dir(); + foreach (FileType type, fileTypes) { + const QStringList qmakeVariables = varNames(type); + + QStringList newFilePaths; + foreach (const QString &qmakeVariable, qmakeVariables) + newFilePaths += reader->absolutePathValues(qmakeVariable, projectDir.path(), ProFileReader::ExistingFilePaths, includeFile); + + QList<FileNode*> existingFileNodes; + foreach (FileNode *fileNode, fileNodes()) { + if (fileNode->fileType() == type && !fileNode->isGenerated()) + existingFileNodes << fileNode; + } + + QList<FileNode*> toRemove; + QList<FileNode*> toAdd; + + qSort(newFilePaths); + qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath); + + QList<FileNode*>::const_iterator existingNodeIter = existingFileNodes.constBegin(); + QList<QString>::const_iterator newPathIter = newFilePaths.constBegin(); + while (existingNodeIter != existingFileNodes.constEnd() + && newPathIter != newFilePaths.constEnd()) { + if ((*existingNodeIter)->path() < *newPathIter) { + toRemove << *existingNodeIter; + ++existingNodeIter; + } else if ((*existingNodeIter)->path() > *newPathIter) { + toAdd << new FileNode(*newPathIter, type, false); + ++newPathIter; + } else { // *existingNodeIter->path() == *newPathIter + ++existingNodeIter; + ++newPathIter; + } + } + while (existingNodeIter != existingFileNodes.constEnd()) { + toRemove << *existingNodeIter; + ++existingNodeIter; + } + while (newPathIter != newFilePaths.constEnd()) { + toAdd << new FileNode(*newPathIter, type, false); + ++newPathIter; + } + + if (!toRemove.isEmpty()) + removeFileNodes(toRemove, this); + if (!toAdd.isEmpty()) + addFileNodes(toAdd, this); + } +} + +QList<ProjectNode::ProjectAction> Qt4PriFileNode::supportedActions() const +{ + QList<ProjectAction> actions; + if (m_includeFile) { + const FolderNode *folderNode = this; + const Qt4ProFileNode *proFileNode; + while (!(proFileNode = qobject_cast<const Qt4ProFileNode*>(folderNode))) + folderNode = folderNode->parentFolderNode(); + Q_ASSERT(proFileNode); + + switch (proFileNode->projectType()) { + case ApplicationTemplate: + case LibraryTemplate: + actions << AddFile << RemoveFile; + break; + case SubDirsTemplate: + actions << AddSubProject << RemoveSubProject; + break; + default: + break; + } + } + return actions; +} + +bool Qt4PriFileNode::addSubProjects(const QStringList &proFilePaths) +{ + if (!m_includeFile) + return false; + return changeIncludes(m_includeFile, proFilePaths, AddToProFile); +} + +bool Qt4PriFileNode::removeSubProjects(const QStringList &proFilePaths) +{ + if (!m_includeFile) + return false; + return changeIncludes(m_includeFile, proFilePaths, RemoveFromProFile); +} + +bool Qt4PriFileNode::addFiles(const FileType fileType, const QStringList &filePaths, + QStringList *notAdded) +{ + if (!m_includeFile) + return false; + QStringList failedFiles; + + changeFiles(fileType, filePaths, &failedFiles, AddToProFile); + if (notAdded) + *notAdded = failedFiles; + return failedFiles.isEmpty(); +} + +bool Qt4PriFileNode::removeFiles(const FileType fileType, const QStringList &filePaths, + QStringList *notRemoved) +{ + if (!m_includeFile) + return false; + QStringList failedFiles; + changeFiles(fileType, filePaths, &failedFiles, RemoveFromProFile); + if (notRemoved) + *notRemoved = failedFiles; + return failedFiles.isEmpty(); +} + +bool Qt4PriFileNode::renameFile(const FileType fileType, const QString &filePath, + const QString &newFilePath) +{ + if (!m_includeFile || newFilePath.isEmpty()) + return false; + + if (!QFile::rename(filePath, newFilePath)) + return false; + + QStringList dummy; + changeFiles(fileType, QStringList() << filePath, &dummy, RemoveFromProFile); + if (!dummy.isEmpty()) + return false; + changeFiles(fileType, QStringList() << newFilePath, &dummy, AddToProFile); + if (!dummy.isEmpty()) + return false; + return true; +} + +bool Qt4PriFileNode::changeIncludes(ProFile *includeFile, const QStringList &proFilePaths, + ChangeType change) +{ + Q_UNUSED(includeFile); + Q_UNUSED(proFilePaths); + Q_UNUSED(change); + // TODO + return false; +} + + +namespace { + enum ReadOnlyAction { RO_Cancel, RO_OpenSCC, RO_MakeWriteable }; +} + +static ReadOnlyAction promptReadOnly(const QString &fileName, bool hasSCC, QWidget *parent) +{ + QMessageBox msgBox(QMessageBox::Question, QObject::tr("File is Read Only"), + QObject::tr("The file %1 is read only.").arg(fileName), + QMessageBox::Cancel, parent); + + QPushButton *sccButton = 0; + if (hasSCC) + sccButton = msgBox.addButton(QObject::tr("Open with SCC"), QMessageBox::AcceptRole); + QPushButton *makeWritableButton = msgBox.addButton(QObject::tr("Make writable"), QMessageBox::AcceptRole); + if (hasSCC) + msgBox.setDefaultButton(sccButton); + else + msgBox.setDefaultButton(makeWritableButton); + msgBox.exec(); + QAbstractButton *clickedButton = msgBox.clickedButton(); + if (clickedButton == sccButton) + return RO_OpenSCC; + if (clickedButton == makeWritableButton) + return RO_MakeWriteable; + return RO_Cancel; +} + +bool Qt4PriFileNode::priFileWritable(const QString &path) +{ + const QString dir = QFileInfo(path).dir().path(); + Core::IVersionControl *versionControl = m_core->vcsManager()->findVersionControlForDirectory(dir); + switch (promptReadOnly(path, versionControl != 0, m_core->mainWindow())) { + case RO_OpenSCC: + if (!versionControl->vcsOpen(path)) { + QMessageBox::warning(m_core->mainWindow(), tr("Failed!"), tr("Could not open the file for edit with SCC.")); + return false; + } + break; + case RO_MakeWriteable: { + const bool permsOk = QFile::setPermissions(path, QFile::permissions(path) | QFile::WriteUser); + if (!permsOk) { + QMessageBox::warning(m_core->mainWindow(), tr("Failed!"), tr("Could not set permissions to writable.")); + return false; + } + break; + } + case RO_Cancel: { + return false; + } + } + return true; +} + +bool Qt4PriFileNode::saveModifiedEditors(const QString &path) +{ + QList<Core::IFile*> allFileHandles; + QList<Core::IFile*> modifiedFileHandles; + + foreach (Core::IFile *file, m_core->fileManager()->managedFiles(path)) { + allFileHandles << file; + } + + foreach (Core::IEditor *editor, m_core->editorManager()->editorsForFileName(path)) { + if (Core::IFile *editorFile = editor->file()) { + if (editorFile->isModified()) + modifiedFileHandles << editorFile; + } + } + + if (!modifiedFileHandles.isEmpty()) { + bool cancelled; + m_core->fileManager()->saveModifiedFiles(modifiedFileHandles, &cancelled, + tr("There are unsaved changes for project file %1.").arg(path)); + if (cancelled) + return false; + // force instant reload + foreach (Core::IFile *fileHandle, allFileHandles) { + Core::IFile::ReloadBehavior reload = Core::IFile::ReloadAll; + fileHandle->modified(&reload); + } + } + return true; +} + +void Qt4PriFileNode::changeFiles(const FileType fileType, + const QStringList &filePaths, + QStringList *notChanged, + ChangeType change) +{ + if (filePaths.isEmpty()) + return; + + *notChanged = filePaths; + + // Check for modified editors + if (!saveModifiedEditors(m_projectFilePath)) + return; + + // Check if file is readonly + ProFileCache *cache = m_project->qt4ProjectManager()->proFileCache(); + if (cache->fileInterface(m_projectFilePath)->isReadOnly() && !priFileWritable(m_projectFilePath)) + return; + + ProEditorModel proModel; + proModel.setProFiles(QList<ProFile*>() << m_includeFile); + + const QStringList vars = varNames(fileType); + QDir priFileDir = QDir(m_projectDir); + + if (change == AddToProFile) { + // root item "<Global Scope>" + const QModelIndex root = proModel.index(0, 0); + + // Check if variable item exists as child of root item + ProVariable *proVar = 0; + int row = 0; + for (; row < proModel.rowCount(root); ++row) { + if ((proVar = proModel.proVariable(root.child(row, 0)))) { + if (vars.contains(proVar->variable()) + && proVar->variableOperator() != ProVariable::RemoveOperator + && proVar->variableOperator() != ProVariable::ReplaceOperator) + break; + else + proVar = 0; + } + } + + if (!proVar) { + // Create & append new variable item + + // TODO: This will always store e.g. a source file in SOURCES and not OBJECTIVE_SOURCES + proVar = new ProVariable(vars.first(), proModel.proBlock(root)); + proVar->setVariableOperator(ProVariable::AddOperator); + proModel.insertItem(proVar, row, root); + } + const QModelIndex varIndex = root.child(row, 0); + + foreach (const QString &filePath, filePaths) { + const QString &relativeFilePath = priFileDir.relativeFilePath(filePath); + proModel.insertItem(new ProValue(relativeFilePath, proVar), + proModel.rowCount(varIndex), varIndex); + notChanged->removeOne(filePath); + } + } else { // RemoveFromProFile + QList<QModelIndex> proVarIndexes = proModel.findVariables(vars); + QList<QModelIndex> toRemove; + + QStringList relativeFilePaths; + foreach (const QString &absoluteFilePath, filePaths) + relativeFilePaths << priFileDir.relativeFilePath(absoluteFilePath); + + foreach (const QModelIndex &proVarIndex, proVarIndexes) { + ProVariable *proVar = proModel.proVariable(proVarIndex); + + if (proVar->variableOperator() != ProVariable::RemoveOperator + && proVar->variableOperator() != ProVariable::ReplaceOperator) { + + for (int row = proModel.rowCount(proVarIndex) - 1; row >= 0; --row) { + QModelIndex itemIndex = proModel.index(row, 0, proVarIndex); + ProItem *item = proModel.proItem(itemIndex); + + if (item->kind() == ProItem::ValueKind) { + ProValue *val = static_cast<ProValue *>(item); + int index = relativeFilePaths.indexOf(val->value()); + if (index != -1) { + toRemove.append(itemIndex); + notChanged->removeAt(index); + } + } + } + } + } + + foreach (const QModelIndex &index, toRemove) { + proModel.removeItem(index); + } + } + + // save file + if (!m_saveTimer->isActive()) + m_saveTimer->start(); +} + +void Qt4PriFileNode::save() +{ + // Prevent any reload questions; just reload + Core::FileManager *fileManager = m_core->fileManager(); + ProFileCache *cache = m_project->qt4ProjectManager()->proFileCache(); + + QList<Core::IFile *> allFileHandles = fileManager->managedFiles(m_includeFile->fileName()); + Core::IFile *modifiedFileHandle = cache->fileInterface(m_includeFile->fileName()); + + fileManager->blockFileChange(modifiedFileHandle); + modifiedFileHandle->save(); + fileManager->unblockFileChange(modifiedFileHandle); + + Core::IFile::ReloadBehavior tempBehavior = + Core::IFile::ReloadAll; + foreach (Core::IFile *file, allFileHandles) { + file->modified(&tempBehavior); + } +} + +/* + Deletes all subprojects/files/virtual folders + */ +void Qt4PriFileNode::clear() +{ + // delete files && folders && projects + if (!fileNodes().isEmpty()) + removeFileNodes(fileNodes(), this); + if (!subProjectNodes().isEmpty()) + removeProjectNodes(subProjectNodes()); + if (!subFolderNodes().isEmpty()) + removeFolderNodes(subFolderNodes(), this); +} + +QStringList Qt4PriFileNode::varNames(FileType type) +{ + QStringList vars; + switch (type) { + case ProjectExplorer::HeaderType: + vars << QLatin1String("HEADERS"); + break; + case ProjectExplorer::SourceType: + vars << QLatin1String("SOURCES"); + vars << QLatin1String("OBJECTIVE_SOURCES"); + break; + case ProjectExplorer::ResourceType: + vars << QLatin1String("RESOURCES"); + break; + case ProjectExplorer::FormType: + vars << QLatin1String("FORMS"); + break; + default: + vars << QLatin1String("OTHER_FILES"); + break; + } + return vars; +} + +/*! + \class Qt4ProFileNode + Implements abstract ProjectNode class + */ +Qt4ProFileNode::Qt4ProFileNode(Qt4Project *project, + const QString &filePath, + QObject *parent) + : Qt4PriFileNode(project, filePath), + // own stuff + m_projectType(InvalidProject), + m_isQBuildProject(false), + m_cache(project->qt4ProjectManager()->proFileCache()), + m_dirWatcher(new DirectoryWatcher(this)) +{ + if (parent) + setParent(parent); + + connect(m_dirWatcher, SIGNAL(directoryChanged(const QString&)), + this, SLOT(update())); + connect(m_dirWatcher, SIGNAL(fileChanged(const QString&)), + this, SLOT(fileChanged(const QString&))); + connect(m_project, SIGNAL(activeBuildConfigurationChanged()), + this, SLOT(update())); +} + +bool Qt4ProFileNode::hasTargets() const +{ + return (projectType() == ApplicationTemplate) || (projectType() == LibraryTemplate); +} + +Qt4ProjectType Qt4ProFileNode::projectType() const +{ + return m_projectType; +} + +QStringList Qt4ProFileNode::variableValue(const Qt4Variable var) const +{ + return m_varValues.value(var); +} + +void Qt4ProFileNode::update() +{ + ProFileReader *reader = createProFileReader(); + if (!reader->readProFile(m_projectFilePath)) { + m_project->proFileParseError(tr("Error while parsing file %1. Giving up.").arg(m_projectFilePath)); + delete reader; + invalidate(); + return; + } + + if (debug) + qDebug() << "Qt4ProFileNode - updating files for file " << m_projectFilePath; + +#ifdef QTEXTENDED_QBUILD_SUPPORT + if (m_projectFilePath.endsWith("qbuild.pro")) { + m_isQBuildProject = true; + } +#endif + + Qt4ProjectType projectType = InvalidProject; + switch (reader->templateType()) { + case ProFileEvaluator::TT_Unknown: + case ProFileEvaluator::TT_Application: { + projectType = ApplicationTemplate; + break; + } + case ProFileEvaluator::TT_Library: { + projectType = LibraryTemplate; + break; + } + case ProFileEvaluator::TT_Script: { + projectType = ScriptTemplate; + break; + } + case ProFileEvaluator::TT_Subdirs: + projectType = SubDirsTemplate; + break; + } + if (projectType != m_projectType) { + Qt4ProjectType oldType = m_projectType; + // probably all subfiles/projects have changed anyway ... + clear(); + m_projectType = projectType; + foreach (NodesWatcher *watcher, watchers()) + if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher)) + emit qt4Watcher->projectTypeChanged(this, oldType, projectType); + } + + // + // Add/Remove pri files, sub projects + // + + QList<ProjectNode*> existingProjectNodes = subProjectNodes(); + + QList<QString> newProjectFiles; + QHash<QString, ProFile*> includeFiles; + ProFile *fileForCurrentProject = 0; + { + if (projectType == SubDirsTemplate) { + foreach (const QString &subDirProject, subDirsPaths(reader)) + newProjectFiles << subDirProject; + } + + foreach (ProFile *includeFile, reader->includeFiles()) { + if (includeFile->fileName() == m_projectFilePath) { // this file + fileForCurrentProject = includeFile; + } else { + newProjectFiles << includeFile->fileName(); + includeFiles.insert(includeFile->fileName(), includeFile); + } + } + } + + qSort(existingProjectNodes.begin(), existingProjectNodes.end(), + sortNodesByPath); + qSort(newProjectFiles.begin(), newProjectFiles.end()); + + QList<ProjectNode*> toAdd; + QList<ProjectNode*> toRemove; + + QList<ProjectNode*>::const_iterator existingNodeIter = existingProjectNodes.constBegin(); + QList<QString>::const_iterator newProjectFileIter = newProjectFiles.constBegin(); + while (existingNodeIter != existingProjectNodes.constEnd() + && newProjectFileIter != newProjectFiles.constEnd()) { + if ((*existingNodeIter)->path() < *newProjectFileIter) { + toRemove << *existingNodeIter; + ++existingNodeIter; + } else if ((*existingNodeIter)->path() > *newProjectFileIter) { + if (ProFile *file = includeFiles.value(*newProjectFileIter)) { + Qt4PriFileNode *priFileNode + = new Qt4PriFileNode(m_project, + *newProjectFileIter); + priFileNode->update(file, reader); + toAdd << priFileNode; + } else { + toAdd << createSubProFileNode(*newProjectFileIter); + } + ++newProjectFileIter; + } else { // *existingNodeIter->path() == *newProjectFileIter + if (ProFile *file = includeFiles.value(*newProjectFileIter)) { + Qt4PriFileNode *priFileNode = static_cast<Qt4PriFileNode*>(*existingNodeIter); + priFileNode->update(file, reader); + } + + ++existingNodeIter; + ++newProjectFileIter; + } + } + while (existingNodeIter != existingProjectNodes.constEnd()) { + toRemove << *existingNodeIter; + ++existingNodeIter; + } + while (newProjectFileIter != newProjectFiles.constEnd()) { + if (ProFile *file = includeFiles.value(*newProjectFileIter)) { + Qt4PriFileNode *priFileNode + = new Qt4PriFileNode(m_project, + *newProjectFileIter); + priFileNode->update(file, reader); + toAdd << priFileNode; + } else { + toAdd << createSubProFileNode(*newProjectFileIter); + } + ++newProjectFileIter; + } + + if (!toRemove.isEmpty()) + removeProjectNodes(toRemove); + if (!toAdd.isEmpty()) + addProjectNodes(toAdd); + + Qt4PriFileNode::update(fileForCurrentProject, reader); + + // update other variables + QHash<Qt4Variable, QStringList> newVarValues; + newVarValues[CxxCompilerVar] << reader->value(QLatin1String("QMAKE_CXX")); + newVarValues[DefinesVar] = reader->values(QLatin1String("DEFINES")); + newVarValues[IncludePathVar] = includePaths(reader); + newVarValues[UiDirVar] = uiDirPaths(reader); + newVarValues[MocDirVar] = mocDirPaths(reader); + + if (m_varValues != newVarValues) { + m_varValues = newVarValues; + foreach (NodesWatcher *watcher, watchers()) + if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher)) + emit qt4Watcher->variablesChanged(this, m_varValues, newVarValues); + } + + updateGeneratedFiles(); + m_project->qt4ProjectManager()->proFileCache()->updateDependencies(reader->includeFiles().toSet(), this); + + delete reader; + foreach (NodesWatcher *watcher, watchers()) + if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher)) + emit qt4Watcher->proFileUpdated(this); +} + +void Qt4ProFileNode::fileChanged(const QString &filePath) +{ + CppTools::CppModelManagerInterface *modelManager = + m_core->pluginManager()->getObject<CppTools::CppModelManagerInterface>(); + + modelManager->updateSourceFiles(QStringList() << filePath); +} + +namespace { + // find all ui files in project + class FindUiFileNodesVisitor : public ProjectExplorer::NodesVisitor { + public: + void visitProjectNode(ProjectNode *projectNode) + { + visitFolderNode(projectNode); + } + void visitFolderNode(FolderNode *folderNode) + { + foreach (FileNode *fileNode, folderNode->fileNodes()) { + if (fileNode->fileType() == ProjectExplorer::FormType) + uiFileNodes << fileNode; + } + } + QList<FileNode*> uiFileNodes; + }; +} + +/* + Adds ui_xxx.h files to tree and monitors them / the UI_DIR directory for changes + */ +void Qt4ProFileNode::updateGeneratedFiles() +{ + if (m_projectType != ApplicationTemplate + && m_projectType != LibraryTemplate) + return; + + FindUiFileNodesVisitor uiFilesVisitor; + this->accept(&uiFilesVisitor); + const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes; + + // monitor uic dir (only if there are .ui files) + + QSet<QString> oldUiDirs = m_dirWatcher->directories().toSet(); + QSet<QString> newUiDirs = + (!uiFiles.isEmpty()) ? m_varValues[UiDirVar].toSet() : QSet<QString>(); + foreach (const QString &uiDir, oldUiDirs - newUiDirs) + m_dirWatcher->removeDirectory(uiDir); + foreach (const QString &uiDir, newUiDirs - oldUiDirs) + m_dirWatcher->addDirectory(uiDir); + + // update generated files + + QList<FileNode*> existingFileNodes; + foreach (FileNode *file, fileNodes()) { + if (file->isGenerated()) + existingFileNodes << file; + } + QStringList newFilePaths; + foreach (const QString &uicDir, m_varValues[UiDirVar]) { + foreach (FileNode *uiFile, uiFiles) { + const QString uiHeaderFilePath + = QString("%1/ui_%2.h").arg(uicDir, QFileInfo(uiFile->path()).baseName()); + if (QFileInfo(uiHeaderFilePath).exists()) + newFilePaths << uiHeaderFilePath; + } + } + + QList<FileNode*> toRemove; + QList<FileNode*> toAdd; + + qSort(newFilePaths); + qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath); + + QList<FileNode*>::const_iterator existingNodeIter = existingFileNodes.constBegin(); + QList<QString>::const_iterator newPathIter = newFilePaths.constBegin(); + while (existingNodeIter != existingFileNodes.constEnd() + && newPathIter != newFilePaths.constEnd()) { + if ((*existingNodeIter)->path() < *newPathIter) { + toRemove << *existingNodeIter; + ++existingNodeIter; + } else if ((*existingNodeIter)->path() > *newPathIter) { + toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true); + ++newPathIter; + } else { // *existingNodeIter->path() == *newPathIter + ++existingNodeIter; + ++newPathIter; + } + } + while (existingNodeIter != existingFileNodes.constEnd()) { + toRemove << *existingNodeIter; + ++existingNodeIter; + } + while (newPathIter != newFilePaths.constEnd()) { + toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true); + ++newPathIter; + } + + if (!toRemove.isEmpty()) { + foreach (FileNode *file, toRemove) + m_dirWatcher->removeFile(file->path()); + removeFileNodes(toRemove, this); + } + if (!toAdd.isEmpty()) { + foreach (FileNode *file, toAdd) + m_dirWatcher->addFile(file->path()); + addFileNodes(toAdd, this); + } +} + +ProFileReader *Qt4ProFileNode::createProFileReader() const +{ + ProFileReader *reader = new ProFileReader(m_cache); + connect(reader, SIGNAL(errorFound(const QString &)), + m_project, SLOT(proFileParseError(const QString &))); + + QtVersion *version = m_project->qtVersion(m_project->activeBuildConfiguration()); + if (version->isValid()) { + reader->setQtVersion(version); + } + + QHash<QString,QStringList> variables; + variables.insert(QLatin1String("OUT_PWD"), QStringList(buildDir())); + reader->addVariables(variables); + + return reader; +} + +Qt4ProFileNode *Qt4ProFileNode::createSubProFileNode(const QString &path) +{ + Qt4ProFileNode *subProFileNode = new Qt4ProFileNode(m_project, path); + subProFileNode->update(); + return subProFileNode; +} + +QStringList Qt4ProFileNode::uiDirPaths(ProFileReader *reader) const +{ + QStringList candidates = reader->absolutePathValues(QLatin1String("UI_DIR"), + buildDir(), + ProFileReader::ExistingPaths); + return candidates; +} + +QStringList Qt4ProFileNode::mocDirPaths(ProFileReader *reader) const +{ + QStringList candidates = reader->absolutePathValues(QLatin1String("MOC_DIR"), + buildDir(), + ProFileReader::ExistingPaths); + return candidates; +} + +QStringList Qt4ProFileNode::includePaths(ProFileReader *reader) const +{ + QStringList paths; + paths = reader->absolutePathValues(QLatin1String("INCLUDEPATH"), + m_projectDir, + ProFileReader::ExistingPaths); + paths << uiDirPaths(reader) << mocDirPaths(reader); + paths.removeDuplicates(); + return paths; +} + +QStringList Qt4ProFileNode::subDirsPaths(ProFileReader *reader) const +{ + QStringList subProjectPaths; + + const QStringList subDirVars = reader->values(QLatin1String("SUBDIRS")); + + foreach (const QString &subDirVar, subDirVars) { + // Special case were subdir is just an identifier: + // "SUBDIR = subid + // subid.subdir = realdir" + + QString realDir; + QString realFile; + const QString subDirKey = subDirVar + QLatin1String(".subdir"); + if (reader->contains(subDirKey)) + realDir = reader->value(subDirKey); + else + realDir = subDirVar; + QFileInfo info(realDir); + if (!info.isAbsolute()) + realDir = QString("%1/%2").arg(m_projectDir, realDir); + +#ifdef QTEXTENDED_QBUILD_SUPPORT + // QBuild only uses project files named qbuild.pro, and subdirs are implied + if (m_isQBuildProject) + return qBuildSubDirsPaths(realDir); +#endif + if (info.suffix().isEmpty() || info.isDir()) { + realFile = QString("%1/%2.pro").arg(realDir, info.fileName()); + if (!QFile::exists(realFile)) { + // parse directory for pro files - if there is only one, use that + QDir dir(realDir); + QStringList files = dir.entryList(QStringList() << "*.pro", QDir::Files); + if (files.size() == 1) { + realFile = QString("%1/%2").arg(realDir, files.first()); + } else { + m_project->proFileParseError(tr("Could not find .pro file for sub dir '%1' in '%2'") + .arg(subDirVar).arg(realDir)); + realFile = QString::null; + } + } + } else { + realFile = realDir; + } + + if (!realFile.isEmpty() && !subProjectPaths.contains(realFile)) + subProjectPaths << realFile; + } + + return subProjectPaths; +} + +QStringList Qt4ProFileNode::qBuildSubDirsPaths(const QString &scanDir) const +{ + QStringList subProjectPaths; + + // With QBuild we only look for project files named qbuild.pro + QString realFile = scanDir + "/qbuild.pro"; + if (QFile::exists(realFile)) + subProjectPaths << realFile; + + // With QBuild 'subdirs' are implied + QDir dir(scanDir); + QStringList subDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + foreach (QString subDir, subDirs) { + // 'tests' sub directories are an exception to the 'QBuild scans everything' rule. + // Tests are only build with the 'make test' command, in which case QBuild WILL look + // for a tests subdir and run everything in there. + if (subDir != "tests") + subProjectPaths += qBuildSubDirsPaths(scanDir + "/" + subDir); + } + + return subProjectPaths; +} + +QString Qt4ProFileNode::buildDir() const +{ + const QDir srcDirRoot = QFileInfo(m_project->rootProjectNode()->path()).absoluteDir(); + const QString relativeDir = srcDirRoot.relativeFilePath(m_projectDir); + return QDir(m_project->buildDirectory(m_project->activeBuildConfiguration())).absoluteFilePath(relativeDir); +} + +/* + Sets project type to InvalidProject & deletes all subprojects/files/virtual folders + */ +void Qt4ProFileNode::invalidate() +{ + if (m_projectType == InvalidProject) + return; + + clear(); + + // remove monitored files/directories + foreach (const QString &file, m_dirWatcher->files()) + m_dirWatcher->removeFile(file); + foreach (const QString &dir, m_dirWatcher->directories()) + m_dirWatcher->removeDirectory(dir); + + + // change project type + Qt4ProjectType oldType = m_projectType; + m_projectType = InvalidProject; + + + foreach (NodesWatcher *watcher, watchers()) + if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher)) + emit qt4Watcher->projectTypeChanged(this, oldType, InvalidProject); +} + +Qt4NodesWatcher::Qt4NodesWatcher(QObject *parent) + : NodesWatcher(parent) +{ +} diff --git a/src/plugins/qt4projectmanager/qt4nodes.h b/src/plugins/qt4projectmanager/qt4nodes.h new file mode 100644 index 00000000000..7f311e813c1 --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4nodes.h @@ -0,0 +1,234 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QT4NODES_H +#define QT4NODES_H + +#include <projectexplorer/projectnodes.h> + +#include <QtCore/QHash> +#include <QtCore/QStringList> +#include <QtCore/QTimer> + +// defined in proitems.h +QT_BEGIN_NAMESPACE +class ProFile; +QT_END_NAMESPACE + +namespace Core { +class ICore; +} + +namespace Qt4ProjectManager { + +// Import base classes into namespace +using ProjectExplorer::Node; +using ProjectExplorer::FileNode; +using ProjectExplorer::FolderNode; +using ProjectExplorer::ProjectNode; +using ProjectExplorer::NodesWatcher; + +// Import enums into namespace +using ProjectExplorer::NodeType; +using ProjectExplorer::FileNodeType; +using ProjectExplorer::FolderNodeType; +using ProjectExplorer::ProjectNodeType; + +using ProjectExplorer::UnknownFileType; +using ProjectExplorer::ProjectFileType; + +class Qt4Project; + +namespace Internal { + +using ProjectExplorer::FileType; + +class ProFileCache; +class ProFileReader; +class DirectoryWatcher; + +// Type of projects +enum Qt4ProjectType { + InvalidProject = 0, + ApplicationTemplate, + LibraryTemplate, + ScriptTemplate, + SubDirsTemplate +}; + +// Other variables of interest +enum Qt4Variable { + DefinesVar = 1, + IncludePathVar, + CxxCompilerVar, + UiDirVar, + MocDirVar +}; + +class Qt4PriFileNode; +class Qt4ProFileNode; + +// Implements ProjectNode for qt4 pro files +class Qt4PriFileNode : public ProjectExplorer::ProjectNode { + Q_OBJECT + Q_DISABLE_COPY(Qt4PriFileNode) +public: + Qt4PriFileNode(Qt4Project *project, + const QString &filePath); + + void update(ProFile *includeFile, ProFileReader *reader); + +// ProjectNode interface + QList<ProjectAction> supportedActions() const; + + bool hasTargets() const { return false; } + + bool addSubProjects(const QStringList &proFilePaths); + bool removeSubProjects(const QStringList &proFilePaths); + + bool addFiles(const FileType fileType, const QStringList &filePaths, + QStringList *notAdded = 0); + bool removeFiles(const FileType fileType, const QStringList &filePaths, + QStringList *notRemoved = 0); + bool renameFile(const FileType fileType, + const QString &filePath, const QString &newFilePath); + +private slots: + void save(); + +protected: + void clear(); + static QStringList varNames(FileType type); + + enum ChangeType { + AddToProFile, + RemoveFromProFile + }; + + bool changeIncludes(ProFile *includeFile, + const QStringList &proFilePaths, + ChangeType change); + + void changeFiles(const FileType fileType, + const QStringList &filePaths, + QStringList *notChanged, + ChangeType change); + +private: + bool priFileWritable(const QString &path); + bool saveModifiedEditors(const QString &path); + + Core::ICore *m_core; + Qt4Project *m_project; + QString m_projectFilePath; + QString m_projectDir; + ProFile *m_includeFile; + QTimer *m_saveTimer; + + // managed by Qt4ProFileNode + friend class Qt4ProFileNode; +}; + +// Implements ProjectNode for qt4 pro files +class Qt4ProFileNode : public Qt4PriFileNode { + Q_OBJECT + Q_DISABLE_COPY(Qt4ProFileNode) +public: + Qt4ProFileNode(Qt4Project *project, + const QString &filePath, + QObject *parent = 0); + + bool hasTargets() const; + + Qt4ProjectType projectType() const; + + QStringList variableValue(const Qt4Variable var) const; + +public slots: + void update(); + +private slots: + void fileChanged(const QString &filePath); + +private: + void updateGeneratedFiles(); + + ProFileReader *createProFileReader() const; + Qt4ProFileNode *createSubProFileNode(const QString &path); + + QStringList uiDirPaths(ProFileReader *reader) const; + QStringList mocDirPaths(ProFileReader *reader) const; + QStringList includePaths(ProFileReader *reader) const; + QStringList subDirsPaths(ProFileReader *reader) const; + QStringList qBuildSubDirsPaths(const QString &scanDir) const; + + QString buildDir() const; + + void invalidate(); + + Qt4ProjectType m_projectType; + QHash<Qt4Variable, QStringList> m_varValues; + bool m_isQBuildProject; + + ProFileCache *m_cache; + DirectoryWatcher *m_dirWatcher; + + friend class Qt4NodeHierarchy; +}; + +class Qt4NodesWatcher : public ProjectExplorer::NodesWatcher { + Q_OBJECT + Q_DISABLE_COPY(Qt4NodesWatcher) +public: + Qt4NodesWatcher(QObject *parent = 0); + +signals: + void projectTypeChanged(Qt4ProjectManager::Internal::Qt4ProFileNode *projectNode, + const Qt4ProjectManager::Internal::Qt4ProjectType oldType, + const Qt4ProjectManager::Internal::Qt4ProjectType newType); + + void variablesChanged(Qt4ProFileNode *projectNode, + const QHash<Qt4Variable, QStringList> &oldValues, + const QHash<Qt4Variable, QStringList> &newValues); + + void proFileUpdated(Qt4ProjectManager::Internal::Qt4ProFileNode *projectNode); + +private: + // let them emit signals + friend class Qt4ProFileNode; + friend class Qt4PriFileNode; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // QT4NODES_H diff --git a/src/plugins/qt4projectmanager/qt4project.cpp b/src/plugins/qt4projectmanager/qt4project.cpp new file mode 100644 index 00000000000..e642c309f78 --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4project.cpp @@ -0,0 +1,897 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qt4project.h" +#include "qt4projectmanager.h" +#include "profilecache.h" +#include "profilereader.h" +#include "makestep.h" +#include "qmakestep.h" +#include "deployhelper.h" +#include "qt4runconfiguration.h" +#include "qtversionmanager.h" +#include "qt4nodes.h" +#include "qt4buildconfigwidget.h" +#include "qt4buildenvironmentwidget.h" +#include "qt4projectmanagerconstants.h" +#include "projectloadwizard.h" +#include "gdbmacrosbuildstep.h" + +#include <coreplugin/messagemanager.h> +#include <coreplugin/coreconstants.h> +#include <cpptools/cppmodelmanagerinterface.h> +#include <projectexplorer/nodesvisitor.h> +#include <projectexplorer/project.h> +#include <projectexplorer/customexecutablerunconfiguration.h> + +#include <QtGui/QFileDialog> +#include <QtCore/QDir> +#include <QtCore/QDebug> + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; +using namespace ProjectExplorer; +using Core::VariableManager; + +enum { debug = 0 }; + +namespace Qt4ProjectManager { +namespace Internal { + +// Qt4ProjectFiles: Struct for (Cached) lists of files in a project +struct Qt4ProjectFiles { + void clear(); + bool equals(const Qt4ProjectFiles &f) const; + + QStringList files[ProjectExplorer::FileTypeSize]; + QStringList generatedFiles[ProjectExplorer::FileTypeSize]; + QStringList proFiles; +}; + +void Qt4ProjectFiles::clear() +{ + for (int i = 0; i < FileTypeSize; ++i) { + files[i].clear(); + generatedFiles[i].clear(); + } + proFiles.clear(); +} + +bool Qt4ProjectFiles::equals(const Qt4ProjectFiles &f) const +{ + for (int i = 0; i < FileTypeSize; ++i) + if (files[i] != f.files[i] || generatedFiles[i] != f.generatedFiles[i]) + return false; + if (proFiles != f.proFiles) + return false; + return true; +} + +inline bool operator==(const Qt4ProjectFiles &f1, const Qt4ProjectFiles &f2) +{ return f1.equals(f2); } + +inline bool operator!=(const Qt4ProjectFiles &f1, const Qt4ProjectFiles &f2) +{ return !f1.equals(f2); } + +QDebug operator<<(QDebug d, const Qt4ProjectFiles &f) +{ + QDebug nsp = d.nospace(); + nsp << "Qt4ProjectFiles: proFiles=" << f.proFiles << '\n'; + for (int i = 0; i < FileTypeSize; ++i) + nsp << "Type " << i << " files=" << f.files[i] << " generated=" << f.generatedFiles[i] << '\n'; + return d; +} + +// A visitor to collect all files of a project in a Qt4ProjectFiles struct +class ProjectFilesVisitor : public ProjectExplorer::NodesVisitor +{ + Q_DISABLE_COPY(ProjectFilesVisitor) + ProjectFilesVisitor(Qt4ProjectFiles *files); +public: + + static void findProjectFiles(Qt4ProFileNode *rootNode, Qt4ProjectFiles *files); + + void visitProjectNode(ProjectNode *projectNode); + void visitFolderNode(FolderNode *folderNode); + +private: + Qt4ProjectFiles *m_files; +}; + +ProjectFilesVisitor::ProjectFilesVisitor(Qt4ProjectFiles *files) : + m_files(files) +{ +} + +void ProjectFilesVisitor::findProjectFiles(Qt4ProFileNode *rootNode, Qt4ProjectFiles *files) +{ + files->clear(); + ProjectFilesVisitor visitor(files); + rootNode->accept(&visitor); + for (int i = 0; i < FileTypeSize; ++i) { + qSort(files->files[i]); + qSort(files->generatedFiles[i]); + } + qSort(files->proFiles); +} + +void ProjectFilesVisitor::visitProjectNode(ProjectNode *projectNode) +{ + const QString path = projectNode->path(); + if (!m_files->proFiles.contains(path)) + m_files->proFiles.append(path); + visitFolderNode(projectNode); +} + +void ProjectFilesVisitor::visitFolderNode(FolderNode *folderNode) +{ + foreach (FileNode *fileNode, folderNode->fileNodes()) { + const QString path = fileNode->path(); + const int type = fileNode->fileType(); + QStringList &targetList = fileNode->isGenerated() ? m_files->generatedFiles[type] : m_files->files[type]; + if (!targetList.contains(path)) + targetList.push_back(path); + } +} + +} +} + +// ----------- Qt4ProjectFile +Qt4ProjectFile::Qt4ProjectFile(Qt4Project *project, const QString &filePath, QObject *parent) + : Core::IFile(parent), + m_mimeType(QLatin1String(Qt4ProjectManager::Constants::PROFILE_MIMETYPE)), + m_project(project), + m_filePath(filePath) +{ +} + +bool Qt4ProjectFile::save(const QString &) +{ + Core::IFile *file = fileFromCache(); + return file && file->save(); +} + +QString Qt4ProjectFile::fileName() const +{ + return m_filePath; +} + +QString Qt4ProjectFile::defaultPath() const +{ + return QString(); +} + +QString Qt4ProjectFile::suggestedFileName() const +{ + return QString(); +} + +QString Qt4ProjectFile::mimeType() const +{ + return m_mimeType; +} + +bool Qt4ProjectFile::isModified() const +{ + Core::IFile *file = fileFromCache(); + return file && fileFromCache()->isModified(); +} + +bool Qt4ProjectFile::isReadOnly() const +{ + Core::IFile *file = fileFromCache(); + return file && fileFromCache()->isReadOnly(); +} + +bool Qt4ProjectFile::isSaveAsAllowed() const +{ + return false; +} + +void Qt4ProjectFile::modified(Core::IFile::ReloadBehavior *) +{ +} + +Core::IFile *Qt4ProjectFile::fileFromCache() const +{ + ProFileCache *cache = m_project->qt4ProjectManager()->proFileCache(); + Core::IFile *fi = cache->fileInterface(fileName()); + if (!fi && debug) + qWarning() << "Could not retrieve IFile interface from ProFileCache"; + return fi; +} + +/*! + /class Qt4Project + + Qt4Project manages information about an individual Qt 4 (.pro) project file. + */ + +Qt4Project::Qt4Project(Qt4Manager *manager, const QString& fileName) : + m_manager(manager), + m_rootProjectNode(new Qt4ProFileNode(this, fileName, this)), + m_nodesWatcher(new Internal::Qt4NodesWatcher(this)), + m_fileInfo(new Qt4ProjectFile(this, fileName, this)), + m_isApplication(true), + m_projectFiles(new Qt4ProjectFiles) +{ + m_rootProjectNode->registerWatcher(m_nodesWatcher); + connect(m_nodesWatcher, SIGNAL(foldersAdded()), this, SLOT(updateFileList())); + connect(m_nodesWatcher, SIGNAL(foldersRemoved()), this, SLOT(updateFileList())); + connect(m_nodesWatcher, SIGNAL(filesAdded()), this, SLOT(updateFileList())); + connect(m_nodesWatcher, SIGNAL(filesRemoved()), this, SLOT(updateFileList())); + connect(m_nodesWatcher, SIGNAL(proFileUpdated(Qt4ProjectManager::Internal::Qt4ProFileNode *)), + this, SLOT(scheduleUpdateCodeModel())); + + connect(qt4ProjectManager()->versionManager(), SIGNAL(defaultQtVersionChanged()), + this, SLOT(defaultQtVersionChanged())); + connect(qt4ProjectManager()->versionManager(), SIGNAL(qtVersionsChanged()), + this, SLOT(qtVersionsChanged())); + + m_updateCodeModelTimer.setSingleShot(true); + m_updateCodeModelTimer.setInterval(20); + connect(&m_updateCodeModelTimer, SIGNAL(timeout()), this, SLOT(updateCodeModel())); +} + +Qt4Project::~Qt4Project() +{ + delete m_projectFiles; +} + +void Qt4Project::defaultQtVersionChanged() +{ + if (qtVersionId(activeBuildConfiguration()) == 0) + update(); +} + +void Qt4Project::qtVersionsChanged() +{ + foreach (QString bc, buildConfigurations()) { + if (!qt4ProjectManager()->versionManager()->version(qtVersionId(bc))->isValid()) { + setQtVersion(bc, 0); + if(bc == activeBuildConfiguration()) + update(); + } + } +} + +void Qt4Project::updateFileList() +{ + Qt4ProjectFiles newFiles; + ProjectFilesVisitor::findProjectFiles(m_rootProjectNode, &newFiles); + if (newFiles != *m_projectFiles) { + *m_projectFiles = newFiles; + emit fileListChanged(); + if (debug) + qDebug() << Q_FUNC_INFO << *m_projectFiles; + } +} + +void Qt4Project::restoreSettingsImpl(PersistentSettingsReader &settingsReader) +{ + Project::restoreSettingsImpl(settingsReader); + + addDefaultBuild(); + + // Ensure that the qt version in each build configuration is valid + // or if not, is reset to the default + foreach (const QString &bc, buildConfigurations()) + qtVersionId(bc); + + update(); + + // restored old runconfigurations + if (runConfigurations().isEmpty()) { + // Oha no runConfigurations, add some + QList<Qt4ProFileNode *> list; + collectApplicationProFiles(list, m_rootProjectNode); + + if (!list.isEmpty()) { + foreach (Qt4ProFileNode *node, list) { + QSharedPointer<RunConfiguration> rc(new Qt4RunConfiguration(this, node->path())); + addRunConfiguration(rc); + } + setActiveRunConfiguration(runConfigurations().first()); + } else { + QSharedPointer<RunConfiguration> rc(new ProjectExplorer::CustomExecutableRunConfiguration(this)); + addRunConfiguration(rc); + setActiveRunConfiguration(rc); + m_isApplication = false; + } + } + + // Now connect + connect(m_nodesWatcher, SIGNAL(foldersAboutToBeAdded(FolderNode *, const QList<FolderNode*> &)), + this, SLOT(foldersAboutToBeAdded(FolderNode *, const QList<FolderNode*> &))); + connect(m_nodesWatcher, SIGNAL(foldersAdded()), this, SLOT(checkForNewApplicationProjects())); + + connect(m_nodesWatcher, SIGNAL(foldersRemoved()), this, SLOT(checkForDeletedApplicationProjects())); + + connect(m_nodesWatcher, SIGNAL(projectTypeChanged(Qt4ProjectManager::Internal::Qt4ProFileNode *, + const Qt4ProjectManager::Internal::Qt4ProjectType, + const Qt4ProjectManager::Internal::Qt4ProjectType)), + this, SLOT(projectTypeChanged(Qt4ProjectManager::Internal::Qt4ProFileNode *, + const Qt4ProjectManager::Internal::Qt4ProjectType, + const Qt4ProjectManager::Internal::Qt4ProjectType))); + + connect(m_nodesWatcher, SIGNAL(proFileUpdated(Qt4ProjectManager::Internal::Qt4ProFileNode *)), + this, SLOT(proFileUpdated(Qt4ProjectManager::Internal::Qt4ProFileNode *))); + +} + +void Qt4Project::saveSettingsImpl(ProjectExplorer::PersistentSettingsWriter &writer) +{ + Project::saveSettingsImpl(writer); +} + +namespace { + class FindQt4ProFiles: protected ProjectExplorer::NodesVisitor { + QList<Qt4ProFileNode *> m_proFiles; + + public: + QList<Qt4ProFileNode *> operator()(ProjectNode *root) + { + m_proFiles.clear(); + root->accept(this); + return m_proFiles; + } + + protected: + virtual void visitProjectNode(ProjectNode *projectNode) + { + if (Qt4ProFileNode *pro = qobject_cast<Qt4ProFileNode *>(projectNode)) + m_proFiles.append(pro); + } + }; +} + +void Qt4Project::scheduleUpdateCodeModel() +{ + m_updateCodeModelTimer.start(); +} + +void Qt4Project::updateCodeModel() +{ + if (debug) + qDebug()<<"Qt4Project::updateCodeModel()"; + + CppTools::CppModelManagerInterface *modelmanager = + m_manager->pluginManager()->getObject<CppTools::CppModelManagerInterface>(); + + if (! modelmanager) + return; + + QStringList allIncludePaths; + QStringList allFrameworkPaths; + + const QHash<QString, QString> versionInfo = qtVersion(activeBuildConfiguration())->versionInfo(); + const QString newQtIncludePath = versionInfo.value(QLatin1String("QT_INSTALL_HEADERS")); + const QString newQtLibsPath = versionInfo.value(QLatin1String("QT_INSTALL_LIBS")); + + QByteArray predefinedMacros; + QtVersion::ToolchainType t = qtVersion(activeBuildConfiguration())->toolchainType(); + if (t == QtVersion::MinGW || t == QtVersion::OTHER) { + QStringList list = rootProjectNode()->variableValue(Internal::CxxCompilerVar); + QString qmake_cxx = list.isEmpty() ? QString::null : list.first(); + qmake_cxx = environment(activeBuildConfiguration()).searchInPath(qmake_cxx); + m_preproc.setGcc(qmake_cxx); + predefinedMacros = m_preproc.predefinedMacros(); + foreach (HeaderPath headerPath, m_preproc.systemHeaderPaths()) { + if (headerPath.kind() == HeaderPath::FrameworkHeaderPath) + allFrameworkPaths.append(headerPath.path()); + else + allIncludePaths.append(headerPath.path()); + } + + } else if (t == QtVersion::MSVC || t == QtVersion::WINCE) { +#ifdef QTCREATOR_WITH_MSVC_INCLUDES + Environment env = environment(activeBuildConfiguration()); + allIncludePaths.append(env.value("INCLUDE").split(QLatin1Char(';'))); +#endif + predefinedMacros += + "#define __WIN32__\n" + "#define __WIN32\n" + "#define _WIN32\n" + "#define WIN32\n" + "#define __WINNT__\n" + "#define __WINNT\n" + "#define WINNT\n" + "#define _X86_\n" + "#define __MSVCRT__\n"; + } + + allIncludePaths.append(newQtIncludePath); + + QDir dir(newQtIncludePath); + foreach (QFileInfo info, dir.entryInfoList(QDir::Dirs)) { + if (! info.fileName().startsWith(QLatin1String("Qt"))) + continue; + allIncludePaths.append(info.absoluteFilePath()); + } + +#ifdef Q_OS_MAC + allFrameworkPaths.append(newQtLibsPath); + // put QtXXX.framework/Headers directories in include path since that qmake's behavior + QDir frameworkDir(newQtLibsPath); + foreach (QFileInfo info, frameworkDir.entryInfoList(QDir::Dirs)) { + if (! info.fileName().startsWith(QLatin1String("Qt"))) + continue; + allIncludePaths.append(info.absoluteFilePath()+"/Headers"); + } +#endif + + FindQt4ProFiles findQt4ProFiles; + QList<Qt4ProFileNode *> proFiles = findQt4ProFiles(rootProjectNode()); + QByteArray definedMacros; + + foreach (Qt4ProFileNode *pro, proFiles) { + foreach (const QString def, pro->variableValue(DefinesVar)) { + definedMacros += "#define "; + const int index = def.indexOf(QLatin1Char('=')); + if (index == -1) { + definedMacros += def.toLatin1(); + definedMacros += " 1\n"; + } else { + const QString name = def.left(index); + const QString value = def.mid(index + 1); + definedMacros += name.toLatin1(); + definedMacros += ' '; + definedMacros += value.toLocal8Bit(); + definedMacros += '\n'; + } + } + + const QStringList proIncludePaths = pro->variableValue(IncludePathVar); + foreach (QString includePath, proIncludePaths) { + if (allIncludePaths.contains(includePath)) + continue; + + allIncludePaths.append(includePath); + } + } + + QStringList files; + files += m_projectFiles->files[HeaderType]; + files += m_projectFiles->generatedFiles[HeaderType]; + files += m_projectFiles->files[SourceType]; + files += m_projectFiles->generatedFiles[SourceType]; + + CppTools::CppModelManagerInterface::ProjectInfo *pinfo = modelmanager->projectInfo(this); + + if (pinfo->defines == predefinedMacros && + pinfo->includePaths == allIncludePaths && + pinfo->frameworkPaths == allFrameworkPaths && + pinfo->sourceFiles == files) { + // Nothing to update... + } else { + pinfo->defines = predefinedMacros; + // pinfo->defines += definedMacros; // ### FIXME: me + pinfo->includePaths = allIncludePaths; + pinfo->frameworkPaths = allFrameworkPaths; + pinfo->sourceFiles = files; + + modelmanager->GC(); + modelmanager->updateSourceFiles(pinfo->sourceFiles); + } +} + + +/*! + Updates complete project + */ +void Qt4Project::update() +{ + // TODO Maybe remove this method completely? + m_rootProjectNode->update(); + //updateCodeModel(); +} + +ProFileReader *Qt4Project::createProFileReader() const +{ + ProFileReader *reader = new ProFileReader(m_manager->proFileCache()); + connect(reader, SIGNAL(errorFound(const QString&)), + this, SLOT(proFileParseError(const QString&))); + QtVersion *version = qtVersion(activeBuildConfiguration()); + if (version->isValid()) { + reader->setQtVersion(version); + } + return reader; +} + +/*! + Returns whether the project is an application, or has an application as a subproject. + */ +bool Qt4Project::isApplication() const +{ + return m_isApplication; +} + +ProjectExplorer::ProjectExplorerPlugin *Qt4Project::projectExplorer() const +{ + return m_manager->projectExplorer(); +} + +ProjectExplorer::IProjectManager *Qt4Project::projectManager() const +{ + return m_manager; +} + +Qt4Manager *Qt4Project::qt4ProjectManager() const +{ + return m_manager; +} + +QString Qt4Project::name() const +{ + return QFileInfo(file()->fileName()).completeBaseName(); +} + +Core::IFile *Qt4Project::file() const +{ + return m_fileInfo; +} + +QStringList Qt4Project::files(FilesMode fileMode) const +{ + QStringList files; + for (int i = 0; i < FileTypeSize; ++i) { + files += m_projectFiles->files[i]; + if (fileMode == AllFiles) + files += m_projectFiles->generatedFiles[i]; + } + files += m_projectFiles->proFiles; + return files; +} + +QList<Core::IFile *> Qt4Project::dependencies() +{ + QList<Core::IFile *> result; + ProFileCache *cache = m_manager->proFileCache(); + foreach (const QString &file, cache->dependencies(m_rootProjectNode)) { + result << cache->fileInterface(file); + } + return result; +} + +QList<ProjectExplorer::Project*> Qt4Project::dependsOn() +{ + // NBS implement dependsOn + return QList<Project *>(); +} + +void Qt4Project::addDefaultBuild() +{ + if (buildConfigurations().isEmpty()) { + // We don't have any buildconfigurations, so this is a new project + // The Project Load Wizard is a work of art + // It will ask the user what kind of build setup he want + // It will add missing Qt Versions + // And get the project into a buildable state + + //TODO have a better check wheter there is already a configuration? + QMakeStep *qmakeStep = 0; + MakeStep *makeStep = 0; + GdbMacrosBuildStep *gdbmacrostep; + + gdbmacrostep = new GdbMacrosBuildStep(this); + insertBuildStep(0, gdbmacrostep); + + qmakeStep = new QMakeStep(this); + qmakeStep->setValue("mkspec", ""); + insertBuildStep(1, qmakeStep); + + makeStep = new MakeStep(this); + insertBuildStep(2, makeStep); + + MakeStep* cleanStep = new MakeStep(this); + cleanStep->setValue("clean", true); + insertCleanStep(0, cleanStep); + + ProjectLoadWizard wizard(this); + wizard.execDialog(); + } else { + // Restoring configuration + // Do we already have a gdbmacrobuildstep? + // If not add it and disable linking of debugging helper + // TODO + } +} + +void Qt4Project::newBuildConfiguration(const QString &buildConfiguration) +{ + Q_UNUSED(buildConfiguration); +} + +void Qt4Project::proFileParseError(const QString &errorMessage) +{ + m_manager->core()->messageManager()->printToOutputPane(errorMessage); +} + +Qt4ProFileNode *Qt4Project::rootProjectNode() const +{ + return m_rootProjectNode; +} + +ProjectExplorer::Environment Qt4Project::baseEnvironment(const QString &buildConfiguration) const +{ + Environment env = useSystemEnvironment(buildConfiguration) ? Environment(QProcess::systemEnvironment()) : Environment(); + env = qtVersion(buildConfiguration)->addToEnvironment(env); + return env; +} + +ProjectExplorer::Environment Qt4Project::environment(const QString &buildConfiguration) const +{ + Environment env = baseEnvironment(buildConfiguration); + env.modify(userEnvironmentChanges(buildConfiguration)); + return env; +} + +QString Qt4Project::buildDirectory(const QString &buildConfiguration) const +{ + QString workingDirectory; + if (value(buildConfiguration, "useShadowBuild").toBool()) + workingDirectory = value(buildConfiguration, "buildDirectory").toString(); + if(workingDirectory.isEmpty()) + workingDirectory = QFileInfo(file()->fileName()).absolutePath(); + return workingDirectory; +} + +void Qt4Project::setUseSystemEnvironment(const QString &buildConfiguration, bool b) +{ + setValue(buildConfiguration, "clearSystemEnvironment", !b); +} + +bool Qt4Project::useSystemEnvironment(const QString &buildConfiguration) const +{ + bool b = !(value(buildConfiguration, "clearSystemEnvironment").isValid() && value(buildConfiguration, "clearSystemEnvironment").toBool()); + return b; +} + +QString Qt4Project::qtDir(const QString &buildConfiguration) const +{ + QtVersion *version = qtVersion(buildConfiguration); + if (version) + return version->path(); + return QString::null; +} + +QtVersion *Qt4Project::qtVersion(const QString &buildConfiguration) const +{ + return m_manager->versionManager()->version(qtVersionId(buildConfiguration)); +} + +int Qt4Project::qtVersionId(const QString &buildConfiguration) const +{ + if (debug) + qDebug()<<"Looking for qtVersion ID of "<<buildConfiguration; + int id = 0; + QVariant vid = value(buildConfiguration, "QtVersionId"); + if(vid.isValid()) { + id = vid.toInt(); + if (m_manager->versionManager()->version(id)->isValid()) { + return id; + } else { + const_cast<Qt4Project *>(this)->setValue(buildConfiguration, "QtVersionId", 0); + return 0; + } + } else { + // Backward compatibilty, we might have just the name: + QString vname = value(buildConfiguration, "QtVersion").toString(); + if (debug) + qDebug()<<" Backward compatibility reading QtVersion"<<vname; + if(!vname.isEmpty()) { + const QList<QtVersion *> &versions = m_manager->versionManager()->versions(); + foreach (const QtVersion * const version, versions) { + if(version->name() == vname) { + if (debug) + qDebug()<<"found name in versions"; + const_cast<Qt4Project *>(this)->setValue(buildConfiguration, "QtVersionId", version->uniqueId()); + return version->uniqueId(); + } + } + } + } + if (debug) + qDebug()<<" using qtversion with id ="<<id; + // Nothing found, reset to default + const_cast<Qt4Project *>(this)->setValue(buildConfiguration, "QtVersionId", id); + return id; +} + +void Qt4Project::setQtVersion(const QString &buildConfiguration, int id) +{ + setValue(buildConfiguration, "QtVersionId", id); +} + +BuildStepConfigWidget *Qt4Project::createConfigWidget() +{ + return new Qt4BuildConfigWidget(this); +} + +QList<BuildStepConfigWidget*> Qt4Project::subConfigWidgets() +{ + QList<BuildStepConfigWidget*> subWidgets; + subWidgets << new Qt4BuildEnvironmentWidget(this); + return subWidgets; +} + +QList<ProjectExplorer::EnvironmentItem> Qt4Project::userEnvironmentChanges(const QString &buildConfig) const +{ + return EnvironmentItem::fromStringList(value(buildConfig, "userEnvironmentChanges").toStringList()); +} + +void Qt4Project::setUserEnvironmentChanges(const QString &buildConfig, const QList<ProjectExplorer::EnvironmentItem> &diff) +{ + setValue(buildConfig, "userEnvironmentChanges", EnvironmentItem::toStringList(diff)); +} + +/// ************************** +/// Qt4ProjectBuildConfigWidget +/// ************************** + + +void Qt4Project::collectApplicationProFiles(QList<Qt4ProFileNode *> &list, Qt4ProFileNode *node) +{ + if (node->projectType() == Internal::ApplicationTemplate + || node->projectType() == Internal::ScriptTemplate) { + list.append(node); + } + foreach (ProjectNode *n, node->subProjectNodes()) { + Qt4ProFileNode *qt4ProFileNode = qobject_cast<Qt4ProFileNode *>(n); + if (qt4ProFileNode) + collectApplicationProFiles(list, qt4ProFileNode); + } +} + +void Qt4Project::foldersAboutToBeAdded(FolderNode *, const QList<FolderNode*> &nodes) +{ + QList<Qt4ProFileNode *> list; + foreach (FolderNode *node, nodes) { + Qt4ProFileNode *qt4ProFileNode = qobject_cast<Qt4ProFileNode *>(node); + if (qt4ProFileNode) + collectApplicationProFiles(list, qt4ProFileNode); + } + m_applicationProFileChange = list; +} + +void Qt4Project::checkForNewApplicationProjects() +{ + // Check all new project nodes + // against all runConfigurations + + foreach (Qt4ProFileNode *qt4proFile, m_applicationProFileChange) { + bool found = false; + foreach (QSharedPointer<RunConfiguration> rc, runConfigurations()) { + QSharedPointer<Qt4RunConfiguration> qtrc = rc.dynamicCast<Qt4RunConfiguration>(); + if (qtrc && qtrc->proFilePath() == qt4proFile->path()) { + found = true; + break; + } + } + if (!found) { + QSharedPointer<Qt4RunConfiguration> newRc(new Qt4RunConfiguration(this, qt4proFile->path())); + addRunConfiguration(newRc); + m_isApplication = true; + } + } +} + +void Qt4Project::checkForDeletedApplicationProjects() +{ + QStringList paths; + foreach (Qt4ProFileNode * node, applicationProFiles()) + paths.append(node->path()); + + qDebug()<<"Still existing paths :"<<paths; + + QList<QSharedPointer<Qt4RunConfiguration> > removeList; + foreach (QSharedPointer<RunConfiguration> rc, runConfigurations()) { + if (QSharedPointer<Qt4RunConfiguration> qt4rc = rc.dynamicCast<Qt4RunConfiguration>()) { + if (!paths.contains(qt4rc->proFilePath())) { + removeList.append(qt4rc); + qDebug()<<"Removing runConfiguration for "<<qt4rc->proFilePath(); + } + } + } + + bool resetActiveRunConfiguration = false; + QSharedPointer<RunConfiguration> rc(new ProjectExplorer::CustomExecutableRunConfiguration(this)); + foreach(QSharedPointer<Qt4RunConfiguration> qt4rc, removeList) { + removeRunConfiguration(qt4rc); + if (activeRunConfiguration() == qt4rc) + resetActiveRunConfiguration = true; + } + + if (runConfigurations().isEmpty()) { + QSharedPointer<RunConfiguration> rc(new ProjectExplorer::CustomExecutableRunConfiguration(this)); + addRunConfiguration(rc); + setActiveRunConfiguration(rc); + m_isApplication = false; + } else if (resetActiveRunConfiguration) { + setActiveRunConfiguration(runConfigurations().first()); + } +} + +QList<Qt4ProFileNode *> Qt4Project::applicationProFiles() const +{ + QList<Qt4ProFileNode *> list; + collectApplicationProFiles(list, rootProjectNode()); + return list; +} + +void Qt4Project::projectTypeChanged(Qt4ProFileNode *node, const Qt4ProjectType oldType, const Qt4ProjectType newType) +{ + if (oldType == Internal::ApplicationTemplate + || oldType == Internal::ScriptTemplate) { + // check wheter we need to delete a Run Configuration + checkForDeletedApplicationProjects(); + } + + if (newType == Internal::ApplicationTemplate + || newType == Internal::ScriptTemplate) { + // add a new Run Configuration + m_applicationProFileChange.clear(); + m_applicationProFileChange.append(node); + checkForNewApplicationProjects(); + } +} + +void Qt4Project::proFileUpdated(Qt4ProjectManager::Internal::Qt4ProFileNode *node) +{ + foreach (QSharedPointer<RunConfiguration> rc, runConfigurations()) { + if (QSharedPointer<Qt4RunConfiguration> qt4rc = rc.dynamicCast<Qt4RunConfiguration>()) { + if (qt4rc->proFilePath() == node->path()) { + qt4rc->updateCachedValues(); + } + } + } +} + + +QMakeStep *Qt4Project::qmakeStep() const +{ + QMakeStep *qs = 0; + foreach(BuildStep *bs, buildSteps()) + if ( (qs = qobject_cast<QMakeStep *>(bs)) != 0) + return qs; + return 0; +} + +MakeStep *Qt4Project::makeStep() const +{ + MakeStep *qs = 0; + foreach(BuildStep *bs, buildSteps()) + if ((qs = qobject_cast<MakeStep *>(bs)) != 0) + return qs; + return 0; +} diff --git a/src/plugins/qt4projectmanager/qt4project.h b/src/plugins/qt4projectmanager/qt4project.h new file mode 100644 index 00000000000..c67c5dc9bde --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4project.h @@ -0,0 +1,239 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QT4PROJECT_H +#define QT4PROJECT_H + +#include "qtversionmanager.h" +#include "qt4nodes.h" +#include "gccpreprocessor.h" +#include "qmakestep.h" +#include "makestep.h" + +#include <coreplugin/ifile.h> +#include <projectexplorer/applicationrunconfiguration.h> +#include <projectexplorer/projectnodes.h> + +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QtCore/QStringList> +#include <QtCore/QPointer> +#include <QtGui/QDirModel> +#include "qtextended_integration.h" + +namespace Core { + class IWizard; +} + +namespace CppTools { + class ICppCodeModel; +} + +QT_BEGIN_NAMESPACE +class ProFile; +QT_END_NAMESPACE + +namespace Qt4ProjectManager { + +namespace Internal { + class ProFileReader; + class DeployHelperRunStep; + class FileItem; + class Qt4ProFileNode; + class Qt4RunConfiguration; + class GCCPreprocessor; + struct Qt4ProjectFiles; +} + +class QMakeStep; +class MakeStep; + +class Qt4Manager; +class Qt4Project; +class Qt4RunStep; + +class Qt4ProjectFile + : public Core::IFile +{ + Q_OBJECT + + // needed for createProFileReader + friend class Internal::Qt4RunConfiguration; + +public: + Qt4ProjectFile(Qt4Project *project, const QString &filePath, QObject *parent = 0); + + bool save(const QString &fileName = QString()); + QString fileName() const; + + QString defaultPath() const; + QString suggestedFileName() const; + virtual QString mimeType() const; + + bool isModified() const; + bool isReadOnly() const; + bool isSaveAsAllowed() const; + + void modified(Core::IFile::ReloadBehavior *behavior); + +private: + Core::IFile *fileFromCache() const; + + const QString m_mimeType; + Qt4Project *m_project; + QString m_filePath; +}; + +class Qt4Project + : public ProjectExplorer::Project +{ + Q_OBJECT + +public: + explicit Qt4Project(Qt4Manager *manager, const QString &proFile); + virtual ~Qt4Project(); + + QString name() const; + Core::IFile *file() const; + ProjectExplorer::IProjectManager *projectManager() const; + Qt4Manager *qt4ProjectManager() const; + + QList<Core::IFile *> dependencies(); //NBS remove + QList<ProjectExplorer::Project *>dependsOn(); + + bool isApplication() const; + + Internal::Qt4ProFileNode *rootProjectNode() const; + + virtual QStringList files(FilesMode fileMode) const; + + //building environment + ProjectExplorer::Environment environment(const QString &buildConfiguration) const; + ProjectExplorer::Environment baseEnvironment(const QString &buildConfiguration) const; + void setUserEnvironmentChanges(const QString &buildConfig, const QList<ProjectExplorer::EnvironmentItem> &diff); + QList<ProjectExplorer::EnvironmentItem> userEnvironmentChanges(const QString &buildConfig) const; + + virtual QString buildDirectory(const QString &buildConfiguration) const; + + //Qt4Project specific(?) + bool useSystemEnvironment(const QString &buildConfiguration) const; + void setUseSystemEnvironment(const QString &buildConfiguration, bool b); + + // returns the CONFIG variable from the .pro file + QStringList qmakeConfig() const; + // returns the qtdir (depends on the current QtVersion) + QString qtDir(const QString &buildConfiguration) const; + //returns the qtVersion, if the project is set to use the default qt version, then + // that is returned + // to check wheter the project uses the default qt version use qtVersionId + Internal::QtVersion *qtVersion(const QString &buildConfiguration) const; + + // returns the id of the qt version, if the project is using the default qt version + // this function returns 0 + int qtVersionId(const QString &buildConfiguration) const; + //returns the name of the qt version, might be QString::Null, which means default qt version + // qtVersion is in general the better method to use + QString qtVersionName(const QString &buildConfiguration) const; + + ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); + QList<ProjectExplorer::BuildStepConfigWidget*> subConfigWidgets(); + + void setQtVersion(const QString &buildConfiguration, int id); + virtual void newBuildConfiguration(const QString &buildConfiguration); + + QList<Internal::Qt4ProFileNode *> applicationProFiles() const; + Internal::ProFileReader *createProFileReader() const; + + // Those functions arein a few places. + // The drawback is that we shouldn't actually depend on them beeing always there + // That is generally the stuff that is asked should normally be transfered to + // Qt4Project * + // So that we can later enable people to build qt4projects the way they would like + QMakeStep *qmakeStep() const; + MakeStep *makeStep() const; + +public slots: + void update(); + void proFileParseError(const QString &errorMessage); + void scheduleUpdateCodeModel(); + +private slots: + void updateCodeModel(); + void defaultQtVersionChanged(); + void qtVersionsChanged(); + void updateFileList(); + + void foldersAboutToBeAdded(FolderNode *, const QList<FolderNode*> &); + void checkForNewApplicationProjects(); + void checkForDeletedApplicationProjects(); + void projectTypeChanged(Qt4ProjectManager::Internal::Qt4ProFileNode *node, + const Qt4ProjectManager::Internal::Qt4ProjectType oldType, + const Qt4ProjectManager::Internal::Qt4ProjectType newType); + void proFileUpdated(Qt4ProjectManager::Internal::Qt4ProFileNode *node); + +protected: + virtual void restoreSettingsImpl(ProjectExplorer::PersistentSettingsReader &settingsReader); + virtual void saveSettingsImpl(ProjectExplorer::PersistentSettingsWriter &writer); + +private: + static void collectApplicationProFiles(QList<Internal::Qt4ProFileNode *> &list, Internal::Qt4ProFileNode *node); + + QList<Internal::Qt4ProFileNode *> m_applicationProFileChange; + ProjectExplorer::ProjectExplorerPlugin *projectExplorer() const; + + void addDefaultBuild(); + + static QString qmakeVarName(ProjectExplorer::FileType type); + + Qt4Manager *m_manager; + Internal::Qt4ProFileNode *m_rootProjectNode; + Internal::Qt4NodesWatcher *m_nodesWatcher; + + Qt4ProjectFile *m_fileInfo; + bool m_isApplication; + + // Current configuration + QString m_oldQtIncludePath; + QString m_oldQtLibsPath; + + // cached lists of all of files + Internal::Qt4ProjectFiles *m_projectFiles; + + QTimer m_updateCodeModelTimer; + Internal::GCCPreprocessor m_preproc; + + friend class Qt4ProjectFile; +}; + +} // namespace Qt4ProjectManager + +#endif // QT4PROJECT_H diff --git a/src/plugins/qt4projectmanager/qt4projectmanager.cpp b/src/plugins/qt4projectmanager/qt4projectmanager.cpp new file mode 100644 index 00000000000..43c509d234c --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4projectmanager.cpp @@ -0,0 +1,229 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qt4projectmanager.h" +#include "qt4projectmanagerconstants.h" +#include "qt4projectmanagerplugin.h" +#include "qt4nodes.h" +#include "qt4project.h" +#include "profilecache.h" +#include "profilereader.h" +#include "qtversionmanager.h" +#include "qmakestep.h" + +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/basefilewizard.h> +#include <coreplugin/messagemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/iversioncontrol.h> +#include <coreplugin/vcsmanager.h> +#include <projectexplorer/project.h> +#include <utils/listutils.h> + +#include <QtCore/QVariant> +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QCoreApplication> +#include <QtCore/QLinkedList> +#include <QtGui/QMenu> +#include <QtGui/QFileDialog> +#include <QtGui/QMessageBox> + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; + +using ProjectExplorer::BuildStep; +using ProjectExplorer::FileType; +using ProjectExplorer::HeaderType; +using ProjectExplorer::SourceType; +using ProjectExplorer::FormType; +using ProjectExplorer::ResourceType; +using ProjectExplorer::UnknownFileType; + +// Known file types of a Qt 4 project +static const char* qt4FileTypes[] = {"CppHeaderFiles", "CppSourceFiles", "Qt4FormFiles", "Qt4ResourceFiles" }; + +Qt4Manager::Qt4Manager(Qt4ProjectManagerPlugin *plugin, Core::ICore *core) : + m_mimeType(QLatin1String(Qt4ProjectManager::Constants::PROFILE_MIMETYPE)), + m_plugin(plugin), + m_core(core), + m_projectExplorer(0), + m_contextProject(0), + m_languageID(0), + m_proFileCache(0) +{ + m_languageID = m_core->uniqueIDManager()-> + uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX); + m_proFileCache = new ProFileCache(this); +} + +Qt4Manager::~Qt4Manager() +{ +} + +void Qt4Manager::init() +{ + m_projectExplorer = m_core->pluginManager()->getObject<ProjectExplorer::ProjectExplorerPlugin>(); +} + +int Qt4Manager::projectContext() const +{ + return m_plugin->projectContext(); +} + +int Qt4Manager::projectLanguage() const +{ + return m_languageID; +} + +QString Qt4Manager::mimeType() const +{ + return m_mimeType; +} + +ProjectExplorer::Project* Qt4Manager::openProject(const QString &fileName) +{ + typedef QMultiMap<QString, QString> DependencyMap; + const QString dotSubDir = QLatin1String(".subdir"); + const QString dotDepends = QLatin1String(".depends"); + const QChar slash = QLatin1Char('/'); + + QString errorMessage; + + m_core->messageManager()->displayStatusBarMessage(tr("Loading project %1 ...").arg(fileName), 50000); + + // TODO Make all file paths relative & remove this hack + // We convert the path to an absolute one here because qt4project.cpp + // && profileevaluator use absolute/canonical file paths all over the place + // Correct fix would be to remove these calls ... + QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath(); + + if (canonicalFilePath.isEmpty()) { + m_core->messageManager()->printToOutputPane(tr("Failed opening project '%1': Project file does not exist").arg(canonicalFilePath)); + m_core->messageManager()->displayStatusBarMessage(tr("Failed opening project"), 5000); + return 0; + } + + foreach (ProjectExplorer::Project *pi, projectExplorer()->session()->projects()) { + if (canonicalFilePath == pi->file()->fileName()) { + m_core->messageManager()->printToOutputPane(tr("Failed opening project '%1': Project already open").arg(canonicalFilePath)); + m_core->messageManager()->displayStatusBarMessage(tr("Failed opening project"), 5000); + return 0; + } + } + + m_core->messageManager()->displayStatusBarMessage(tr("Opening %1 ...").arg(fileName)); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + + Qt4Project *pro = new Qt4Project(this, canonicalFilePath); + + m_core->messageManager()->displayStatusBarMessage(tr("Done opening project"), 5000); + return pro; +} + +ProjectExplorer::ProjectExplorerPlugin *Qt4Manager::projectExplorer() const +{ + return m_projectExplorer; +} + +Core::ICore *Qt4Manager::core() const +{ + return m_core; +} + +ExtensionSystem::PluginManager *Qt4Manager::pluginManager() const +{ + return m_core->pluginManager(); +} + +ProjectExplorer::Node *Qt4Manager::contextNode() const +{ + return m_contextNode; +} + +void Qt4Manager::setContextNode(ProjectExplorer::Node *node) +{ + m_contextNode = node; +} + +void Qt4Manager::setContextProject(ProjectExplorer::Project *project) +{ + m_contextProject = project; +} + +ProjectExplorer::Project *Qt4Manager::contextProject() const +{ + return m_contextProject; +} + +QtVersionManager *Qt4Manager::versionManager() const +{ + return m_plugin->versionManager(); +} + +void Qt4Manager::runQMake() +{ + runQMake(m_projectExplorer->currentProject()); +} + +void Qt4Manager::runQMakeContextMenu() +{ + runQMake(m_contextProject); +} + +void Qt4Manager::runQMake(ProjectExplorer::Project *p) +{ + QMakeStep *qmakeStep = qobject_cast<Qt4Project *>(p)->qmakeStep(); + //found qmakeStep, now use it + qmakeStep->setForced(true); + const QString &config = p->activeBuildConfiguration(); + m_projectExplorer->buildManager()->appendStep(qmakeStep, config); +} + +QString Qt4Manager::fileTypeId(ProjectExplorer::FileType type) +{ + switch (type) { + case HeaderType: + return QLatin1String(qt4FileTypes[0]); + case SourceType: + return QLatin1String(qt4FileTypes[1]); + case FormType: + return QLatin1String(qt4FileTypes[2]); + case ResourceType: + return QLatin1String(qt4FileTypes[3]); + case UnknownFileType: + default: + break; + } + return QString(); +} diff --git a/src/plugins/qt4projectmanager/qt4projectmanager.h b/src/plugins/qt4projectmanager/qt4projectmanager.h new file mode 100644 index 00000000000..86126cae5e7 --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4projectmanager.h @@ -0,0 +1,123 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QT4PROJECTMANAGER_H +#define QT4PROJECTMANAGER_H + +#include <projectexplorer/iprojectmanager.h> +#include <projectexplorer/projectnodes.h> +#include <coreplugin/dialogs/iwizard.h> + +#include <QtCore/QModelIndex> + +namespace ExtensionSystem { +class PluginManager; +} + +namespace Core { +class ICore; +} + +namespace ProjectExplorer { +class Project; +class ProjectExplorerPlugin; +} + +namespace Qt4ProjectManager { + +namespace Internal { +class Qt4Builder; +class ProFileCache; +class ProFileEditor; +class Qt4ProjectManagerPlugin; +class QtVersionManager; +} + +class Qt4Project; + +class Qt4Manager + : public ProjectExplorer::IProjectManager +{ + Q_OBJECT + +public: + Qt4Manager(Internal::Qt4ProjectManagerPlugin *plugin, Core::ICore *core); + ~Qt4Manager(); + + void init(); + inline Internal::ProFileCache *proFileCache() const { return m_proFileCache; } + + ProjectExplorer::ProjectExplorerPlugin *projectExplorer() const; + ExtensionSystem::PluginManager *pluginManager() const; + Core::ICore *core() const; + + //ProjectExplorer::IProjectManager + int projectContext() const; + int projectLanguage() const; + + virtual QString mimeType() const; + ProjectExplorer::Project* openProject(const QString &fileName); + + // Context information used in the slot implementations + ProjectExplorer::Node *contextNode() const; + void setContextNode(ProjectExplorer::Node *node); + void setContextProject(ProjectExplorer::Project *project); + ProjectExplorer::Project *contextProject() const; + + Internal::QtVersionManager *versionManager() const; + + // Return the id string of a file + static QString fileTypeId(ProjectExplorer::FileType type); + +public slots: + void runQMake(); + void runQMakeContextMenu(); + +private: + void runQMake(ProjectExplorer::Project *p); + + const QString m_mimeType; + Internal::Qt4ProjectManagerPlugin *m_plugin; + Core::ICore *m_core; + ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer; + + ProjectExplorer::Node *m_contextNode; + ProjectExplorer::Project *m_contextProject; + + int m_languageID; + + Internal::ProFileCache *m_proFileCache; +}; + +} //namespace Qt4ProjectManager + +#endif //QT4PROJECTMANAGER_H diff --git a/src/plugins/qt4projectmanager/qt4projectmanager.pro b/src/plugins/qt4projectmanager/qt4projectmanager.pro new file mode 100644 index 00000000000..0118cf967c6 --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4projectmanager.pro @@ -0,0 +1,101 @@ +TEMPLATE = lib +TARGET = Qt4ProjectManager +QT += network +include(../../qworkbenchplugin.pri) +include(qt4projectmanager_dependencies.pri) +HEADERS = qt4projectmanagerplugin.h \ + qt4projectmanager.h \ + qt4projectmanagerenums.h \ + qtversionmanager.h \ + qt4project.h \ + qt4nodes.h \ + profileeditor.h \ + profilehighlighter.h \ + profileeditorfactory.h \ + profilereader.h \ + profilecache.h \ + wizards/qtprojectparameters.h \ + wizards/guiappwizard.h \ + wizards/consoleappwizard.h \ + wizards/consoleappwizarddialog.h \ + wizards/libraryparameters.h \ + wizards/librarywizard.h \ + wizards/librarywizarddialog.h \ + wizards/guiappwizarddialog.h \ + wizards/modulespage.h \ + wizards/filespage.h \ + wizards/qtwizard.h \ + qt4projectmanagerconstants.h \ + makestep.h \ + qmakestep.h \ + qmakebuildstepfactory.h \ + gccparser.h \ + msvcparser.h \ + buildparserfactory.h \ + deployhelper.h \ + msvcenvironment.h \ + cesdkhandler.h \ + embeddedpropertiespage.h \ + qt4runconfiguration.h \ + speinfo.h \ + headerpath.h \ + gccpreprocessor.h \ + qt4buildconfigwidget.h \ + qt4buildenvironmentwidget.h \ + projectloadwizard.h \ + directorywatcher.h \ + gdbmacrosbuildstep.h + +SOURCES = qt4projectmanagerplugin.cpp \ + qt4projectmanager.cpp \ + qtversionmanager.cpp \ + qt4project.cpp \ + qt4nodes.cpp \ + profileeditor.cpp \ + profilehighlighter.cpp \ + profileeditorfactory.cpp \ + profilereader.cpp \ + profilecache.cpp \ + wizards/qtprojectparameters.cpp \ + wizards/guiappwizard.cpp \ + wizards/consoleappwizard.cpp \ + wizards/consoleappwizarddialog.cpp \ + wizards/libraryparameters.cpp \ + wizards/librarywizard.cpp \ + wizards/librarywizarddialog.cpp \ + wizards/guiappwizarddialog.cpp \ + wizards/modulespage.cpp \ + wizards/filespage.cpp \ + wizards/qtwizard.cpp \ + makestep.cpp \ + qmakestep.cpp \ + qmakebuildstepfactory.cpp \ + gccparser.cpp \ + msvcparser.cpp \ + buildparserfactory.cpp \ + deployhelper.cpp \ + msvcenvironment.cpp \ + cesdkhandler.cpp \ + embeddedpropertiespage.cpp \ + qt4runconfiguration.cpp \ + speinfo.cpp \ + gccpreprocessor.cpp \ + qt4buildconfigwidget.cpp \ + qt4buildenvironmentwidget.cpp \ + projectloadwizard.cpp \ + directorywatcher.cpp \ + gdbmacrosbuildstep.cpp + +FORMS = qtversionmanager.ui \ + envvariablespage.ui \ + enveditdialog.ui \ + proeditorcontainer.ui \ + makestep.ui \ + qmakestep.ui \ + qt4buildconfigwidget.ui \ + embeddedpropertiespage.ui \ + qt4buildenvironmentwidget.ui +RESOURCES = qt4projectmanager.qrc \ + wizards/wizards.qrc +include(../../../shared/proparser/proparser.pri) +DEFINES += QT_NO_CAST_TO_ASCII diff --git a/src/plugins/qt4projectmanager/qt4projectmanager.qrc b/src/plugins/qt4projectmanager/qt4projectmanager.qrc new file mode 100644 index 00000000000..f733770e2f4 --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4projectmanager.qrc @@ -0,0 +1,9 @@ +<RCC> + <qresource prefix="/qt4projectmanager" > + <file>images/window.png</file> + <file>images/run_qmake.png</file> + <file>images/run_qmake_small.png</file> + <file>images/qt_project.png</file> + <file>Qt4ProjectManager.mimetypes.xml</file> + </qresource> +</RCC> diff --git a/src/plugins/qt4projectmanager/qt4projectmanager_dependencies.pri b/src/plugins/qt4projectmanager/qt4projectmanager_dependencies.pri new file mode 100644 index 00000000000..df694f31fcc --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4projectmanager_dependencies.pri @@ -0,0 +1,4 @@ +include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/cpptools/cpptools.pri) +include(../../plugins/cppeditor/cppeditor.pri) +include(../../plugins/help/help.pri) diff --git a/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h b/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h new file mode 100644 index 00000000000..67e5985f5f5 --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4projectmanagerconstants.h @@ -0,0 +1,90 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QT4PROJECTMANAGERCONSTANTS_H +#define QT4PROJECTMANAGERCONSTANTS_H + +namespace Qt4ProjectManager { +namespace Constants { + +//contexts +const char * const C_PROFILEEDITOR = ".pro File Editor"; +const char * const C_PROFILEEDITOR_PANEL = ".pro File Editor (embedded)"; + +//settings pages +const char * const QT_CATEGORY = "Qt4"; +const char * const QTVERSION_PAGE = "Qt Versions"; +const char * const BUILD_ENVIRONMENT_PAGE = "Build Environments"; + +// kinds +const char * const PROJECT_KIND = "Qt4"; +const char * const PROFILE_EDITOR = ".pro File Editor"; +const char * const PROFILE_MIMETYPE = "application/vnd.nokia.qt.qmakeprofile"; +const char * const PROINCLUDEFILE_MIMETYPE = "application/vnd.nokia.qt.qmakeproincludefile"; +const char * const CPP_SOURCE_MIMETYPE = "text/x-c++src"; +const char * const CPP_HEADER_MIMETYPE = "text/x-c++hdr"; +const char * const FORM_MIMETYPE = "application/x-designer"; + +//actions +const char * const NEWMENU = "Qt4.NewMenu"; +const char * const PROJECT_NEWMENU_SEPARATOR = "Qt4.NewMenuSeparator"; +const char * const SUBPROJECT_NEWMENU_SEPARATOR = "Qt4.SubProjectNewMenuSeparator"; +const char * const ADDTOPROJECT = "Qt4.AddToProject"; +const char * const RUNQMAKE = "Qt4Builder.RunQMake"; +const char * const RUNQMAKECONTEXTMENU = "Qt4Builder.RunQMakeContextMenu"; + +//configurations +const char * const CONFIG_DEBUG = "debug"; +const char * const CONFIG_RELEASE = "release"; + +//global configurations +const char * const GC_BUILDCONFIG = "Qt4.BuildConfig"; +const char * const GC_QTVERSION = "Qt4.QtVersion"; +const char * const GC_COMPILER = "Qt4.Compiler"; + +// qmakestep +const char * const QMAKESTEP = "trolltech.qt4projectmanager.qmake"; +const char * const MAKESTEP = "trolltech.qt4projectmanager.make"; +const char * const GDBMACROSBUILDSTEP = "trolltech.qt4projectmanager.gdbmaros"; +const char * const QT4RUNSTEP = "trolltech.qt4projectmanager.qt4runstep"; + +// build parsers +const char * const BUILD_PARSER_MSVC = "BuildParser.MSVC"; +const char * const BUILD_PARSER_GCC = "BuildParser.Gcc"; + +// views +const char * const VIEW_DETAILED = "Qt4.View.Detailed"; +const char * const VIEW_PROFILESONLY = "Qt4.View.ProjectHierarchy"; +} // namespace Constants +} // namespace Qt4ProjectManager + +#endif // QT4PROJECTMANAGERCONSTANTS_H diff --git a/src/plugins/qt4projectmanager/qt4projectmanagerenums.h b/src/plugins/qt4projectmanager/qt4projectmanagerenums.h new file mode 100644 index 00000000000..e6a9883d06d --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4projectmanagerenums.h @@ -0,0 +1,49 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QT4PRO_ENUMS_H +#define QT4PRO_ENUMS_H + +namespace Qt4ProjectManager { +namespace Internal { + +enum ProfileFormats { + ProfileVariableFormat, + ProfileFunctionFormat, + ProfileCommentFormat, + NumProfileFormats +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif //QT4PRO_ENUMS_H diff --git a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp new file mode 100644 index 00000000000..cb599bf5145 --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp @@ -0,0 +1,235 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qt4projectmanagerplugin.h" +#include "qt4projectmanager.h" +#include "wizards/consoleappwizard.h" +#include "wizards/guiappwizard.h" +#include "wizards/librarywizard.h" +#include "profileeditorfactory.h" +#include "qt4projectmanagerconstants.h" +#include "qt4project.h" +#include "profilecache.h" +#include "qmakebuildstepfactory.h" +#include "buildparserfactory.h" +#include "qtversionmanager.h" +#include "embeddedpropertiespage.h" +#include "qt4runconfiguration.h" +#include "profilereader.h" +#include "gdbmacrosbuildstep.h" + +#include <projectexplorer/project.h> +#include <projectexplorer/projectnodes.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <texteditor/texteditoractionhandler.h> + +#include <QtCore/qplugin.h> +#include <QtGui/QMenu> +#include <QDebug> + +#ifdef WITH_TESTS +#include <QTest> +#include <extensionsystem/pluginmanager.h> +#endif + +using namespace Qt4ProjectManager::Internal; +using namespace Qt4ProjectManager; +using ProjectExplorer::Project; + +Qt4ProjectManagerPlugin::~Qt4ProjectManagerPlugin() +{ + //removeObject(m_embeddedPropertiesPage); + //delete m_embeddedPropertiesPage; + + removeObject(m_qtVersionManager); + delete m_qtVersionManager; + + removeObject(m_proFileEditorFactory); + delete m_proFileEditorFactory; + removeObject(m_qt4ProjectManager); + delete m_qt4ProjectManager; +} +/* +static Core::ICommand *createSeparator(Core::ActionManagerInterface *am, + QObject *parent, + const QString &name, + const QList<int> &context) +{ + QAction *tmpaction = new QAction(parent); + tmpaction->setSeparator(true); + return am->registerAction(tmpaction, name, context); +} +*/ +bool Qt4ProjectManagerPlugin::initialize(const QStringList & /*arguments*/, QString *errorMessage) +{ + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + if (!m_core->mimeDatabase()->addMimeTypes(QLatin1String(":qt4projectmanager/Qt4ProjectManager.mimetypes.xml"), errorMessage)) + return false; + + m_projectExplorer = m_core->pluginManager()->getObject<ProjectExplorer::ProjectExplorerPlugin>(); + + Core::ActionManagerInterface *am = m_core->actionManager(); + + //create and register objects + m_qt4ProjectManager = new Qt4Manager(this, m_core); + addObject(m_qt4ProjectManager); + + TextEditor::TextEditorActionHandler *editorHandler + = new TextEditor::TextEditorActionHandler(m_core, Constants::C_PROFILEEDITOR); + + m_proFileEditorFactory = new ProFileEditorFactory(m_qt4ProjectManager, editorHandler); + addObject(m_proFileEditorFactory); + + GuiAppWizard *guiWizard = new GuiAppWizard(m_core); + addAutoReleasedObject(guiWizard); + + ConsoleAppWizard *consoleWizard = new ConsoleAppWizard(m_core); + addAutoReleasedObject(consoleWizard); + + LibraryWizard *libWizard = new LibraryWizard(m_core); + addAutoReleasedObject(libWizard); + + addAutoReleasedObject(new QMakeBuildStepFactory); + addAutoReleasedObject(new MakeBuildStepFactory); + addAutoReleasedObject(new GdbMacrosBuildStepFactory); + + addAutoReleasedObject(new GccParserFactory); + addAutoReleasedObject(new MsvcParserFactory); + + m_qtVersionManager = new QtVersionManager; + addObject(m_qtVersionManager); + + addAutoReleasedObject(new Qt4RunConfigurationFactory); + addAutoReleasedObject(new Qt4RunConfigurationFactoryUser); + + // TODO reenable + //m_embeddedPropertiesPage = new EmbeddedPropertiesPage; + //addObject(m_embeddedPropertiesPage); + + //menus + Core::IActionContainer *mbuild = + am->actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT); + Core::IActionContainer *mproject = + am->actionContainer(ProjectExplorer::Constants::M_PROJECTCONTEXT); + + //register actions + m_projectContext = m_core->uniqueIDManager()-> + uniqueIdentifier(Qt4ProjectManager::Constants::PROJECT_KIND); + QList<int> context = QList<int>() << m_projectContext; + Core::ICommand *command; + + QIcon qmakeIcon(QLatin1String(":/qt4projectmanager/images/run_qmake.png")); + qmakeIcon.addFile(QLatin1String(":/qt4projectmanager/images/run_qmake_small.png")); + m_runQMakeAction = new QAction(qmakeIcon, tr("Run qmake"), this); + command = am->registerAction(m_runQMakeAction, Constants::RUNQMAKE, context); + mbuild->addAction(command, ProjectExplorer::Constants::G_BUILD_RUN); + connect(m_runQMakeAction, SIGNAL(triggered()), m_qt4ProjectManager, SLOT(runQMake())); + + m_runQMakeActionContextMenu = new QAction(qmakeIcon, tr("Run qmake"), this); + command = am->registerAction(m_runQMakeActionContextMenu, Constants::RUNQMAKECONTEXTMENU, context); + mproject->addAction(command, ProjectExplorer::Constants::G_PROJECT_BUILD); + connect(m_runQMakeActionContextMenu, SIGNAL(triggered()), m_qt4ProjectManager, SLOT(runQMakeContextMenu())); + + + connect(m_projectExplorer, + SIGNAL(aboutToShowContextMenu(ProjectExplorer::Project*, ProjectExplorer::Node*)), + this, SLOT(updateContextMenu(ProjectExplorer::Project*, ProjectExplorer::Node*))); + + connect(m_projectExplorer->buildManager(), SIGNAL(buildStateChanged(ProjectExplorer::Project *)), + this, SLOT(buildStateChanged(ProjectExplorer::Project *))); + connect(m_projectExplorer, SIGNAL(currentProjectChanged(ProjectExplorer::Project *)), + this, SLOT(currentProjectChanged())); + + return true; +} + +void Qt4ProjectManagerPlugin::extensionsInitialized() +{ + m_qt4ProjectManager->init(); + m_proFileEditorFactory->initializeActions(); +} + +void Qt4ProjectManagerPlugin::updateContextMenu(Project *project, + ProjectExplorer::Node *node) +{ + m_qt4ProjectManager->setContextProject(project); + m_qt4ProjectManager->setContextNode(node); + m_runQMakeActionContextMenu->setEnabled(false); + + if (qobject_cast<Qt4Project *>(project)) { + if (!m_projectExplorer->buildManager()->isBuilding(project)) + m_runQMakeActionContextMenu->setEnabled(true); + } +} + +QtVersionManager *Qt4ProjectManagerPlugin::versionManager() const +{ + return m_qtVersionManager; +} + +void Qt4ProjectManagerPlugin::currentProjectChanged() +{ + m_runQMakeAction->setEnabled(!m_projectExplorer->buildManager()->isBuilding(m_projectExplorer->currentProject())); +} + +void Qt4ProjectManagerPlugin::buildStateChanged(ProjectExplorer::Project *pro) +{ + ProjectExplorer::Project *currentProject = m_projectExplorer->currentProject(); + if (pro == currentProject) + m_runQMakeAction->setEnabled(!m_projectExplorer->buildManager()->isBuilding(currentProject)); + if (pro == m_qt4ProjectManager->contextProject()) + m_runQMakeActionContextMenu->setEnabled(!m_projectExplorer->buildManager()->isBuilding(pro)); +} + +#ifdef WITH_TESTS +void Qt4ProjectManagerPlugin::testBasicProjectLoading() +{ + + QString testDirectory = ExtensionSystem::PluginManager::instance()->testDataDirectory() + "/qt4projectmanager/"; + QString test1 = testDirectory + "test1/test1.pro"; + m_projectExplorer->openProject(test1); + QVERIFY(!m_projectExplorer->session()->projects().isEmpty()); + Qt4Project *qt4project = qobject_cast<Qt4Project *>(m_projectExplorer->session()->projects().first()); + QVERIFY(qt4project); + QVERIFY(qt4project->rootProjectNode()->projectType() == ApplicationTemplate); + QVERIFY(m_projectExplorer->currentProject() != 0); +} + +void Qt4ProjectManagerPlugin::testNotYetImplemented() +{ + QCOMPARE(1+1, 2); +} +#endif + +Q_EXPORT_PLUGIN(Qt4ProjectManagerPlugin) diff --git a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h new file mode 100644 index 00000000000..d1f0815a5f4 --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.h @@ -0,0 +1,97 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QT4PROJECTMANAGERPLUGIN_H +#define QT4PROJECTMANAGERPLUGIN_H + +#include <projectexplorer/project.h> +#include <projectexplorer/projectexplorer.h> +#include <coreplugin/icore.h> + +namespace Qt4ProjectManager { + +class Qt4Manager; + +namespace Internal { + +class ProFileEditorFactory; +class ConsoleAppWizard; +class GuiAppWizard; +class QMakeBuildStepFactory; +class MakeBuildStepFactory; +class GccParserFactory; +class MsvcParserFactory; +class QtVersionManager; +class EmbeddedPropertiesPage; + +class Qt4ProjectManagerPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + ~Qt4ProjectManagerPlugin(); + bool initialize(const QStringList &arguments, QString *error_message); + void extensionsInitialized(); + + int projectContext() const { return m_projectContext; } + QtVersionManager *versionManager() const; + + +private slots: + void updateContextMenu(ProjectExplorer::Project *project, + ProjectExplorer::Node *node); + void currentProjectChanged(); + void buildStateChanged(ProjectExplorer::Project *pro); + +#ifdef WITH_TESTS + void testBasicProjectLoading(); + void testNotYetImplemented(); +#endif + +private: + Core::ICore *m_core; + ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer; + ProFileEditorFactory *m_proFileEditorFactory; + Qt4Manager *m_qt4ProjectManager; + QtVersionManager *m_qtVersionManager; + EmbeddedPropertiesPage *m_embeddedPropertiesPage; + + int m_projectContext; + + QAction *m_runQMakeAction; + QAction *m_runQMakeActionContextMenu; +}; + +} //namespace Internal +} //namespace Qt4ProjectManager + +#endif // QT4PROJECTMANAGERPLUGIN_H diff --git a/src/plugins/qt4projectmanager/qt4runconfiguration.cpp b/src/plugins/qt4projectmanager/qt4runconfiguration.cpp new file mode 100644 index 00000000000..d2cead008a5 --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4runconfiguration.cpp @@ -0,0 +1,443 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qt4runconfiguration.h" +#include "qt4project.h" +#include "profilereader.h" +#include "qt4nodes.h" +#include "makestep.h" + + +#include <coreplugin/icore.h> +#include <coreplugin/messagemanager.h> +#include <coreplugin/variablemanager.h> +#include <projectexplorer/buildstep.h> + +#include <QtGui/QFormLayout> +#include <QtGui/QInputDialog> + +using namespace Qt4ProjectManager::Internal; +using namespace Qt4ProjectManager; +using ProjectExplorer::ApplicationRunConfiguration; +using ProjectExplorer::PersistentSettingsReader; +using ProjectExplorer::PersistentSettingsWriter; + +Qt4RunConfiguration::Qt4RunConfiguration(Qt4Project *pro, QString proFilePath) + : ApplicationRunConfiguration(pro), m_proFilePath(proFilePath), m_userSetName(false) +{ + setName(tr("Qt4RunConfiguration")); + if (!m_proFilePath.isEmpty()) { + updateCachedValues(); + setName(QFileInfo(m_proFilePath).baseName()); + } +} + +Qt4RunConfiguration::~Qt4RunConfiguration() +{ +} + +QString Qt4RunConfiguration::type() const +{ + return "Qt4ProjectManager.Qt4RunConfiguration"; +} + +QWidget *Qt4RunConfiguration::configurationWidget() +{ + QWidget *configWidget = new QWidget; + QFormLayout *toplayout = new QFormLayout(configWidget); + toplayout->setMargin(0); + + QLabel *nameLabel = new QLabel(tr("Name:")); + QLineEdit *nameLineEdit = new QLineEdit(name()); + nameLabel->setBuddy(nameLineEdit); + toplayout->addRow(nameLabel, nameLineEdit); + + QLabel *executableLabel = new QLabel(tr("Executable:")); + QLabel *executableLabel2 = new QLabel(executable()); + toplayout->addRow(executableLabel, executableLabel2); + + QLabel *workingDirectoryLabel = new QLabel(tr("Working Directory:")); + QLabel *workingDirectoryLabel2 = new QLabel(workingDirectory()); + toplayout->addRow(workingDirectoryLabel, workingDirectoryLabel2); + + QLabel *argumentsLabel = new QLabel(tr("&Arguments:")); + QLineEdit *argumentsLineEdit = new QLineEdit(ProjectExplorer::Environment::joinArgumentList(commandLineArguments())); + argumentsLabel->setBuddy(argumentsLineEdit); + toplayout->addRow(argumentsLabel, argumentsLineEdit); + + connect(argumentsLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(setCommandLineArguments(const QString&))); + + connect(nameLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(nameEdited(const QString&))); + + return configWidget; +} + +void Qt4RunConfiguration::save(PersistentSettingsWriter &writer) const +{ + writer.saveValue("CommandLineArguments", m_commandLineArguments); + writer.saveValue("ProFile", m_proFilePath); + writer.saveValue("UserSetName", m_userSetName); + ApplicationRunConfiguration::save(writer); +} + +void Qt4RunConfiguration::restore(const PersistentSettingsReader &reader) +{ + ApplicationRunConfiguration::restore(reader); + m_commandLineArguments = reader.restoreValue("CommandLineArguments").toStringList(); + m_proFilePath = reader.restoreValue("ProFile").toString(); + m_userSetName = reader.restoreValue("UserSetName").toBool(); + if (!m_proFilePath.isEmpty()) { + updateCachedValues(); + if (!m_userSetName) + setName(QFileInfo(m_proFilePath).baseName()); + } +} + +QString Qt4RunConfiguration::executable() const +{ + return resolveVariables(project()->activeBuildConfiguration(), m_executable); +} + +ApplicationRunConfiguration::RunMode Qt4RunConfiguration::runMode() const +{ + return m_runMode; +} + +QString Qt4RunConfiguration::workingDirectory() const +{ + return resolveVariables(project()->activeBuildConfiguration(), m_workingDir); +} + +QStringList Qt4RunConfiguration::commandLineArguments() const +{ + return m_commandLineArguments; +} + +ProjectExplorer::Environment Qt4RunConfiguration::environment() const +{ + Qt4Project *pro = qobject_cast<Qt4Project *>(project()); + Q_ASSERT(pro); + return pro->environment(pro->activeBuildConfiguration()); +} + +void Qt4RunConfiguration::setCommandLineArguments(const QString &argumentsString) +{ + m_commandLineArguments = ProjectExplorer::Environment::parseCombinedArgString(argumentsString); +} + +void Qt4RunConfiguration::nameEdited(const QString &name) +{ + if (name == "") { + setName(tr("Qt4RunConfiguration")); + m_userSetName = false; + } else { + setName(name); + m_userSetName = true; + } +} + +QString Qt4RunConfiguration::proFilePath() const +{ + return m_proFilePath; +} + +// and needs to be reloaded. +// Check wheter it is +void Qt4RunConfiguration::updateCachedValues() +{ + ProFileReader *reader = static_cast<Qt4Project *>(project())->createProFileReader(); + if (!reader->readProFile(m_proFilePath)) { + delete reader; + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + core->messageManager()->printToOutputPane(QString("Could not parse %1. The Qt4 run configuration %2 can not be started.").arg(m_proFilePath).arg(name())); + return; + } + + QString destDir; + + if (reader->contains("DESTDIR")) { + // TODO Can return different destdirs for different scopes! + destDir = reader->value("DESTDIR"); + if (QDir::isRelativePath(destDir)) { + destDir = "${BASEDIR}" + QLatin1Char('/') + destDir; + } + } else { + destDir = "${BASEDIR}"; +#if defined(Q_OS_WIN) + if (!reader->contains("DESTDIR")) + destDir += QLatin1Char('/') + "${QMAKE_BUILDCONFIG}"; +#endif + } + +#if defined (Q_OS_MAC) + if (!reader->values("-CONFIG").contains("app_bundle")) { + destDir += QLatin1Char('/') + + "${QMAKE_TARGET}" + + QLatin1String(".app/Contents/MacOS"); + } +#endif + m_workingDir = destDir; + m_executable = destDir + QLatin1Char('/') + "${QMAKE_TARGET}"; + +#if defined (Q_OS_WIN) + m_executable += QLatin1String(".exe"); +#endif + + m_targets = reader->values(QLatin1String("TARGET")); + + m_srcDir = QFileInfo(m_proFilePath).path(); + const QStringList config = reader->values(QLatin1String("CONFIG")); + m_runMode = ProjectExplorer::ApplicationRunConfiguration::Gui; + + delete reader; +} + +QString Qt4RunConfiguration::resolveVariables(const QString &buildConfiguration, const QString& in) const +{ + detectQtShadowBuild(buildConfiguration); + + QString relSubDir = QFileInfo(project()->file()->fileName()).absoluteDir().relativeFilePath(m_srcDir); + QString baseDir = QDir(project()->buildDirectory(buildConfiguration)).absoluteFilePath(relSubDir); + + Core::VariableManager *vm = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()->variableManager(); + if (!vm) + return QString(); + QString dest; + bool found = false; + vm->insert("QMAKE_BUILDCONFIG", qmakeBuildConfigFromBuildConfiguration(buildConfiguration)); + vm->insert("BASEDIR", baseDir); + + + /* + TODO This is a hack to detect correct target (there might be different targets in + different scopes) + */ + + // This code also works for workingDirectory, + // since directories are executable. + foreach (const QString &target, m_targets) { + dest = in; + vm->insert("QMAKE_TARGET", target); + dest = QDir::cleanPath(vm->resolve(dest)); + vm->remove("QMAKE_TARGET"); + QFileInfo fi(dest); + if (fi.exists() && (fi.isExecutable() || dest.endsWith(".js"))) { + found = true; + break; + } + } + vm->remove("BASEDIR"); + vm->remove("QMAKE_BUILDCONFIG"); + if (found) + return dest; + else + return QString(); +} + +/* This function tries to find out wheter qmake/make will put the binary in "/debug/" or in "/release/" + That is this function is strictly only for windows. + We look wheter make gets an explicit parameter "debug" or "release" + That works because if we have either debug or release there then it is surely a + debug_and_release buildconfiguration and thus we are put in a subdirectory. + + Now if there is no explicit debug or release parameter, then we need to look at what qmake's CONFIG + value is, if it is not debug_and_release, we don't care and return "" + otherwise we look at wheter the default is debug or not + + Note: When fixing this function consider those cases + qmake CONFIG+=debug_and_release CONFIG+=debug + make release + => we should return release + + qmake CONFIG+=debug_and_release CONFIG+=debug + make + => we should return debug + + qmake CONFIG-=debug_and_release CONFIG+=debug + make + => we should return "", since the executable is not put in a subdirectory + + Not a function to be proud of +*/ +QString Qt4RunConfiguration::qmakeBuildConfigFromBuildConfiguration(const QString &buildConfigurationName) const +{ + MakeStep *ms = qobject_cast<Qt4Project *>(project())->makeStep(); + QStringList makeargs = ms->value(buildConfigurationName, "makeargs").toStringList(); + if (makeargs.contains("debug")) + return "debug"; + else if(makeargs.contains("release")) + return "release"; + + // Oh we don't have an explicit make argument + QMakeStep *qs = qobject_cast<Qt4Project *>(project())->qmakeStep(); + QVariant qmakeBuildConfiguration = qs->value(buildConfigurationName, "buildConfiguration"); + if (qmakeBuildConfiguration.isValid()) { + QtVersion::QmakeBuildConfig projectBuildConfiguration = QtVersion::QmakeBuildConfig(qmakeBuildConfiguration.toInt()); + if (projectBuildConfiguration & QtVersion::BuildAll) { + if (projectBuildConfiguration & QtVersion::DebugBuild) + return "debug"; + else + return "release"; + } else { + return ""; + } + } else { + // Old sytle always CONFIG+=debug_and_release + if (qobject_cast<Qt4Project *>(project())->qtVersion(buildConfigurationName)->defaultBuildConfig() & QtVersion::DebugBuild) + return "debug"; + else + return "release"; + } + + // enable us to infer the right string + return ""; +} + +/*! + Handle special case were a subproject of the qt directory is opened, and + qt was configured to be built as a shadow build -> also build in the sub- + project in the correct shadow build directory. + */ +void Qt4RunConfiguration::detectQtShadowBuild(const QString &buildConfiguration) const +{ + if (project()->activeBuildConfiguration() == buildConfiguration) + return; + + const QString currentQtDir = static_cast<Qt4Project *>(project())->qtDir(buildConfiguration); + const QString qtSourceDir = static_cast<Qt4Project *>(project())->qtVersion(buildConfiguration)->sourcePath(); + + // if the project is a sub-project of Qt and Qt was shadow-built then automatically + // adjust the build directory of the sub-project. + if (project()->file()->fileName().startsWith(qtSourceDir) && qtSourceDir != currentQtDir) { + project()->setValue(buildConfiguration, "useShadowBuild", true); + QString buildDir = QFileInfo(project()->file()->fileName()).absolutePath(); + buildDir.replace(qtSourceDir, currentQtDir); + project()->setValue(buildConfiguration, "buildDirectory", buildDir); + project()->setValue(buildConfiguration, "autoShadowBuild", true); + } +} + + +/// +/// Qt4RunConfigurationFactory +/// This class is used to restore run settings (saved in .user files) +/// + +Qt4RunConfigurationFactory::Qt4RunConfigurationFactory() +{ +} + +Qt4RunConfigurationFactory::~Qt4RunConfigurationFactory() +{ +} + +// used to recreate the runConfigurations when restoring settings +bool Qt4RunConfigurationFactory::canCreate(const QString &type) const +{ + return type == "Qt4ProjectManager.Qt4RunConfiguration"; +} + +QSharedPointer<ProjectExplorer::RunConfiguration> Qt4RunConfigurationFactory::create(ProjectExplorer::Project *project, const QString &type) +{ + Qt4Project *p = qobject_cast<Qt4Project *>(project); + Q_ASSERT(p); + Q_ASSERT(type == "Qt4ProjectManager.Qt4RunConfiguration"); + // The right path is set in restoreSettings + QSharedPointer<ProjectExplorer::RunConfiguration> rc(new Qt4RunConfiguration(p, QString::null)); + return rc; +} + +QStringList Qt4RunConfigurationFactory::canCreate(ProjectExplorer::Project *pro) const +{ + Qt4Project *qt4project = qobject_cast<Qt4Project *>(pro); + if (qt4project) + return QStringList(); + else + return QStringList(); +} + +QString Qt4RunConfigurationFactory::nameForType(const QString &type) const +{ + Q_UNUSED(type); + return "Run Qt4 application"; +} + +/// +/// Qt4RunConfigurationFactoryUser +/// This class is used to create new RunConfiguration from the runsettings page +/// + +Qt4RunConfigurationFactoryUser::Qt4RunConfigurationFactoryUser() +{ +} + +Qt4RunConfigurationFactoryUser::~Qt4RunConfigurationFactoryUser() +{ +} + +bool Qt4RunConfigurationFactoryUser::canCreate(const QString &type) const +{ + Q_UNUSED(type); + return false; +} + +QSharedPointer<ProjectExplorer::RunConfiguration> Qt4RunConfigurationFactoryUser::create(ProjectExplorer::Project *project, const QString &type) +{ + Qt4Project *p = qobject_cast<Qt4Project *>(project); + Q_ASSERT(p); + + QString fileName = type.mid(QString("Qt4RunConfiguration.").size()); + return QSharedPointer<ProjectExplorer::RunConfiguration>(new Qt4RunConfiguration(p, fileName)); +} + +QStringList Qt4RunConfigurationFactoryUser::canCreate(ProjectExplorer::Project *pro) const +{ + Qt4Project *qt4project = qobject_cast<Qt4Project *>(pro); + if (qt4project) { + QStringList applicationProFiles; + QList<Qt4ProFileNode *> list = qt4project->applicationProFiles(); + foreach(Qt4ProFileNode * node, list) { + applicationProFiles.append("Qt4RunConfiguration." + node->path()); + } + return applicationProFiles; + } else { + return QStringList(); + } +} + +QString Qt4RunConfigurationFactoryUser::nameForType(const QString &type) const +{ + QString fileName = type.mid(QString("Qt4RunConfiguration.").size()); + return QFileInfo(fileName).baseName(); +} diff --git a/src/plugins/qt4projectmanager/qt4runconfiguration.h b/src/plugins/qt4projectmanager/qt4runconfiguration.h new file mode 100644 index 00000000000..6262082858d --- /dev/null +++ b/src/plugins/qt4projectmanager/qt4runconfiguration.h @@ -0,0 +1,119 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QT4RUNCONFIGURATION_H +#define QT4RUNCONFIGURATION_H + +#include <projectexplorer/applicationrunconfiguration.h> +#include <QtCore/QStringList> + +namespace Qt4ProjectManager { + +class Qt4Project; + +namespace Internal { + +class Qt4ProFileNode; + +class Qt4RunConfiguration : public ProjectExplorer::ApplicationRunConfiguration +{ + Q_OBJECT +public: + Qt4RunConfiguration(Qt4Project *pro, QString proFilePath); + virtual ~Qt4RunConfiguration(); + + virtual QString type() const; + virtual QWidget *configurationWidget(); + virtual void save(ProjectExplorer::PersistentSettingsWriter &writer) const; + virtual void restore(const ProjectExplorer::PersistentSettingsReader &reader); + + virtual QString executable() const; + virtual RunMode runMode() const; + virtual QString workingDirectory() const; + virtual QStringList commandLineArguments() const; + virtual ProjectExplorer::Environment environment() const; + + QString proFilePath() const; + + // Should just be called from qt4project, since that knows that the file changed on disc + void updateCachedValues(); + +private slots: + void setCommandLineArguments(const QString &argumentsString); + void nameEdited(const QString&); + +private: + void detectQtShadowBuild(const QString &buildConfig) const; + QString resolveVariables(const QString &buildConfiguration, const QString& in) const; + QString qmakeBuildConfigFromBuildConfiguration(const QString &buildConfigurationName) const; + + QStringList m_commandLineArguments; + Qt4ProFileNode *m_proFileNode; + QString m_proFilePath; // Full path to the Application Pro File + + // Cached startup sub project information + QStringList m_targets; + QString m_executable; + QString m_srcDir; + QString m_workingDir; + ProjectExplorer::ApplicationRunConfiguration::RunMode m_runMode; + bool m_userSetName; +}; + +class Qt4RunConfigurationFactory : public ProjectExplorer::IRunConfigurationFactory +{ + Q_OBJECT +public: + Qt4RunConfigurationFactory(); + virtual ~Qt4RunConfigurationFactory(); + virtual bool canCreate(const QString &type) const; + virtual QSharedPointer<ProjectExplorer::RunConfiguration> create(ProjectExplorer::Project *project, const QString &type); + QStringList canCreate(ProjectExplorer::Project *pro) const; + QString nameForType(const QString &type) const; +}; + +class Qt4RunConfigurationFactoryUser : public ProjectExplorer::IRunConfigurationFactory +{ + Q_OBJECT +public: + Qt4RunConfigurationFactoryUser(); + virtual ~Qt4RunConfigurationFactoryUser(); + virtual bool canCreate(const QString &type) const; + virtual QSharedPointer<ProjectExplorer::RunConfiguration> create(ProjectExplorer::Project *project, const QString &type); + QStringList canCreate(ProjectExplorer::Project *pro) const; + QString nameForType(const QString &type) const; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // QT4RUNCONFIGURATION_H diff --git a/src/plugins/qt4projectmanager/qtversionmanager.cpp b/src/plugins/qt4projectmanager/qtversionmanager.cpp new file mode 100644 index 00000000000..a1afa6c65b1 --- /dev/null +++ b/src/plugins/qt4projectmanager/qtversionmanager.cpp @@ -0,0 +1,1278 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qtversionmanager.h" +#include "qt4projectmanagerconstants.h" +#include "msvcenvironment.h" +#include "cesdkhandler.h" + +#include <coreplugin/coreconstants.h> +#include <help/helpplugin.h> + +#include <QtCore/QSettings> +#include <QtCore/QStringRef> +#include <QtCore/QTime> +#include <QtCore/QProcess> +#include <QtGui/QHeaderView> +#include <QtGui/QFileDialog> +#include <QtCore/QDebug> +#include <QtGui/QMessageBox> + +using namespace Qt4ProjectManager::Internal; + +using ProjectExplorer::Environment; + +static const char *QtVersionsSectionName = "QtVersions"; +static const char *defaultQtVersionKey = "DefaultQtVersion"; +static const char *newQtVersionsKey = "NewQtVersions"; + +QtVersionManager::QtVersionManager() + : m_emptyVersion(new QtVersion) +{ + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + QSettings *s = m_core->settings(); + m_defaultVersion = s->value(defaultQtVersionKey, 0).toInt(); + + m_idcount = 1; + int size = s->beginReadArray(QtVersionsSectionName); + for (int i = 0; i < size; ++i) { + s->setArrayIndex(i); + // Find the right id + // Either something saved or something generated + // Note: This code assumes that either all ids are read from the settings + // or generated on the fly. + int id = s->value("Id", -1).toInt(); + if (id == -1) + id = getUniqueId(); + else if (id > m_idcount) + m_idcount = id; + QtVersion *version = new QtVersion(s->value("Name").toString(), + s->value("Path").toString(), + id, + s->value("IsSystemVersion", false).toBool()); + version->setMingwDirectory(s->value("MingwDirectory").toString()); + version->setPrependPath(s->value("PrependPath").toString()); + version->setMsvcVersion(s->value("msvcVersion").toString()); + m_versions.append(version); + } + s->endArray(); + updateUniqueIdToIndexMap(); + + ++m_idcount; + addNewVersionsFromInstaller(); + updateSystemVersion(); + + writeVersionsIntoSettings(); + updateDocumentation(); +} + +QtVersionManager::~QtVersionManager() +{ + qDeleteAll(m_versions); + m_versions.clear(); + delete m_emptyVersion; + m_emptyVersion = 0; +} + +void QtVersionManager::addVersion(QtVersion *version) +{ + m_versions.append(version); + emit qtVersionsChanged(); +} + +void QtVersionManager::updateDocumentation() +{ + Help::HelpManager *helpManager = m_core->pluginManager()->getObject<Help::HelpManager>(); + Q_ASSERT(helpManager); + if (!helpManager) + return; + QStringList fileEndings = QStringList() << "/qch/qt.qch" << "/qch/qmake.qch" << "/qch/designer.qch"; + QStringList files; + foreach (QtVersion *version, m_versions) { + QString docPath = version->versionInfo().value("QT_INSTALL_DOCS"); + foreach (const QString &fileEnding, fileEndings) + files << docPath+fileEnding; + } + helpManager->registerDocumentation(files); +} + +int QtVersionManager::getUniqueId() +{ + return m_idcount++; +} + +QString QtVersionManager::name() const +{ + return tr(Constants::QTVERSION_PAGE); +} + +QString QtVersionManager::category() const +{ + return Constants::QT_CATEGORY; +} + +QString QtVersionManager::trCategory() const +{ + return tr(Constants::QT_CATEGORY); +} + +QWidget *QtVersionManager::createPage(QWidget *parent) +{ + if (m_widget) + delete m_widget; + m_widget = new QtDirWidget(parent, m_versions, m_defaultVersion); + return m_widget; +} + +void QtVersionManager::updateUniqueIdToIndexMap() +{ + m_uniqueIdToIndex.clear(); + for(int i=0; i<m_versions.size(); ++i) + m_uniqueIdToIndex.insert(m_versions.at(i)->uniqueId(), i); +} + +void QtVersionManager::finished(bool accepted) +{ + if (!accepted) + return; + + m_widget->finish(); + QList<QtVersion*> newVersions = m_widget->versions(); + bool versionPathsChanged = m_versions.size() != newVersions.size(); + if (!versionPathsChanged) { + for (int i = 0; i < m_versions.size(); ++i) { + if (m_versions.at(i)->path() != newVersions.at(i)->path()) { + versionPathsChanged = true; + break; + } + } + } + m_versions = m_widget->versions(); + if (versionPathsChanged) + updateDocumentation(); + updateUniqueIdToIndexMap(); + + bool emitDefaultChanged = false; + if (m_defaultVersion != m_widget->defaultVersion()) { + m_defaultVersion = m_widget->defaultVersion(); + emitDefaultChanged = true; + } + + emit qtVersionsChanged(); + if (emitDefaultChanged) + emit defaultQtVersionChanged(); + + writeVersionsIntoSettings(); +} + +void QtVersionManager::writeVersionsIntoSettings() +{ + QSettings *s = m_core->settings(); + s->setValue(defaultQtVersionKey, m_defaultVersion); + s->beginWriteArray("QtVersions"); + for (int i = 0; i < m_versions.size(); ++i) { + s->setArrayIndex(i); + s->setValue("Name", m_versions.at(i)->name()); + s->setValue("Path", m_versions.at(i)->path()); + s->setValue("Id", m_versions.at(i)->uniqueId()); + s->setValue("MingwDirectory", m_versions.at(i)->mingwDirectory()); + s->setValue("PrependPath", m_versions.at(i)->prependPath()); + s->setValue("msvcVersion", m_versions.at(i)->msvcVersion()); + s->setValue("IsSystemVersion", m_versions.at(i)->isSystemVersion()); + } + s->endArray(); +} + +QList<QtVersion* > QtVersionManager::versions() const +{ + return m_versions; +} + +QtVersion *QtVersionManager::version(int id) const +{ + int pos = m_uniqueIdToIndex.value(id, -1); + if (pos != -1) + return m_versions.at(pos); + + if(m_defaultVersion < m_versions.count()) + return m_versions.at(m_defaultVersion); + else + return m_emptyVersion; +} + +void QtVersionManager::addNewVersionsFromInstaller() +{ + // Add new versions which may have been installed by the WB installer in the form: + // NewQtVersions="qt 4.3.2=c:\\qt\\qt432;qt embedded=c:\\qtembedded;" + // or NewQtVersions="qt 4.3.2=c:\\qt\\qt432=c:\\qtcreator\\mingw\\=prependToPath; + // Duplicate entries are not added, the first new version is set as default. + QSettings *settings = m_core->settings(); + if (!settings->contains(newQtVersionsKey)) + return; + +// qDebug()<<"QtVersionManager::addNewVersionsFromInstaller()"; + + QString newVersionsValue = settings->value(newQtVersionsKey).toString(); + QStringList newVersionsList = newVersionsValue.split(';', QString::SkipEmptyParts); + bool defaultVersionWasReset = false; + foreach (QString newVersion, newVersionsList) { + QStringList newVersionData = newVersion.split('='); + if(newVersionData.count()>=2) { + if (QDir(newVersionData[1]).exists()) { + QtVersion *version = new QtVersion(newVersionData[0], newVersionData[1], m_idcount++ ); + if(newVersionData.count() >= 3) + version->setMingwDirectory(newVersionData[2]); + if(newVersionData.count() >= 4) + version->setPrependPath(newVersionData[3]); + + bool versionWasAlreadyInList = false; + foreach(const QtVersion * const it, m_versions) { + if(QDir(version->path()).canonicalPath() == QDir(it->path()).canonicalPath()) { + versionWasAlreadyInList = true; + break; + } + } + + if (!versionWasAlreadyInList) { + m_versions.append(version); + } else { + // clean up + delete version; + } + if (!defaultVersionWasReset) { + m_defaultVersion = versionWasAlreadyInList? m_defaultVersion : m_versions.count() - 1; + defaultVersionWasReset = true; + } + } + } + } + settings->remove(newQtVersionsKey); + updateUniqueIdToIndexMap(); +} + +void QtVersionManager::updateSystemVersion() +{ + bool haveSystemVersion = false; + foreach (QtVersion *version, m_versions) { + if (version->isSystemVersion()) { + version->setPath(findSystemQt()); + haveSystemVersion = true; + } + } + if (haveSystemVersion) + return; + QtVersion *version = new QtVersion(tr("System Qt"), + findSystemQt(), + getUniqueId(), + true); + m_versions.prepend(version); + updateUniqueIdToIndexMap(); + if (m_versions.size() > 1) // we had other versions before adding system version + ++m_defaultVersion; +} + +QStringList QtVersionManager::possibleQMakeCommands() +{ + // On windows noone has renamed qmake, right? +#ifdef Q_OS_WIN + return QStringList() << "qmake.exe"; +#endif + // On unix some distributions renamed qmake to avoid clashes + QStringList result; + result << "qmake-qt4" << "qmake4" << "qmake"; + return result; +} + +bool QtVersionManager::checkQMakeVersion(const QString &qmakePath) +{ + QProcess qmake; + qmake.start(qmakePath, QStringList()<<"--version"); + if (!qmake.waitForFinished()) + return false; + QString output = qmake.readAllStandardOutput(); + QRegExp regexp("(QMake version|Qmake version:)[\\s]*([\\d.]*)"); + regexp.indexIn(output); + if (regexp.cap(2).startsWith("2.")) + return true; + return false; +} + +QString QtVersionManager::findSystemQt() const +{ + Environment env = Environment::systemEnvironment(); + QStringList paths = env.path(); + foreach (const QString &path, paths) { + foreach (const QString &possibleCommand, possibleQMakeCommands()) { + QFileInfo qmake(path + "/" + possibleCommand); + if (qmake.exists()) { + if (checkQMakeVersion(qmake.absoluteFilePath())) { + QDir dir(qmake.absoluteDir()); + dir.cdUp(); + return dir.absolutePath(); + } + } + } + } + return tr("<not found>"); +} + +QtVersion *QtVersionManager::currentQtVersion() const +{ + if(m_defaultVersion < m_versions.count()) + return m_versions.at(m_defaultVersion); + else + return m_emptyVersion; +} + +//----------------------------------------------------- + +QtDirWidget::QtDirWidget(QWidget *parent, QList<QtVersion *> versions, int defaultVersion) + : QWidget(parent) + , m_versions(versions) + , m_defaultVersion(defaultVersion) + , m_specifyNameString(tr("<specify a name>")) + , m_specifyPathString(tr("<specify a path>")) +{ + m_ui.setupUi(this); + + m_ui.addButton->setIcon(QIcon(Core::Constants::ICON_PLUS)); + m_ui.delButton->setIcon(QIcon(Core::Constants::ICON_MINUS)); + + for (int i = 0; i < m_versions.count(); ++i) { + const QtVersion * const version = m_versions.at(i); + QTreeWidgetItem *item = new QTreeWidgetItem(m_ui.qtdirList); + item->setText(0, version->name()); + item->setText(1, version->path()); + item->setData(0, Qt::UserRole, version->uniqueId()); + m_ui.defaultCombo->addItem(version->name()); + if (i == m_defaultVersion) + m_ui.defaultCombo->setCurrentIndex(i); + } + + connect(m_ui.nameEdit, SIGNAL(textEdited(const QString &)), + this, SLOT(updateCurrentQtName())); + connect(m_ui.pathEdit, SIGNAL(textEdited(const QString &)), + this, SLOT(updateCurrentQtPath())); + connect(m_ui.mingwLineEdit, SIGNAL(textEdited(const QString &)), + this, SLOT(updateCurrentMingwDirectory())); + + connect(m_ui.addButton, SIGNAL(clicked()), + this, SLOT(addQtDir())); + connect(m_ui.delButton, SIGNAL(clicked()), + this, SLOT(removeQtDir())); + connect(m_ui.browseButton, SIGNAL(clicked()), + this, SLOT(browse())); + connect(m_ui.mingwBrowseButton, SIGNAL(clicked()), + this, SLOT(mingwBrowse())); + + connect(m_ui.qtdirList, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + this, SLOT(versionChanged(QTreeWidgetItem *, QTreeWidgetItem *))); + connect(m_ui.defaultCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(defaultChanged(int))); + + connect(m_ui.msvcComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(msvcVersionChanged())); + + showEnvironmentPage(0); + updateState(); +} + +void QtDirWidget::addQtDir() +{ + QtVersion *newVersion = new QtVersion(m_specifyNameString, m_specifyPathString); + m_versions.append(newVersion); + + QTreeWidgetItem *item = new QTreeWidgetItem(m_ui.qtdirList); + item->setText(0, newVersion->name()); + item->setText(1, newVersion->path()); + item->setData(0, Qt::UserRole, newVersion->uniqueId()); + + m_ui.nameEdit->setText(newVersion->name()); + m_ui.pathEdit->setText(newVersion->path()); + + m_ui.defaultCombo->addItem(newVersion->name()); + m_ui.qtdirList->setCurrentItem(item); + m_ui.nameEdit->setFocus(); + m_ui.nameEdit->selectAll(); +} + +void QtDirWidget::removeQtDir() +{ + QTreeWidgetItem *item = m_ui.qtdirList->currentItem(); + + + int index = m_ui.qtdirList->indexOfTopLevelItem(item); + if (index < 0) + return; + + for (int i = 0; i < m_ui.defaultCombo->count(); ++i) { + if (m_ui.defaultCombo->itemText(i) == item->text(0)) { + m_ui.defaultCombo->removeItem(i); + break; + } + } + + delete item; + + delete m_versions.takeAt(index); + updateState(); +} + +void QtDirWidget::updateState() +{ + bool enabled = (m_ui.qtdirList->currentItem() != 0); + bool isSystemVersion = (enabled + && m_versions.at(m_ui.qtdirList->indexOfTopLevelItem(m_ui.qtdirList->currentItem()))->isSystemVersion()); + m_ui.delButton->setEnabled(enabled && !isSystemVersion); + m_ui.nameEdit->setEnabled(enabled && !isSystemVersion); + m_ui.pathEdit->setEnabled(enabled && !isSystemVersion); + m_ui.browseButton->setEnabled(enabled && !isSystemVersion); + m_ui.mingwBrowseButton->setEnabled(enabled); + m_ui.mingwLineEdit->setEnabled(enabled); +} + +void QtDirWidget::showEnvironmentPage(QTreeWidgetItem *item) +{ + m_ui.msvcComboBox->setVisible(false); + if(item) { + int index = m_ui.qtdirList->indexOfTopLevelItem(item); + m_ui.errorLabel->setText(""); + QtVersion::ToolchainType t = m_versions.at(index)->toolchainType(); + if(t == QtVersion::MinGW) { + m_ui.msvcComboBox->setVisible(false); + m_ui.msvcLabel->setVisible(false); + m_ui.mingwLineEdit->setVisible(true); + m_ui.mingwLabel->setVisible(true); + m_ui.mingwBrowseButton->setVisible(true); + m_ui.mingwLineEdit->setText(m_versions.at(index)->mingwDirectory()); + } else if(t == QtVersion::MSVC || t == QtVersion::WINCE){ + m_ui.msvcComboBox->setVisible(false); + m_ui.msvcLabel->setVisible(true); + m_ui.mingwLineEdit->setVisible(false); + m_ui.mingwLabel->setVisible(false); + m_ui.mingwBrowseButton->setVisible(false); + QList<MSVCEnvironment> msvcenvironments = MSVCEnvironment::availableVersions(); + if(msvcenvironments.count() == 0) { + m_ui.msvcLabel->setText(tr("No Visual Studio Installation found")); + } else if(msvcenvironments.count() == 1) { + m_ui.msvcLabel->setText( msvcenvironments.at(0).description()); + } else { + m_ui.msvcComboBox->setVisible(true); + m_ui.msvcComboBox->clear(); + bool block = m_ui.msvcComboBox->blockSignals(true); + foreach(const MSVCEnvironment msvcenv, msvcenvironments) { + m_ui.msvcComboBox->addItem(msvcenv.name()); + if (msvcenv.name() == m_versions.at(index)->msvcVersion()) { + m_ui.msvcComboBox->setCurrentIndex(m_ui.msvcComboBox->count() - 1); + m_ui.msvcLabel->setText(msvcenv.description()); + } + } + m_ui.msvcComboBox->blockSignals(block); + } + } else if(t == QtVersion::INVALID) { + m_ui.msvcComboBox->setVisible(false); + m_ui.msvcLabel->setVisible(false); + m_ui.mingwLineEdit->setVisible(false); + m_ui.mingwLabel->setVisible(false); + m_ui.mingwBrowseButton->setVisible(false); + if (!m_versions.at(index)->isInstalled()) + m_ui.errorLabel->setText(tr("The Qt Version is not installed. Run make install") + .arg(m_versions.at(index)->path())); + else + m_ui.errorLabel->setText(tr("%1 is not a valid qt directory").arg(m_versions.at(index)->path())); + } else { //QtVersion::Other + m_ui.msvcComboBox->setVisible(false); + m_ui.msvcLabel->setVisible(false); + m_ui.mingwLineEdit->setVisible(false); + m_ui.mingwLabel->setVisible(false); + m_ui.mingwBrowseButton->setVisible(false); + m_ui.errorLabel->setText("Found qt version: " + m_versions.at(index)->mkspec()); + } + } else { + m_ui.msvcComboBox->setVisible(false); + m_ui.msvcLabel->setVisible(false); + m_ui.mingwLineEdit->setVisible(false); + m_ui.mingwLabel->setVisible(false); + m_ui.mingwBrowseButton->setVisible(false); + } +} + +void QtDirWidget::versionChanged(QTreeWidgetItem *item, QTreeWidgetItem *old) +{ + if(old) { + fixQtVersionName(m_ui.qtdirList->indexOfTopLevelItem(old)); + } + if (item) { + m_ui.nameEdit->setText(item->text(0)); + m_ui.pathEdit->setText(item->text(1)); + } else { + m_ui.nameEdit->clear(); + m_ui.pathEdit->clear(); + } + showEnvironmentPage(item); + updateState(); +} + +void QtDirWidget::browse() +{ + QString dir = QFileDialog::getExistingDirectory(this, tr("Select QTDIR")); + + if (dir.isEmpty()) + return; + + dir = QDir::toNativeSeparators(dir); + m_ui.pathEdit->setText(dir); + updateCurrentQtPath(); + if (m_ui.nameEdit->text().isEmpty() || m_ui.nameEdit->text() == m_specifyNameString) { + QStringList dirSegments = dir.split(QDir::separator(), QString::SkipEmptyParts); + if (!dirSegments.isEmpty()) + m_ui.nameEdit->setText(dirSegments.last()); + updateCurrentQtName(); + } + updateState(); +} + +void QtDirWidget::mingwBrowse() +{ + QString dir = QFileDialog::getExistingDirectory(this, tr("Select MinGW Directory")); + if (dir.isEmpty()) + return; + + dir = QDir::toNativeSeparators(dir); + m_ui.mingwLineEdit->setText(dir); + updateCurrentMingwDirectory(); + updateState(); +} + +void QtDirWidget::defaultChanged(int) +{ + for (int i=0; i<m_ui.defaultCombo->count(); ++i) { + if (m_versions.at(i)->name() == m_ui.defaultCombo->currentText()) { + m_defaultVersion = i; + return; + } + } + + m_defaultVersion = 0; +} + +void QtDirWidget::updateCurrentQtName() +{ + QTreeWidgetItem *currentItem = m_ui.qtdirList->currentItem(); + Q_ASSERT(currentItem); + int currentItemIndex = m_ui.qtdirList->indexOfTopLevelItem(currentItem); + m_versions[currentItemIndex]->setName(m_ui.nameEdit->text()); + currentItem->setText(0, m_versions[currentItemIndex]->name()); + + m_ui.defaultCombo->setItemText(currentItemIndex, m_versions[currentItemIndex]->name()); +} + + +void QtDirWidget::finish() +{ + if(QTreeWidgetItem *item = m_ui.qtdirList->currentItem()) + fixQtVersionName(m_ui.qtdirList->indexOfTopLevelItem(item)); +} + +/* Checks that the qt version name is unique + * and otherwise changes the name + * + */ +void QtDirWidget::fixQtVersionName(int index) +{ + int count = m_versions.count(); + for(int i=0; i<count; ++i) { + if(i != index) { + if(m_versions.at(i)->name() == m_versions.at(index)->name()) { + // Same name, find new name + QString name = m_versions.at(index)->name(); + QRegExp regexp("^(.*)\\((\\d)\\)$"); + if (regexp.exactMatch(name)) { + // Alreay in Name (#) format + name = regexp.cap(1) + "(" + QString().setNum(regexp.cap(2).toInt() + 1) + ")"; + } else { + name = name + " (2)"; + } + // set new name + m_versions[index]->setName(name); + m_ui.qtdirList->topLevelItem(index)->setText(0, name); + m_ui.defaultCombo->setItemText(index, name); + + // Now check again... + fixQtVersionName(index); + } + } + } +} + +void QtDirWidget::updateCurrentQtPath() +{ + QTreeWidgetItem *currentItem = m_ui.qtdirList->currentItem(); + Q_ASSERT(currentItem); + int currentItemIndex = m_ui.qtdirList->indexOfTopLevelItem(currentItem); + m_versions[currentItemIndex]->setPath(m_ui.pathEdit->text()); + currentItem->setText(1, m_versions[currentItemIndex]->path()); + + showEnvironmentPage(currentItem); +} + +void QtDirWidget::updateCurrentMingwDirectory() +{ + QTreeWidgetItem *currentItem = m_ui.qtdirList->currentItem(); + Q_ASSERT(currentItem); + int currentItemIndex = m_ui.qtdirList->indexOfTopLevelItem(currentItem); + m_versions[currentItemIndex]->setMingwDirectory(m_ui.mingwLineEdit->text()); +} + +void QtDirWidget::msvcVersionChanged() +{ + const QString &msvcVersion = m_ui.msvcComboBox->currentText(); + QTreeWidgetItem *currentItem = m_ui.qtdirList->currentItem(); + Q_ASSERT(currentItem); + int currentItemIndex = m_ui.qtdirList->indexOfTopLevelItem(currentItem); + m_versions[currentItemIndex]->setMsvcVersion(msvcVersion); + + //get descriptionx + QList<MSVCEnvironment> msvcEnvironments = MSVCEnvironment::availableVersions(); + foreach(const MSVCEnvironment &msvcEnv, msvcEnvironments) { + if(msvcEnv.name() == msvcVersion) { + m_ui.msvcLabel->setText(msvcEnv.description()); + break; + } + } +} + +QList<QtVersion *> QtDirWidget::versions() const +{ + return m_versions; +} + +int QtDirWidget::defaultVersion() const +{ + return m_defaultVersion; +} + +/// +/// QtVersion +/// + +QtVersion::QtVersion(const QString &name, const QString &path, int id, bool isSystemVersion) + : m_name(name), m_isSystemVersion(isSystemVersion), m_notInstalled(false), m_defaultConfigIsDebug(true), m_defaultConfigIsDebugAndRelease(true) +{ + setPath(path); + if(id == -1) + m_id = getUniqueId(); + else + m_id = id; +} + +QtVersion::QtVersion(const QString &name, const QString &path) + : m_name(name), + m_versionInfoUpToDate(false), + m_mkspecUpToDate(false), + m_isSystemVersion(false) +{ + setPath(path); + m_id = getUniqueId(); +} + +QString QtVersion::name() const +{ + return m_name; +} + +QString QtVersion::path() const +{ + return m_path; +} + +QString QtVersion::sourcePath() const +{ + return m_sourcePath; +} + +QString QtVersion::mkspec() const +{ + updateMkSpec(); + return m_mkspec; +} + +QHash<QString,QString> QtVersion::versionInfo() const +{ + updateVersionInfo(); + return m_versionInfo; +} + +void QtVersion::setName(const QString &name) +{ + m_name = name; +} + +void QtVersion::setPath(const QString &path) +{ + m_path = QDir::cleanPath(path); + updateSourcePath(); + m_versionInfoUpToDate = false; + m_mkspecUpToDate = false; + m_qmakeCommand = QString::null; +} + +void QtVersion::updateSourcePath() +{ + m_sourcePath = m_path; + QFile qmakeCache(m_path + QLatin1String("/.qmake.cache")); + if (qmakeCache.exists()) { + qmakeCache.open(QIODevice::ReadOnly | QIODevice::Text); + QTextStream stream(&qmakeCache); + while (!stream.atEnd()) { + QString line = stream.readLine().trimmed(); + if (line.startsWith(QLatin1String("QT_SOURCE_TREE"))) { + m_sourcePath = line.split(QLatin1Char('=')).at(1).trimmed(); + if (m_sourcePath.startsWith(QLatin1String("$$quote("))) { + m_sourcePath.remove(0, 8); + m_sourcePath.chop(1); + } + break; + } + } + } +} + +// Returns the version that was used to build the project in that directory +// That is returns the directory +// To find out wheter we already have a qtversion for that directory call +// QtVersion *QtVersionManager::qtVersionForDirectory(const QString directory); +QString QtVersionManager::findQtVersionFromMakefile(const QString &directory) +{ + QString result = QString::null; + bool debugAdding = false; + QFile makefile(directory + "/Makefile" ); + if (makefile.exists() && makefile.open(QFile::ReadOnly)) { + QTextStream ts(&makefile); + while (!ts.atEnd()) { + QString line = ts.readLine(); + QRegExp r1("QMAKE\\s*=(.*)"); + if (r1.exactMatch(line)) { + if (debugAdding) + qDebug()<<"#~~ QMAKE is:"<<r1.cap(1).trimmed(); + QFileInfo qmake(r1.cap(1).trimmed()); + QFileInfo binDir(qmake.absolutePath()); + QString qtDir = binDir.absolutePath(); + if (debugAdding) + qDebug() << "#~~ QtDir:"<<qtDir; + // Now we have the qtDir + // look through the qtversions wheter we already have that qt version setup + return qtDir; + } + } + makefile.close(); + } + return result; +} + +QtVersion *QtVersionManager::qtVersionForDirectory(const QString &directory) +{ + foreach(QtVersion *v, versions()) { + if (v->path() == directory) { + return v; + break; + } + } + return 0; +} + +QtVersion::QmakeBuildConfig QtVersionManager::scanMakefileForQmakeConfig(const QString &directory, QtVersion::QmakeBuildConfig defaultBuildConfig) +{ + bool debugScan = false; + QtVersion::QmakeBuildConfig result = QtVersion::NoBuild; + QFile makefile(directory + "/Makefile" ); + if (makefile.exists() && makefile.open(QFile::ReadOnly)) { + QTextStream ts(&makefile); + while (!ts.atEnd()) { + QString line = ts.readLine(); + if (line.startsWith("# Command:")) { + // if nothing is specified + result = defaultBuildConfig; + + // Actually parsing that line is not trivial in the general case + // There might things like this + // # Command: /home/dteske/git/bqt-45/bin/qmake -unix CONFIG+=debug\ release CONFIG\ +=\ debug_and_release\ debug -o Makefile test.pro + // which sets debug_and_release and debug + // or something like this: + //[...] CONFIG+=debug\ release CONFIG\ +=\ debug_and_release\ debug CONFIG\ -=\ debug_and_release CONFIG\ -=\ debug -o Makefile test.pro + // which sets -build_all and release + + // To parse that, we search for the first CONFIG, then look for " " which is not after a "\" or the end + // And then look at each config individually + // we then remove all "\ " with just " " + // += sets adding flags + // -= sets removing flags + // and then split the string after the = + // and go over each item separetly + // debug sets/removes the flag DebugBuild + // release removes/sets the flag DebugBuild + // debug_and_release sets/removes the flag BuildAll + int pos = line.indexOf("CONFIG"); + if (pos != -1) { + // Chopped of anything that is not interesting + line = line.mid(pos); + line = line.trimmed(); + if (debugScan) + qDebug()<<"chopping line :"<<line; + + //Now chop into parts that are intresting + QStringList parts; + int lastpos = 0; + for(int i=1; i<line.size(); ++i) { + if (line.at(i) == QLatin1Char(' ') && line.at(i-1) != QLatin1Char('\\')) { + // found a part + parts.append(line.mid(lastpos, i-lastpos)); + if (debugScan) + qDebug()<<"part appended:"<<line.mid(lastpos, i-lastpos); + lastpos = i + 1; // Nex one starts after the space + } + } + parts.append(line.mid(lastpos)); + if (debugScan) + qDebug()<<"part appended:"<<line.mid(lastpos); + + foreach(const QString &part, parts) { + if(debugScan) + qDebug()<<"now interpreting part"<<part; + bool setFlags; + // Now try to understand each part for that we do a rather stupid approach, optimize it if you care + if (part.startsWith("CONFIG")) { + // Yep something interesting + if (part.indexOf("+=") != -1) { + setFlags = true; + } else if (part.indexOf("-=") != -1) { + setFlags = false; + } else { + setFlags = true; + if (debugScan) + qDebug()<<"This can never happen, except if we can't parse Makefiles..."; + } + if (debugScan) + qDebug()<<"part has setFlags:"<<setFlags; + // now loop forward, looking for something that looks like debug, release or debug_and_release + + for(int i=0; i<part.size(); ++i) { + int left = part.size() - i; + if (left >= 17 && QStringRef(&part, i, 17) == "debug_and_release") { + if (setFlags) + result = QtVersion::QmakeBuildConfig(result | QtVersion::BuildAll); + else + result = QtVersion::QmakeBuildConfig(result & ~QtVersion::BuildAll); + if (debugScan) + qDebug()<<"found debug_and_release new value"<<result; + i += 17; + } else if (left >=7 && QStringRef(&part, i, 7) == "release") { + if (setFlags) + result = QtVersion::QmakeBuildConfig(result & ~QtVersion::DebugBuild); + else + result = QtVersion::QmakeBuildConfig(result | QtVersion::DebugBuild); + if (debugScan) + qDebug()<<"found release new value"<<result; + i +=7; + } else if (left >= 5 && QStringRef(&part, i, 5) == "debug") { + if (setFlags) + result = QtVersion::QmakeBuildConfig(result | QtVersion::DebugBuild); + else + result = QtVersion::QmakeBuildConfig(result & ~QtVersion::DebugBuild); + if (debugScan) + qDebug()<<"found debug new value"<<result; + i+=5; + } + } + } + } + } + if (debugScan) + qDebug()<<"returning: "<<result; + if (debugScan) + qDebug()<<"buildall = "<<bool(result & QtVersion::BuildAll); + if (debugScan) + qDebug()<<"debug ="<<bool(result & QtVersion::DebugBuild); + } + } + makefile.close(); + } + return result; +} + +void QtVersion::updateVersionInfo() const +{ + if (m_versionInfoUpToDate) + return; + // extract data from qmake executable + m_versionInfo.clear(); + m_notInstalled = false; + QFileInfo qmake(qmakeCommand()); + if (qmake.exists()) { + QStringList variables = QStringList() + << "QT_INSTALL_DATA" + << "QT_INSTALL_LIBS" + << "QT_INSTALL_HEADERS" + << "QT_INSTALL_DEMOS" + << "QT_INSTALL_EXAMPLES" + << "QT_INSTALL_CONFIGURATION" + << "QT_INSTALL_TRANSLATIONS" + << "QT_INSTALL_PLUGINS" + << "QT_INSTALL_BINS" + << "QT_INSTALL_DOCS" + << "QT_INSTALL_PREFIX"; + QStringList args = QStringList() << QString("-query") + << variables.join(" -query ").split(" ", QString::SkipEmptyParts); + QProcess process; + process.start(qmake.absoluteFilePath(), args, QIODevice::ReadOnly); + if (process.waitForFinished(2000)) { + QByteArray output = process.readAllStandardOutput(); + QTextStream stream(&output); + while (!stream.atEnd()) { + QString line = stream.readLine(); + int index = line.indexOf(":"); + if (index != -1) + m_versionInfo.insert(line.left(index), line.mid(index+1)); + } + } + + if (m_versionInfo.contains("QT_INSTALL_DATA")) + m_versionInfo.insert("QMAKE_MKSPECS", QDir::cleanPath(m_versionInfo.value("QT_INSTALL_DATA")+"/mkspecs")); + + // Now check for a qt that is configured with a prefix but not installed + if (m_versionInfo.contains("QT_INSTALL_BINS")) { + QFileInfo fi(m_versionInfo.value("QT_INSTALL_BINS")); + if (!fi.exists()) + m_notInstalled = true; + } + if (m_versionInfo.contains("QT_INSTALL_HEADERS")){ + QFileInfo fi(m_versionInfo.value("QT_INSTALL_HEADERS")); + if (!fi.exists()) + m_notInstalled = true; + } + + // Parse qconfigpri + QString baseDir = m_versionInfo.contains("QT_INSTALL_DATA") ? + m_versionInfo.value("QT_INSTALL_DATA") : + m_path; + QFile qconfigpri(baseDir + QLatin1String("/mkspecs/qconfig.pri")); + if (qconfigpri.exists()) { + qconfigpri.open(QIODevice::ReadOnly | QIODevice::Text); + QTextStream stream(&qconfigpri); + while (!stream.atEnd()) { + QString line = stream.readLine().trimmed(); + if (line.startsWith(QLatin1String("CONFIG"))) { + m_defaultConfigIsDebugAndRelease = false; + QStringList values = line.split(QLatin1Char('=')).at(1).trimmed().split(" "); + foreach(const QString &value, values) { + if (value == "debug") + m_defaultConfigIsDebug = true; + else if(value == "release") + m_defaultConfigIsDebug = false; + else if(value == "build_all") + m_defaultConfigIsDebugAndRelease = true; + } + } + } + } + } + m_versionInfoUpToDate = true; +} + +bool QtVersion::isInstalled() const +{ + updateVersionInfo(); + return !m_notInstalled; +} + +void QtVersion::updateMkSpec() const +{ + if (m_mkspecUpToDate) + return; + //qDebug()<<"Finding mkspec for"<<path(); + + QString mkspec; + QFile f(path() + "/.qmake.cache"); + if (f.exists() && f.open(QIODevice::ReadOnly)) { + while(!f.atEnd()) { + QByteArray line = f.readLine(); + if(line.startsWith("QMAKESPEC")) { + const QList<QByteArray> &temp = line.split('='); + if(temp.size() == 2) { + mkspec = temp.at(1).trimmed(); + if (mkspec.startsWith("$$QT_BUILD_TREE/mkspecs/")) + mkspec = mkspec.mid(QString("$$QT_BUILD_TREE/mkspecs/").length()); + else if (mkspec.startsWith("$$QT_BUILD_TREE\\mkspecs\\")) + mkspec = mkspec.mid(QString("$$QT_BUILD_TREE\\mkspecs\\").length()); + } + break; + } + } + f.close(); + } else { + // no .qmake.cache so look at the default mkspec + QString mkspecPath = versionInfo().value("QMAKE_MKSPECS"); + if (mkspecPath.isEmpty()) + mkspecPath = path() + "/mkspecs/default"; + else + mkspecPath = mkspecPath + "/default"; +// qDebug() << "default mkspec is located at" << mkspecPath; +#ifdef Q_OS_WIN + QFile f2(mkspecPath + "/qmake.conf"); + if (f2.exists() && f2.open(QIODevice::ReadOnly)) { + while(!f2.atEnd()) { + QByteArray line = f2.readLine(); + if(line.startsWith("QMAKESPEC_ORIGINAL")) { + const QList<QByteArray> &temp = line.split('='); + if (temp.size() == 2) { + mkspec = temp.at(1); + } + break; + } + } + f2.close(); + } +#elif defined(Q_OS_MAC) + QFile f2(mkspecPath + "/qmake.conf"); + if (f2.exists() && f2.open(QIODevice::ReadOnly)) { + while(!f2.atEnd()) { + QByteArray line = f2.readLine(); + if(line.startsWith("MAKEFILE_GENERATOR")) { + const QList<QByteArray> &temp = line.split('='); + if (temp.size() == 2) { + const QByteArray &value = temp.at(1); + if (value.contains("XCODE")) { + // we don't want to generate xcode projects... +// qDebug() << "default mkspec is xcode, falling back to g++"; + mkspec = "macx-g++"; + } else { + //resolve mkspec link + QFileInfo f3(mkspecPath); + if (f3.isSymLink()) { + mkspec = f3.symLinkTarget(); + } + } + } + break; + } + } + f2.close(); + } +#else + QFileInfo f2(mkspecPath); + if (f2.isSymLink()) { + mkspec = f2.symLinkTarget(); + } +#endif + } + + int index =mkspec.lastIndexOf('/'); + if(index == -1) + index = mkspec.lastIndexOf('\\'); + if (index >= 0 && QDir(mkspec.left(index)).canonicalPath() == QDir(m_path + "/mkspecs/").canonicalPath()) + mkspec = mkspec.mid(index+1).trimmed(); + + m_mkspec = mkspec; + m_mkspecUpToDate = true; +// qDebug()<<"mkspec for "<<m_path<<" is "<<mkspec; +} + +QString QtVersion::makeCommand() const +{ +#ifdef Q_OS_WIN + const QString &spec = mkspec(); + if (spec.startsWith("win32-msvc") || spec == QLatin1String("win32-icc")) + return "nmake.exe"; + else if(spec.startsWith("wince")) + return "nmake.exe"; + else + return "mingw32-make.exe"; +#else + return "make"; +#endif +} + +QString QtVersion::qmakeCommand() const +{ + // We can't use versionInfo QT_INSTALL_BINS here + // because that functions calls us to find out the values for versionInfo + if (!m_qmakeCommand.isNull()) + return m_qmakeCommand; + + QDir qtDir = path() + "/bin/"; + foreach(const QString &possibleCommand, QtVersionManager::possibleQMakeCommands()) { + QString s = qtDir.absoluteFilePath(possibleCommand); + QFileInfo qmake(s); + if (qmake.exists() && qmake.isExecutable()) { + if (QtVersionManager::checkQMakeVersion(qmake.absoluteFilePath())) { + m_qmakeCommand = qmake.absoluteFilePath(); + return qmake.absoluteFilePath(); + } + } + } + return QString::null; +} + +QtVersion::ToolchainType QtVersion::toolchainType() const +{ + if (!isValid()) + return INVALID; + const QString &spec = mkspec(); + if(spec.startsWith("win32-msvc") || spec == QLatin1String("win32-icc")) + return MSVC; + else if(spec == "win32-g++") + return MinGW; + else if(spec == QString::null) + return INVALID; + else if(spec.startsWith("wince")) + return WINCE; + else + return OTHER; +} + +QString QtVersion::mingwDirectory() const +{ + return m_mingwDirectory; +} + +void QtVersion::setMingwDirectory(const QString &directory) +{ + m_mingwDirectory = directory; +} + +QString QtVersion::prependPath() const +{ + return m_prependPath; +} + +void QtVersion::setPrependPath(const QString &directory) +{ + m_prependPath = directory; +} + +QString QtVersion::msvcVersion() const +{ + return m_msvcVersion; +} + +void QtVersion::setMsvcVersion(const QString &version) +{ + m_msvcVersion = version; +} + +Environment QtVersion::addToEnvironment(const Environment &env) +{ + Environment e(env); + e.set("QTDIR", m_path); + QString qtdirbin = versionInfo().value("QT_INSTALL_BINS"); + e.prependOrSetPath(qtdirbin); + // add libdir, includedir and bindir + // or add Mingw dirs + // or do nothing on other + QtVersion::ToolchainType t = toolchainType(); + if(t == QtVersion::MinGW) { + QFileInfo mingwFileInfo(m_mingwDirectory + "/bin"); + if(mingwFileInfo.exists()) + e.prependOrSetPath(m_mingwDirectory + "/bin"); + } else if(t == QtVersion::MSVC) { + QList<MSVCEnvironment> list = MSVCEnvironment::availableVersions(); + if(list.count() == 1) { + e = list.at(0).addToEnvironment(e); + } else { + foreach(const MSVCEnvironment &m, list) { + if(m.name() == m_msvcVersion) { + e = m.addToEnvironment(e); + break; + } + } + } + } else if(t == QtVersion::WINCE) { + QString msvcPath; + // Find MSVC path + QList<MSVCEnvironment> list = MSVCEnvironment::availableVersions(); + if(list.count() == 1) { + msvcPath = list.at(0).path(); + e = list.at(0).addToEnvironment(e); + } else { + foreach(const MSVCEnvironment &m, list) { + if(m.name() == m_msvcVersion) { + e = m.addToEnvironment(e); + msvcPath = m.path(); + break; + } + } + } + msvcPath += "/"; + +// qDebug()<<"MSVC path"<<msvcPath; +// qDebug()<<"looking for platform name in"<< path() + "/mkspecs/" + mkspec() +"/qmake.conf"; + // Find Platform name + + QString platformName = CeSdkHandler::platformName(path() + "/mkspecs/" + mkspec()+ "/qmake.conf"); +// qDebug()<<"Platform Name"<<platformName; + + CeSdkHandler cesdkhandler; + cesdkhandler.parse(msvcPath); + e = cesdkhandler.find(platformName).addToEnvironment(e); + } else if(t == QtVersion::OTHER) { + if(!m_prependPath.isEmpty()) + e.prependOrSetPath(m_prependPath); + } + return e; +} + +int QtVersion::uniqueId() const +{ + return m_id; +} + +int QtVersion::getUniqueId() +{ + QtVersionManager *vm = ExtensionSystem::PluginManager::instance()->getObject<QtVersionManager>(); + return vm->getUniqueId(); +} + +bool QtVersion::isValid() const +{ + return (!(m_id == -1 || m_path == QString::null || m_name == QString::null || mkspec() == QString::null) && !m_notInstalled); +} + +QtVersion::QmakeBuildConfig QtVersion::defaultBuildConfig() const +{ + updateVersionInfo(); + QtVersion::QmakeBuildConfig result = QtVersion::QmakeBuildConfig(0); + if (m_defaultConfigIsDebugAndRelease) + result = QtVersion::BuildAll; + if (m_defaultConfigIsDebug) + result = QtVersion::QmakeBuildConfig(result | QtVersion::DebugBuild); + return result; +} diff --git a/src/plugins/qt4projectmanager/qtversionmanager.h b/src/plugins/qt4projectmanager/qtversionmanager.h new file mode 100644 index 00000000000..833e4655cf9 --- /dev/null +++ b/src/plugins/qt4projectmanager/qtversionmanager.h @@ -0,0 +1,214 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QTVERSIONMANAGER_H +#define QTVERSIONMANAGER_H + +#include <QtCore/QPointer> +#include <QtGui/QWidget> +#include <coreplugin/dialogs/ioptionspage.h> +#include <coreplugin/icore.h> +#include <projectexplorer/ProjectExplorerInterfaces> + +#include <QDebug> +#include "ui_qtversionmanager.h" + +namespace Qt4ProjectManager { +namespace Internal { + +class QtDirWidget; + +class QtVersion { + friend class QtDirWidget; //for changing name and path + friend class QtVersionManager; +public: + QtVersion(const QString &name, const QString &path); + QtVersion(const QString &name, const QString &path, int id, bool isSystemVersion = false); + QtVersion() + :m_name(QString::null), m_path(QString::null), m_id(-1) + { } + + bool isValid() const; //TOOD check that the dir exists and the name is non empty + bool isInstalled() const; + bool isSystemVersion() const { return m_isSystemVersion; } + + QString name() const; + QString path() const; + QString sourcePath() const; + QString mkspec() const; + QString makeCommand() const; + QString qmakeCommand() const; + // Returns the PREFIX, BINPREFIX, DOCPREFIX and similar information + QHash<QString,QString> versionInfo() const; + + enum ToolchainType { MinGW, MSVC, WINCE, OTHER, INVALID }; + + ToolchainType toolchainType() const; + + QString mingwDirectory() const; + void setMingwDirectory(const QString &directory); + QString prependPath() const; + void setPrependPath(const QString &string); + QString msvcVersion() const; + void setMsvcVersion(const QString &version); + ProjectExplorer::Environment addToEnvironment(const ProjectExplorer::Environment &env); + + int uniqueId() const; + + enum QmakeBuildConfig + { + NoBuild = 1, + DebugBuild = 2, + BuildAll = 8 + }; + + QmakeBuildConfig defaultBuildConfig() const; +private: + static int getUniqueId(); + void setName(const QString &name); + void setPath(const QString &path); + void updateSourcePath(); + void updateVersionInfo() const; + void updateMkSpec() const; + QString m_name; + mutable bool m_versionInfoUpToDate; + mutable bool m_mkspecUpToDate; + QString m_path; + QString m_sourcePath; + mutable QString m_mkspec; // updated lazily + QString m_mingwDirectory; + QString m_prependPath; + QString m_msvcVersion; + mutable QHash<QString,QString> m_versionInfo; // updated lazily + int m_id; + bool m_isSystemVersion; + mutable bool m_notInstalled; + mutable bool m_defaultConfigIsDebug; + mutable bool m_defaultConfigIsDebugAndRelease; + mutable QString m_qmakeCommand; + Q_DISABLE_COPY(QtVersion); +}; + + +class QtDirWidget : public QWidget +{ + Q_OBJECT +public: + QtDirWidget(QWidget *parent, QList<QtVersion *> versions, int defaultVersion); + QList<QtVersion *> versions() const; + int defaultVersion() const; + void finish(); +private: + void showEnvironmentPage(QTreeWidgetItem * item); + void fixQtVersionName(int index); + Ui::QtVersionManager m_ui; + QList<QtVersion *> m_versions; + int m_defaultVersion; + QString m_specifyNameString; + QString m_specifyPathString; + +private slots: + void versionChanged(QTreeWidgetItem *item, QTreeWidgetItem *old); + void addQtDir(); + void removeQtDir(); + void updateState(); + void browse(); + void mingwBrowse(); + void defaultChanged(int index); + void updateCurrentQtName(); + void updateCurrentQtPath(); + void updateCurrentMingwDirectory(); + void msvcVersionChanged(); +}; + +class QtVersionManager : public Core::IOptionsPage +{ + Q_OBJECT + +public: + QtVersionManager(); + ~QtVersionManager(); + + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + + void writeVersionsIntoSettings(); + + QList<QtVersion *> versions() const; + + QtVersion * version(int id) const; + QtVersion * currentQtVersion() const; + + // internal + int getUniqueId(); + + QtVersion::QmakeBuildConfig scanMakefileForQmakeConfig(const QString &directory, QtVersion::QmakeBuildConfig defaultBuildConfig); + QString findQtVersionFromMakefile(const QString &directory); + QtVersion *qtVersionForDirectory(const QString &directory); + + // Used by the projectloadwizard + void addVersion(QtVersion *version); + + // returns something like qmake4, qmake, qmake-qt4 or whatever distributions have chosen (used by QtVersion) + static QStringList possibleQMakeCommands(); + // return true if the qmake at qmakePath is qt4 (used by QtVersion) + static bool checkQMakeVersion(const QString &qmakePath); +signals: + void defaultQtVersionChanged(); + void qtVersionsChanged(); +private: + + void addNewVersionsFromInstaller(); + void updateSystemVersion(); + void updateDocumentation(); + QString findSystemQt() const; + static int indexOfVersionInList(const QtVersion * const version, const QList<QtVersion *> &list); + void updateUniqueIdToIndexMap(); + + Core::ICore *m_core; + QPointer<QtDirWidget> m_widget; + + QtVersion *m_emptyVersion; + int m_defaultVersion; + QList<QtVersion *> m_versions; + QMap<int, int> m_uniqueIdToIndex; + int m_idcount; +}; + +} //namespace Internal +} //namespace Qt4ProjectManager + +#endif //QTVERSIONMANAGER_H diff --git a/src/plugins/qt4projectmanager/qtversionmanager.ui b/src/plugins/qt4projectmanager/qtversionmanager.ui new file mode 100644 index 00000000000..de93505cb09 --- /dev/null +++ b/src/plugins/qt4projectmanager/qtversionmanager.ui @@ -0,0 +1,212 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Qt4ProjectManager::Internal::QtVersionManager</class> + <widget class="QWidget" name="Qt4ProjectManager::Internal::QtVersionManager"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>823</width> + <height>929</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Qt versions</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="3"> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QToolButton" name="addButton"> + <property name="minimumSize"> + <size> + <width>21</width> + <height>23</height> + </size> + </property> + <property name="text"> + <string>+</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="delButton"> + <property name="minimumSize"> + <size> + <width>21</width> + <height>23</height> + </size> + </property> + <property name="text"> + <string>-</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="0" column="0" colspan="3"> + <widget class="QTreeWidget" name="qtdirList"> + <property name="uniformRowHeights"> + <bool>true</bool> + </property> + <property name="itemsExpandable"> + <bool>false</bool> + </property> + <property name="columnCount"> + <number>2</number> + </property> + <column> + <property name="text"> + <string>Name</string> + </property> + </column> + <column> + <property name="text"> + <string>Path</string> + </property> + </column> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="versionNameLabel"> + <property name="text"> + <string>Version Name:</string> + </property> + </widget> + </item> + <item row="1" column="1" colspan="2"> + <widget class="QLineEdit" name="nameEdit"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="pathLabel"> + <property name="text"> + <string>Path:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="pathEdit"/> + </item> + <item row="2" column="2"> + <widget class="QToolButton" name="browseButton"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="mingwLabel"> + <property name="text"> + <string>MinGw Directory:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="mingwLineEdit"/> + </item> + <item row="3" column="2"> + <widget class="QToolButton" name="mingwBrowseButton"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + <item row="4" column="0" colspan="3"> + <widget class="QLabel" name="msvcLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="7" column="0" colspan="4"> + <widget class="QLabel" name="errorLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="5" column="1" colspan="2"> + <widget class="QComboBox" name="msvcComboBox"/> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="defaultLabel"> + <property name="text"> + <string>Default Qt Version:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="defaultCombo"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <tabstops> + <tabstop>qtdirList</tabstop> + <tabstop>delButton</tabstop> + <tabstop>nameEdit</tabstop> + <tabstop>pathEdit</tabstop> + <tabstop>defaultCombo</tabstop> + <tabstop>browseButton</tabstop> + </tabstops> + <resources> + <include location="../../libs/cplusplus/cplusplus.qrc"/> + <include location="../../libs/extensionsystem/pluginview.qrc"/> + <include location="../bookmarks/bookmarks.qrc"/> + <include location="../coreplugin/core.qrc"/> + <include location="../coreplugin/fancyactionbar.qrc"/> + <include location="../cppeditor/cppeditor.qrc"/> + <include location="../cpptools/cpptools.qrc"/> + <include location="../designer/designer.qrc"/> + <include location="../find/find.qrc"/> + <include location="../gdbdebugger/gdbdebugger.qrc"/> + <include location="../help/help.qrc"/> + <include location="../perforce/perforce.qrc"/> + <include location="../projectexplorer/projectexplorer.qrc"/> + <include location="../../../shared/proparser/proparser.qrc"/> + <include location="qt4projectmanager.qrc"/> + <include location="wizards/wizards.qrc"/> + <include location="../quickopen/quickopen.qrc"/> + <include location="../resourceeditor/resourceeditor.qrc"/> + <include location="../texteditor/texteditor.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/plugins/qt4projectmanager/speinfo.cpp b/src/plugins/qt4projectmanager/speinfo.cpp new file mode 100644 index 00000000000..f82e9d875ae --- /dev/null +++ b/src/plugins/qt4projectmanager/speinfo.cpp @@ -0,0 +1,805 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "speinfo.h" +#include <QtCore/QCoreApplication> +#include <QtCore/QVariant> +#include <QtDebug> + +using namespace Qt4ProjectManager::Internal; + +bool SPEInfo::m_listsInitialized = false; +QList<SPEInfoItem*> SPEInfo::m_configurationList; +QList<SPEInfoItem*> SPEInfo::m_platformList; +QList<SPEInfoItem*> SPEInfo::m_variableList; +QList<SPEInfoItem*> SPEInfo::m_qtmoduleList; +QList<SPEInfoItem*> SPEInfo::m_templateList; +QList<SPEInfoItem*> SPEInfo::m_operatorList; + +QHash<QPair<SPEInfoItem::InfoKind, QString> ,SPEInfoItem*> SPEInfo::m_itemHash; + +const QString SPEInfoItem::keyType("valuetype"); +const QString SPEInfoItem::valueFile("file"); +const QString SPEInfoItem::valuePath("path"); +const QString SPEInfoItem::keyIncludedByDefault("includedbydefault"); +const QString SPEInfoItem::keyImageFileName("imagefilename"); + +// Configurations (Debug, Release, ...) +class InfoItemConfigurationCross : public SPEInfoItem +{ +public: + InfoItemConfigurationCross(): SPEInfoItem("", Configuration) {} + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Debug and Release"); } +}; + +class InfoItemConfigurationDebug : public SPEInfoItem +{ +public: + InfoItemConfigurationDebug(): SPEInfoItem("debug", Configuration) {} + QString name(void) const {return QCoreApplication::translate("SimpleProEditor", "Debug specific");} +}; + +class InfoItemConfigurationRelease : public SPEInfoItem +{ +public: + InfoItemConfigurationRelease(): SPEInfoItem("release", Configuration) {} + QString name(void) const {return QCoreApplication::translate("SimpleProEditor", "Release specific");} +}; + + +// Platforms (Windows, Mac, ...) +class InfoItemPlatformCross : public SPEInfoItem +{ +public: + InfoItemPlatformCross(): SPEInfoItem("", Platform) {} + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "All platforms"); } +}; + +class InfoItemPlatformWindows : public SPEInfoItem +{ +public: + InfoItemPlatformWindows(): SPEInfoItem("win32", Platform) {} + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "MS Windows specific"); } +}; + +class InfoItemPlatformUnix : public SPEInfoItem +{ +public: + InfoItemPlatformUnix(): SPEInfoItem("unix", Platform) {} + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Linux/Unix specific"); } +}; + +class InfoItemPlatformOSX : public SPEInfoItem +{ +public: + InfoItemPlatformOSX(): SPEInfoItem("macx", Platform) {} + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Mac OSX specific"); } +}; + + +// Variables (Target options, Libraries, Defines, ...) +class InfoItemVariableTargetOptions : public SPEInfoItem +{ +public: + InfoItemVariableTargetOptions(): SPEInfoItem("TEMPLATE", Variable) + { + m_data.insert(keyImageFileName, ":/variableimages/images/target.png"); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Target Options");} + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Type and name of the target."); + } +}; + +class InfoItemVariableDefines : public SPEInfoItem +{ +public: + InfoItemVariableDefines(): SPEInfoItem("DEFINES", Variable) + { + m_data.insert(keyImageFileName, ":/variableimages/images/defines.png"); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Preprocessor Definitions");} + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Setting of the preprocessor definitions."); + } +}; + +class InfoItemVariableIncludePath : public SPEInfoItem +{ +public: + InfoItemVariableIncludePath(): SPEInfoItem("INCLUDEPATH", Variable) + { + m_data.insert(keyType, valuePath); + m_data.insert(keyImageFileName, ":/variableimages/images/includes.png"); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Include path"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Setting of the pathes where the header files are located."); + } +}; + +class InfoItemVariableLibs : public SPEInfoItem +{ +public: + InfoItemVariableLibs(): SPEInfoItem("LIBS", Variable) + { + m_data.insert(keyImageFileName, ":/variableimages/images/libs.png"); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Libraries");} + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Defining the libraries to link the target against and the pathes where these are located."); + } +}; + +class InfoItemVariableSources : public SPEInfoItem +{ +public: + InfoItemVariableSources(): SPEInfoItem("SOURCES", Variable) + { + m_data.insert(keyType, valueFile); + m_data.insert(keyImageFileName, ":/variableimages/images/sources.png"); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Source Files");} + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + ""); + } +}; + +class InfoItemVariableHeaders : public SPEInfoItem +{ +public: + InfoItemVariableHeaders(): SPEInfoItem("HEADERS", Variable) + { + m_data.insert(keyType, valueFile); + m_data.insert(keyImageFileName, ":/variableimages/images/headers.png"); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Header Files");} + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + ""); + } +}; + +class InfoItemVariableForms : public SPEInfoItem +{ +public: + InfoItemVariableForms(): SPEInfoItem("FORMS", Variable) + { + m_data.insert(keyType, valueFile); + m_data.insert(keyImageFileName, ":/variableimages/images/forms.png"); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Forms");} + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + ""); + } +}; + +class InfoItemVariableQtModules : public SPEInfoItem +{ +public: + InfoItemVariableQtModules(): SPEInfoItem("QT", Variable) + { + m_data.insert(keyImageFileName, ":/variableimages/images/qtmodules.png"); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Qt Modules");} + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Setting up which of the Qt modules will be used in the target application."); + } +}; + +class InfoItemVariableResources : public SPEInfoItem +{ +public: + InfoItemVariableResources(): SPEInfoItem("RESOURCES", Variable) + { + m_data.insert(keyType, valueFile); + m_data.insert(keyImageFileName, ":/variableimages/images/resources.png"); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Resource files");} + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + ""); + } +}; + +class InfoItemVariableTarget : public SPEInfoItem +{ +public: + InfoItemVariableTarget(): SPEInfoItem("TARGET", Variable) {} + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Target name");} + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "The name of the resulting target."); + } +}; + +class InfoItemVariableConfig : public SPEInfoItem +{ +public: + InfoItemVariableConfig(): SPEInfoItem("CONFIG", Variable) {} + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Configuration");} + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Configuration."); + } +}; + +class InfoItemVariableDestdir : public SPEInfoItem +{ +public: + InfoItemVariableDestdir(): SPEInfoItem("DESTDIR", Variable) {} + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Destination directory");} + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Where the resulting target will be created."); + } +}; + + +// Qt modules +class InfoItemModulesCore : public SPEInfoItem +{ +public: + InfoItemModulesCore(): SPEInfoItem("core", QtModule) + { + m_data.insert(keyIncludedByDefault, true); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "QtCore Module"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Core non-GUI classes used by other modules"); + } +}; + +class InfoItemModulesGui : public SPEInfoItem +{ +public: + InfoItemModulesGui(): SPEInfoItem("gui", QtModule) + { + m_data.insert(keyIncludedByDefault, true); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "QtGui Module"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Graphical user interface components"); + } +}; + +class InfoItemModulesNetwork : public SPEInfoItem +{ +public: + InfoItemModulesNetwork(): SPEInfoItem("network", QtModule) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "QtNetwork Module"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Classes for network programming"); + } +}; + +class InfoItemModulesOpenGL : public SPEInfoItem +{ +public: + InfoItemModulesOpenGL(): SPEInfoItem("opengl", QtModule) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "QtOpenGL Module"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "OpenGL support classes"); + } +}; + +class InfoItemModulesSql : public SPEInfoItem +{ +public: + InfoItemModulesSql(): SPEInfoItem("sql", QtModule) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "QtSql Module"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Classes for database integration using SQL"); + } +}; + +class InfoItemModulesScript : public SPEInfoItem +{ +public: + InfoItemModulesScript(): SPEInfoItem("script", QtModule) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "QtScript Module"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Classes for evaluating Qt Scripts"); + } +}; + +class InfoItemModulesSvg : public SPEInfoItem +{ +public: + InfoItemModulesSvg(): SPEInfoItem("svg", QtModule) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "QtSvg Module"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Classes for displaying the contents of SVG files"); + } +}; + +class InfoItemModulesWebKit : public SPEInfoItem +{ +public: + InfoItemModulesWebKit(): SPEInfoItem("webkit", QtModule) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "QtWebKit Module"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Classes for displaying and editing Web content"); + } +}; + +class InfoItemModulesXml : public SPEInfoItem +{ +public: + InfoItemModulesXml(): SPEInfoItem("xml", QtModule) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "QtXml Module"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Classes for handling XML"); + } +}; + +class InfoItemModulesXmlPatterns : public SPEInfoItem +{ +public: + InfoItemModulesXmlPatterns(): SPEInfoItem("xmlpatterns", QtModule) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "QtXmlPatterns Module"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "An XQuery/XPath engine for XML and custom data models"); + } +}; + +class InfoItemModulesPhonon : public SPEInfoItem +{ +public: + InfoItemModulesPhonon(): SPEInfoItem("phonon", QtModule) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Phonon Module"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Multimedia framework classes"); + } +}; + +class InfoItemModulesQt3Support : public SPEInfoItem +{ +public: + InfoItemModulesQt3Support(): SPEInfoItem("qt3support", QtModule) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Qt3Support Module"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Classes that ease porting from Qt 3 to Qt 4"); + } +}; + +class InfoItemModulesTest : public SPEInfoItem +{ +public: + InfoItemModulesTest(): SPEInfoItem("qtestlib", QtModule) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "QtTest Module"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Tool classes for unit testing"); + } +}; + +class InfoItemModulesDBus : public SPEInfoItem +{ +public: + InfoItemModulesDBus(): SPEInfoItem("dbus", QtModule) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "QtDBus module"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Classes for Inter-Process Communication using the D-Bus"); + } +}; + + +// Target templates +class InfoItemTemplatesApp : public SPEInfoItem +{ +public: + InfoItemTemplatesApp(): SPEInfoItem("app", Template) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Application"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Create a standalone application"); + } +}; + +class InfoItemTemplatesDynamicLib : public SPEInfoItem +{ +public: + InfoItemTemplatesDynamicLib(): SPEInfoItem("lib", Template) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Dynamic Library"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Create a dynamic library for usage in other applications"); + } +}; + +class InfoItemTemplatesStaticLib : public SPEInfoItem +{ +public: + InfoItemTemplatesStaticLib(): SPEInfoItem("staticlib", Template) + { + m_data.insert(keyIncludedByDefault, false); + } + + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Static Library"); } + QString description(void) const + { + return QCoreApplication::translate("SimpleProEditor", + "Create a static library for usage in other applications"); + } +}; + +// Variable operators +class InfoItemOperatorsAdd : public SPEInfoItem +{ +public: + InfoItemOperatorsAdd(): SPEInfoItem("+=", Operator) {} + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Add Operator"); } +}; + +class InfoItemOperatorsRemove : public SPEInfoItem +{ +public: + InfoItemOperatorsRemove(): SPEInfoItem("-=", Operator) {} + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Remove Operator"); } +}; + +class InfoItemOperatorsReplace : public SPEInfoItem +{ +public: + InfoItemOperatorsReplace(): SPEInfoItem("~=", Operator) {} + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Replace Operator"); } +}; + +class InfoItemOperatorsSet : public SPEInfoItem +{ +public: + InfoItemOperatorsSet(): SPEInfoItem("=", Operator) {} + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Set Operator"); } +}; + +class InfoItemOperatorsUniqueAdd : public SPEInfoItem +{ +public: + InfoItemOperatorsUniqueAdd(): SPEInfoItem("*=", Operator) {} + QString name(void) const { return QCoreApplication::translate("SimpleProEditor", "Unique Add Operator"); } +}; + + +SPEInfoItem::SPEInfoItem(const QString &id, InfoKind kind) +: m_id(id) +, m_infoKind(kind) +, m_parentItem(0) +{ +} + +QString SPEInfoItem::name() const +{ + return ""; +} + +QString SPEInfoItem::description() const +{ + return ""; +} + +QVariant SPEInfoItem::data(const QString &key) const +{ + return m_data.value(key); +} + +const SPEInfoItem *SPEInfoItem::parentItem(void) const +{ + return m_parentItem; +} + +void SPEInfoItem::setParentItem(const SPEInfoItem *parentItem) +{ + m_parentItem = parentItem; +} + +bool SPEInfoItem::isAncestorOf(const SPEInfoItem *successor) const +{ + const SPEInfoItem *ancestorCursor = successor; + + while ((ancestorCursor = ancestorCursor->parentItem()) != NULL) + if (ancestorCursor == this) + return true; + + return false; +} + +QString SPEInfoItem::id() const +{ + return m_id; +} + +SPEInfoItem::InfoKind SPEInfoItem::infoKind(void) const +{ + return m_infoKind; +} + +SPEInfo::~SPEInfo() +{ + deleteLists(); +} + +const QList<SPEInfoItem*> *SPEInfo::list(SPEInfoItem::InfoKind kind) +{ + if (!m_listsInitialized) + initializeLists(); + return + kind == SPEInfoItem::Configuration?&m_configurationList + :kind == SPEInfoItem::Platform?&m_platformList + :kind == SPEInfoItem::Variable?&m_variableList + :kind == SPEInfoItem::QtModule?&m_qtmoduleList + :kind == SPEInfoItem::Template?&m_templateList + :/*kind == SPEInfoItem::Operator?*/&m_operatorList + ; +} + +const SPEInfoItem *SPEInfo::defaultInfoOfKind(SPEInfoItem::InfoKind kind) +{ + return list(kind)->at(0); +} + +void SPEInfo::addListToHash(const QList<SPEInfoItem*> &list) +{ + foreach (SPEInfoItem *item, list) + m_itemHash.insert(qMakePair(item->infoKind(), item->id()), item); +} + +void SPEInfo::initializeLists(void) +{ + InfoItemConfigurationCross *infoItemConfigurationCross = new InfoItemConfigurationCross; + InfoItemConfigurationDebug *infoItemConfigurationDebug = new InfoItemConfigurationDebug; + infoItemConfigurationDebug->setParentItem(infoItemConfigurationCross); + InfoItemConfigurationRelease *infoItemConfigurationRelease = new InfoItemConfigurationRelease; + infoItemConfigurationRelease->setParentItem(infoItemConfigurationCross); + m_configurationList + << infoItemConfigurationCross + << infoItemConfigurationDebug + << infoItemConfigurationRelease; + addListToHash(m_configurationList); + + InfoItemPlatformCross *infoItemPlatformCross = new InfoItemPlatformCross; + InfoItemPlatformWindows *infoItemPlatformWindows = new InfoItemPlatformWindows; + infoItemPlatformWindows->setParentItem(infoItemPlatformCross); + InfoItemPlatformUnix *infoItemPlatformUnix = new InfoItemPlatformUnix; + infoItemPlatformUnix->setParentItem(infoItemPlatformCross); + InfoItemPlatformOSX *infoItemPlatformOSX = new InfoItemPlatformOSX; + infoItemPlatformOSX->setParentItem(infoItemPlatformUnix); + m_platformList + << infoItemPlatformCross + << infoItemPlatformWindows + << infoItemPlatformUnix + << infoItemPlatformOSX; + addListToHash(m_platformList); + + m_variableList + << new InfoItemVariableTargetOptions + << new InfoItemVariableDefines + << new InfoItemVariableLibs + << new InfoItemVariableIncludePath + << new InfoItemVariableSources + << new InfoItemVariableHeaders + << new InfoItemVariableForms + << new InfoItemVariableQtModules + << new InfoItemVariableResources + << new InfoItemVariableTarget + << new InfoItemVariableConfig + << new InfoItemVariableDestdir; + addListToHash(m_variableList); + + m_qtmoduleList + << new InfoItemModulesCore + << new InfoItemModulesGui + << new InfoItemModulesNetwork + << new InfoItemModulesOpenGL + << new InfoItemModulesScript + << new InfoItemModulesSql + << new InfoItemModulesSvg + << new InfoItemModulesWebKit + << new InfoItemModulesXml + << new InfoItemModulesXmlPatterns + << new InfoItemModulesPhonon + << new InfoItemModulesQt3Support + << new InfoItemModulesTest + << new InfoItemModulesDBus; + addListToHash(m_qtmoduleList); + + m_templateList + << new InfoItemTemplatesApp + << new InfoItemTemplatesDynamicLib + << new InfoItemTemplatesStaticLib; + addListToHash(m_templateList); + + m_operatorList + << new InfoItemOperatorsAdd + << new InfoItemOperatorsRemove + << new InfoItemOperatorsReplace + << new InfoItemOperatorsSet + << new InfoItemOperatorsUniqueAdd; + addListToHash(m_operatorList); + + m_listsInitialized = true; +} + +void SPEInfo::deleteLists(void) +{ + m_itemHash.clear(); + + static QList<SPEInfoItem*> *lists[] = { + &m_configurationList, + &m_platformList, + &m_variableList, + &m_qtmoduleList, + &m_templateList, + &m_operatorList + }; + + for (size_t i = 0; i < sizeof(lists)/sizeof(lists[0]); i++) { + qDeleteAll(*lists[i]); + lists[i]->clear(); + } + + m_listsInitialized = false; +} + +const SPEInfoItem *SPEInfo::infoOfKindForId(SPEInfoItem::InfoKind kind, + const QString &id, const SPEInfoItem *defaultInfoItem) +{ + QPair<SPEInfoItem::InfoKind, QString > keyPair = qMakePair(kind, id); + return m_itemHash.contains(keyPair)?m_itemHash.value(keyPair):defaultInfoItem; +} + +const SPEInfoItem *SPEInfo::platformInfoForId(const QString &id) +{ + return infoOfKindForId(SPEInfoItem::Platform, id, SPEInfo::defaultInfoOfKind(SPEInfoItem::Platform)); +} + +const SPEInfoItem *SPEInfo::configurationInfoForId(const QString &id) +{ + return infoOfKindForId(SPEInfoItem::Configuration, id, SPEInfo::defaultInfoOfKind(SPEInfoItem::Configuration)); +} + +static SPEInfo speInfoInstance; // it's destructor will call deleteLists() diff --git a/src/plugins/qt4projectmanager/speinfo.h b/src/plugins/qt4projectmanager/speinfo.h new file mode 100644 index 00000000000..4f5634a3b97 --- /dev/null +++ b/src/plugins/qt4projectmanager/speinfo.h @@ -0,0 +1,116 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SIMPLEPROEDITORINFO_H +#define SIMPLEPROEDITORINFO_H + +#include <QtGui/QPixmap> +#include <QVariant> +#include <QtCore/QHash> + +namespace Qt4ProjectManager { +namespace Internal { + +class SPEInfoItem +{ +public: + enum InfoKind { + Configuration, + Platform, + Variable, + QtModule, + Template, + Operator + }; + + SPEInfoItem(const QString &id, InfoKind kind); + virtual ~SPEInfoItem() {} + + QString id(void) const; + InfoKind infoKind(void) const; + virtual QString name(void) const; + virtual QString description(void) const; + QVariant data(const QString &key) const; + const SPEInfoItem *parentItem(void) const; + void setParentItem(const SPEInfoItem *parentItem); + + bool isAncestorOf(const SPEInfoItem *ancestor) const; + + static const QString keyType; + static const QString valueFile; + static const QString valuePath; + static const QString keyIncludedByDefault; + static const QString keyImageFileName; + +protected: + QHash<QString, QVariant> m_data; + +private: + QString m_id; + InfoKind m_infoKind; + QPixmap m_image; + const class SPEInfoItem *m_parentItem; +}; + +class SPEInfo +{ +public: + ~SPEInfo(); + + static const QList<SPEInfoItem*> *list(SPEInfoItem::InfoKind kind); + static const SPEInfoItem *defaultInfoOfKind(SPEInfoItem::InfoKind kind); + static const SPEInfoItem *platformInfoForId(const QString &id); + static const SPEInfoItem *configurationInfoForId(const QString &id); + static const SPEInfoItem *infoOfKindForId(SPEInfoItem::InfoKind kind, + const QString &id, const SPEInfoItem *defaultInfoItem = NULL); + +private: + static void addListToHash(const QList<SPEInfoItem*> &list); + static void initializeLists(void); + static void deleteLists(void); + + static QList<SPEInfoItem*> m_configurationList; + static QList<SPEInfoItem*> m_platformList; + static QList<SPEInfoItem*> m_variableList; + static QList<SPEInfoItem*> m_qtmoduleList; + static QList<SPEInfoItem*> m_templateList; + static QList<SPEInfoItem*> m_operatorList; + + static QHash<QPair<SPEInfoItem::InfoKind, QString> ,SPEInfoItem* > m_itemHash; + + static bool m_listsInitialized; +}; + +} //namespace Internal +} //namespace Qt4ProjectManager + +#endif diff --git a/src/plugins/qt4projectmanager/wizards/consoleappwizard.cpp b/src/plugins/qt4projectmanager/wizards/consoleappwizard.cpp new file mode 100644 index 00000000000..0bb662012ec --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/consoleappwizard.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "consoleappwizard.h" +#include "consoleappwizarddialog.h" +#include "qt4projectmanager.h" +#include "qt4projectmanagerconstants.h" + +#include <utils/pathchooser.h> + +#include <QtCore/QDir> +#include <QtCore/QTextStream> +#include <QtCore/QDebug> + +#include <QtGui/QIcon> + +static const char *mainCppC = +"#include <QtCore/QCoreApplication>\n\n" +"int main(int argc, char *argv[])\n" +"{\n" +" QCoreApplication a(argc, argv);\n\n" +" return a.exec();\n" +"}\n"; + +static const char *mainSourceFileC = "main"; + +namespace Qt4ProjectManager { + +namespace Internal { + +ConsoleAppWizard::ConsoleAppWizard(Core::ICore *core) : + QtWizard(core, tr("Qt4 Console Application"), + tr("Creates a Qt4 console application."), + QIcon(":/wizards/images/console.png")) +{ +} + +QWizard *ConsoleAppWizard::createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const +{ + ConsoleAppWizardDialog *dialog = new ConsoleAppWizardDialog(name(), icon(), extensionPages, parent); + dialog->setPath(defaultPath.isEmpty() ? Core::Utils::PathChooser::homePath() : defaultPath); + return dialog; +} + +Core::GeneratedFiles + ConsoleAppWizard::generateFiles(const QWizard *w, + QString * /*errorMessage*/) const +{ + const ConsoleAppWizardDialog *wizard = qobject_cast< const ConsoleAppWizardDialog *>(w); + const QtProjectParameters params = wizard->parameters(); + const QString projectPath = params.projectPath(); + + // Create files: Source + + const QString sourceFileName = Core::BaseFileWizard::buildFileName(projectPath, QLatin1String(mainSourceFileC), sourceSuffix()); + Core::GeneratedFile source(sourceFileName); + source.setContents(QLatin1String(mainCppC)); + // Create files: Profile + const QString profileName = Core::BaseFileWizard::buildFileName(projectPath, params.name,profileSuffix()); + + Core::GeneratedFile profile(profileName); + QString contents; + { + QTextStream proStr(&contents); + QtProjectParameters::writeProFileHeader(proStr); + params.writeProFile(proStr); + proStr << "\n\nSOURCES += " << QFileInfo(sourceFileName).fileName() << '\n'; + } + profile.setContents(contents); + return Core::GeneratedFiles() << source << profile; +} + +} +} diff --git a/src/plugins/qt4projectmanager/wizards/consoleappwizard.h b/src/plugins/qt4projectmanager/wizards/consoleappwizard.h new file mode 100644 index 00000000000..5469923c334 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/consoleappwizard.h @@ -0,0 +1,62 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CONSOLEAPPWIZARD_H +#define CONSOLEAPPWIZARD_H + +#include "qtwizard.h" + +namespace Qt4ProjectManager { +namespace Internal { + +class ModulesPage; + +class ConsoleAppWizard : public QtWizard +{ + Q_OBJECT + +public: + explicit ConsoleAppWizard(Core::ICore *core); + +protected: + virtual QWizard *createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const; + + virtual Core::GeneratedFiles generateFiles(const QWizard *w, + QString *errorMessage) const; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // CONSOLEAPPWIZARD_H diff --git a/src/plugins/qt4projectmanager/wizards/consoleappwizarddialog.cpp b/src/plugins/qt4projectmanager/wizards/consoleappwizarddialog.cpp new file mode 100644 index 00000000000..b893b30e6ff --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/consoleappwizarddialog.cpp @@ -0,0 +1,90 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "consoleappwizarddialog.h" +#include "consoleappwizard.h" +#include "modulespage.h" + +#include <utils/projectintropage.h> + +namespace Qt4ProjectManager { +namespace Internal { + +ConsoleAppWizardDialog::ConsoleAppWizardDialog(const QString &templateName, + const QIcon &icon, + const QList<QWizardPage*> &extensionPages, + QWidget *parent) : + QWizard(parent), + m_introPage(new Core::Utils::ProjectIntroPage), + m_modulesPage(new ModulesPage) +{ + setWindowIcon(icon); + setWindowTitle(templateName); + Core::BaseFileWizard::setupWizard(this); + setOptions(QWizard::IndependentPages | QWizard::HaveNextButtonOnLastPage); + + m_introPage->setDescription(tr("This wizard generates a Qt4 console application " + "project. The application derives from QCoreApplication and does not " + "present a GUI. You can press 'Finish' at any point in time.")); + + m_introPage->setFinalPage(true); + addPage(m_introPage); + + m_modulesPage->setModuleSelected(QLatin1String("core")); + addPage(m_modulesPage); + foreach (QWizardPage *p, extensionPages) + addPage(p); +} + +void ConsoleAppWizardDialog::setPath(const QString &path) +{ + m_introPage->setPath(path); +} + +void ConsoleAppWizardDialog::setName(const QString &name) +{ + m_introPage->setName(name); +} + +QtProjectParameters ConsoleAppWizardDialog::parameters() const +{ + QtProjectParameters rc; + rc.type = QtProjectParameters::ConsoleApp; + rc.name = m_introPage->name(); + rc.path = m_introPage->path(); + rc.selectedModules = m_modulesPage->selectedModules(); + rc.deselectedModules = m_modulesPage-> deselectedModules(); + return rc; +} + +} +} diff --git a/src/plugins/qt4projectmanager/wizards/consoleappwizarddialog.h b/src/plugins/qt4projectmanager/wizards/consoleappwizarddialog.h new file mode 100644 index 00000000000..21d0fb55404 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/consoleappwizarddialog.h @@ -0,0 +1,74 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CONSOLEAPPWIZARDDIALOG_H +#define CONSOLEAPPWIZARDDIALOG_H + +#include <QtGui/QWizard> + +namespace Core { + namespace Utils { + class ProjectIntroPage; + } +} + +namespace Qt4ProjectManager { +namespace Internal { + +struct QtProjectParameters; +class ModulesPage; + +class ConsoleAppWizardDialog : public QWizard +{ + Q_OBJECT + +public: + explicit ConsoleAppWizardDialog(const QString &templateName, + const QIcon &icon, + const QList<QWizardPage*> &extensionPages, + QWidget *parent = 0); + + QtProjectParameters parameters() const; + +public slots: + void setPath(const QString &path); + void setName(const QString &name); + +private: + Core::Utils::ProjectIntroPage *m_introPage; + ModulesPage *m_modulesPage; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // CONSOLEAPPWIZARDDIALOG_H diff --git a/src/plugins/qt4projectmanager/wizards/filespage.cpp b/src/plugins/qt4projectmanager/wizards/filespage.cpp new file mode 100644 index 00000000000..2c2fb6c6ec4 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/filespage.cpp @@ -0,0 +1,185 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "filespage.h" + +#include <utils/newclasswidget.h> + +#include <QtGui/QLabel> +#include <QtGui/QLayout> + +namespace Qt4ProjectManager { +namespace Internal { + +FilesPage::FilesPage(QWidget *parent) : + QWizardPage(parent), + m_newClassWidget(new Core::Utils::NewClassWidget) +{ + m_newClassWidget->setPathInputVisible(false); + setTitle(tr("Class Information")); + + QLabel *label = new QLabel(tr("Specify basic information about the classes " + "for which you want to generate skeleton source code files.")); + label->setWordWrap(true); + + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->addWidget(label); + vlayout->addItem(new QSpacerItem(0, 20)); + + vlayout->addWidget(m_newClassWidget); + + vlayout->addItem(new QSpacerItem(0, 20)); + m_errorLabel = new QLabel; + m_errorLabel->setStyleSheet(QLatin1String("color: red;")); + vlayout->addWidget(m_errorLabel); + setLayout(vlayout); + + connect(m_newClassWidget, SIGNAL(validChanged()), this, SIGNAL(completeChanged())); +} + +void FilesPage::setSuffixes(const QString &header, const QString &source, const QString &form) +{ + m_newClassWidget->setSourceExtension(source); + m_newClassWidget->setHeaderExtension(header); + if (!form.isEmpty()) + m_newClassWidget->setFormExtension(form); +} + +void FilesPage::setClassName(const QString &suggestedClassName) +{ + m_newClassWidget->setClassName(suggestedClassName); +} + + +bool FilesPage::isComplete() const +{ + QString error; + const bool complete = m_newClassWidget->isValid(&error); + m_errorLabel->setText(error); + return complete; +} + +QString FilesPage::className() const +{ + return m_newClassWidget->className(); +} + +QString FilesPage::baseClassName() const +{ + return m_newClassWidget->baseClassName(); +} + +void FilesPage::setBaseClassName(const QString &b) +{ + m_newClassWidget->setBaseClassName(b); +} + +QString FilesPage::sourceFileName() const +{ + return m_newClassWidget->sourceFileName(); +} + +QString FilesPage::headerFileName() const +{ + return m_newClassWidget->headerFileName(); +} + +QString FilesPage::formFileName() const +{ + return m_newClassWidget->formFileName(); +} + +bool FilesPage::namespacesEnabled() const +{ + return m_newClassWidget->namespacesEnabled(); +} + +void FilesPage::setNamespacesEnabled(bool b) +{ + m_newClassWidget->setNamespacesEnabled(b); +} + +void FilesPage::setBaseClassInputVisible(bool visible) +{ + m_newClassWidget->setBaseClassInputVisible(visible); +} + +bool FilesPage::isBaseClassInputVisible() const +{ + return m_newClassWidget->isBaseClassInputVisible(); +} + +QStringList FilesPage::baseClassChoices() const +{ + return m_newClassWidget->baseClassChoices(); +} + +void FilesPage::setBaseClassChoices(const QStringList &choices) +{ + m_newClassWidget->setBaseClassChoices(choices); +} + +void FilesPage::setFormFileInputVisible(bool visible) +{ + m_newClassWidget->setFormInputVisible(visible); +} + +bool FilesPage::isFormInputVisible() const +{ + return m_newClassWidget->isFormInputVisible(); +} + +bool FilesPage::formInputCheckable() const +{ + return m_newClassWidget->formInputCheckable(); +} + +bool FilesPage::formInputChecked() const +{ + return m_newClassWidget->formInputChecked(); +} + +void FilesPage::setFormInputCheckable(bool checkable) +{ + m_newClassWidget->setFormInputCheckable(checkable); +} + +void FilesPage::setFormInputChecked(bool checked) +{ + m_newClassWidget->setFormInputChecked(checked); +} + + + + +} // namespace Internal +} // namespace Qt4ProjectManager diff --git a/src/plugins/qt4projectmanager/wizards/filespage.h b/src/plugins/qt4projectmanager/wizards/filespage.h new file mode 100644 index 00000000000..fff6ec3fdc1 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/filespage.h @@ -0,0 +1,95 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FILESPAGE_H +#define FILESPAGE_H + +#include <QtGui/QWizard> + +QT_BEGIN_NAMESPACE +class QLabel; +QT_END_NAMESPACE + +namespace Core { + namespace Utils { + class NewClassWidget; + } +} + +namespace Qt4ProjectManager { +namespace Internal { + +class FilesPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit FilesPage(QWidget *parent = 0); + virtual bool isComplete() const; + + QString className() const; + void setClassName(const QString &suggestedClassName); + + QString baseClassName() const; + QString sourceFileName() const; + QString headerFileName() const; + QString formFileName() const; + + // API of the embedded NewClassWidget + bool namespacesEnabled() const; + bool isBaseClassInputVisible() const; + bool isFormInputVisible() const; + bool formInputCheckable() const; + bool formInputChecked() const; + QStringList baseClassChoices() const; + + void setSuffixes(const QString &header, const QString &source, const QString &form = QString()); + +public slots: + void setBaseClassName(const QString &); + void setNamespacesEnabled(bool b); + void setBaseClassInputVisible(bool visible); + void setBaseClassChoices(const QStringList &choices); + void setFormFileInputVisible(bool visible); + void setFormInputCheckable(bool checkable); + void setFormInputChecked(bool checked); + + +private: + Core::Utils::NewClassWidget *m_newClassWidget; + QLabel *m_errorLabel; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // FILESPAGE_H diff --git a/src/plugins/qt4projectmanager/wizards/guiappwizard.cpp b/src/plugins/qt4projectmanager/wizards/guiappwizard.cpp new file mode 100644 index 00000000000..58b2808a0f3 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/guiappwizard.cpp @@ -0,0 +1,201 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "guiappwizard.h" +#include "guiappwizarddialog.h" +#include "qt4projectmanager.h" +#include "modulespage.h" +#include "filespage.h" +#include "qt4projectmanagerconstants.h" + +#include <utils/pathchooser.h> +#include <projectexplorer/projectnodes.h> + +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QByteArray> +#include <QtCore/QDebug> +#include <QtCore/QTextStream> +#include <QtCore/QFileInfo> +#include <QtCore/QSharedPointer> + +#include <QtGui/QIcon> + +static const char *mainSourceFileC = "main"; +static const char *mainWindowUiContentsC = +"\n <widget class=\"QMenuBar\" name=\"menuBar\" />" +"\n <widget class=\"QToolBar\" name=\"mainToolBar\" />" +"\n <widget class=\"QWidget\" name=\"centralWidget\" />" +"\n <widget class=\"QStatusBar\" name=\"statusBar\" />"; + +static const char *baseClassesC[] = { "QMainWindow", "QWidget", "QDialog" }; + +static inline QStringList baseClasses() +{ + QStringList rc; + const int baseClassCount = sizeof(baseClassesC)/sizeof(const char *); + for (int i = 0; i < baseClassCount; i++) + rc.push_back(QLatin1String(baseClassesC[i])); + return rc; +} + +namespace Qt4ProjectManager { + +namespace Internal { + +GuiAppWizard::GuiAppWizard(Core::ICore *core) : + QtWizard(core, + tr("Qt4 Gui Application"), + tr("Creates a Qt4 Gui Application with one form."), + QIcon(":/wizards/images/gui.png")) +{ +} + +QWizard *GuiAppWizard::createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const +{ + GuiAppWizardDialog *dialog = new GuiAppWizardDialog(name(), icon(), extensionPages, parent); + dialog->setPath(defaultPath.isEmpty() ? Core::Utils::PathChooser::homePath() : defaultPath); + // Order! suffixes first to generate files correctly + dialog->setSuffixes(headerSuffix(), sourceSuffix(), formSuffix()); + dialog->setBaseClasses(baseClasses()); + return dialog; +} + +Core::GeneratedFiles GuiAppWizard::generateFiles(const QWizard *w, + QString *errorMessage) const +{ + const GuiAppWizardDialog *dialog = qobject_cast<const GuiAppWizardDialog *>(w); + const QtProjectParameters projectParams = dialog->projectParameters(); + const QString projectPath = projectParams.projectPath(); + const GuiAppParameters params = dialog->parameters(); + + // Generate file names. Note that the path for the project files is the + // newly generated project directory. + const QString templatePath = templateDir(); + // Create files: main source + QString contents; + const QString mainSourceFileName = buildFileName(projectPath, QLatin1String(mainSourceFileC), sourceSuffix()); + Core::GeneratedFile mainSource(mainSourceFileName); + if (!parametrizeTemplate(templatePath, QLatin1String("main.cpp"), params, &contents, errorMessage)) + return Core::GeneratedFiles(); + mainSource.setContents(contents); + // Create files: form source + const QString formSourceTemplate = params.designerForm ? QLatin1String("mywidget_form.cpp") : QLatin1String("mywidget.cpp"); + const QString formSourceFileName = buildFileName(projectPath, params.sourceFileName, sourceSuffix()); + Core::GeneratedFile formSource(formSourceFileName); + if (!parametrizeTemplate(templatePath, formSourceTemplate, params, &contents, errorMessage)) + return Core::GeneratedFiles(); + formSource.setContents(contents); + // Create files: form header + const QString formHeaderName = buildFileName(projectPath, params.headerFileName, headerSuffix()); + const QString formHeaderTemplate = params.designerForm ? QLatin1String("mywidget_form.h") : QLatin1String("mywidget.h"); + Core::GeneratedFile formHeader(formHeaderName); + if (!parametrizeTemplate(templatePath, formHeaderTemplate, params, &contents, errorMessage)) + return Core::GeneratedFiles(); + formHeader.setContents(contents); + // Create files: form + QSharedPointer<Core::GeneratedFile> form; + if (params.designerForm) { + const QString formName = buildFileName(projectPath, params.formFileName, formSuffix()); + form = QSharedPointer<Core::GeneratedFile>(new Core::GeneratedFile(formName)); + if (!parametrizeTemplate(templatePath, QLatin1String("widget.ui"), params, &contents, errorMessage)) + return Core::GeneratedFiles(); + form->setContents(contents); + } + // Create files: profile + const QString profileName = buildFileName(projectPath, projectParams.name, profileSuffix()); + Core::GeneratedFile profile(profileName); + contents.clear(); + { + QTextStream proStr(&contents); + QtProjectParameters::writeProFileHeader(proStr); + projectParams.writeProFile(proStr); + proStr << "\n\nSOURCES += " << QFileInfo(mainSourceFileName).fileName() + << "\\\n " << QFileInfo(formSource.path()).fileName() + << "\n\nHEADERS += " << QFileInfo(formHeader.path()).fileName(); + if (params.designerForm) + proStr << "\n\nFORMS += " << QFileInfo(form->path()).fileName(); + proStr << '\n'; + } + profile.setContents(contents); + // List + Core::GeneratedFiles rc; + rc << mainSource << formSource << formHeader; + if (params.designerForm) + rc << *form; + rc << profile; + return rc; +} + +bool GuiAppWizard::parametrizeTemplate(const QString &templatePath, const QString &templateName, + const GuiAppParameters ¶ms, + QString *target, QString *errorMessage) +{ + QString fileName = templatePath; + fileName += QDir::separator(); + fileName += templateName; + QFile inFile(fileName); + if (!inFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + *errorMessage = tr("The template file '%1' could not be opened for reading: %2").arg(fileName, inFile.errorString()); + return false; + } + QString contents = QString::fromUtf8(inFile.readAll()); + + contents.replace(QLatin1String("%QAPP_INCLUDE%"), QLatin1String("QtGui/QApplication")); + contents.replace(QLatin1String("%INCLUDE%"), params.headerFileName); + contents.replace(QLatin1String("%CLASS%"), params.className); + contents.replace(QLatin1String("%BASECLASS%"), params.baseClassName); + + const QChar dot = QLatin1Char('.'); + + QString preDef = params.headerFileName.toUpper(); + preDef.replace(dot, QLatin1Char('_')); + contents.replace("%PRE_DEF%", preDef.toUtf8()); + + const QString uiFileName = params.formFileName; + QString uiHdr = QLatin1String("ui_"); + uiHdr += uiFileName.left(uiFileName.indexOf(dot)); + uiHdr += QLatin1String(".h"); + + contents.replace(QLatin1String("%UI_HDR%"), uiHdr); + if (params.baseClassName == QLatin1String("QMainWindow")) { + contents.replace(QLatin1String("%CENTRAL_WIDGET%"), QLatin1String(mainWindowUiContentsC)); + } else { + contents.remove(QLatin1String("%CENTRAL_WIDGET%")); + } + *target = contents; + return true; +} +} +} diff --git a/src/plugins/qt4projectmanager/wizards/guiappwizard.h b/src/plugins/qt4projectmanager/wizards/guiappwizard.h new file mode 100644 index 00000000000..455c48fde81 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/guiappwizard.h @@ -0,0 +1,70 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GUIAPPWIZARD_H +#define GUIAPPWIZARD_H + +#include <QtGui/QWizard> + +#include "qtwizard.h" + +namespace Qt4ProjectManager { +namespace Internal { + +struct GuiAppParameters; + +class GuiAppWizard : public QtWizard +{ + Q_DISABLE_COPY(GuiAppWizard) + Q_OBJECT + +public: + explicit GuiAppWizard(Core::ICore *core); + +protected: + virtual QWizard *createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const; + + virtual Core::GeneratedFiles generateFiles(const QWizard *w, + QString *errorMessage) const; + +private: + static bool parametrizeTemplate(const QString &templatePath, const QString &templateName, + const GuiAppParameters ¶ms, + QString *target, QString *errorMessage); +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // GUIAPPWIZARD_H diff --git a/src/plugins/qt4projectmanager/wizards/guiappwizarddialog.cpp b/src/plugins/qt4projectmanager/wizards/guiappwizarddialog.cpp new file mode 100644 index 00000000000..80205c0604d --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/guiappwizarddialog.cpp @@ -0,0 +1,136 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "guiappwizarddialog.h" +#include "consoleappwizard.h" +#include "modulespage.h" +#include "filespage.h" +#include "qtprojectparameters.h" + +#include <utils/projectintropage.h> + +#include <QtGui/QAbstractButton> + +enum PageId { IntroPageId, ModulesPageId, FilesPageId }; + +namespace Qt4ProjectManager { +namespace Internal { + + +GuiAppParameters::GuiAppParameters() : + designerForm(true) +{ + +} + + +GuiAppWizardDialog::GuiAppWizardDialog(const QString &templateName, + const QIcon &icon, + const QList<QWizardPage*> &extensionPages, + QWidget *parent) : + QWizard(parent), + m_introPage(new Core::Utils::ProjectIntroPage), + m_modulesPage(new ModulesPage), + m_filesPage(new FilesPage) +{ + setWindowIcon(icon); + setWindowTitle(templateName); + Core::BaseFileWizard::setupWizard(this); + setOptions(QWizard::IndependentPages); + + m_introPage->setDescription(tr("This wizard generates a Qt4 GUI application " + "project. The application derives by default from QApplication " + "and includes an empty widget.")); + setPage(IntroPageId, m_introPage); + + const QString coreModule = QLatin1String("core"); + const QString guiModule = QLatin1String("gui"); + m_modulesPage->setModuleSelected(coreModule); + m_modulesPage->setModuleEnabled(coreModule, false); + m_modulesPage->setModuleSelected(guiModule); + m_modulesPage->setModuleEnabled(guiModule, false); + setPage(ModulesPageId, m_modulesPage); + + m_filesPage->setFormInputCheckable(true); + setPage(FilesPageId, m_filesPage); + + foreach (QWizardPage *p, extensionPages) + addPage(p); +} + +void GuiAppWizardDialog::setBaseClasses(const QStringList &baseClasses) +{ + m_filesPage->setBaseClassChoices(baseClasses); + if (!baseClasses.empty()) + m_filesPage->setBaseClassName(baseClasses.front()); +} + +void GuiAppWizardDialog::setSuffixes(const QString &header, const QString &source, const QString &form) +{ + m_filesPage->setSuffixes(header, source, form); +} + +void GuiAppWizardDialog::setPath(const QString &path) +{ + m_introPage->setPath(path); +} + +void GuiAppWizardDialog::setName(const QString &name) +{ + m_introPage->setName(name); +} + +QtProjectParameters GuiAppWizardDialog::projectParameters() const +{ + QtProjectParameters rc; + rc.type = QtProjectParameters::GuiApp; + rc.name = m_introPage->name(); + rc.path = m_introPage->path(); + rc.selectedModules = m_modulesPage->selectedModules(); + rc.deselectedModules = m_modulesPage-> deselectedModules(); + return rc; +} + +GuiAppParameters GuiAppWizardDialog::parameters() const +{ + GuiAppParameters rc; + rc.className = m_filesPage->className(); + rc.baseClassName = m_filesPage->baseClassName(); + rc.sourceFileName = m_filesPage->sourceFileName(); + rc.headerFileName = m_filesPage->headerFileName(); + rc.formFileName = m_filesPage->formFileName(); + rc.designerForm = m_filesPage->formInputChecked(); + return rc; +} + +} +} diff --git a/src/plugins/qt4projectmanager/wizards/guiappwizarddialog.h b/src/plugins/qt4projectmanager/wizards/guiappwizarddialog.h new file mode 100644 index 00000000000..7ab8d0a197c --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/guiappwizarddialog.h @@ -0,0 +1,91 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GUIAPPWIZARDDIALOG_H +#define GUIAPPWIZARDDIALOG_H + +#include <QtGui/QWizard> + +namespace Core { + namespace Utils { + class ProjectIntroPage; + } +} + +namespace Qt4ProjectManager { +namespace Internal { + +struct QtProjectParameters; +class ModulesPage; +class FilesPage; + +// Additional parameters required besides QtProjectParameters +struct GuiAppParameters { + GuiAppParameters(); + QString className; + QString baseClassName; + QString sourceFileName; + QString headerFileName; + QString formFileName; + bool designerForm; +}; + +class GuiAppWizardDialog : public QWizard +{ + Q_OBJECT + +public: + explicit GuiAppWizardDialog(const QString &templateName, + const QIcon &icon, + const QList<QWizardPage*> &extensionPages, + QWidget *parent = 0); + + void setBaseClasses(const QStringList &baseClasses); + void setSuffixes(const QString &header, const QString &source, const QString &form); + + QtProjectParameters projectParameters() const; + GuiAppParameters parameters() const; + +public slots: + void setPath(const QString &path); + void setName(const QString &name); + +private: + Core::Utils::ProjectIntroPage *m_introPage; + ModulesPage *m_modulesPage; + FilesPage *m_filesPage; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // GUIAPPWIZARDDIALOG_H diff --git a/src/plugins/qt4projectmanager/wizards/images/console.png b/src/plugins/qt4projectmanager/wizards/images/console.png Binary files differnew file mode 100644 index 00000000000..7569a988f4a --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/images/console.png diff --git a/src/plugins/qt4projectmanager/wizards/images/gui.png b/src/plugins/qt4projectmanager/wizards/images/gui.png Binary files differnew file mode 100644 index 00000000000..c121959d9e2 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/images/gui.png diff --git a/src/plugins/qt4projectmanager/wizards/images/lib.png b/src/plugins/qt4projectmanager/wizards/images/lib.png Binary files differnew file mode 100644 index 00000000000..a4e818d986d --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/images/lib.png diff --git a/src/plugins/qt4projectmanager/wizards/libraryparameters.cpp b/src/plugins/qt4projectmanager/wizards/libraryparameters.cpp new file mode 100644 index 00000000000..c9f50fd335a --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/libraryparameters.cpp @@ -0,0 +1,166 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "libraryparameters.h" + +#include <QtCore/QTextStream> +#include <QtCore/QStringList> + +#include <utils/codegeneration.h> + +// Contents of the header defining the shared library export. +#define GUARD_VARIABLE "<GUARD>" +#define EXPORT_MACRO_VARIABLE "<EXPORT_MACRO>" +#define LIBRARY_MACRO_VARIABLE "<LIBRARY_MACRO>" + +static const char *globalHeaderContentsC = +"#ifndef "GUARD_VARIABLE"\n" +"#define "GUARD_VARIABLE"\n" +"\n" +"#include <QtCore/qglobal.h>\n" +"\n" +"#if defined("LIBRARY_MACRO_VARIABLE")\n" +"# define "EXPORT_MACRO_VARIABLE" Q_DECL_EXPORT\n" +"#else\n" +"# define "EXPORT_MACRO_VARIABLE" Q_DECL_IMPORT\n" +"#endif\n" +"\n" +"#endif // "GUARD_VARIABLE"\n"; + +namespace Qt4ProjectManager { +namespace Internal { + +void LibraryParameters::generateCode(QtProjectParameters:: Type t, + const QString &projectTarget, + const QString &headerName, + const QString &sharedHeader, + const QString &exportMacro, + int indentation, + QString *header, + QString *source) const +{ + QString rc; + QTextStream headerStr(header); + + const QString indent = QString(indentation, QLatin1Char(' ')); + + // 1) Header + const QString guard = Core::Utils::headerGuard(headerFileName); + headerStr << "#ifndef " << guard + << "\n#define " << guard << '\n' << '\n'; + + if (!sharedHeader.isEmpty()) + Core::Utils::writeIncludeFileDirective(sharedHeader, false, headerStr); + + // include base class header + if (!baseClassName.isEmpty()) { + QString include; + if (!baseClassModule.isEmpty()) { + include += baseClassModule; + include += QLatin1Char('/'); + } + include += baseClassName; + Core::Utils::writeIncludeFileDirective(include, true, headerStr); + headerStr << '\n'; + } + + // Do we have namespaces? + QStringList namespaceList = className.split(QLatin1String("::")); + if (namespaceList.empty()) // Paranoia! + return; + + const QString unqualifiedClassName = namespaceList.back(); + namespaceList.pop_back(); + + const QString namespaceIndent = Core::Utils::writeOpeningNameSpaces(namespaceList, indent, headerStr); + + // Class declaraction + headerStr << '\n' << namespaceIndent << "class "; + if (t == QtProjectParameters::SharedLibrary && !exportMacro.isEmpty()) + headerStr << exportMacro << ' '; + + headerStr << unqualifiedClassName; + if (!baseClassName.isEmpty()) + headerStr << " : public " << baseClassName; + headerStr << " {\n"; + + // Is this a QObject (plugin) + const bool inheritsQObject = t == QtProjectParameters::Qt4Plugin; + if (inheritsQObject) { + headerStr << namespaceIndent << indent << "Q_OBJECT\n" + << namespaceIndent << indent << "Q_DISABLE_COPY(" << unqualifiedClassName << ")\n"; + } + headerStr << namespaceIndent << "public:\n"; + if (inheritsQObject) { + headerStr << namespaceIndent << indent << "explicit " << unqualifiedClassName << "(QObject *parent = 0);\n"; + } else { + headerStr << namespaceIndent << indent << unqualifiedClassName << "();\n"; + } + headerStr << namespaceIndent << "};\n\n"; + Core::Utils::writeClosingNameSpaces(namespaceList, indent, headerStr); + headerStr << "#endif // "<< guard << '\n'; + /// 2) Source + QTextStream sourceStr(source); + + Core::Utils::writeIncludeFileDirective(headerName, false, sourceStr); + sourceStr << '\n'; + + Core::Utils::writeOpeningNameSpaces(namespaceList, indent, sourceStr); + // Constructor + sourceStr << '\n' << namespaceIndent << unqualifiedClassName << "::" << unqualifiedClassName; + if (inheritsQObject) { + sourceStr << "(QObject *parent) :\n" + << namespaceIndent << indent << baseClassName << "(parent)\n"; + } else { + sourceStr << "()\n"; + } + sourceStr << namespaceIndent << "{\n" << namespaceIndent << "}\n"; + + Core::Utils::writeClosingNameSpaces(namespaceList, indent, sourceStr); + + if (t == QtProjectParameters::Qt4Plugin) + sourceStr << '\n' << "Q_EXPORT_PLUGIN2(" << projectTarget << ", " << className << ")\n"; +} + +QString LibraryParameters::generateSharedHeader(const QString &globalHeaderFileName, + const QString &projectTarget, + const QString &exportMacro) +{ + QString contents = QLatin1String(globalHeaderContentsC); + contents.replace(QLatin1String(GUARD_VARIABLE), Core::Utils::headerGuard(globalHeaderFileName)); + contents.replace(QLatin1String(EXPORT_MACRO_VARIABLE), exportMacro); + contents.replace(QLatin1String(LIBRARY_MACRO_VARIABLE), QtProjectParameters::libraryMacro(projectTarget)); + return contents; +} +} +} + diff --git a/src/plugins/qt4projectmanager/wizards/libraryparameters.h b/src/plugins/qt4projectmanager/wizards/libraryparameters.h new file mode 100644 index 00000000000..ef70c32bf0d --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/libraryparameters.h @@ -0,0 +1,72 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef LIBRARYPARAMETERS_H +#define LIBRARYPARAMETERS_H + +#include "qtprojectparameters.h" + +#include <QtCore/QString> + +namespace Qt4ProjectManager { +namespace Internal { + +// Additional parameters required besides QtProjectParameters for creating +// libraries +struct LibraryParameters { + + // generate class + void generateCode(QtProjectParameters:: Type t, + const QString &projectTarget, + const QString &headerName, + const QString &sharedHeader, + const QString &exportMacro, + int indentation, + QString *header, + QString *source) const; + + // Generate the code of the shared header containing the export macro + static QString generateSharedHeader(const QString &globalHeaderFileName, + const QString &projectTarget, + const QString &exportMacro); + + QString className; + QString baseClassName; + QString sourceFileName; + QString headerFileName; + QString baseClassModule; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // LIBRARYPARAMETERS_H diff --git a/src/plugins/qt4projectmanager/wizards/librarywizard.cpp b/src/plugins/qt4projectmanager/wizards/librarywizard.cpp new file mode 100644 index 00000000000..953ef3908c6 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/librarywizard.cpp @@ -0,0 +1,128 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "librarywizard.h" +#include "librarywizarddialog.h" +#include "qt4projectmanager.h" +#include "qt4projectmanagerconstants.h" + +#include <utils/codegeneration.h> +#include <utils/pathchooser.h> + +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QTextStream> +#include <QtGui/QIcon> + +static const char *sharedHeaderPostfixC = "_global"; + +namespace Qt4ProjectManager { + +namespace Internal { + +LibraryWizard::LibraryWizard(Core::ICore *core) : + QtWizard(core, tr("C++ Library"), + tr("Creates a C++ Library."), + QIcon(":/wizards/images/lib.png")) +{ +} + +QWizard *LibraryWizard::createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const +{ + LibraryWizardDialog *dialog = new LibraryWizardDialog(name(), icon(), extensionPages, parent); + dialog->setPath(defaultPath.isEmpty() ? Core::Utils::PathChooser::homePath() : defaultPath); + dialog->setSuffixes(headerSuffix(), sourceSuffix(), formSuffix()); + return dialog; +} + + +Core::GeneratedFiles LibraryWizard::generateFiles(const QWizard *w, + QString * /*errorMessage*/) const +{ + const LibraryWizardDialog *dialog = qobject_cast<const LibraryWizardDialog *>(w); + const QtProjectParameters projectParams = dialog->parameters(); + const QString projectPath = projectParams.projectPath(); + const LibraryParameters params = dialog->libraryParameters(); + + const QString sharedLibExportMacro = QtProjectParameters::exportMacro(projectParams.name); + + Core::GeneratedFiles rc; + // Class header + source + const QString sourceFileName = buildFileName(projectPath, params.sourceFileName, sourceSuffix()); + Core::GeneratedFile source(sourceFileName); + + const QString headerFileFullName = buildFileName(projectPath, params.headerFileName, headerSuffix()); + const QString headerFileName = QFileInfo(headerFileFullName).fileName(); + Core::GeneratedFile header(headerFileFullName); + + // Create files: global header for shared libs + QString globalHeaderFileName; + if (projectParams.type == QtProjectParameters::SharedLibrary) { + const QString globalHeaderName = buildFileName(projectPath, projectParams.name + QLatin1String(sharedHeaderPostfixC), headerSuffix()); + Core::GeneratedFile globalHeader(globalHeaderName); + globalHeaderFileName = QFileInfo(globalHeader.path()).fileName(); + globalHeader.setContents(LibraryParameters::generateSharedHeader(globalHeaderFileName, projectParams.name, sharedLibExportMacro)); + rc.push_back(globalHeader); + } + + // Generate code + QString headerContents, sourceContents; + params.generateCode(projectParams.type, projectParams.name, headerFileName, + globalHeaderFileName, sharedLibExportMacro, + /* indentation*/ 4, &headerContents, &sourceContents); + + source.setContents(sourceContents); + header.setContents(headerContents); + rc.push_back(source); + rc.push_back(header); + // Create files: profile + const QString profileName = buildFileName(projectPath, projectParams.name, profileSuffix()); + Core::GeneratedFile profile(profileName); + QString profileContents; + { + QTextStream proStr(&profileContents); + QtProjectParameters::writeProFileHeader(proStr); + projectParams.writeProFile(proStr); + proStr << "\nSOURCES += " << QFileInfo(source.path()).fileName() + << "\n\nHEADERS += " << headerFileName; + if (!globalHeaderFileName.isEmpty()) + proStr << "\\\n " << globalHeaderFileName << '\n'; + } + profile.setContents(profileContents); + rc.push_back(profile); + return rc; +} + +} +} diff --git a/src/plugins/qt4projectmanager/wizards/librarywizard.h b/src/plugins/qt4projectmanager/wizards/librarywizard.h new file mode 100644 index 00000000000..0b797034b61 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/librarywizard.h @@ -0,0 +1,65 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef LIBRARYWIZARD_H +#define LIBRARYWIZARD_H + +#include "qtwizard.h" +#include "libraryparameters.h" + +namespace Qt4ProjectManager { +namespace Internal { + +struct LibraryParameters; +class ModulesPage; + +class LibraryWizard : public QtWizard +{ + Q_OBJECT + +public: + explicit LibraryWizard(Core::ICore *core); + +protected: + virtual QWizard *createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const; + + virtual Core::GeneratedFiles generateFiles(const QWizard *w, + QString *errorMessage) const; + +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // LIBRARYWIZARD_H diff --git a/src/plugins/qt4projectmanager/wizards/librarywizarddialog.cpp b/src/plugins/qt4projectmanager/wizards/librarywizarddialog.cpp new file mode 100644 index 00000000000..c0b2a755fc4 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/librarywizarddialog.cpp @@ -0,0 +1,268 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "librarywizarddialog.h" +#include "consoleappwizard.h" +#include "modulespage.h" +#include "filespage.h" +#include "libraryparameters.h" + +#include <utils/projectintropage.h> + +#include <QtGui/QComboBox> +#include <QtGui/QLabel> + +#include <QtCore/QDebug> + +enum { debugLibWizard = 0 }; +enum { IntroPageId, ModulesPageId, FilePageId }; + +namespace Qt4ProjectManager { +namespace Internal { + +struct PluginBaseClasses { + const char *name; + const char *module; + // blank separated list or 0 + const char *dependentModules; + const char *targetDirectory; +}; + +static const PluginBaseClasses pluginBaseClasses[] = +{ + { "QAccessiblePlugin", "QtGui", "QtCore", "accessible" }, + { "QDecorationPlugin", "QtGui", "QtCore", 0}, + { "QFontEnginePlugin", "QtGui", "QtCore", 0}, + { "QIconEnginePluginV2", "QtGui", "QtCore", "imageformats" }, + { "QImageIOPlugin", "QtGui", "QtCore", "imageformats" }, + { "QScriptExtensionPlugin", "QtScript", "QtCore", 0 }, + { "QSqlDriverPlugin", "QtSql", "QtCore", "sqldrivers" }, + { "QStylePlugin", "QtGui", "QtCore", "styles" }, + { "QTextCodecPlugin", "QtCore", 0, "codecs" } +}; + +enum { defaultPluginBaseClass = 7 }; + +static const PluginBaseClasses *findPluginBaseClass(const QString &name) +{ + const int pluginBaseClassCount = sizeof(pluginBaseClasses)/sizeof(PluginBaseClasses); + for (int i = 0; i < pluginBaseClassCount; i++) + if (name == QLatin1String(pluginBaseClasses[i].name)) + return pluginBaseClasses + i; + return 0; +} + +// return dependencies of a plugin as a line ready for the 'QT=' line in a pro +// file +static QString pluginDependencies(const PluginBaseClasses *plb) +{ + QString dependencies; + const QChar blank = QLatin1Char(' '); + // Find the module names and convert to ids + QStringList pluginModules= plb->dependentModules ? + QString(QLatin1String(plb->dependentModules)).split(blank) : + QStringList(); + pluginModules.push_back(QLatin1String(plb->module)); + foreach(const QString &module, pluginModules) { + if (!dependencies.isEmpty()) + dependencies += blank; + dependencies += ModulesPage::idOfModule(module); + } + return dependencies; +} + +// A Project intro page with an additional type chooser. +class LibraryIntroPage : public Core::Utils::ProjectIntroPage +{ + Q_DISABLE_COPY(LibraryIntroPage) +public: + explicit LibraryIntroPage(QWidget *parent = 0); + + QtProjectParameters::Type type() const; + + virtual int nextId() const; + +private: + QComboBox *m_typeCombo; +}; + +LibraryIntroPage::LibraryIntroPage(QWidget *parent) : + Core::Utils::ProjectIntroPage(parent), + m_typeCombo(new QComboBox) +{ + m_typeCombo->setEditable(false); + m_typeCombo->addItem(LibraryWizardDialog::tr("Shared library"), + QVariant(QtProjectParameters::SharedLibrary)); + m_typeCombo->addItem(LibraryWizardDialog::tr("Statically linked library"), + QVariant(QtProjectParameters::StaticLibrary)); + m_typeCombo->addItem(LibraryWizardDialog::tr("Qt 4 plugin"), + QVariant(QtProjectParameters::Qt4Plugin)); + insertControl(0, new QLabel(LibraryWizardDialog::tr("Type")), m_typeCombo); +} + +QtProjectParameters::Type LibraryIntroPage::type() const +{ + return static_cast<QtProjectParameters::Type>(m_typeCombo->itemData(m_typeCombo->currentIndex()).toInt()); +} + +int LibraryIntroPage::nextId() const +{ + // The modules page is skipped in the case of a plugin since it knows its + // dependencies by itself + const int rc = type() == QtProjectParameters::Qt4Plugin ? FilePageId : ModulesPageId; + if (debugLibWizard) + qDebug() << Q_FUNC_INFO << "returns" << rc; + return rc; +} + +// ------------------- LibraryWizardDialog +LibraryWizardDialog::LibraryWizardDialog(const QString &templateName, + const QIcon &icon, + const QList<QWizardPage*> &extensionPages, + QWidget *parent) : + QWizard(parent), + m_introPage(new LibraryIntroPage), + m_modulesPage(new ModulesPage), + m_filesPage(new FilesPage), + m_pluginBaseClassesInitialized(false) +{ + setWindowIcon(icon); + setWindowTitle(templateName); + Core::BaseFileWizard::setupWizard(this); + + // Note that QWizard::currentIdChanged() is emitted at strange times. + // Use the intro page instead, set up initially + m_introPage->setDescription(tr("This wizard generates a C++ library project.")); + + setPage(IntroPageId, m_introPage); + + m_modulesPage->setModuleSelected(QLatin1String("core")); + setPage(ModulesPageId, m_modulesPage); + + m_filesPage->setNamespacesEnabled(true); + m_filesPage->setFormFileInputVisible(false); + setPage(FilePageId, m_filesPage); + + connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(slotCurrentIdChanged(int))); + + foreach (QWizardPage *p, extensionPages) + addPage(p); +} + +void LibraryWizardDialog::setSuffixes(const QString &header, const QString &source, const QString &form) +{ + m_filesPage->setSuffixes(header, source, form); +} + +void LibraryWizardDialog::setPath(const QString &path) +{ + m_introPage->setPath(path); +} + +void LibraryWizardDialog::setName(const QString &name) +{ + m_introPage->setName(name); +} + +QtProjectParameters LibraryWizardDialog::parameters() const +{ + QtProjectParameters rc; + rc.type = m_introPage->type(); + rc.name = m_introPage->name(); + rc.path = m_introPage->path(); + if (rc.type == QtProjectParameters::Qt4Plugin) { + // Plugin: Dependencies & Target directory + if (const PluginBaseClasses *plb = findPluginBaseClass(m_filesPage->baseClassName())) { + rc.selectedModules = pluginDependencies(plb); + if (plb->targetDirectory) { + rc.targetDirectory = QLatin1String("$$[QT_INSTALL_PLUGINS]/"); + rc.targetDirectory += QLatin1String(plb->targetDirectory); + } + } + } else { + // Modules from modules page + rc.selectedModules = m_modulesPage->selectedModules(); + rc.deselectedModules = m_modulesPage-> deselectedModules(); + } + return rc; +} + +void LibraryWizardDialog::slotCurrentIdChanged(int id) +{ + if (debugLibWizard) + qDebug() << Q_FUNC_INFO << id; + // Switching to files page: Set up base class accordingly (plugin) + if (id != FilePageId) + return; + switch (m_introPage->type()) { + case QtProjectParameters::Qt4Plugin: + if (!m_pluginBaseClassesInitialized) { + if (debugLibWizard) + qDebug("initializing for plugins"); + QStringList baseClasses; + const int pluginBaseClassCount = sizeof(pluginBaseClasses)/sizeof(PluginBaseClasses); + Q_ASSERT(defaultPluginBaseClass < pluginBaseClassCount); + for (int i = 0; i < pluginBaseClassCount; i++) + baseClasses.push_back(QLatin1String(pluginBaseClasses[i].name)); + m_filesPage->setBaseClassChoices(baseClasses); + m_filesPage->setBaseClassName(baseClasses.at(defaultPluginBaseClass)); + m_pluginBaseClassesInitialized = true; + } + m_filesPage->setBaseClassInputVisible(true); + break; + default: { + // Urrm, figure out a good class name. Use project name this time + QString name = m_introPage->name(); + name[0] = name.at(0).toUpper(); + m_filesPage->setClassName(name); + m_filesPage->setBaseClassInputVisible(false); + } + break; + } +} + +LibraryParameters LibraryWizardDialog::libraryParameters() const +{ + LibraryParameters rc; + rc.className = m_filesPage->className(); + rc.baseClassName = m_filesPage->baseClassName(); + rc.sourceFileName = m_filesPage->sourceFileName(); + rc.headerFileName = m_filesPage->headerFileName(); + if (!rc.baseClassName.isEmpty()) + if (const PluginBaseClasses *plb = findPluginBaseClass(rc.baseClassName)) { + rc.baseClassModule = QLatin1String(plb->module); + } + return rc; +} + +} +} diff --git a/src/plugins/qt4projectmanager/wizards/librarywizarddialog.h b/src/plugins/qt4projectmanager/wizards/librarywizarddialog.h new file mode 100644 index 00000000000..0fa91d1d702 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/librarywizarddialog.h @@ -0,0 +1,80 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef LIBRARYWIZARDDIALOG_H +#define LIBRARYWIZARDDIALOG_H + +#include <QtGui/QWizard> + +namespace Qt4ProjectManager { +namespace Internal { + +struct QtProjectParameters; +class ModulesPage; +class LibraryIntroPage; +class FilesPage; +struct LibraryParameters; + +// Library wizard dialog. +class LibraryWizardDialog : public QWizard +{ + Q_OBJECT + +public: + explicit LibraryWizardDialog(const QString &templateName, + const QIcon &icon, + const QList<QWizardPage*> &extensionPages, + QWidget *parent = 0); + + void setSuffixes(const QString &header, const QString &source, const QString &form= QString()); + + QtProjectParameters parameters() const; + LibraryParameters libraryParameters() const; + +public slots: + void setPath(const QString &path); + void setName(const QString &name); + +private slots: + void slotCurrentIdChanged(int); + +private: + LibraryIntroPage *m_introPage; + ModulesPage *m_modulesPage; + FilesPage *m_filesPage; + bool m_pluginBaseClassesInitialized; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // LIBRARYWIZARDDIALOG_H diff --git a/src/plugins/qt4projectmanager/wizards/modulespage.cpp b/src/plugins/qt4projectmanager/wizards/modulespage.cpp new file mode 100644 index 00000000000..3894250b4c4 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/modulespage.cpp @@ -0,0 +1,128 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "modulespage.h" +#include "speinfo.h" + +#include <QtGui/QLabel> +#include <QtGui/QLayout> +#include <QtGui/QCheckBox> +#include <QtGui/QWidget> + +#include <QtCore/QDebug> + +#include <math.h> + +using namespace Qt4ProjectManager::Internal; + +ModulesPage::ModulesPage(QWidget *parent) + : QWizardPage(parent) +{ + setTitle(tr("Select required modules")); + QLabel *label = new QLabel(tr("Select the modules you want to include in your " + "project. The recommended modules for this project are selected by default.")); + label->setWordWrap(true); + + QVBoxLayout *vlayout = new QVBoxLayout(); + vlayout->addWidget(label); + vlayout->addItem(new QSpacerItem(0, 20)); + + QGridLayout *layout = new QGridLayout; + + const QList<SPEInfoItem*> infoItemsList = *SPEInfo::list(SPEInfoItem::QtModule); + int itemId = 0; + int rowsCount = (infoItemsList.count() + 1) / 2; + foreach (const SPEInfoItem *infoItem, infoItemsList) { + QCheckBox *moduleCheckBox = new QCheckBox(infoItem->name()); + moduleCheckBox->setToolTip(infoItem->description()); + moduleCheckBox->setWhatsThis(infoItem->description()); + registerField(infoItem->id(), moduleCheckBox); + int row = itemId % rowsCount; + int column = itemId / rowsCount; + layout->addWidget(moduleCheckBox, row, column); + m_moduleCheckBoxMap[infoItem->id()] = moduleCheckBox; + itemId++; + } + + vlayout->addLayout(layout); + setLayout(vlayout); +} + +// Return the key that goes into the Qt config line for a module +QString ModulesPage::idOfModule(const QString &module) +{ + const QList<SPEInfoItem*> infoItemsList = *SPEInfo::list(SPEInfoItem::QtModule); + foreach (const SPEInfoItem *infoItem, infoItemsList) + if (infoItem->name().startsWith(module)) + return infoItem->id(); + return QString(); +} + +QString ModulesPage::selectedModules() const +{ + return modules(true); +} + +QString ModulesPage::deselectedModules() const +{ + return modules(false); +} + +void ModulesPage::setModuleSelected(const QString &module, bool selected) const +{ + QCheckBox *checkBox = m_moduleCheckBoxMap[module]; + Q_ASSERT(checkBox); + if (checkBox) + checkBox->setCheckState(selected?Qt::Checked:Qt::Unchecked); +} + +void ModulesPage::setModuleEnabled(const QString &module, bool enabled) const +{ + QCheckBox *checkBox = m_moduleCheckBoxMap[module]; + Q_ASSERT(checkBox); + if (checkBox) + checkBox->setEnabled(enabled); +} + +QString ModulesPage::modules(bool selected) const +{ + QStringList modules; + + const QList<SPEInfoItem*> infoItemsList = *SPEInfo::list(SPEInfoItem::QtModule); + foreach (const SPEInfoItem *infoItem, infoItemsList) { + if (selected != infoItem->data(SPEInfoItem::keyIncludedByDefault).toBool() + && selected == field(infoItem->id()).toBool()) + modules << infoItem->id(); + } + + return modules.join(QString(QLatin1Char(' '))); +} diff --git a/src/plugins/qt4projectmanager/wizards/modulespage.h b/src/plugins/qt4projectmanager/wizards/modulespage.h new file mode 100644 index 00000000000..a637fecc9bf --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/modulespage.h @@ -0,0 +1,68 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef MODULESPAGE_H +#define MODULESPAGE_H + +#include <QtCore/QMap> +#include <QtGui/QWizard> + +QT_BEGIN_NAMESPACE +class QCheckBox; +QT_END_NAMESPACE + +namespace Qt4ProjectManager { +namespace Internal { + +class ModulesPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit ModulesPage(QWidget* parent = 0); + QString selectedModules() const; + QString deselectedModules() const; + void setModuleSelected(const QString &module, bool selected = true) const; + void setModuleEnabled(const QString &module, bool enabled = true) const; + + // Return the key that goes into the Qt config line for a module + static QString idOfModule(const QString &module); + +private: + QMap<QString, QCheckBox*> m_moduleCheckBoxMap; + QString modules(bool selected = true) const; +}; + +} //namespace Internal +} //namespace Qt4ProjectManager + +#endif diff --git a/src/plugins/qt4projectmanager/wizards/qtprojectparameters.cpp b/src/plugins/qt4projectmanager/wizards/qtprojectparameters.cpp new file mode 100644 index 00000000000..59bbbec58d8 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/qtprojectparameters.cpp @@ -0,0 +1,128 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qtprojectparameters.h" + +#include <QtCore/QTextStream> +#include <QtCore/QDir> +#include <QtCore/QCoreApplication> +#include <QtCore/QDateTime> +#include <QtCore/QDebug> + +namespace Qt4ProjectManager { +namespace Internal { + +// ----------- QtProjectParameters +QtProjectParameters::QtProjectParameters() : + type(ConsoleApp) +{ + } + +QString QtProjectParameters::projectPath() const +{ + QString rc = path; + if (!rc.isEmpty()) + rc += QDir::separator(); + rc += name; + return rc; +} + +void QtProjectParameters::writeProFile(QTextStream &str) const +{ + if (!selectedModules.isEmpty()) + str << "QT += " << selectedModules << "\n\n"; + if (!deselectedModules.isEmpty()) + str << "QT -= " << deselectedModules << "\n\n"; + if (!name.isEmpty()) + str << "TARGET = " << name << '\n'; + switch (type) { + case ConsoleApp: + // Mac: Command line apps should not be bundles + str << "CONFIG += console\nCONFIG -= app_bundle\n\n"; + case GuiApp: + str << "TEMPLATE = app\n"; + break; + case StaticLibrary: + str << "TEMPLATE = lib\nCONFIG += staticlib\n"; + break; + case SharedLibrary: + str << "TEMPLATE = lib\n\nDEFINES += " << libraryMacro(name) << '\n'; + break; + case Qt4Plugin: + str << "TEMPLATE = lib\nCONFIG += plugin\n"; + break; + } + + if (!targetDirectory.isEmpty()) + str << "\nDESTDIR = " << targetDirectory << '\n'; +} + +void QtProjectParameters::writeProFileHeader(QTextStream &str) +{ + const QChar hash = QLatin1Char ('#'); + const QChar nl = QLatin1Char('\n'); + const QChar blank = QLatin1Char(' '); + // Format as '#-------\n# <Header> \n#---------' + QString header = QLatin1String(" Project created by "); + header += QCoreApplication::applicationName(); + header += blank; + header += QDateTime::currentDateTime().toString(Qt::ISODate); + const QString line = QString(header.size(), QLatin1Char('-')); + str << hash << line << nl << hash << nl << hash << header << nl + << hash <<nl << hash << line << nl << nl; +} + + +QString createMacro(const QString &name, const QString &suffix) +{ + QString rc = name.toUpper(); + const int extensionPosition = rc.indexOf(QLatin1Char('.')); + if (extensionPosition != -1) + rc.truncate(extensionPosition); + rc += suffix; + return rc; +} + +QString QtProjectParameters::exportMacro(const QString &projectName) +{ + return createMacro(projectName, QLatin1String("SHARED_EXPORT")); +} + +QString QtProjectParameters::libraryMacro(const QString &projectName) +{ + return createMacro(projectName, QLatin1String("_LIBRARY")); +} + + + +} +} diff --git a/src/plugins/qt4projectmanager/wizards/qtprojectparameters.h b/src/plugins/qt4projectmanager/wizards/qtprojectparameters.h new file mode 100644 index 00000000000..94b0988183c --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/qtprojectparameters.h @@ -0,0 +1,77 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QTPROJECTPARAMETERS_H +#define QTPROJECTPARAMETERS_H + +#include <QtCore/QString> + +QT_BEGIN_NAMESPACE +class QTextStream; +QT_END_NAMESPACE + +namespace Qt4ProjectManager { +namespace Internal { + +// Create a macro name by taking a file name, upper casing it and +// appending a suffix. +QString createMacro(const QString &name, const QString &suffix); + +// Base parameters for application project generation with functionality to +// write a .pro-file section. + +struct QtProjectParameters { + enum Type { ConsoleApp, GuiApp, StaticLibrary, SharedLibrary, Qt4Plugin }; + + QtProjectParameters(); + // Return project path as "path/name" + QString projectPath() const; + void writeProFile(QTextStream &) const; + static void writeProFileHeader(QTextStream &); + + // Shared library: Name of export macro (XXX_EXPORT) + static QString exportMacro(const QString &projectName); + // Shared library: name of #define indicating compilation within library + static QString libraryMacro(const QString &projectName); + + Type type; + QString name; + QString path; + QString selectedModules; + QString deselectedModules; + QString targetDirectory; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // QTPROJECTPARAMETERS_H diff --git a/src/plugins/qt4projectmanager/wizards/qtwizard.cpp b/src/plugins/qt4projectmanager/wizards/qtwizard.cpp new file mode 100644 index 00000000000..0788caab047 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/qtwizard.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qtwizard.h" +#include "qt4project.h" +#include "qt4projectmanagerconstants.h" + +#include <projectexplorer/projectexplorer.h> + +#include <QtCore/QByteArray> +#include <QtCore/QCoreApplication> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QTextStream> + +using namespace Qt4ProjectManager; +using namespace Qt4ProjectManager::Internal; + +static inline Core::BaseFileWizardParameters + wizardParameters(const QString &name, + const QString &description, + const QIcon &icon) +{ + Core::BaseFileWizardParameters rc(Core::IWizard::ProjectWizard); + rc.setCategory(QLatin1String("Projects")); + rc.setTrCategory("Projects"); + rc.setIcon(icon); + rc.setName(name); + rc.setDescription(description); + return rc; +} + +// -------------------- QtWizard +QtWizard::QtWizard(Core::ICore *core, const QString &name, + const QString &description, const QIcon &icon) : + Core::BaseFileWizard(wizardParameters(name, description, icon), core), + m_projectExplorer(ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::ProjectExplorerPlugin>()) +{ +} + +QString QtWizard::sourceSuffix() const +{ + return preferredSuffix(QLatin1String(Constants::CPP_SOURCE_MIMETYPE)); +} + +QString QtWizard::headerSuffix() const +{ + return preferredSuffix(QLatin1String(Constants::CPP_HEADER_MIMETYPE)); +} + +QString QtWizard::formSuffix() const +{ + return preferredSuffix(QLatin1String(Constants::FORM_MIMETYPE)); +} + +QString QtWizard::profileSuffix() const +{ + return preferredSuffix(QLatin1String(Constants::PROFILE_MIMETYPE)); +} + +bool QtWizard::postGenerateFiles(const Core::GeneratedFiles &l, QString *errorMessage) +{ + // Post-Generate: Open the project + const QString proFileName = l.back().path(); + if (!m_projectExplorer->openProject(proFileName)) { + *errorMessage = tr("The project %1 could not be opened.").arg(proFileName); + return false; + } + return true; +} + +QString QtWizard::templateDir() const +{ + QString rc = core()->resourcePath(); + rc += QLatin1String("/templates/qt4project"); + return rc; +} diff --git a/src/plugins/qt4projectmanager/wizards/qtwizard.h b/src/plugins/qt4projectmanager/wizards/qtwizard.h new file mode 100644 index 00000000000..7b74f1381ae --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/qtwizard.h @@ -0,0 +1,87 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QTWIZARD_H +#define QTWIZARD_H + +#include "qtprojectparameters.h" + +#include <coreplugin/basefilewizard.h> + +QT_BEGIN_NAMESPACE +class QTextStream; +class QDir; +QT_END_NAMESPACE + +namespace ProjectExplorer { + class ProjectExplorerPlugin; +} + +namespace Qt4ProjectManager { +namespace Internal { + +/* Base class for wizard creating Qt projects using QtProjectParameters. + * To implement a project wizard, overwrite: + * - createWizardDialog() to create up the dialog + * - generateFiles() to set their contents + * The base implementation provides the wizard parameters and opens + * and opens the finished project in postGenerateFiles(). + * The pro-file must be the last one of the generated files. */ + +class QtWizard : public Core::BaseFileWizard +{ + Q_DISABLE_COPY(QtWizard) + Q_OBJECT + +public: + +protected: + explicit QtWizard(Core::ICore *core, const QString &name, + const QString &description, const QIcon &icon); + + QString templateDir() const; + + QString sourceSuffix() const; + QString headerSuffix() const; + QString formSuffix() const; + QString profileSuffix() const; + +private: + bool postGenerateFiles(const Core::GeneratedFiles &l, QString *errorMessage); + + ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer; +}; + +} // namespace Internal +} // namespace Qt4ProjectManager + +#endif // QTWIZARD_H diff --git a/src/plugins/qt4projectmanager/wizards/wizards.qrc b/src/plugins/qt4projectmanager/wizards/wizards.qrc new file mode 100644 index 00000000000..e4633690092 --- /dev/null +++ b/src/plugins/qt4projectmanager/wizards/wizards.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/wizards" > + <file>images/console.png</file> + <file>images/gui.png</file> + <file>images/lib.png</file> + </qresource> +</RCC> diff --git a/src/plugins/qtestlib/QTestLibPlugin.qwp b/src/plugins/qtestlib/QTestLibPlugin.qwp new file mode 100644 index 00000000000..d48704e2764 --- /dev/null +++ b/src/plugins/qtestlib/QTestLibPlugin.qwp @@ -0,0 +1,16 @@ +<!DOCTYPE QtWorkbenchManifest> +<qwp> + <pluginName>QTestLibPlugin</pluginName> + <author>Trolltech</author> + <requiredPluginList> + <requiredPlugin> + <name>ProjectExplorer</name> + <provider></provider> + </requiredPlugin> + </requiredPluginList> + <extendsInterfaceList> + <extendsInterface> + <name>Core::OutputPaneInterface</name> + </extendsInterface> + </extendsInterfaceList> +</qwp> diff --git a/src/plugins/qtestlib/images/pass.png b/src/plugins/qtestlib/images/pass.png Binary files differnew file mode 100644 index 00000000000..b5238f7680a --- /dev/null +++ b/src/plugins/qtestlib/images/pass.png diff --git a/src/plugins/qtestlib/qtestlib.pro b/src/plugins/qtestlib/qtestlib.pro new file mode 100644 index 00000000000..5031b1d6b47 --- /dev/null +++ b/src/plugins/qtestlib/qtestlib.pro @@ -0,0 +1,14 @@ +TEMPLATE = lib +TARGET = QTestLibPlugin +QT += xml + +include(../../qworkbenchplugin.pri) +include(../../plugins/coreplugin/coreplugin.pri) + +SOURCES += qtestlibplugin.cpp +HEADERS += qtestlibplugin.h +RESOURCES += qtestlib.qrc + +LIBS += -lProjectExplorer \ + -lQuickOpen \ + -lUtils diff --git a/src/plugins/qtestlib/qtestlib.qrc b/src/plugins/qtestlib/qtestlib.qrc new file mode 100644 index 00000000000..fc0610e954e --- /dev/null +++ b/src/plugins/qtestlib/qtestlib.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/Trolltech/QTestLibPlugin"> + <file>images/pass.png</file> +</qresource> +</RCC> diff --git a/src/plugins/qtestlib/qtestlibplugin.cpp b/src/plugins/qtestlib/qtestlibplugin.cpp new file mode 100644 index 00000000000..d32c36467e2 --- /dev/null +++ b/src/plugins/qtestlib/qtestlibplugin.cpp @@ -0,0 +1,501 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "qtestlibplugin.h" + +#include <QtCore/qplugin.h> +#include <QIcon> +#include <QDebug> +#include <QKeySequence> +#include <QAction> +#include <QHeaderView> +#include <QDomDocument> +#include <QTemporaryFile> +#include <texteditor/TextEditorInterfaces> +#include <Qt4IProjectManagers> +#include <QFileInfo> +#include <QDir> +#include <QStandardItemModel> +#include <QTreeView> +#include <QTextEdit> +#include <QSplitter> +#include <QVBoxLayout> +#include <QComboBox> +#include <QLabel> + +using namespace QTestLib::Internal; + +static QString incidentString(QTestFunction::IncidentType type) +{ + static QMap<QTestFunction::IncidentType, QString> strings; + if (strings.empty()) { + strings.insert(QTestFunction::Pass, QObject::tr("Pass")); + strings.insert(QTestFunction::XFail, QObject::tr("Expected Failure")); + strings.insert(QTestFunction::Fail, QObject::tr("Failure")); + strings.insert(QTestFunction::XPass, QObject::tr("Expected Pass")); + } + return strings.value(type, QString()); +} + +static QString messageString(QTestFunction::MessageType type) +{ + static QMap<QTestFunction::MessageType, QString> strings; + if (strings.empty()) { + strings.insert(QTestFunction::Warning, QObject::tr("Warning")); + strings.insert(QTestFunction::QWarning, QObject::tr("Qt Warning")); + strings.insert(QTestFunction::QDebug, QObject::tr("Qt Debug")); + strings.insert(QTestFunction::QSystem, QObject::tr("Critical")); + strings.insert(QTestFunction::QFatal, QObject::tr("Fatal")); + strings.insert(QTestFunction::Skip, QObject::tr("Skipped")); + strings.insert(QTestFunction::Info, QObject::tr("Info")); + } + return strings.value(type, QString()); +} + +static QTestFunction::IncidentType stringToIncident(const QString &str) +{ + if (str == QLatin1String("pass")) + return QTestFunction::Pass; + else if (str == QLatin1String("fail")) + return QTestFunction::Fail; + else if (str == QLatin1String("xfail")) + return QTestFunction::XFail; + else if (str == QLatin1String("xpass")) + return QTestFunction::XPass; + return QTestFunction::Fail; // ... +} + +static QTestFunction::MessageType stringToMessageType(const QString &str) +{ + if (str == QLatin1String("warn")) + return QTestFunction::Warning; + else if (str == QLatin1String("system")) + return QTestFunction::QSystem; + else if (str == QLatin1String("qdebug")) + return QTestFunction::QDebug; + else if (str == QLatin1String("qwarn")) + return QTestFunction::QWarning; + else if (str == QLatin1String("qfatal")) + return QTestFunction::QFatal; + else if (str == QLatin1String("skip")) + return QTestFunction::Skip; + else if (str == QLatin1String("info")) + return QTestFunction::Info; + return QTestFunction::QSystem; // ... +} + +// ----------------------------------- +QTestLibPlugin::QTestLibPlugin() : + m_projectExplorer(0), + m_core(0), + m_outputPane(0) +{ +} + +QTestLibPlugin::~QTestLibPlugin() +{ + if (m_core && m_outputPane) + m_core->pluginManager()->removeObject(m_outputPane); +} + +bool QTestLibPlugin::init(ExtensionSystem::PluginManagerInterface *app, QString * /*error_message*/) +{ + m_core = app->getObject<Core::ICore>(); + + m_projectExplorer = app->getObject<ProjectExplorer::ProjectExplorerPlugin>(); + connect(m_projectExplorer->qObject(), SIGNAL(aboutToExecuteProject(ProjectExplorer::Project *)), + this, SLOT(projectRunHook(ProjectExplorer::Project *))); + + m_outputPane = new QTestOutputPane(this); + app->addObject(m_outputPane); + + return true; +} + +void QTestLibPlugin::extensionsInitialized() +{ +} + +void QTestLibPlugin::projectRunHook(ProjectExplorer::Project *proj) +{ + return; //NBS TODO QTestlibplugin + if (!proj) + return; + + m_projectDirectory = QString(); + //NBS proj->setExtraApplicationRunArguments(QStringList()); + //NBS proj->setCustomApplicationOutputHandler(0); + + const QVariant config; //NBS = proj->projectProperty(ProjectExplorer::Constants::P_CONFIGVAR); + if (!config.toStringList().contains(QLatin1String("qtestlib"))) + return; + + { + QTemporaryFile tempFile; + tempFile.setAutoRemove(false); + tempFile.open(); + m_outputFile = tempFile.fileName(); + } + + //NBS proj->setCustomApplicationOutputHandler(this); + //NBS proj->setExtraApplicationRunArguments(QStringList() << QLatin1String("-xml") << QLatin1String("-o") << m_outputFile); + const QString proFile = proj->fileName(); + const QFileInfo fi(proFile); + if (QFile::exists(fi.absolutePath())) + m_projectDirectory = fi.absolutePath(); +} + +void QTestLibPlugin::clear() +{ + m_projectExplorer->applicationOutputWindow()->clear(); +} + +void QTestLibPlugin::appendOutput(const QString &out) +{ + m_projectExplorer->applicationOutputWindow()->appendOutput(out); +} + +void QTestLibPlugin::processExited(int exitCode) +{ + m_projectExplorer->applicationOutputWindow()->processExited(exitCode); + + QFile f(m_outputFile); + if (!f.open(QIODevice::ReadOnly)) + return; + + QDomDocument doc; + if (!doc.setContent(&f)) + return; + + f.close(); + f.remove(); + + m_outputPane->clearContents(); + + const QString testFunctionTag = QLatin1String("TestFunction"); + const QString nameAttr = QLatin1String("name"); + const QString typeAttr = QLatin1String("type"); + const QString incidentTag = QLatin1String("Incident"); + const QString fileAttr = QLatin1String("file"); + const QString lineAttr = QLatin1String("line"); + const QString messageTag = QLatin1String("Message"); + const QString descriptionItem = QLatin1String("Description"); + + for (QDomElement testElement = doc.documentElement().firstChildElement(); + !testElement.isNull(); testElement = testElement.nextSiblingElement()) { + + if (testElement.tagName() != testFunctionTag) + continue; + + QTestFunction *function = new QTestFunction(testElement.attribute(nameAttr)); + + for (QDomElement e = testElement.firstChildElement(); + !e.isNull(); e = e.nextSiblingElement()) { + + const QString type = e.attribute(typeAttr); + + if (e.tagName() == incidentTag) { + QString file = e.attribute(fileAttr); + + if (!file.isEmpty() + && QFileInfo(file).isRelative() + && !m_projectDirectory.isEmpty()) { + + QFileInfo fi(m_projectDirectory, file); + if (fi.exists()) + file = fi.absoluteFilePath(); + } + + const QString line = e.attribute(lineAttr); + const QString details = e.text(); + + QTestFunction::IncidentType itype = stringToIncident(type); + function->addIncident(itype, file, line, details); + } else if (e.tagName() == messageTag ) { + QTestFunction::MessageType msgType = stringToMessageType(type); + function->addMessage(msgType, e.namedItem(descriptionItem).toElement().text()); + } + } + + m_outputPane->addFunction(function); + } + + m_outputPane->show(); +} + +// -------- QTestFunction +void QTestFunction::addIncident(IncidentType type, + const QString &file, + const QString &line, + const QString &details) +{ + QStandardItem *status = new QStandardItem(incidentString(type)); + status->setData(QVariant::fromValue(type)); + + switch (type) { + case QTestFunction::Pass: status->setForeground(Qt::green); break; + case QTestFunction::Fail: status->setForeground(Qt::red); break; + case QTestFunction::XFail: status->setForeground(Qt::darkMagenta); break; + case QTestFunction::XPass: status->setForeground(Qt::darkGreen); break; + } + + QStandardItem *location = new QStandardItem; + if (!file.isEmpty()) { + location->setText(file + QLatin1Char(':') + line); + location->setForeground(Qt::red); + + QTestLocation loc; + loc.file = file; + loc.line = line; + location->setData(QVariant::fromValue(loc)); + } + + appendRow(QList<QStandardItem *>() << status << location); + + if (!details.isEmpty()) { + status->setColumnCount(2); + status->appendRow(QList<QStandardItem *>() << new QStandardItem() << new QStandardItem(details)); + } +} + +void QTestFunction::addMessage(MessageType type, const QString &text) +{ + QStandardItem *status = new QStandardItem(messageString(type)); + status->setData(QVariant::fromValue(type)); + QStandardItem *msg = new QStandardItem(text); + appendRow(QList<QStandardItem *>() << status << msg); +} + +bool QTestFunction::indexHasIncidents(const QModelIndex &function, IncidentType type) +{ + if (!function.isValid()) + return false; + const QAbstractItemModel *m = function.model(); + if (!m->hasChildren(function)) + return false; + + const int rows = m->rowCount(function); + for (int row = 0; row < rows; ++row) { + const QModelIndex child = m->index(row, 0, function); + + QVariant tag = child.data(Qt::UserRole + 1); + if (tag.type() != QVariant::UserType + || tag.userType() != qMetaTypeId<QTestFunction::IncidentType>()) + continue; + + if (tag.value<QTestFunction::IncidentType>() == type) + return true; + } + + return false; +} +// -------------- QTestOutputPane +QTestOutputPane::QTestOutputPane(QTestLibPlugin *plugin) : + QObject(plugin), + m_plugin(plugin), + m_widget(0), + m_model(new QStandardItemModel(this)) +{ + clearContents(); +} + +void QTestOutputPane::addFunction(QTestFunction *function) +{ + m_model->appendRow(function); +} + +QWidget *QTestOutputPane::outputWidget(QWidget *parent) +{ + if (!m_widget) + m_widget = new QTestOutputWidget(m_model, m_plugin->coreInterface(), parent); + return m_widget; +} + +QString QTestOutputPane::name() const +{ + return tr("Test Results"); +} + +void QTestOutputPane::clearContents() +{ + m_model->clear(); + m_model->setColumnCount(2); + m_model->setHorizontalHeaderLabels(QStringList() << tr("Result") << tr("Message")); +} + +void QTestOutputPane::visibilityChanged(bool visible) +{ + Q_UNUSED(visible) +} + +void QTestOutputPane::show() +{ + if (m_widget) + m_widget->expand(); + emit showPage(); +} + +// -------- QTestOutputFilter +bool QTestOutputFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + if (sourceParent.isValid()) { + return true; + } + + QModelIndex idx = sourceModel()->index(sourceRow, 0); + if (QTestFunction::indexHasIncidents(idx, m_filter)) + return true; + else + return false; +} + +// ------- QTestOutputWidget + + +QTestOutputWidget::QTestOutputWidget(QStandardItemModel *model, Core::ICore *coreInterface, QWidget *parent): + QWidget(parent), + m_coreInterface(coreInterface), + m_model(model), + m_resultsView(new QTreeView(this)), + m_filterCombo(new QComboBox(this)), + m_filterModel(new QTestOutputFilter(this)) +{ + m_resultsView->setModel(model); + m_resultsView->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_resultsView->header()->setStretchLastSection(true); + connect(m_resultsView, SIGNAL(activated(const QModelIndex &)), + this, SLOT(gotoLocation(QModelIndex))); + + m_filterCombo->addItem(tr("All Incidents")); + m_filterCombo->addItem(incidentString(QTestFunction::Fail), QVariant::fromValue(QTestFunction::Fail)); + m_filterCombo->addItem(incidentString(QTestFunction::Pass), QVariant::fromValue(QTestFunction::Pass)); + m_filterCombo->addItem(incidentString(QTestFunction::XFail), QVariant::fromValue(QTestFunction::XFail)); + m_filterCombo->addItem(incidentString(QTestFunction::XPass), QVariant::fromValue(QTestFunction::XPass)); + connect(m_filterCombo, SIGNAL(activated(int)), + this, SLOT(activateComboFilter(int))); + + QHBoxLayout *filterLayout = new QHBoxLayout; + filterLayout->addWidget(new QLabel(tr("Show Only:"), this)); + filterLayout->addWidget(m_filterCombo); + filterLayout->addStretch(); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addLayout(filterLayout); + layout->addWidget(m_resultsView); + + m_filterModel->setDynamicSortFilter(true); + m_filterModel->setSourceModel(m_model); +} + +void QTestOutputWidget::expand() +{ + /* + const QAbstractItemModel *m = m_resultsView->model(); + for (int r = 0, count = m->rowCount(); r < count; ++r) { + m_resultsView->expand(m->index(r, 0)); + } + */ + m_resultsView->expandAll(); + m_resultsView->header()->resizeSections(QHeaderView::ResizeToContents); +} + +void QTestOutputWidget::activateComboFilter(int index) +{ + QVariant tag = m_filterCombo->itemData(index); + if (!tag.isValid()) { + if (m_resultsView->model() != m_model) + m_resultsView->setModel(m_model); + } else { + + QTestFunction::IncidentType incident = tag.value<QTestFunction::IncidentType>(); + m_filterModel->setIncidentFilter(incident); + + if (m_resultsView->model() != m_filterModel) + m_resultsView->setModel(m_filterModel); + } + expand(); +} + +void QTestOutputWidget::gotoLocation(QModelIndex index) +{ + if (!index.isValid()) + return; + + if (m_resultsView->model() == m_filterModel) + index = m_filterModel->mapToSource(index); + + if (!index.isValid()) + return; + + const QAbstractItemModel *m = index.model(); + + QModelIndex parent = index.parent(); + if (!parent.isValid()) + return; + + QModelIndex functionIndex = parent; + QModelIndex failureIndex = index; + + QModelIndex grandParent = parent.parent(); + if (grandParent.isValid()) { + functionIndex = grandParent; + failureIndex = parent; + } + + if (!functionIndex.isValid()) + return; + + QModelIndex locationIndex = m->index(failureIndex.row(), 1, functionIndex); + if (!locationIndex.isValid()) + return; + + QVariant tag = locationIndex.data(Qt::UserRole + 1); + if (tag.type() != QVariant::UserType + || tag.userType() != qMetaTypeId<QTestLocation>()) + return; + + QTestLocation loc = tag.value<QTestLocation>(); + + m_coreInterface->editorManager()->openEditor(loc.file); + Core::EditorInterface *edtIface = m_coreInterface->editorManager()->currentEditor(); + if (!edtIface) + return; + TextEditor::ITextEditor *editor = + qobject_cast<TextEditor::ITextEditor*>(edtIface->qObject()); + if (!editor) + return; + + editor->gotoLine(loc.line.toInt()); +} + +Q_EXPORT_PLUGIN(QTestLibPlugin) diff --git a/src/plugins/qtestlib/qtestlibplugin.h b/src/plugins/qtestlib/qtestlibplugin.h new file mode 100644 index 00000000000..dacd0115200 --- /dev/null +++ b/src/plugins/qtestlib/qtestlibplugin.h @@ -0,0 +1,209 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QTESTLIBPLUGIN_H +#define QTESTLIBPLUGIN_H + +#include <coreplugin/ioutputpane.h> +#include <projectexplorer/ProjectExplorerInterfaces> +#include <QPixmap> +#include <QStandardItem> +#include <QWidget> +#include <QSortFilterProxyModel> + +class QStandardItemModel; +class QTreeView; +class QTextEdit; +class QComboBox; + +namespace QTestLib { +namespace Internal { + +class QTestLibPlugin; +class QTestOutputWidget; + +struct QTestLocation +{ + QString file; + QString line; +}; + +class QTestFunction : public QStandardItem +{ +public: + enum IncidentType { + Pass, + XFail, + Fail, + XPass + }; + + enum MessageType { + Warning, + QWarning, + QDebug, + QSystem, + QFatal, + Skip, + Info + }; + + inline QTestFunction(const QString &name) + : QStandardItem(name) { + setColumnCount(2); + // ### hardcoding colors sucks... + setForeground(Qt::darkBlue); + } + + void addIncident(IncidentType type, + const QString &file = QString(), + const QString &line = QString(), + const QString &details = QString()); + + void addMessage(MessageType type, const QString &text); + + static bool indexHasIncidents(const QModelIndex &function, IncidentType type); +}; + +class QTestOutputPane : public QObject, + public Core::IOutputPane +{ + Q_OBJECT + Q_INTERFACES(Core::IOutputPane) +public: + QTestOutputPane(QTestLibPlugin *plugin); + + void addFunction(QTestFunction *function); + + virtual QWidget *outputWidget(QWidget *parent); + QList<QWidget*> toolBarWidgets(void) const { return QList<QWidget *>(); } + virtual QString name() const; + + virtual void clearContents(); + virtual void visibilityChanged(bool visible); + + void show(); + +Q_SIGNALS: +//signals + void showPage(); + +private: + QTestLibPlugin *m_plugin; + QTestOutputWidget *m_widget; + QStandardItemModel *m_model; +}; + +class QTestOutputFilter : public QSortFilterProxyModel +{ +public: + inline QTestOutputFilter(QObject *parent) + : QSortFilterProxyModel(parent), m_filter(QTestFunction::Fail) + {} + + inline void setIncidentFilter(QTestFunction::IncidentType incident) { + m_filter = incident; + filterChanged(); + } + +protected: + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + +private: + QTestFunction::IncidentType m_filter; +}; + +class QTestOutputWidget : public QWidget +{ + Q_OBJECT +public: + QTestOutputWidget(QStandardItemModel *model, + Core::ICore *iCore, + QWidget *parent); + + void expand(); + +private Q_SLOTS: + void activateComboFilter(int index); + void gotoLocation(QModelIndex index); + +private: + Core::ICore *m_coreInterface; + QStandardItemModel *m_model; + QTreeView *m_resultsView; + QComboBox *m_filterCombo; + QTestOutputFilter *m_filterModel; +}; + +class QTestLibPlugin : public QObject, + public ExtensionSystem::PluginInterface, + public ProjectExplorer::IApplicationOutput +{ + Q_OBJECT + Q_INTERFACES(ExtensionSystem::PluginInterface + ProjectExplorer::IApplicationOutput) + +public: + QTestLibPlugin(); + virtual ~QTestLibPlugin(); + + bool init(ExtensionSystem::PluginManagerInterface *app, QString *error_message); + void extensionsInitialized(); + + inline Core::ICore *coreInterface() const { + return m_core; + } + + // IApplicationOutput + virtual void clear(); + virtual void appendOutput(const QString &out); + virtual void processExited(int exitCode); + +private slots: + void projectRunHook(ProjectExplorer::Project *project); + +private: + ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer; + Core::ICore *m_core; + QString m_outputFile; + QString m_projectDirectory; + QTestOutputPane *m_outputPane; +}; + +} // namespace Internal +} // namespace QTestLibPlugin + +Q_DECLARE_METATYPE(QTestLib::Internal::QTestLocation) +Q_DECLARE_METATYPE(QTestLib::Internal::QTestFunction::IncidentType) +Q_DECLARE_METATYPE(QTestLib::Internal::QTestFunction::MessageType) + +#endif // QTESTLIBPLUGIN_H diff --git a/src/plugins/qtscripteditor/QtScriptEditor.mimetypes.xml b/src/plugins/qtscripteditor/QtScriptEditor.mimetypes.xml new file mode 100644 index 00000000000..45f6c0b00b8 --- /dev/null +++ b/src/plugins/qtscripteditor/QtScriptEditor.mimetypes.xml @@ -0,0 +1,41 @@ +<?xml version="1.0"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="application/javascript"> + <alias type="application/x-javascript"/> + <alias type="text/javascript"/> + <sub-class-of type="text/plain"/> + <comment>Qt Script file</comment> + <comment xml:lang="bg">Програма на JavaScript</comment> + <comment xml:lang="ca">programa JavaScript</comment> + <comment xml:lang="cs">Program v JavaScriptu</comment> + <comment xml:lang="da">JavaScript-program</comment> + <comment xml:lang="de">JavaScript-Programm</comment> + <comment xml:lang="el">πρόγραμμα JavaScript</comment> + <comment xml:lang="eo">JavaScript-programo</comment> + <comment xml:lang="es">programa en JavaScript</comment> + <comment xml:lang="eu">JavaScript programa</comment> + <comment xml:lang="fi">JavaScript-ohjelma</comment> + <comment xml:lang="fr">programme JavaScript</comment> + <comment xml:lang="hu">JavaScript-program</comment> + <comment xml:lang="it">Programma JavaScript</comment> + <comment xml:lang="ja">JavaScript プログラム</comment> + <comment xml:lang="ko">자바스크립트 프로그램</comment> + <comment xml:lang="lt">JavaScript programa</comment> + <comment xml:lang="ms">Program JavaScript</comment> + <comment xml:lang="nb">JavaScript-program</comment> + <comment xml:lang="nl">JavaScript-programma</comment> + <comment xml:lang="nn">JavaScript-program</comment> + <comment xml:lang="pl">Pogram JavaScript</comment> + <comment xml:lang="pt">programa JavaScript</comment> + <comment xml:lang="pt_BR">Programa JavaScript</comment> + <comment xml:lang="ru">программа JavaScript</comment> + <comment xml:lang="sq">program JavaScript</comment> + <comment xml:lang="sr">Јаваскрипт програм</comment> + <comment xml:lang="sv">JavaScript-program</comment> + <comment xml:lang="uk">Програма на мові JavaScript</comment> + <comment xml:lang="vi">Chương trình JavaScript</comment> + <comment xml:lang="zh_CN">JavaScript 程序</comment> + <comment xml:lang="zh_TW">JavaScript 程式</comment> + <glob pattern="*.js"/> + </mime-type> +</mime-info> diff --git a/src/plugins/qtscripteditor/QtScriptEditor.pluginspec b/src/plugins/qtscripteditor/QtScriptEditor.pluginspec new file mode 100644 index 00000000000..3e70cf5568e --- /dev/null +++ b/src/plugins/qtscripteditor/QtScriptEditor.pluginspec @@ -0,0 +1,11 @@ +<plugin name="QtScriptEditor" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Editor for QtScript.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> + <dependency name="TextEditor" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/qtscripteditor/qtscripteditor.cpp b/src/plugins/qtscripteditor/qtscripteditor.cpp new file mode 100644 index 00000000000..d72fc1f6314 --- /dev/null +++ b/src/plugins/qtscripteditor/qtscripteditor.cpp @@ -0,0 +1,172 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qtscripteditor.h" +#include "qtscripteditorconstants.h" +#include "qtscripthighlighter.h" +#include "qtscripteditorplugin.h" + +#include <indenter.h> + +#include <coreplugin/icore.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <texteditor/basetextdocument.h> +#include <texteditor/fontsettings.h> +#include <texteditor/textblockiterator.h> +#include <texteditor/texteditorconstants.h> + +#include <QtGui/QMenu> + +namespace QtScriptEditor { +namespace Internal { + +ScriptEditorEditable::ScriptEditorEditable(ScriptEditor *editor, const QList<int>& context) + : BaseTextEditorEditable(editor), m_context(context) +{ + +} + +ScriptEditor::ScriptEditor(const Context &context, + Core::ICore *core, + TextEditor::TextEditorActionHandler *ah, + QWidget *parent) : + TextEditor::BaseTextEditor(parent), + m_context(context), + m_core(core), + m_ah(ah) +{ + setParenthesesMatchingEnabled(true); + setMarksVisible(true); + setCodeFoldingVisible(true); + setMimeType(QtScriptEditor::Constants::C_QTSCRIPTEDITOR_MIMETYPE); + + baseTextDocument()->setSyntaxHighlighter(new QtScriptHighlighter); +} + +ScriptEditor::~ScriptEditor() +{ +} + +Core::IEditor *ScriptEditorEditable::duplicate(QWidget *parent) +{ + return qobject_cast<ScriptEditor*>(editor())->duplicate(parent)->editableInterface(); +} + +ScriptEditor *ScriptEditor::duplicate(QWidget *parent) +{ + ScriptEditor *editor = new ScriptEditor(m_context, m_core, m_ah, parent); + editor->duplicateFrom(this); + QtScriptEditorPlugin::initializeEditor(editor); + return editor; +} + +const char *ScriptEditorEditable::kind() const +{ + return QtScriptEditor::Constants::C_QTSCRIPTEDITOR; +} + +ScriptEditor::Context ScriptEditorEditable::context() const +{ + return m_context; +} + +void ScriptEditor::setFontSettings(const TextEditor::FontSettings &fs) +{ + TextEditor::BaseTextEditor::setFontSettings(fs); + QtScriptHighlighter *highlighter = qobject_cast<QtScriptHighlighter*>(baseTextDocument()->syntaxHighlighter()); + if (!highlighter) + return; + + static QVector<QString> categories; + if (categories.isEmpty()) { + categories << QLatin1String(TextEditor::Constants::C_NUMBER) + << QLatin1String(TextEditor::Constants::C_STRING) + << QLatin1String(TextEditor::Constants::C_TYPE) + << QLatin1String(TextEditor::Constants::C_KEYWORD) + << QLatin1String(TextEditor::Constants::C_PREPROCESSOR) + << QLatin1String(TextEditor::Constants::C_LABEL) + << QLatin1String(TextEditor::Constants::C_COMMENT); + } + + highlighter->setFormats(fs.toTextCharFormats(categories)); + highlighter->rehighlight(); +} + +bool ScriptEditor::isElectricCharacter(const QChar &ch) const +{ + if (ch == QLatin1Char('{') || ch == QLatin1Char('}')) + return true; + return false; +} + + // Indent a code line based on previous +template <class Iterator> +static void indentScriptBlock(const TextEditor::TabSettings &ts, + const QTextBlock &block, + const Iterator &programBegin, + const Iterator &programEnd, + QChar typedChar) +{ + typedef typename SharedTools::Indenter<Iterator> Indenter ; + Indenter &indenter = Indenter::instance(); + indenter.setIndentSize(ts.m_tabSize); + const TextEditor::TextBlockIterator current(block); + const int indent = indenter.indentForBottomLine(current, programBegin, + programEnd, typedChar); + ts.indentLine(block, indent); +} + +void ScriptEditor::indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar) +{ + const TextEditor::TextBlockIterator begin(doc->begin()); + const TextEditor::TextBlockIterator end(block.next()); + indentScriptBlock(tabSettings(), block, begin, end, typedChar); +} + +void ScriptEditor::contextMenuEvent(QContextMenuEvent *e) +{ + QMenu *menu = createStandardContextMenu(); + + if (Core::IActionContainer *mcontext = m_core->actionManager()->actionContainer(QtScriptEditor::Constants::M_CONTEXT)) { + QMenu *contextMenu = mcontext->menu(); + foreach(QAction *action, contextMenu->actions()) { + menu->addAction(action); + } + } + + menu->exec(e->globalPos()); + delete menu; + +} + +} // namespace Internal +} // namespace QtScriptEditor diff --git a/src/plugins/qtscripteditor/qtscripteditor.h b/src/plugins/qtscripteditor/qtscripteditor.h new file mode 100644 index 00000000000..f005da2af72 --- /dev/null +++ b/src/plugins/qtscripteditor/qtscripteditor.h @@ -0,0 +1,99 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QTSCRIPTDITORW_H +#define QTSCRIPTDITORW_H + +#include <texteditor/basetexteditor.h> + +namespace Core { + class ICore; +} + +namespace QtScriptEditor { +namespace Internal { + +class QtScriptHighlighter; + +class ScriptEditor; + +class ScriptEditorEditable : public TextEditor::BaseTextEditorEditable +{ +public: + ScriptEditorEditable(ScriptEditor *, const QList<int>&); + QList<int> context() const; + + bool duplicateSupported() const { return true; } + Core::IEditor *duplicate(QWidget *parent); + const char *kind() const; + QToolBar *toolBar() { return 0; } + +private: + QList<int> m_context; +}; + + +class ScriptEditor : public TextEditor::BaseTextEditor +{ + Q_OBJECT + +public: + typedef QList<int> Context; + + ScriptEditor(const Context &context, + Core::ICore *core, + TextEditor::TextEditorActionHandler *ah, + QWidget *parent = 0); + ~ScriptEditor(); + + ScriptEditor *duplicate(QWidget *parent); + +public slots: + virtual void setFontSettings(const TextEditor::FontSettings &); + +protected: + void contextMenuEvent(QContextMenuEvent *e); + TextEditor::BaseTextEditorEditable *createEditableInterface() { return new ScriptEditorEditable(this, m_context); } + +private: + virtual bool isElectricCharacter(const QChar &ch) const; + virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar); + + const Context m_context; + Core::ICore *m_core; + TextEditor::TextEditorActionHandler *m_ah; +}; + +} // namespace Internal +} // namespace QtScriptEditor + +#endif // QTSCRIPTDITORW_H diff --git a/src/plugins/qtscripteditor/qtscripteditor.pro b/src/plugins/qtscripteditor/qtscripteditor.pro new file mode 100644 index 00000000000..1e93663ec4e --- /dev/null +++ b/src/plugins/qtscripteditor/qtscripteditor.pro @@ -0,0 +1,22 @@ +TEMPLATE = lib +TARGET = QtScriptEditor +QT += script + +include(../../qworkbenchplugin.pri) +include(../../plugins/texteditor/texteditor.pri) +include(../../../shared/qscripthighlighter/qscripthighlighter.pri) +include(../../../shared/indenter/indenter.pri) +include(../../plugins/coreplugin/coreplugin.pri) + +HEADERS += qtscripteditor.h \ +qtscripteditorfactory.h \ +qtscripteditorplugin.h \ +qtscripthighlighter.h \ +qtscripteditoractionhandler.h + +SOURCES += qtscripteditor.cpp \ +qtscripteditorfactory.cpp \ +qtscripteditorplugin.cpp \ +qtscripthighlighter.cpp \ +qtscripteditoractionhandler.cpp +RESOURCES += qtscripteditor.qrc diff --git a/src/plugins/qtscripteditor/qtscripteditor.qrc b/src/plugins/qtscripteditor/qtscripteditor.qrc new file mode 100644 index 00000000000..1e987b5a4a2 --- /dev/null +++ b/src/plugins/qtscripteditor/qtscripteditor.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/qtscripteditor" > + <file>QtScriptEditor.mimetypes.xml</file> + </qresource> +</RCC> diff --git a/src/plugins/qtscripteditor/qtscripteditoractionhandler.cpp b/src/plugins/qtscripteditor/qtscripteditoractionhandler.cpp new file mode 100644 index 00000000000..9fd78777c14 --- /dev/null +++ b/src/plugins/qtscripteditor/qtscripteditoractionhandler.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qtscripteditoractionhandler.h" +#include "qtscripteditorconstants.h" +#include "qtscripteditor.h" + +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/scriptmanager/scriptmanagerinterface.h> + +#include <QtGui/QAction> +#include <QtGui/QMessageBox> +#include <QtGui/QMainWindow> +#include <QtCore/QDebug> + +static QAction *actionFromId(Core::ICore *core, const QString &id) +{ + Core::ICommand *cmd = core->actionManager()->command(id); + if (!cmd) + return 0; + return cmd->action(); +} + +namespace QtScriptEditor { +namespace Internal { + +QtScriptEditorActionHandler::QtScriptEditorActionHandler(Core::ICore *core) : + TextEditor::TextEditorActionHandler(core, + QLatin1String(QtScriptEditor::Constants::C_QTSCRIPTEDITOR), + Format), + m_runAction(0) +{ +} + +void QtScriptEditorActionHandler::createActions() +{ + TextEditor::TextEditorActionHandler::createActions(); + m_runAction = actionFromId(core(), QLatin1String(QtScriptEditor::Constants::RUN)); + connect(m_runAction, SIGNAL(triggered()), this, SLOT(run())); +} + + +void QtScriptEditorActionHandler::updateActions(UpdateMode um) +{ + TextEditor::TextEditorActionHandler::updateActions(um); + if (m_runAction) + m_runAction->setEnabled(um != NoEditor); +} + +void QtScriptEditorActionHandler::run() +{ + typedef Core::ScriptManagerInterface::Stack Stack; + if (!currentEditor()) + return; + + const QString script = currentEditor()->toPlainText(); + + // run + Stack errorStack; + QString errorMessage; + if (core()->scriptManager()->runScript(script, &errorMessage, &errorStack)) + return; + + // try to find a suitable error line in the stack + // ignoring 0 and other files (todo: open other files?) + int errorLineNumber = 0; + if (const int numFrames = errorStack.size()) { + for (int f = 0; f < numFrames; f++) { + if (errorStack[f].lineNumber && errorStack[f].fileName.isEmpty()) { + errorLineNumber = errorStack[f].lineNumber; + break; + } + } + } + if (errorLineNumber) + currentEditor()->gotoLine(errorLineNumber); + QMessageBox::critical(core()->mainWindow(), tr("Qt Script Error"), errorMessage); +} + +} // namespace Internal +} // namespace QtScriptEditor diff --git a/src/plugins/qtscripteditor/qtscripteditoractionhandler.h b/src/plugins/qtscripteditor/qtscripteditoractionhandler.h new file mode 100644 index 00000000000..65073540d8e --- /dev/null +++ b/src/plugins/qtscripteditor/qtscripteditoractionhandler.h @@ -0,0 +1,62 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QTSCRIPTDITORACTIONHANDLER_H +#define QTSCRIPTDITORACTIONHANDLER_H + +#include <texteditor/texteditoractionhandler.h> + +namespace QtScriptEditor { +namespace Internal { + +class QtScriptEditorActionHandler : public TextEditor::TextEditorActionHandler +{ + Q_OBJECT + +public: + QtScriptEditorActionHandler(Core::ICore *core); + +private: + virtual void createActions(); + virtual void updateActions(UpdateMode um); + +private slots: + void run(); + +private: + QAction *m_runAction; +}; + +} // namespace Internal +} // namespace QtScriptEditor + +#endif // QTSCRIPTDITORACTIONHANDLER_H diff --git a/src/plugins/qtscripteditor/qtscripteditorconstants.h b/src/plugins/qtscripteditor/qtscripteditorconstants.h new file mode 100644 index 00000000000..c26384cf46f --- /dev/null +++ b/src/plugins/qtscripteditor/qtscripteditorconstants.h @@ -0,0 +1,46 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QTSCRIPTEDITOR_CONSTANTS_H +#define QTSCRIPTEDITOR_CONSTANTS_H + +namespace QtScriptEditor { +namespace Constants { +const char * const M_CONTEXT = "Qt Script Editor.ContextMenu"; +const char * const RUN = "Qt Script Editor.Run"; +const char * const RUN_SEP = "Qt Script Editor.Run.Separator"; +const char * const C_QTSCRIPTEDITOR = "Qt Script Editor"; +const char * const C_QTSCRIPTEDITOR_MIMETYPE = "application/javascript"; +} +} + +#endif //QTSCRIPTEDITOR_CONSTANTS_H diff --git a/src/plugins/qtscripteditor/qtscripteditorfactory.cpp b/src/plugins/qtscripteditor/qtscripteditorfactory.cpp new file mode 100644 index 00000000000..bf5c12dcfeb --- /dev/null +++ b/src/plugins/qtscripteditor/qtscripteditorfactory.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qtscripteditorfactory.h" +#include "qtscripteditor.h" +#include "qtscripteditoractionhandler.h" +#include "qtscripteditorconstants.h" +#include "qtscripteditorplugin.h" + +#include <coreplugin/editormanager/editormanager.h> + +#include <QtCore/QFileInfo> +#include <QtCore/qdebug.h> + +using namespace QtScriptEditor::Internal; +using namespace QtScriptEditor::Constants; + +QtScriptEditorFactory::QtScriptEditorFactory(Core::ICore *core, + const Context &context, + QObject *parent) : + Core::IEditorFactory(parent), + m_kind(QLatin1String(C_QTSCRIPTEDITOR)), + m_mimeTypes(QLatin1String(QtScriptEditor::Constants::C_QTSCRIPTEDITOR_MIMETYPE)), + m_context(context), + m_core(core), + m_actionHandler(new QtScriptEditorActionHandler(core)) +{ +} + +QtScriptEditorFactory::~QtScriptEditorFactory() +{ + delete m_actionHandler; +} + +QString QtScriptEditorFactory::kind() const +{ + return m_kind; +} + +Core::IFile *QtScriptEditorFactory::open(const QString &fileName) +{ + Core::IEditor *iface = m_core->editorManager()->openEditor(fileName, kind()); + if (!iface) { + qWarning() << "QtScriptEditorFactory::open: openEditor failed for " << fileName; + return 0; + } + return iface->file(); +} + +Core::IEditor *QtScriptEditorFactory::createEditor(QWidget *parent) +{ + ScriptEditor *rc = new ScriptEditor(m_context, m_core, m_actionHandler, parent); + QtScriptEditorPlugin::initializeEditor(rc); + return rc->editableInterface(); +} + +QStringList QtScriptEditorFactory::mimeTypes() const +{ + return m_mimeTypes; +} diff --git a/src/plugins/qtscripteditor/qtscripteditorfactory.h b/src/plugins/qtscripteditor/qtscripteditorfactory.h new file mode 100644 index 00000000000..600277d2c0a --- /dev/null +++ b/src/plugins/qtscripteditor/qtscripteditorfactory.h @@ -0,0 +1,84 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef RQTSCRIPTEDITORFACTORY_H +#define RQTSCRIPTEDITORFACTORY_H + +#include <extensionsystem/ExtensionSystemInterfaces> +#include <coreplugin/editormanager/ieditorfactory.h> + +#include <QtCore/QStringList> + +namespace Core { +class ICore; +} + +namespace TextEditor { +class TextEditorActionHandler; +} + +namespace QtScriptEditor { +namespace Internal { + +class QtScriptEditorActionHandler; + +class QtScriptEditorFactory : public Core::IEditorFactory +{ + Q_OBJECT + +public: + typedef QList<int> Context; + + QtScriptEditorFactory(Core::ICore *core, + const Context &context, + QObject *parent); + ~QtScriptEditorFactory(); + + virtual QStringList mimeTypes() const; + //EditorFactory + QString kind() const; + Core::IFile *open(const QString &fileName); + Core::IEditor *createEditor(QWidget *parent); + +private: + const QString m_kind; + const QStringList m_mimeTypes; + const Context m_context; + + Core::ICore *m_core; + TextEditor::TextEditorActionHandler *m_actionHandler; +}; + +} // namespace Internal +} // namespace QtScriptEditor + +#endif // RQTSCRIPTEDITORFACTORY_H diff --git a/src/plugins/qtscripteditor/qtscripteditorplugin.cpp b/src/plugins/qtscripteditor/qtscripteditorplugin.cpp new file mode 100644 index 00000000000..7216a77f350 --- /dev/null +++ b/src/plugins/qtscripteditor/qtscripteditorplugin.cpp @@ -0,0 +1,145 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qtscripteditorplugin.h" +#include "qtscripteditorconstants.h" +#include "qtscripteditorfactory.h" +#include "qtscripteditor.h" +#include "qscripthighlighter.h" + +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <texteditor/fontsettings.h> +#include <texteditor/storagesettings.h> +#include <texteditor/texteditorconstants.h> +#include <texteditor/texteditorsettings.h> +#include <texteditor/textfilewizard.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QDebug> +#include <QtGui/QAction> + +using namespace QtScriptEditor::Internal; +using namespace QtScriptEditor::Constants; + +QtScriptEditorPlugin *QtScriptEditorPlugin::m_instance = 0; + +QtScriptEditorPlugin::QtScriptEditorPlugin() : + m_wizard(0), + m_editor(0) +{ + m_instance = this; +} + +QtScriptEditorPlugin::~QtScriptEditorPlugin() +{ + removeObject(m_editor); + removeObject(m_wizard); + m_instance = 0; +} + +bool QtScriptEditorPlugin::initialize(const QStringList & /*arguments*/, QString *error_message) +{ + typedef SharedTools::QScriptHighlighter QScriptHighlighter; + + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/qtscripteditor/QtScriptEditor.mimetypes.xml"), error_message)) + return false; + m_scriptcontext << core->uniqueIDManager()->uniqueIdentifier(QtScriptEditor::Constants::C_QTSCRIPTEDITOR); + m_context = m_scriptcontext; + m_context << core->uniqueIDManager()->uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR); + + registerActions(core); + + m_editor = new QtScriptEditorFactory(core, m_context, this); + addObject(m_editor); + + Core::BaseFileWizardParameters wizardParameters(Core::IWizard::FileWizard); + wizardParameters.setDescription(tr("Qt Script file")); + wizardParameters.setName(tr("Qt Script file")); + wizardParameters.setCategory(QLatin1String("Qt")); + wizardParameters.setTrCategory(tr("Qt")); + m_wizard = new TextEditor::TextFileWizard(QLatin1String(QtScriptEditor::Constants::C_QTSCRIPTEDITOR_MIMETYPE), + QLatin1String(QtScriptEditor::Constants::C_QTSCRIPTEDITOR), + QLatin1String("qtscript$"), + wizardParameters, core, this); + addObject(m_wizard); + + error_message->clear(); + return true; +} + +void QtScriptEditorPlugin::extensionsInitialized() +{ +} + +void QtScriptEditorPlugin::initializeEditor(QtScriptEditor::Internal::ScriptEditor *editor) +{ + Q_ASSERT(m_instance); + + TextEditor::TextEditorSettings *settings = TextEditor::TextEditorSettings::instance(); + connect(settings, SIGNAL(fontSettingsChanged(TextEditor::FontSettings)), + editor, SLOT(setFontSettings(TextEditor::FontSettings))); + connect(settings, SIGNAL(tabSettingsChanged(TextEditor::TabSettings)), + editor, SLOT(setTabSettings(TextEditor::TabSettings))); + connect(settings, SIGNAL(storageSettingsChanged(TextEditor::StorageSettings)), + editor, SLOT(setStorageSettings(TextEditor::StorageSettings))); + connect(settings, SIGNAL(displaySettingsChanged(TextEditor::DisplaySettings)), + editor, SLOT(setDisplaySettings(TextEditor::DisplaySettings))); + + // tab settings rely on font settings + editor->setFontSettings(settings->fontSettings()); + editor->setTabSettings(settings->tabSettings()); + editor->setStorageSettings(settings->storageSettings()); + editor->setDisplaySettings(settings->displaySettings()); +} + +void QtScriptEditorPlugin::registerActions(Core::ICore *core) +{ + Core::ActionManagerInterface *am = core->actionManager(); + Core::IActionContainer *mcontext = am->createMenu(QtScriptEditor::Constants::M_CONTEXT); + + QAction *action = new QAction(this); + action->setSeparator(true); + Core::ICommand *cmd = am->registerAction(action, QtScriptEditor::Constants::RUN_SEP, m_scriptcontext); + mcontext->addAction(cmd, Core::Constants::G_DEFAULT_THREE); + + action = new QAction(tr("Run"), this); + cmd = am->registerAction(action, QtScriptEditor::Constants::RUN, m_scriptcontext); + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+R"))); + mcontext->addAction(cmd, Core::Constants::G_DEFAULT_THREE); +} + +Q_EXPORT_PLUGIN(QtScriptEditorPlugin) diff --git a/src/plugins/qtscripteditor/qtscripteditorplugin.h b/src/plugins/qtscripteditor/qtscripteditorplugin.h new file mode 100644 index 00000000000..449f8263f15 --- /dev/null +++ b/src/plugins/qtscripteditor/qtscripteditorplugin.h @@ -0,0 +1,84 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QTSCRIPTEDITORPLUGIN_H +#define QTSCRIPTEDITORPLUGIN_H + +#include <extensionsystem/iplugin.h> + +namespace Core { +class ICore; +} + +namespace TextEditor { +class FontSettingsPage; +class TextFileWizard; +} + +namespace QtScriptEditor { +namespace Internal { + +class QtScriptWizard; +class QtScriptEditorFactory; +class ScriptEditor; + +class QtScriptEditorPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + QtScriptEditorPlugin(); + virtual ~QtScriptEditorPlugin(); + + //Plugin + bool initialize(const QStringList &arguments, QString *error_message = 0); + void extensionsInitialized(); + + static void initializeEditor(ScriptEditor *editor); + +private: + void registerActions(Core::ICore *core); + + static QtScriptEditorPlugin *m_instance; + + typedef QList<int> Context; + Context m_context; + Context m_scriptcontext; + + TextEditor::TextFileWizard *m_wizard; + QtScriptEditorFactory *m_editor; +}; + +} // namespace Internal +} // namespace QtScriptEditor + +#endif // QTSCRIPTEDITORPLUGIN_H diff --git a/src/plugins/qtscripteditor/qtscripthighlighter.cpp b/src/plugins/qtscripteditor/qtscripthighlighter.cpp new file mode 100644 index 00000000000..16d8f114728 --- /dev/null +++ b/src/plugins/qtscripteditor/qtscripthighlighter.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "qtscripthighlighter.h" + +namespace QtScriptEditor { +namespace Internal { + +QtScriptHighlighter::QtScriptHighlighter(QTextDocument *parent) : + SharedTools::QScriptHighlighter(parent) +{ + m_currentBlockParentheses.reserve(20); + m_braceDepth = 0; +} + +int QtScriptHighlighter::onBlockStart() +{ + m_currentBlockParentheses.clear(); + m_braceDepth = 0; + + int state = 0; + int previousState = previousBlockState(); + if (previousState != -1) { + state = previousState & 0xff; + m_braceDepth = previousState >> 8; + } + + return state; +} + +void QtScriptHighlighter::onOpeningParenthesis(QChar parenthesis, int pos) +{ + if (parenthesis == QLatin1Char('{')) + ++m_braceDepth; + m_currentBlockParentheses.push_back(Parenthesis(Parenthesis::Opened, parenthesis, pos)); +} + +void QtScriptHighlighter::onClosingParenthesis(QChar parenthesis, int pos) +{ + if (parenthesis == QLatin1Char('}')) + --m_braceDepth; + m_currentBlockParentheses.push_back(Parenthesis(Parenthesis::Closed, parenthesis, pos)); +} + +void QtScriptHighlighter::onBlockEnd(int state, int firstNonSpace) +{ + typedef TextEditor::TextBlockUserData TextEditorBlockData; + + setCurrentBlockState((m_braceDepth << 8) | state); + + // Set block data parentheses. Force creation of block data unless empty + TextEditorBlockData *blockData = 0; + + if (QTextBlockUserData *userData = currentBlockUserData()) + blockData = static_cast<TextEditorBlockData *>(userData); + + if (!blockData && !m_currentBlockParentheses.empty()) { + blockData = new TextEditorBlockData; + setCurrentBlockUserData(blockData); + } + if (blockData) { + blockData->setParentheses(m_currentBlockParentheses); + blockData->setClosingCollapseMode(TextEditor::TextBlockUserData::NoClosingCollapse); + blockData->setCollapseMode(TextEditor::TextBlockUserData::NoCollapse); + } + if (!m_currentBlockParentheses.isEmpty()) { + Q_ASSERT(blockData); + int collapse = Parenthesis::collapseAtPos(m_currentBlockParentheses); + if (collapse >= 0) { + if (collapse == firstNonSpace) + blockData->setCollapseMode(TextEditor::TextBlockUserData::CollapseThis); + else + blockData->setCollapseMode(TextEditor::TextBlockUserData::CollapseAfter); + } + if (Parenthesis::hasClosingCollapse(m_currentBlockParentheses)) + blockData->setClosingCollapseMode(TextEditor::TextBlockUserData::NoClosingCollapse); + } +} + +} // namespace Internal +} // namespace QtScriptEditor diff --git a/src/plugins/qtscripteditor/qtscripthighlighter.h b/src/plugins/qtscripteditor/qtscripthighlighter.h new file mode 100644 index 00000000000..2e9f23d144d --- /dev/null +++ b/src/plugins/qtscripteditor/qtscripthighlighter.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QTSCRIPTSYNTAXHIGHLIGHTER_H +#define QTSCRIPTSYNTAXHIGHLIGHTER_H + +#include "qscripthighlighter.h" +#include <texteditor/basetexteditor.h> + +namespace QtScriptEditor { +namespace Internal { + +// Highlighter for Scripts that stores +// the parentheses encountered in the block data +// for parentheses matching to work. + +class QtScriptHighlighter : public SharedTools::QScriptHighlighter +{ + Q_OBJECT +public: + QtScriptHighlighter(QTextDocument *parent = 0); + +private: + virtual int onBlockStart(); + virtual void onOpeningParenthesis(QChar parenthesis, int pos); + virtual void onClosingParenthesis(QChar parenthesis, int pos); + virtual void onBlockEnd(int state, int firstNonSpace); + + typedef TextEditor::Parenthesis Parenthesis; + typedef TextEditor::Parentheses Parentheses; + Parentheses m_currentBlockParentheses; + int m_braceDepth; +}; + +} // namespace Internal +} // namespace QtScriptEditor + +#endif // QTSCRIPTSYNTAXHIGHLIGHTER_H diff --git a/src/plugins/quickopen/QuickOpen.pluginspec b/src/plugins/quickopen/QuickOpen.pluginspec new file mode 100644 index 00000000000..3c92b9cbf8b --- /dev/null +++ b/src/plugins/quickopen/QuickOpen.pluginspec @@ -0,0 +1,10 @@ +<plugin name="QuickOpen" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Provides the QuickOpen widget and the hooks for QuickOpen filter implementations.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/quickopen/basefilefilter.cpp b/src/plugins/quickopen/basefilefilter.cpp new file mode 100644 index 00000000000..7fc4814cf4a --- /dev/null +++ b/src/plugins/quickopen/basefilefilter.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "basefilefilter.h" + +#include <coreplugin/editormanager/editormanager.h> + +#include <QtCore/QDir> + +using namespace Core; +using namespace QuickOpen; + +BaseFileFilter::BaseFileFilter(ICore *core) + : m_core(core), + m_files(QStringList()), + m_fileNames(QStringList()), + m_forceNewSearchList(false) +{ +} + +QList<FilterEntry> BaseFileFilter::matchesFor(const QString &origEntry) +{ + QList<FilterEntry> value; + QString entry = trimWildcards(origEntry); + QStringMatcher matcher(entry, Qt::CaseInsensitive); + const QRegExp regexp("*"+entry+"*", Qt::CaseInsensitive, QRegExp::Wildcard); + if (!regexp.isValid()) + return value; + bool hasWildcard = (entry.contains('*') || entry.contains('?')); + QStringList searchListPaths; + QStringList searchListNames; + if (!m_previousEntry.isEmpty() && !m_forceNewSearchList && entry.contains(m_previousEntry)) { + searchListPaths = m_previousResultPaths; + searchListNames = m_previousResultNames; + } else { + searchListPaths = m_files; + searchListNames = m_fileNames; + } + m_previousResultPaths.clear(); + m_previousResultNames.clear(); + m_forceNewSearchList = false; + m_previousEntry = entry; + QStringListIterator paths(searchListPaths); + QStringListIterator names(searchListNames); + while (paths.hasNext() && names.hasNext()) { + QString path = paths.next(); + QString name = names.next(); + if ((hasWildcard && regexp.exactMatch(name)) + || (!hasWildcard && matcher.indexIn(name) != -1)) { + QFileInfo fi(path); + FilterEntry entry(this, fi.fileName(), path); + entry.extraInfo = QDir::toNativeSeparators(fi.path()); + entry.resolveFileIcon = true; + value.append(entry); + m_previousResultPaths.append(path); + m_previousResultNames.append(name); + } + } + return value; +} + +void BaseFileFilter::accept(QuickOpen::FilterEntry selection) const +{ + m_core->editorManager()->openEditor(selection.internalData.toString()); + m_core->editorManager()->ensureEditorManagerVisible(); +} + +void BaseFileFilter::generateFileNames() +{ + m_fileNames.clear(); + foreach (const QString &fileName, m_files) { + QFileInfo fi(fileName); + m_fileNames.append(fi.fileName()); + } + m_forceNewSearchList = true; +} diff --git a/src/plugins/quickopen/basefilefilter.h b/src/plugins/quickopen/basefilefilter.h new file mode 100644 index 00000000000..e0f780dc146 --- /dev/null +++ b/src/plugins/quickopen/basefilefilter.h @@ -0,0 +1,71 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BASEFILEFILTER_H +#define BASEFILEFILTER_H + +#include "quickopen_global.h" +#include "iquickopenfilter.h" + +#include <coreplugin/icore.h> + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QByteArray> +#include <QtGui/QWidget> + +namespace QuickOpen { + +class QUICKOPEN_EXPORT BaseFileFilter : public QuickOpen::IQuickOpenFilter +{ + Q_OBJECT + +public: + BaseFileFilter(Core::ICore *core); + QList<QuickOpen::FilterEntry> matchesFor(const QString &entry); + void accept(QuickOpen::FilterEntry selection) const; + +protected: + void generateFileNames(); + + Core::ICore *m_core; + QStringList m_files; + QStringList m_fileNames; + QStringList m_previousResultPaths; + QStringList m_previousResultNames; + bool m_forceNewSearchList; + QString m_previousEntry; +}; + +} // namespace QuickOpen + +#endif // BASEFILEFILTER_H diff --git a/src/plugins/quickopen/directoryfilter.cpp b/src/plugins/quickopen/directoryfilter.cpp new file mode 100644 index 00000000000..0b1564d3d6e --- /dev/null +++ b/src/plugins/quickopen/directoryfilter.cpp @@ -0,0 +1,258 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "directoryfilter.h" + +#include <QtCore/QDir> +#include <QtCore/QStack> +#include <QtGui/QDirModel> +#include <QtGui/QCompleter> +#include <QtGui/QFileDialog> +#include <QtGui/QMessageBox> + +using namespace Core; +using namespace QuickOpen; +using namespace QuickOpen::Internal; + +DirectoryFilter::DirectoryFilter(ICore *core) + : BaseFileFilter(core), + m_name(tr("Generic Directory Filter")), + m_directories(QStringList()), + m_filters(QStringList() << "*.h" << "*.cpp" << "*.ui" << "*.qrc") +{ + setIncludedByDefault(true); +} + +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 << defaultActiveState(); + out << m_files; + return value; +} + +bool DirectoryFilter::restoreState(const QByteArray &state) +{ + QMutexLocker locker(&m_lock); + + QStringList dirEntries; + QString shortcut; + bool defaultFilter; + + QDataStream in(state); + in >> m_name; + in >> dirEntries; + in >> m_filters; + in >> shortcut; + in >> defaultFilter; + in >> m_files; + + setShortcutString(shortcut); + setIncludedByDefault(defaultFilter); + + // ### TODO throw that out again: + // clear the list of directories of empty entries (which might at least happen at a format change) + m_directories.clear(); + foreach (const QString &dir, dirEntries) { + if (dir.isEmpty()) + continue; + m_directories.append(dir); + } + + 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(tr(","))); + m_ui.shortcutEdit->setText(shortcutString()); + m_ui.defaultFlag->setChecked(!defaultActiveState()); + 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(tr(",")); + 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("Choose a directory to add")); + 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("Choose a directory to add"), + 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) +{ + const int MAX = 360; + future.setProgressRange(0, MAX); + if (m_directories.count() < 1) { + QMutexLocker locker(&m_lock); + m_files.clear(); + generateFileNames(); + future.setProgressValueAndText(MAX, tr("%1 filter update: 0 files").arg(m_name)); + return; + } + int progress = 0; + int MAX_PER = MAX; + QStringList files; + QStack<QDir> dirs; + QStack<int> progressValues; + QStack<bool> processedValues; + { // initialize + QMutexLocker locker(&m_lock); + MAX_PER = MAX/m_directories.count(); + foreach (const QString &directoryEntry, m_directories) { + if (!directoryEntry.isEmpty()) { + dirs.push(QDir(directoryEntry)); + progressValues.push(MAX_PER); + processedValues.push(false); + } + } + } + while (!dirs.isEmpty() && !future.isCanceled()) { + if (future.isProgressUpdateNeeded()) { + future.setProgressValueAndText(progress, tr("%1 filter update: %2 files").arg(m_name).arg(files.size())); + } + QDir dir = dirs.pop(); + int dirProgressMax = progressValues.pop(); + bool processed = processedValues.pop(); + if (dir.exists()) { + QStringList subDirs; + if (!processed) { + subDirs = dir.entryList(QDir::Dirs|QDir::Hidden|QDir::NoDotAndDotDot, + QDir::Name|QDir::IgnoreCase|QDir::LocaleAware); + } + if (subDirs.isEmpty()) { + QStringList fileEntries = dir.entryList(m_filters, + QDir::Files|QDir::Hidden, + QDir::Name|QDir::IgnoreCase|QDir::LocaleAware); + foreach (const QString &file, fileEntries) + files.append(dir.path()+"/"+file); + progress += dirProgressMax; + } else { + int subProgress = dirProgressMax/(subDirs.size()+1); + int selfProgress = subProgress + dirProgressMax%(subDirs.size()+1); + dirs.push(dir); + progressValues.push(selfProgress); + processedValues.push(true); + foreach (const QString &directory, subDirs) { + dirs.push(QDir(dir.path()+"/"+directory)); + progressValues.push(subProgress); + processedValues.push(false); + } + } + } else { + progress += dirProgressMax; + } + } + + if (!future.isCanceled()) { + QMutexLocker locker(&m_lock); + m_files = files; + generateFileNames(); + future.setProgressValue(MAX); + } else { + future.setProgressValueAndText(progress, tr("%1 filter update: canceled").arg(m_name)); + } +} diff --git a/src/plugins/quickopen/directoryfilter.h b/src/plugins/quickopen/directoryfilter.h new file mode 100644 index 00000000000..e228bde4f0d --- /dev/null +++ b/src/plugins/quickopen/directoryfilter.h @@ -0,0 +1,87 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DIRECTORYFILTER_H +#define DIRECTORYFILTER_H + +#include "ui_directoryfilter.h" +#include "basefilefilter.h" + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QByteArray> +#include <QtCore/QFutureInterface> +#include <QtCore/QMutex> +#include <QtGui/QWidget> +#include <QtGui/QDialog> + +#include <coreplugin/icore.h> +#include <qtconcurrent/QtConcurrentTools> + +namespace QuickOpen { +namespace Internal { + +class DirectoryFilter : public BaseFileFilter +{ + Q_OBJECT + +public: + DirectoryFilter(Core::ICore *core); + QString trName() const { return m_name; } + QString name() const { return m_name; } + QuickOpen::IQuickOpenFilter::Priority priority() const { return QuickOpen::IQuickOpenFilter::Medium; } + 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 QuickOpen + +#endif // DIRECTORYFILTER_H diff --git a/src/plugins/quickopen/directoryfilter.ui b/src/plugins/quickopen/directoryfilter.ui new file mode 100644 index 00000000000..41fa3d5bdeb --- /dev/null +++ b/src/plugins/quickopen/directoryfilter.ui @@ -0,0 +1,197 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QuickOpen::Internal::DirectoryFilterOptions</class> + <widget class="QDialog" name="QuickOpen::Internal::DirectoryFilterOptions"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>393</width> + <height>271</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </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="Expanding" 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 QuickOpen 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="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>2</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <property name="text"> + <string>Limit to prefix</string> + </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> + </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"/> + </item> + </layout> + </item> + <item row="1" column="0"> + <spacer> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>369</width> + <height>31</height> + </size> + </property> + </spacer> + </item> + <item row="2" 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>QuickOpen::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>QuickOpen::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/quickopen/directoryparser.cpp b/src/plugins/quickopen/directoryparser.cpp new file mode 100644 index 00000000000..09b8c220163 --- /dev/null +++ b/src/plugins/quickopen/directoryparser.cpp @@ -0,0 +1,104 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "directoryparser.h" +#include "quickopenplugin.h" + +using namespace QuickOpen::Internal; + +DirectoryParser::DirectoryParser(QObject *parent) + : QThread(parent) +{ +} + +DirectoryParser::~DirectoryParser() +{ + if (isRunning()) + terminate(); +} + +void DirectoryParser::parse(Filter filter) +{ + m_dirs = filter.directories(); + m_filters = filter.acceptedFileExtensions().split(';'); + m_blackList.clear(); + foreach (QString s, filter.skipDirectories()) { + if (!s.trimmed().isEmpty() && !m_blackList.contains(s)) + m_blackList.insert(s); + } + if (!isRunning()) + start(QThread::NormalPriority); +} + +void DirectoryParser::setDirectoryNameBlackList(const QStringList &lst) +{ + m_blackList.clear(); + foreach (QString s, lst) { + if (!m_blackList.contains(s)) + m_blackList.insert(s); + } +} + +QSet<QString> DirectoryParser::files() const +{ + return m_files; +} + +void DirectoryParser::run() +{ + m_files.clear(); + m_runFiles.clear(); + foreach (QString s, m_dirs) { + if (s.isEmpty()) + continue; + QDir dir(s); + if (dir.exists()) { + m_runFilters = m_filters; + m_runBlackList = m_blackList; + collectFiles(dir); + } + } + m_files = m_runFiles; + emit directoriesParsed(); +} + +void DirectoryParser::collectFiles(const QDir &dir) +{ + QString dirName = dir.absolutePath() + QLatin1String("/"); + foreach (QString f, dir.entryList(m_runFilters, QDir::Files)) { + m_runFiles.insert(dirName + f); + } + foreach (QString d, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { + if (!m_runBlackList.contains(d)) + collectFiles(dir.absolutePath() + QDir::separator() + d); + } +} diff --git a/src/plugins/quickopen/directoryparser.h b/src/plugins/quickopen/directoryparser.h new file mode 100644 index 00000000000..af317a0fe46 --- /dev/null +++ b/src/plugins/quickopen/directoryparser.h @@ -0,0 +1,77 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DIRECTORYPARSER_H +#define DIRECTORYPARSER_H + +#include <QtCore/QThread> +#include <QtCore/QDir> +#include <QtCore/QSet> + +namespace QuickOpen { +namespace Internal { + +class Filter; + +class DirectoryParser : public QThread +{ + Q_OBJECT + +public: + DirectoryParser(QObject *parent); + ~DirectoryParser(); + void parse(Filter filter); + + void setDirectoryNameBlackList(const QStringList &lst); + QSet<QString> files() const; + +signals: + void directoriesParsed(); + +private: + void run(); + void collectFiles(const QDir &dir); + + QStringList m_dirs; + QSet<QString> m_files; + + QSet<QString> m_runFiles; + QStringList m_filters; + QStringList m_runFilters; + QSet<QString> m_blackList; + QSet<QString> m_runBlackList; +}; + +} //namespace Internal +} //namespace QuickOpen + +#endif // DIRECTORYPARSER_H diff --git a/src/plugins/quickopen/filesystemfilter.cpp b/src/plugins/quickopen/filesystemfilter.cpp new file mode 100644 index 00000000000..17fcd060ad2 --- /dev/null +++ b/src/plugins/quickopen/filesystemfilter.cpp @@ -0,0 +1,161 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "filesystemfilter.h" +#include "quickopentoolwindow.h" + +#include <QtCore/QDir> + +using namespace Core; +using namespace QuickOpen; +using namespace QuickOpen::Internal; + +FileSystemFilter::FileSystemFilter(EditorManager *editorManager, QuickOpenToolWindow *toolWindow) + : m_editorManager(editorManager), m_toolWindow(toolWindow), m_includeHidden(true) +{ + setShortcutString("f"); + setIncludedByDefault(false); +} + +QList<FilterEntry> FileSystemFilter::matchesFor(const QString &entry) +{ + QList<FilterEntry> value; + QFileInfo entryInfo(entry); + QString name = entryInfo.fileName(); + QString directory = entryInfo.path(); + QString filePath = entryInfo.filePath(); + bool isDrive = false; + foreach (const QFileInfo &drive, QDir::drives()) { + if (filePath.startsWith(drive.path())) { + isDrive = true; + break; + } + } + if (!isDrive) { + if (filePath.startsWith("~/")) { + directory.replace(0, 1, QDir::homePath()); + } else { + IEditor *editor = m_editorManager->currentEditor(); + if (editor && !editor->file()->fileName().isEmpty()) { + QFileInfo info(editor->file()->fileName()); + directory.prepend(info.absolutePath()+"/"); + } + } + } + QDir dirInfo(directory); + QDir::Filters dirFilter = QDir::Dirs|QDir::Drives; + QDir::Filters fileFilter = QDir::Files; + if (m_includeHidden) { + dirFilter |= QDir::Hidden; + fileFilter |= QDir::Hidden; + } + 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 (dir != "." && (name.isEmpty() || dir.startsWith(name, Qt::CaseInsensitive))) { + FilterEntry entry(this, dir, directory + "/" + dir); + entry.resolveFileIcon = true; + value.append(entry); + } + } + foreach (const QString &file, files) { + if (name.isEmpty() || file.startsWith(name, Qt::CaseInsensitive)) { + const QString fullPath = directory + "/" + file; + FilterEntry entry(this, file, fullPath); + entry.resolveFileIcon = true; + value.append(entry); + } + } + return value; +} + +void FileSystemFilter::accept(FilterEntry selection) const +{ + QFileInfo info(selection.internalData.toString()); + if (info.isDir()) { + m_toolWindow->show(shortcutString()+" " + +QDir::toNativeSeparators(info.absoluteFilePath()+"/")); + return; + } + m_editorManager->openEditor(selection.internalData.toString()); + m_editorManager->ensureEditorManagerVisible(); +} + +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(!defaultActiveState()); + 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 << defaultActiveState(); + 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/quickopen/filesystemfilter.h b/src/plugins/quickopen/filesystemfilter.h new file mode 100644 index 00000000000..3ea20b60592 --- /dev/null +++ b/src/plugins/quickopen/filesystemfilter.h @@ -0,0 +1,81 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FILESYSTEMFILTER_H +#define FILESYSTEMFILTER_H + +#include "iquickopenfilter.h" +#include "ui_filesystemfilter.h" + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QByteArray> +#include <QtCore/QFutureInterface> + +#include <coreplugin/icore.h> +#include <coreplugin/editormanager/editormanager.h> + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +namespace QuickOpen { +namespace Internal { + +class QuickOpenToolWindow; + +class FileSystemFilter : public QuickOpen::IQuickOpenFilter +{ + Q_OBJECT + +public: + FileSystemFilter(Core::EditorManager *editorManager, QuickOpenToolWindow *toolWindow); + QString trName() const { return tr("File in file system"); } + QString name() const { return "File in file system"; } + QuickOpen::IQuickOpenFilter::Priority priority() const { return QuickOpen::IQuickOpenFilter::Medium; } + QList<QuickOpen::FilterEntry> matchesFor(const QString &entry); + void accept(QuickOpen::FilterEntry selection) const; + QByteArray saveState() const; + bool restoreState(const QByteArray &state); + bool openConfigDialog(QWidget *parent, bool &needsRefresh); + void refresh(QFutureInterface<void> &) {} + +private: + Core::EditorManager *m_editorManager; + QuickOpenToolWindow *m_toolWindow; + bool m_includeHidden; +}; + +} // namespace Internal +} // namespace QuickOpen + +#endif // FILESYSTEMFILTER_H diff --git a/src/plugins/quickopen/filesystemfilter.ui b/src/plugins/quickopen/filesystemfilter.ui new file mode 100644 index 00000000000..c9a3ed3a51b --- /dev/null +++ b/src/plugins/quickopen/filesystemfilter.ui @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QuickOpen::Internal::FileSystemFilterOptions</class> + <widget class="QDialog" name="QuickOpen::Internal::FileSystemFilterOptions"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>327</width> + <height>131</height> + </rect> + </property> + <property name="windowTitle"> + <string>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>QuickOpen::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>QuickOpen::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/quickopen/images/quickopen.png b/src/plugins/quickopen/images/quickopen.png Binary files differnew file mode 100644 index 00000000000..000ee1c0185 --- /dev/null +++ b/src/plugins/quickopen/images/quickopen.png diff --git a/src/plugins/quickopen/images/reload.png b/src/plugins/quickopen/images/reload.png Binary files differnew file mode 100644 index 00000000000..b5afefb32bc --- /dev/null +++ b/src/plugins/quickopen/images/reload.png diff --git a/src/plugins/quickopen/iquickopenfilter.cpp b/src/plugins/quickopen/iquickopenfilter.cpp new file mode 100644 index 00000000000..c0af2f26a12 --- /dev/null +++ b/src/plugins/quickopen/iquickopenfilter.cpp @@ -0,0 +1,130 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "iquickopenfilter.h" + +#include <QtGui/QBoxLayout> +#include <QtGui/QCheckBox> +#include <QtGui/QDialog> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QLabel> +#include <QtGui/QLineEdit> + +using namespace QuickOpen; + +IQuickOpenFilter::IQuickOpenFilter(QObject *parent): + QObject(parent) +{ +} + +QString IQuickOpenFilter::shortcutString() const +{ + return m_shortcut; +} + +void IQuickOpenFilter::setShortcutString(const QString &shortcut) +{ + m_shortcut = shortcut; +} + +QByteArray IQuickOpenFilter::saveState() const +{ + QByteArray value; + QDataStream out(&value, QIODevice::WriteOnly); + out << shortcutString(); + out << defaultActiveState(); + return value; +} + +bool IQuickOpenFilter::restoreState(const QByteArray &state) +{ + QString shortcut; + bool defaultFilter; + + QDataStream in(state); + in >> shortcut; + in >> defaultFilter; + + setShortcutString(shortcut); + setIncludedByDefault(defaultFilter); + return true; +} + +bool IQuickOpenFilter::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(!defaultActiveState()); + + 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; +} + +bool IQuickOpenFilter::isConfigurable() const +{ + return true; +} + +bool IQuickOpenFilter::defaultActiveState() const +{ + return m_default; +} + +void IQuickOpenFilter::setIncludedByDefault(bool includedByDefault) +{ + m_default = includedByDefault; +} diff --git a/src/plugins/quickopen/iquickopenfilter.h b/src/plugins/quickopen/iquickopenfilter.h new file mode 100644 index 00000000000..9184db49153 --- /dev/null +++ b/src/plugins/quickopen/iquickopenfilter.h @@ -0,0 +1,152 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef IQUICKOPENFILTER_H +#define IQUICKOPENFILTER_H + +#include "quickopen_global.h" + +#include <QtCore/QDir> +#include <QtCore/QVariant> +#include <QtCore/QFutureInterface> +#include <QtGui/QIcon> + +namespace QuickOpen { + +class IQuickOpenFilter; + +struct FilterEntry +{ + FilterEntry() {} + FilterEntry(IQuickOpenFilter *fromFilter, const QString &name, const QVariant &data, + const QIcon &icon = QIcon()) + : filter(fromFilter) + , displayName(name) + , internalData(data) + , displayIcon(icon) + , resolveFileIcon(false) + {} + + bool operator==(const FilterEntry &other) const { + if (internalData.canConvert(QVariant::String)) + return (internalData.toString() == other.internalData.toString()); + return internalData.constData() == other.internalData.constData(); + } + + /* backpointer to creating filter */ + IQuickOpenFilter *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; + /* internal data is interpreted as file name and icon is retrieved from the file system if true */ + bool resolveFileIcon; +}; + +class QUICKOPEN_EXPORT IQuickOpenFilter : public QObject +{ + Q_OBJECT + +public: + enum Priority {High = 0, Medium = 1, Low = 2}; + + IQuickOpenFilter(QObject *parent = 0); + virtual ~IQuickOpenFilter() {} + + /* visible name */ + virtual QString trName() const = 0; + + /* internal name */ + virtual QString name() const = 0; + + /* selection list order in case of multiple active filters (high goes on top) */ + virtual Priority priority() const = 0; + + /* string to type to use this filter exclusively */ + virtual QString shortcutString() const; + void setShortcutString(const QString &shortcut); + + /* list of matches for the given user entry */ + virtual QList<FilterEntry> matchesFor(const QString &entry) = 0; + + /* user has selected the given entry that belongs to this filter */ + virtual void accept(FilterEntry 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. */ + virtual bool isConfigurable() const; + + /* is this filter used also when the shortcutString is not used? */ + virtual bool defaultActiveState() const; + void setIncludedByDefault(bool includedByDefault); + + static QString trimWildcards(const QString &str) { + if (str.isEmpty()) + return str; + int first = 0, last = str.size()-1; + while (first < str.size() && (str.at(first) == '*' || str.at(first) == '?')) + ++first; + while (last >= 0 && (str.at(last) == '*' || str.at(last) == '?')) + --last; + if (first > last) + return QString(); + return str.mid(first, last-first+1); + } + +private: + QString m_shortcut; + bool m_default; +}; + +} // namespace QuickOpen + +#endif // IQUICKOPENFILTER_H diff --git a/src/plugins/quickopen/opendocumentsfilter.cpp b/src/plugins/quickopen/opendocumentsfilter.cpp new file mode 100644 index 00000000000..9b9843002ce --- /dev/null +++ b/src/plugins/quickopen/opendocumentsfilter.cpp @@ -0,0 +1,104 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "opendocumentsfilter.h" + +Q_DECLARE_METATYPE(Core::IEditor*); + +using namespace Core; +using namespace QuickOpen; +using namespace QuickOpen::Internal; + +OpenDocumentsFilter::OpenDocumentsFilter(EditorManager *editorManager) : + m_editorManager(editorManager) +{ + connect(m_editorManager, SIGNAL(editorOpened(Core::IEditor*)), + this, SLOT(refreshInternally())); + connect(m_editorManager, SIGNAL(editorsClosed(QList<Core::IEditor*>)), + this, SLOT(refreshInternally())); + setShortcutString("o"); + setIncludedByDefault(true); +} + +QList<FilterEntry> OpenDocumentsFilter::matchesFor(const QString &entry) +{ + QList<FilterEntry> value; + const QChar asterisk = QLatin1Char('*'); + QString pattern = QString(asterisk); + pattern += entry; + pattern += asterisk; + const QRegExp regexp(pattern, Qt::CaseInsensitive, QRegExp::Wildcard); + if (!regexp.isValid()) + return value; + foreach (IEditor *editor, m_editors) { + QString fileName = editor->file()->fileName(); + if (regexp.exactMatch(editor->displayName())) { + QString visibleName; + QVariant data; + if (fileName.isEmpty()) { + value.append(FilterEntry(this, editor->displayName(), qVariantFromValue(editor))); + } else { + QFileInfo fi(fileName); + FilterEntry entry(this, fi.fileName(), fileName); + entry.extraInfo = QDir::toNativeSeparators(fi.path()); + entry.resolveFileIcon = true; + value.append(entry); + } + } + } + return value; +} + +void OpenDocumentsFilter::refreshInternally() +{ + m_editors = m_editorManager->openedEditors(); +} + +void OpenDocumentsFilter::refresh(QFutureInterface<void> &future) +{ + Q_UNUSED(future); + // invokeAsyncronouslyOnGuiThread + connect(this, SIGNAL(invokeRefresh()), this, SLOT(refreshInternally())); + emit invokeRefresh(); + disconnect(this, SIGNAL(invokeRefresh()), this, SLOT(refreshInternally())); +} + +void OpenDocumentsFilter::accept(FilterEntry selection) const +{ + IEditor *editor = selection.internalData.value<IEditor *>(); + if (editor) { + m_editorManager->setCurrentEditor(editor); + return; + } + m_editorManager->openEditor(selection.internalData.toString()); + m_editorManager->ensureEditorManagerVisible(); +} diff --git a/src/plugins/quickopen/opendocumentsfilter.h b/src/plugins/quickopen/opendocumentsfilter.h new file mode 100644 index 00000000000..5854d1f4868 --- /dev/null +++ b/src/plugins/quickopen/opendocumentsfilter.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef OPENDOCUMENTSFILTER_H +#define OPENDOCUMENTSFILTER_H + +#include "iquickopenfilter.h" + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QByteArray> +#include <QtCore/QFutureInterface> +#include <QtGui/QWidget> + +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/ieditor.h> + +namespace QuickOpen { +namespace Internal { + +class OpenDocumentsFilter : public QuickOpen::IQuickOpenFilter +{ + Q_OBJECT + +public: + OpenDocumentsFilter(Core::EditorManager *editorManager); + QString trName() const { return tr("Open document"); } + QString name() const { return "Open document"; } + QuickOpen::IQuickOpenFilter::Priority priority() const { return QuickOpen::IQuickOpenFilter::Medium; } + QList<QuickOpen::FilterEntry> matchesFor(const QString &entry); + void accept(QuickOpen::FilterEntry selection) const; + void refresh(QFutureInterface<void> &future); + +public slots: + void refreshInternally(); +signals: + void invokeRefresh(); +private: + Core::EditorManager *m_editorManager; + + QList<Core::IEditor *> m_editors; +}; + +} // namespace Internal +} // namespace QuickOpen + +#endif // OPENDOCUMENTSFILTER_H diff --git a/src/plugins/quickopen/quickopen.pri b/src/plugins/quickopen/quickopen.pri new file mode 100644 index 00000000000..a5645e529ea --- /dev/null +++ b/src/plugins/quickopen/quickopen.pri @@ -0,0 +1,3 @@ +include(quickopen_dependencies.pri) + +LIBS *= -l$$qtLibraryTarget(QuickOpen) diff --git a/src/plugins/quickopen/quickopen.pro b/src/plugins/quickopen/quickopen.pro new file mode 100644 index 00000000000..dbacef6df53 --- /dev/null +++ b/src/plugins/quickopen/quickopen.pro @@ -0,0 +1,31 @@ +TEMPLATE = lib +TARGET = QuickOpen +DEFINES += QUICKOPEN_LIBRARY +include(../../qworkbenchplugin.pri) +include(quickopen_dependencies.pri) +HEADERS += quickopenplugin.h \ + quickopentoolwindow.h \ + quickopenfiltersfilter.h \ + settingspage.h \ + iquickopenfilter.h \ + opendocumentsfilter.h \ + filesystemfilter.h \ + quickopenconstants.h \ + directoryfilter.h \ + quickopenmanager.h \ + basefilefilter.h \ + quickopen_global.h +SOURCES += quickopenplugin.cpp \ + quickopentoolwindow.cpp \ + quickopenfiltersfilter.cpp \ + opendocumentsfilter.cpp \ + filesystemfilter.cpp \ + settingspage.cpp \ + directoryfilter.cpp \ + quickopenmanager.cpp \ + basefilefilter.cpp \ + iquickopenfilter.cpp +FORMS += settingspage.ui \ + filesystemfilter.ui \ + directoryfilter.ui +RESOURCES += quickopen.qrc diff --git a/src/plugins/quickopen/quickopen.qrc b/src/plugins/quickopen/quickopen.qrc new file mode 100644 index 00000000000..0ab0ff14fc2 --- /dev/null +++ b/src/plugins/quickopen/quickopen.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/quickopen" > + <file>images/quickopen.png</file> + <file>images/reload.png</file> + </qresource> +</RCC> diff --git a/src/plugins/quickopen/quickopen_dependencies.pri b/src/plugins/quickopen/quickopen_dependencies.pri new file mode 100644 index 00000000000..96d71e68c8d --- /dev/null +++ b/src/plugins/quickopen/quickopen_dependencies.pri @@ -0,0 +1,2 @@ +include(../../libs/qtconcurrent/qtconcurrent.pri) +include(../../plugins/coreplugin/coreplugin.pri) diff --git a/src/plugins/quickopen/quickopen_global.h b/src/plugins/quickopen/quickopen_global.h new file mode 100644 index 00000000000..a16e134c8e4 --- /dev/null +++ b/src/plugins/quickopen/quickopen_global.h @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/**************************************************************************** +** +** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef QUICKOPEN_GLOBAL_H +#define QUICKOPEN_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(QUICKOPEN_LIBRARY) +# define QUICKOPEN_EXPORT Q_DECL_EXPORT +#else +# define QUICKOPEN_EXPORT Q_DECL_IMPORT +#endif + +#endif // QUICKOPEN_GLOBAL_H diff --git a/src/plugins/quickopen/quickopenconstants.h b/src/plugins/quickopen/quickopenconstants.h new file mode 100644 index 00000000000..174d14475f8 --- /dev/null +++ b/src/plugins/quickopen/quickopenconstants.h @@ -0,0 +1,44 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QUICKOPENCONSTANTS_H +#define QUICKOPENCONSTANTS_H + +namespace QuickOpen { +namespace Constants { + const char * const FILTER_OPTIONS_PAGE = "Filters"; + const char * const QUICKOPEN_CATEGORY = "QuickOpen"; + const char * const TASK_INDEX = "QuickOpen.Task.Index"; +} +} + +#endif // QUICKOPENCONSTANTS_H diff --git a/src/plugins/quickopen/quickopenfiltersfilter.cpp b/src/plugins/quickopen/quickopenfiltersfilter.cpp new file mode 100644 index 00000000000..18cabe45bad --- /dev/null +++ b/src/plugins/quickopen/quickopenfiltersfilter.cpp @@ -0,0 +1,104 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "quickopenfiltersfilter.h" +#include "quickopenplugin.h" +#include "quickopentoolwindow.h" + +#include <coreplugin/coreconstants.h> + +using namespace QuickOpen; +using namespace QuickOpen::Internal; + +Q_DECLARE_METATYPE(IQuickOpenFilter*); + +QuickOpenFiltersFilter::QuickOpenFiltersFilter(QuickOpenPlugin *plugin, + QuickOpenToolWindow *toolWindow): + m_plugin(plugin), + m_toolWindow(toolWindow), + m_icon(QIcon(Core::Constants::ICON_NEXT)) +{ + setIncludedByDefault(true); + setShortcutString(QString()); +} + +QString QuickOpenFiltersFilter::trName() const +{ + return tr("Available filters"); +} + +QString QuickOpenFiltersFilter::name() const +{ + return QLatin1String("FiltersFilter"); +} + +IQuickOpenFilter::Priority QuickOpenFiltersFilter::priority() const +{ + return High; +} + +QList<FilterEntry> QuickOpenFiltersFilter::matchesFor(const QString &entry) +{ + QList<FilterEntry> entries; + if (entry.isEmpty()) { + foreach (IQuickOpenFilter* filter, m_plugin->filter()) { + if (!filter->shortcutString().isEmpty()) { + FilterEntry entry(this, + filter->shortcutString(), + QVariant::fromValue(filter), + m_icon); + entry.extraInfo = filter->trName(); + entries.append(entry); + } + } + } + return entries; +} + +void QuickOpenFiltersFilter::accept(FilterEntry selection) const +{ + IQuickOpenFilter *filter = selection.internalData.value<IQuickOpenFilter*>(); + if (filter) + m_toolWindow->show(filter->shortcutString() + " ", + filter->shortcutString().length() + 1); +} + +void QuickOpenFiltersFilter::refresh(QFutureInterface<void> &future) +{ + Q_UNUSED(future); + // Nothing to refresh +} + +bool QuickOpenFiltersFilter::isConfigurable() const +{ + return false; +} diff --git a/src/plugins/quickopen/quickopenfiltersfilter.h b/src/plugins/quickopen/quickopenfiltersfilter.h new file mode 100644 index 00000000000..d501d8db157 --- /dev/null +++ b/src/plugins/quickopen/quickopenfiltersfilter.h @@ -0,0 +1,74 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QUICKOPENFILTERSFILTER_H +#define QUICKOPENFILTERSFILTER_H + +#include "iquickopenfilter.h" + +#include <QtGui/QIcon> + +namespace QuickOpen { +namespace Internal { + +class QuickOpenPlugin; +class QuickOpenToolWindow; + +/*! + This filter provides the user with the list of available QuickOpen filters. + The list is only shown when nothing has been typed yet. + */ +class QuickOpenFiltersFilter : public IQuickOpenFilter +{ +public: + QuickOpenFiltersFilter(QuickOpenPlugin *plugin, + QuickOpenToolWindow *toolWindow); + + // IQuickOpenFilter + QString trName() const; + QString name() const; + Priority priority() const; + QList<FilterEntry> matchesFor(const QString &entry); + void accept(FilterEntry selection) const; + void refresh(QFutureInterface<void> &future); + bool isConfigurable() const; + +private: + QuickOpenPlugin *m_plugin; + QuickOpenToolWindow *m_toolWindow; + QIcon m_icon; +}; + +} // namespace Internal +} // namespace QuickOpen + +#endif // QUICKOPENFILTERSFILTER_H diff --git a/src/plugins/quickopen/quickopenmanager.cpp b/src/plugins/quickopen/quickopenmanager.cpp new file mode 100644 index 00000000000..91a86195820 --- /dev/null +++ b/src/plugins/quickopen/quickopenmanager.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "quickopenmanager.h" +#include "quickopentoolwindow.h" + +#include <extensionsystem/pluginmanager.h> + +using namespace QuickOpen; +using namespace QuickOpen::Internal; + +QuickOpenManager *QuickOpenManager::m_instance = 0; + +QuickOpenManager::QuickOpenManager(QuickOpenToolWindow *toolWindow) + : QObject(toolWindow), + m_toolWindow(toolWindow) +{ + m_instance = this; +} + +QuickOpenManager::~QuickOpenManager() +{ + ExtensionSystem::PluginManager::instance()->removeObject(this); + m_instance = 0; +} + +void QuickOpenManager::show(const QString &text, + int selectionStart, int selectionLength) +{ + Q_ASSERT(m_toolWindow); + m_toolWindow->show(text, selectionStart, selectionLength); +} diff --git a/src/plugins/quickopen/quickopenmanager.h b/src/plugins/quickopen/quickopenmanager.h new file mode 100644 index 00000000000..850ce6cfa1a --- /dev/null +++ b/src/plugins/quickopen/quickopenmanager.h @@ -0,0 +1,66 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QUICKOPENMANAGER_H +#define QUICKOPENMANAGER_H + +#include "quickopen_global.h" + +#include <QtCore/QObject> +#include <QtCore/QString> + +namespace QuickOpen { + +namespace Internal { +class QuickOpenToolWindow; +} + +class QUICKOPEN_EXPORT QuickOpenManager : public QObject +{ + Q_OBJECT + +public: + QuickOpenManager(Internal::QuickOpenToolWindow *toolWindow); + ~QuickOpenManager(); + + static QuickOpenManager* instance() { return m_instance; } + + void show(const QString &text, int selectionStart = -1, int selectionLength = 0); + +private: + Internal::QuickOpenToolWindow *m_toolWindow; + static QuickOpenManager *m_instance; +}; + +} // namespace QuickOpen + +#endif // QUICKOPENMANAGER_H diff --git a/src/plugins/quickopen/quickopenplugin.cpp b/src/plugins/quickopen/quickopenplugin.cpp new file mode 100644 index 00000000000..c6a6b006028 --- /dev/null +++ b/src/plugins/quickopen/quickopenplugin.cpp @@ -0,0 +1,252 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "quickopenplugin.h" +#include "quickopenfiltersfilter.h" +#include "quickopenmanager.h" +#include "quickopentoolwindow.h" +#include "opendocumentsfilter.h" +#include "filesystemfilter.h" +#include "directoryfilter.h" +#include "settingspage.h" + +#include <QtCore/qplugin.h> +#include <QtCore/QSettings> +#include <QtCore/QFuture> +#include <QtCore/QFutureWatcher> + +#include <coreplugin/baseview.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/progressmanager/progressmanagerinterface.h> +#include <coreplugin/actionmanager/actionmanager.h> +#include <qtconcurrent/QtConcurrentTools> + +using namespace QuickOpen; +using namespace QuickOpen::Internal; + +namespace { + static bool filterLessThan(const IQuickOpenFilter *first, const IQuickOpenFilter *second) + { + return first->priority() < second->priority(); + } +} + +QuickOpenPlugin::QuickOpenPlugin() +{ + m_refreshTimer.setSingleShot(false); + connect(&m_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh())); +} + +QuickOpenPlugin::~QuickOpenPlugin() +{ + removeObject(m_openDocumentsFilter); + removeObject(m_fileSystemFilter); + removeObject(m_settingsPage); + delete m_openDocumentsFilter; + delete m_fileSystemFilter; + delete m_settingsPage; + foreach (IQuickOpenFilter *filter, m_customFilter) + delete filter; +} + +bool QuickOpenPlugin::initialize(const QStringList &, QString *) +{ + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + + m_settingsPage = new SettingsPage(core, this); + addObject(m_settingsPage); + + m_quickOpenToolWindow = new QuickOpenToolWindow(this); + m_quickOpenToolWindow->setEnabled(false); + Core::BaseView *view = new Core::BaseView("QuickOpen.ToolWindow", + m_quickOpenToolWindow, + QList<int>() << core->uniqueIDManager()->uniqueIdentifier(QLatin1String("QuickOpenToolWindow")), + Core::IView::First); + addAutoReleasedObject(view); + + const QString actionId = QLatin1String("QtCreator.View.QuickOpen.ToolWindow"); + QAction *action = new QAction(m_quickOpenToolWindow->windowIcon(), m_quickOpenToolWindow->windowTitle(), this); + Core::ICommand *cmd = core->actionManager()->registerAction(action, actionId, QList<int>() << Core::Constants::C_GLOBAL_ID); + cmd->setDefaultKeySequence(QKeySequence("Ctrl+K")); + connect(action, SIGNAL(triggered()), this, SLOT(openQuickOpen())); + + Core::IActionContainer *mtools = core->actionManager()->actionContainer(Core::Constants::M_TOOLS); + mtools->addAction(cmd); + + addObject(new QuickOpenManager(m_quickOpenToolWindow)); + + m_openDocumentsFilter = new OpenDocumentsFilter(core->editorManager()); + addObject(m_openDocumentsFilter); + + m_fileSystemFilter = new FileSystemFilter(core->editorManager(), m_quickOpenToolWindow); + addObject(m_fileSystemFilter); + + addAutoReleasedObject(new QuickOpenFiltersFilter(this, m_quickOpenToolWindow)); + + connect(core, SIGNAL(coreOpened()), this, SLOT(startSettingsLoad())); + return true; +} + +void QuickOpenPlugin::openQuickOpen() +{ + m_quickOpenToolWindow->setFocus(); +} + +void QuickOpenPlugin::extensionsInitialized() +{ + m_filter = ExtensionSystem::PluginManager::instance()->getObjects<IQuickOpenFilter>(); + qSort(m_filter.begin(), m_filter.end(), filterLessThan); +} + +void QuickOpenPlugin::startSettingsLoad() +{ + m_loadWatcher.setFuture(QtConcurrent::run(this, &QuickOpenPlugin::loadSettings)); + connect(&m_loadWatcher, SIGNAL(finished()), this, SLOT(settingsLoaded())); +} + +void QuickOpenPlugin::loadSettings() +{ + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + QSettings settings; + settings.beginGroup("QuickOpen"); + m_refreshTimer.setInterval(settings.value("RefreshInterval", 60).toInt()*60000); + foreach (IQuickOpenFilter *filter, m_filter) { + if (settings.contains(filter->name())) { + const QByteArray state = settings.value(filter->name()).toByteArray(); + if (!state.isEmpty()) + filter->restoreState(state); + } + } + settings.beginGroup("CustomFilters"); + QList<IQuickOpenFilter *> customFilters; + foreach (const QString &key, settings.childKeys()) { + IQuickOpenFilter *filter = new DirectoryFilter(core); + filter->restoreState(settings.value(key).toByteArray()); + m_filter.append(filter); + customFilters.append(filter); + } + setCustomFilter(customFilters); + settings.endGroup(); + settings.endGroup(); +} + +void QuickOpenPlugin::settingsLoaded() +{ + m_quickOpenToolWindow->updateFilterList(); + m_quickOpenToolWindow->setEnabled(true); + m_refreshTimer.start(); +} + +void QuickOpenPlugin::saveSettings() +{ + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + if (core && core->settings()) { + QSettings *s = core->settings(); + s->beginGroup("QuickOpen"); + s->setValue("Interval", m_refreshTimer.interval()/60000); + s->remove(""); + foreach (IQuickOpenFilter *filter, m_filter) { + if (!m_customFilter.contains(filter)) { + s->setValue(filter->name(), filter->saveState()); + } + } + s->beginGroup("CustomFilters"); + int i = 0; + foreach (IQuickOpenFilter *filter, m_customFilter) { + s->setValue(QString("directory%1").arg(i), filter->saveState()); + ++i; + } + s->endGroup(); + s->endGroup(); + } +} + +/*! + \fn QList<IQuickOpenFilter*> QuickOpenPlugin::filter() + + Return all filters, including the ones created by the user. +*/ +QList<IQuickOpenFilter*> QuickOpenPlugin::filter() +{ + return m_filter; +} + +/*! + \fn QList<IQuickOpenFilter*> QuickOpenPlugin::customFilter() + + 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<IQuickOpenFilter*> QuickOpenPlugin::customFilter() +{ + return m_customFilter; +} + +void QuickOpenPlugin::setFilter(QList<IQuickOpenFilter*> f) +{ + m_filter = f; + m_quickOpenToolWindow->updateFilterList(); +} + +void QuickOpenPlugin::setCustomFilter(QList<IQuickOpenFilter *> filter) +{ + m_customFilter = filter; +} + +int QuickOpenPlugin::refreshInterval() +{ + return m_refreshTimer.interval()/60000; +} + +void QuickOpenPlugin::setRefreshInterval(int interval) +{ + if (interval < 1) { + m_refreshTimer.stop(); + m_refreshTimer.setInterval(0); + return; + } + m_refreshTimer.setInterval(interval*60000); + m_refreshTimer.start(); +} + +void QuickOpenPlugin::refresh(QList<IQuickOpenFilter*> filters) +{ + if (filters.isEmpty()) + filters = m_filter; + QFuture<void> task = QtConcurrent::run(&IQuickOpenFilter::refresh, filters); + Core::FutureProgress *progress = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>() + ->progressManager()->addTask(task, tr("Indexing"), Constants::TASK_INDEX, Core::ProgressManagerInterface::CloseOnSuccess); + connect(progress, SIGNAL(finished()), this, SLOT(saveSettings())); +} + +Q_EXPORT_PLUGIN(QuickOpenPlugin) diff --git a/src/plugins/quickopen/quickopenplugin.h b/src/plugins/quickopen/quickopenplugin.h new file mode 100644 index 00000000000..5400154cb8b --- /dev/null +++ b/src/plugins/quickopen/quickopenplugin.h @@ -0,0 +1,97 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QUICKOPENPLUGIN_H +#define QUICKOPENPLUGIN_H + +#include "iquickopenfilter.h" + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> +#include <QtCore/QTimer> +#include <QtCore/QFutureWatcher> + +namespace QuickOpen { +namespace Internal { + +class QuickOpenToolWindow; +class OpenDocumentsFilter; +class FileSystemFilter; +class SettingsPage; + +class QuickOpenPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + QuickOpenPlugin(); + ~QuickOpenPlugin(); + + bool initialize(const QStringList &arguments, QString *error_message); + void extensionsInitialized(); + + QList<IQuickOpenFilter*> filter(); + QList<IQuickOpenFilter*> customFilter(); + void setFilter(QList<IQuickOpenFilter*> f); + void setCustomFilter(QList<IQuickOpenFilter*> f); + int refreshInterval(); + void setRefreshInterval(int interval); + +public slots: + void refresh(QList<IQuickOpenFilter*> filters = QList<IQuickOpenFilter*>()); + void saveSettings(); + void openQuickOpen(); + +private slots: + void startSettingsLoad(); + void settingsLoaded(); + +private: + void loadSettings(); + + QuickOpenToolWindow *m_quickOpenToolWindow; + SettingsPage *m_settingsPage; + + QList<IQuickOpenFilter*> m_filter; + QList<IQuickOpenFilter*> m_customFilter; + int m_refreshInterval; + QTimer m_refreshTimer; + OpenDocumentsFilter *m_openDocumentsFilter; + FileSystemFilter *m_fileSystemFilter; + QFutureWatcher<void> m_loadWatcher; +}; + +} // namespace Internal +} // namespace QuickOpen + +#endif // QUICKOPENPLUGIN_H diff --git a/src/plugins/quickopen/quickopentoolwindow.cpp b/src/plugins/quickopen/quickopentoolwindow.cpp new file mode 100644 index 00000000000..f6ca212c82e --- /dev/null +++ b/src/plugins/quickopen/quickopentoolwindow.cpp @@ -0,0 +1,484 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include <qglobal.h> + +namespace QuickOpen { +struct FilterEntry; +} + +QT_BEGIN_NAMESPACE +unsigned int qHash(const QuickOpen::FilterEntry &entry); +QT_END_NAMESPACE + +#include "quickopentoolwindow.h" +#include "quickopenplugin.h" +#include "quickopenconstants.h" + +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/fileiconprovider.h> +#include <utils/fancylineedit.h> + +#include <QtCore/QFileInfo> +#include <QtCore/QFile> +#include <QtCore/QTimer> +#include <QtCore/QRegExp> +#include <QtCore/QSettings> +#include <QtCore/QDebug> +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QContextMenuEvent> +#include <QtGui/QHBoxLayout> +#include <QtGui/QHeaderView> +#include <QtGui/QKeyEvent> +#include <QtGui/QLabel> +#include <QtGui/QLineEdit> +#include <QtGui/QMenu> +#include <QtGui/QMouseEvent> +#include <QtGui/QPushButton> +#include <QtGui/QScrollBar> +#include <QtGui/QTreeView> + +Q_DECLARE_METATYPE(QuickOpen::IQuickOpenFilter*); +Q_DECLARE_METATYPE(QuickOpen::FilterEntry); + +namespace QuickOpen { +namespace Internal { + +/*! A model to represent the QuickOpen results. */ +class QuickOpenModel : public QAbstractListModel +{ +public: + QuickOpenModel(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<FilterEntry> &entries); + //void setDisplayCount(int count); + +private: + mutable QList<FilterEntry> mEntries; + //int mDisplayCount; +}; + +class CompletionList : public QTreeView +{ +public: + CompletionList(QWidget *parent = 0); + + void updatePreferredSize(); + QSize preferredSize() const { return m_preferredSize; } + +private: + QSize m_preferredSize; +}; + +} // namespace Internal +} // namespace QuickOpen + +using namespace QuickOpen; +using namespace QuickOpen::Internal; + +QT_BEGIN_NAMESPACE +uint qHash(const FilterEntry &entry) +{ + if (entry.internalData.canConvert(QVariant::String)) + return qHash(entry.internalData.toString()); + return qHash(entry.internalData.constData()); +} +QT_END_NAMESPACE + + +// =========== QuickOpenModel =========== + +int QuickOpenModel::rowCount(const QModelIndex & /* parent */) const +{ + return mEntries.size(); +} + +int QuickOpenModel::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : 2; +} + +/*! + * When asked for the icon via Qt::DecorationRole, the QuickOpenModel lazily + * resolves and caches the Greehouse-specific file icon when + * FilterEntry::resolveFileIcon is true. FilterEntry::internalData is assumed + * to be the filename. + */ +QVariant QuickOpenModel::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::DecorationRole && index.column() == 0) { + FilterEntry &entry = mEntries[index.row()]; + if (entry.resolveFileIcon && entry.displayIcon.isNull()) { + entry.resolveFileIcon = false; + entry.displayIcon = + Core::FileIconProvider::instance()->icon(QFileInfo(entry.internalData.toString())); + } + return entry.displayIcon; + } else if (role == Qt::ForegroundRole && index.column() == 1) { + return Qt::darkGray; + } else if (role == Qt::UserRole) { + return qVariantFromValue(mEntries.at(index.row())); + } + + return QVariant(); +} + +void QuickOpenModel::setEntries(const QList<FilterEntry> &entries) +{ + mEntries = entries; + reset(); +} +#if 0 +void QuickOpenModel::setDisplayCount(int count) +{ + // TODO: This method is meant to be used for increasing the number of items displayed at the + // user's request. There is however no way yet for the user to request this. + if (count == mDisplayCount) + return; + + const int displayedOld = qMin(mDisplayCount, mEntries.size()); + const int displayedNew = qMin(count, mEntries.size()); + + if (displayedNew < displayedOld) + beginRemoveRows(QModelIndex(), displayedNew - 1, displayedOld - 1); + else if (displayedNew > displayedOld) + beginInsertRows(QModelIndex(), displayedOld - 1, displayedNew - 1); + + mDisplayCount = count; + + if (displayedNew < displayedOld) + endRemoveRows(); + else if (displayedNew > displayedOld) + endInsertRows(); +} +#endif + +// =========== 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); +} + +void CompletionList::updatePreferredSize() +{ + //header()->setStretchLastSection(false); + //updateGeometries(); + + const QStyleOptionViewItem &option = viewOptions(); + const QSize shint = itemDelegate()->sizeHint(option, model()->index(0, 0)); +#if 0 + const int visibleItems = model()->rowCount(); + + // TODO: Look into enabling preferred height as well. Current problem is that this doesn't + // work nicely from the user perspective if the list is popping up instead of down. + //const int h = shint.height() * visibleItems; + + const QScrollBar *vscroll = verticalScrollBar(); + int preferredWidth = header()->length() + frameWidth() * 2 + + (vscroll->isVisibleTo(this) ? vscroll->width() : 0); + const int diff = preferredWidth - width(); + + // Avoid resizing the list widget when there are no results or when the preferred size is + // only a little smaller than our current size + if (visibleItems == 0 || (diff > -100 && diff < 0)) + preferredWidth = width(); +#endif + + m_preferredSize = QSize(600, //qMax(600, preferredWidth), + shint.height() * 17 + frameWidth() * 2); + //header()->setStretchLastSection(true); +} + + +// =========== QuickOpenToolWindow =========== + +QuickOpenToolWindow::QuickOpenToolWindow(QuickOpenPlugin *qop) : + m_quickOpenPlugin(qop), + m_quickOpenModel(new QuickOpenModel(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 Core::Utils::FancyLineEdit) +{ + // Explcitly hide the completion list popup. + m_completionList->hide(); + + setWindowTitle("Quick Open"); + 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(":/quickopen/images/quickopen.png")); + QPixmap image(Core::Constants::ICON_MAGNIFIER); + m_fileLineEdit->setPixmap(image); + m_fileLineEdit->setUseLayoutDirection(true); + m_fileLineEdit->setHintText(tr("Type to QuickOpen")); + m_fileLineEdit->setFocusPolicy(Qt::ClickFocus); + + m_fileLineEdit->installEventFilter(this); + this->installEventFilter(this); + + m_completionList->setModel(m_quickOpenModel); + 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->setMenu( m_filterMenu); + + connect(m_refreshAction, SIGNAL(triggered()), m_quickOpenPlugin, SLOT(refresh())); + connect(m_configureAction, SIGNAL(triggered()), this, SLOT(showConfigureDialog())); + connect(m_fileLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(textEdited(const QString&))); + connect(m_completionList, SIGNAL(activated(QModelIndex)), + this, SLOT(acceptCurrentEntry())); +} + +bool QuickOpenToolWindow::isShowingTypeHereMessage() const +{ + return m_fileLineEdit->isShowingHintText(); +} + +void QuickOpenToolWindow::updateFilterList() +{ + m_filterMenu->clear(); + foreach (IQuickOpenFilter *filter, m_quickOpenPlugin->filter()) { + if (!filter->shortcutString().isEmpty()) { + QAction *action = m_filterMenu->addAction(filter->trName(), this, SLOT(filterSelected())); + action->setData(qVariantFromValue(filter)); + } + } + m_filterMenu->addSeparator(); + m_filterMenu->addAction(m_refreshAction); + m_filterMenu->addAction(m_configureAction); +} + +bool QuickOpenToolWindow::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == m_fileLineEdit && event->type() == QEvent::KeyPress) { + 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: + acceptCurrentEntry(); + return true; + case Qt::Key_Escape: + m_completionList->hide(); + return true; + default: + break; + } + } else if (obj == m_fileLineEdit && event->type() == QEvent::FocusOut) { + m_completionList->hide(); + } else if (obj == m_fileLineEdit && event->type() == QEvent::FocusIn) { + updateCompletionList(m_fileLineEdit->typedText()); + showCompletionList(); + } else if (obj == this && event->type() == QEvent::ShortcutOverride) { + QKeyEvent *ke = static_cast<QKeyEvent *>(event); + if (ke->key() == Qt::Key_Escape && !ke->modifiers()) { + event->accept(); + QTimer::singleShot(0, Core::ModeManager::instance(), SLOT(setFocusToCurrentMode())); + return true; + } + } + return QWidget::eventFilter(obj, event); +} + +void QuickOpenToolWindow::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 QuickOpenToolWindow::textEdited(const QString &text) +{ + updateCompletionList(text); + showCompletionList(); +} + +QList<IQuickOpenFilter*> QuickOpenToolWindow::filtersFor(const QString &text, QString &searchText) +{ + QList<IQuickOpenFilter*> filters = m_quickOpenPlugin->filter(); + int whiteSpace = text.indexOf(" "); + QString prefix; + if (whiteSpace >= 0) + prefix = text.left(whiteSpace); + if (!prefix.isEmpty()) { + prefix = prefix.toLower(); + foreach (IQuickOpenFilter *filter, filters) { + if (prefix == filter->shortcutString()) { + searchText = text.mid(whiteSpace+1); + return QList<IQuickOpenFilter*>() << filter; + } + } + } + searchText = text; + QList<IQuickOpenFilter*> activeFilters; + foreach (IQuickOpenFilter *filter, filters) + if (filter->defaultActiveState()) + activeFilters << filter; + return activeFilters; +} + +void QuickOpenToolWindow::updateCompletionList(const QString &text) +{ + QString searchText; + const QList<IQuickOpenFilter*> filters = filtersFor(text, searchText); + QSet<FilterEntry> alreadyAdded; + const bool checkDuplicates = (filters.size() > 1); + QList<FilterEntry> entries; + foreach (IQuickOpenFilter *filter, filters) { + foreach (const FilterEntry &entry, filter->matchesFor(searchText)) { + if (checkDuplicates && alreadyAdded.contains(entry)) + continue; + entries.append(entry); + if (checkDuplicates) + alreadyAdded.insert(entry); + } + } + m_quickOpenModel->setEntries(entries); + if (m_quickOpenModel->rowCount() > 0) { + m_completionList->setCurrentIndex(m_quickOpenModel->index(0, 0)); + } +#if 0 + m_completionList->updatePreferredSize(); +#endif +} + +void QuickOpenToolWindow::acceptCurrentEntry() +{ + if (!m_completionList->isVisible()) + return; + const QModelIndex index = m_completionList->currentIndex(); + if (!index.isValid()) + return; + const FilterEntry entry = m_quickOpenModel->data(index, Qt::UserRole).value<FilterEntry>(); + m_completionList->hide(); + entry.filter->accept(entry); +} + +void QuickOpenToolWindow::show(const QString &text, int selectionStart, int selectionLength) +{ + m_fileLineEdit->hideHintText(); + m_fileLineEdit->setText(text); + setFocus(); + if (selectionStart >= 0) + m_fileLineEdit->setSelection(selectionStart, selectionLength); + else + m_fileLineEdit->deselect(); +} + +void QuickOpenToolWindow::filterSelected() +{ + const char * const TEXT = "<type here>"; + QAction *action = qobject_cast<QAction*>(sender()); + Q_ASSERT(action); + IQuickOpenFilter *filter = action->data().value<IQuickOpenFilter*>(); + Q_ASSERT(filter); + show(filter->shortcutString() + " " + TEXT, + filter->shortcutString().length() + 1, + QString(TEXT).length()); + updateCompletionList(m_fileLineEdit->text()); + m_fileLineEdit->setFocus(); +} + +void QuickOpenToolWindow::focusInEvent(QFocusEvent *e) +{ + m_fileLineEdit->setFocus(e->reason()); + if (e->reason() != Qt::MouseFocusReason) { + m_fileLineEdit->selectAll(); + } + QWidget::focusInEvent(e); +} + +void QuickOpenToolWindow::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); +} + +void QuickOpenToolWindow::showConfigureDialog() +{ + ExtensionSystem::PluginManager::instance() + ->getObject<Core::ICore>()->showOptionsDialog(Constants::QUICKOPEN_CATEGORY, + Constants::FILTER_OPTIONS_PAGE); +} diff --git a/src/plugins/quickopen/quickopentoolwindow.h b/src/plugins/quickopen/quickopentoolwindow.h new file mode 100644 index 00000000000..e31280d1afe --- /dev/null +++ b/src/plugins/quickopen/quickopentoolwindow.h @@ -0,0 +1,102 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QUICKOPENTOOLWINDOW_H +#define QUICKOPENTOOLWINDOW_H + +#include <QtCore/QEvent> +#include <QtGui/QWidget> + +#include "quickopenplugin.h" + +QT_BEGIN_NAMESPACE +class QAction; +class QLabel; +class QLineEdit; +class QMenu; +class QTreeView; +QT_END_NAMESPACE + +namespace Core { + namespace Utils { + class FancyLineEdit; + } +} +namespace QuickOpen { +namespace Internal { + +class QuickOpenModel; +class CompletionList; + +class QuickOpenToolWindow + : public QWidget +{ + Q_OBJECT + +public: + QuickOpenToolWindow(QuickOpenPlugin *qop); + + void updateFilterList(); + + void show(const QString &text, int selectionStart = -1, int selectionLength = 0); + +private slots: + void textEdited(const QString &text); + void acceptCurrentEntry(); + void filterSelected(); + void showConfigureDialog(); + +private: + bool eventFilter(QObject *obj, QEvent *event); + + void showEvent(QShowEvent *e); + void focusInEvent(QFocusEvent *e); + + bool isShowingTypeHereMessage() const; + void showCompletionList(); + void updateCompletionList(const QString &text); + QList<IQuickOpenFilter*> filtersFor(const QString &text, QString &searchText); + + QuickOpenPlugin *m_quickOpenPlugin; + QuickOpenModel *m_quickOpenModel; + + CompletionList *m_completionList; + QMenu *m_filterMenu; + QAction *m_refreshAction; + QAction *m_configureAction; + Core::Utils::FancyLineEdit *m_fileLineEdit; +}; + +} // namespace Internal +} // namespace QuickOpen + +#endif // QUICKOPENTOOLWINDOW_H diff --git a/src/plugins/quickopen/settingspage.cpp b/src/plugins/quickopen/settingspage.cpp new file mode 100644 index 00000000000..4b75aa1756f --- /dev/null +++ b/src/plugins/quickopen/settingspage.cpp @@ -0,0 +1,187 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "settingspage.h" +#include "quickopenplugin.h" +#include "iquickopenfilter.h" +#include "directoryfilter.h" + +#include <QtGui/QMessageBox> + +#include <qtconcurrent/QtConcurrentTools> + +Q_DECLARE_METATYPE(QuickOpen::IQuickOpenFilter*) + +using namespace QuickOpen; +using namespace QuickOpen::Internal; + +SettingsPage::SettingsPage(Core::ICore *core, QuickOpenPlugin *plugin) + : m_core(core), m_plugin(plugin), m_page(0) +{ +} + +QWidget *SettingsPage::createPage(QWidget *parent) +{ + if (!m_page) { + m_page = new QWidget(parent); + m_ui.setupUi(m_page); + 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->filter(); + m_customFilters = m_plugin->customFilter(); + saveFilterStates(); + updateFilterList(); + return m_page; +} + +void SettingsPage::finished(bool accepted) +{ + if (!accepted) { + restoreFilterStates(); + foreach (IQuickOpenFilter *filter, m_addedFilters) + delete filter; + } else { + foreach (IQuickOpenFilter *filter, m_removedFilters) + delete filter; + m_plugin->setFilter(m_filters); + m_plugin->setCustomFilter(m_customFilters); + m_plugin->setRefreshInterval(m_ui.refreshInterval->value()); + requestRefresh(); + m_plugin->saveSettings(); + } + m_addedFilters.clear(); + m_removedFilters.clear(); + m_filters.clear(); + m_customFilters.clear(); + m_refreshFilters.clear(); +} + +void SettingsPage::requestRefresh() +{ + if (!m_refreshFilters.isEmpty()) + m_plugin->refresh(m_refreshFilters); +} + +void SettingsPage::saveFilterStates() +{ + m_filterStates.clear(); + foreach (IQuickOpenFilter *filter, m_filters) + m_filterStates.insert(filter, filter->saveState()); +} + +void SettingsPage::restoreFilterStates() +{ + foreach (IQuickOpenFilter *filter, m_filterStates.keys()) + filter->restoreState(m_filterStates.value(filter)); +} + +void SettingsPage::updateFilterList() +{ + m_ui.filterList->clear(); + foreach (IQuickOpenFilter *filter, m_filters) { + QString title; + if (filter->defaultActiveState()) + title = filter->trName(); + else + title = tr("%1 (Prefix: %2)").arg(filter->trName()).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(); + IQuickOpenFilter *filter = (item ? item->data(Qt::UserRole).value<IQuickOpenFilter *>() : 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(); + Q_ASSERT(item); + IQuickOpenFilter *filter = item->data(Qt::UserRole).value<IQuickOpenFilter *>(); + Q_ASSERT(filter); + if (!filter->isConfigurable()) + return; + bool needsRefresh = false; + filter->openConfigDialog(m_page, needsRefresh); + if (needsRefresh && !m_refreshFilters.contains(filter)) + m_refreshFilters.append(filter); + updateFilterList(); +} + +void SettingsPage::addCustomFilter() +{ + IQuickOpenFilter *filter = new DirectoryFilter(m_core); + bool needsRefresh = false; + if (filter->openConfigDialog(m_page, 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(); + Q_ASSERT(item); + IQuickOpenFilter *filter = item->data(Qt::UserRole).value<IQuickOpenFilter *>(); + Q_ASSERT(m_customFilters.contains(filter)); + 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/quickopen/settingspage.h b/src/plugins/quickopen/settingspage.h new file mode 100644 index 00000000000..2ad79be3cde --- /dev/null +++ b/src/plugins/quickopen/settingspage.h @@ -0,0 +1,97 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SETTINGSPAGE_H +#define SETTINGSPAGE_H + +#include "ui_settingspage.h" +#include "quickopenconstants.h" + +#include <QtCore/QPointer> +#include <QtCore/QHash> + +#include <coreplugin/dialogs/ioptionspage.h> +#include <coreplugin/icore.h> + +QT_BEGIN_NAMESPACE +class QListWidgetItem; +QT_END_NAMESPACE + +namespace QuickOpen { + +class IQuickOpenFilter; + +namespace Internal { + +class QuickOpenPlugin; + +class SettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + SettingsPage(Core::ICore *core, QuickOpenPlugin *plugin); + QString name() const { return tr(Constants::FILTER_OPTIONS_PAGE); } + QString category() const { return Constants::QUICKOPEN_CATEGORY; } + QString trCategory() const { return tr(Constants::QUICKOPEN_CATEGORY); } + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + +private slots: + void updateButtonStates(); + void configureFilter(QListWidgetItem *item = 0); + void addCustomFilter(); + void removeCustomFilter(); + +private: + void updateFilterList(); + void saveFilterStates(); + void restoreFilterStates(); + void requestRefresh(); + + Ui::SettingsWidget m_ui; + Core::ICore *m_core; + QuickOpenPlugin *m_plugin; + QPointer<QWidget> m_page; + QList<IQuickOpenFilter *> m_filters; + QList<IQuickOpenFilter *> m_addedFilters; + QList<IQuickOpenFilter *> m_removedFilters; + QList<IQuickOpenFilter *> m_customFilters; + QList<IQuickOpenFilter *> m_refreshFilters; + QHash<IQuickOpenFilter *, QByteArray> m_filterStates; +}; + +} // namespace Internal +} // namespace QuickOpen + +#endif // SETTINGSPAGE_H diff --git a/src/plugins/quickopen/settingspage.ui b/src/plugins/quickopen/settingspage.ui new file mode 100644 index 00000000000..f9145404d23 --- /dev/null +++ b/src/plugins/quickopen/settingspage.ui @@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QuickOpen::Internal::SettingsWidget</class> + <widget class="QWidget" name="QuickOpen::Internal::SettingsWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>460</width> + <height>353</height> + </rect> + </property> + <property name="windowTitle"> + <string>Configure Filters</string> + </property> + <layout class="QGridLayout"> + <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="label"> + <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="value"> + <number>60</number> + </property> + <property name="suffix"> + <string> min</string> + </property> + <property name="maximum"> + <number>320</number> + </property> + <property name="singleStep"> + <number>5</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/quickopen/settingswidget.ui b/src/plugins/quickopen/settingswidget.ui new file mode 100644 index 00000000000..bc01858c81d --- /dev/null +++ b/src/plugins/quickopen/settingswidget.ui @@ -0,0 +1,133 @@ +<ui version="4.0" > + <class>QuickOpen::Internal::SettingsDialog</class> + <widget class="QWidget" name="QuickOpen::Internal::SettingsDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>460</width> + <height>353</height> + </rect> + </property> + <property name="windowTitle" > + <string>Configure Filters</string> + </property> + <layout class="QGridLayout" > + <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="enabled" > + <bool>false</bool> + </property> + <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" > + <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="label" > + <property name="text" > + <string>Refresh Intervall:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="refreshIntervall" > + <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> + <widget class="QToolButton" name="refreshButton" > + <property name="font" > + <font/> + </property> + <property name="text" > + <string>Refresh now!</string> + </property> + <property name="toolButtonStyle" > + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/regexp/RegExp.pluginspec b/src/plugins/regexp/RegExp.pluginspec new file mode 100644 index 00000000000..03d09009670 --- /dev/null +++ b/src/plugins/regexp/RegExp.pluginspec @@ -0,0 +1,10 @@ +<plugin name="RegExp" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Regular Expression test widget.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/regexp/regexp.pro b/src/plugins/regexp/regexp.pro new file mode 100644 index 00000000000..dfe52ef0245 --- /dev/null +++ b/src/plugins/regexp/regexp.pro @@ -0,0 +1,10 @@ +TEMPLATE = lib +TARGET = RegExp + +include(../../qworkbenchplugin.pri) +include(../../plugins/coreplugin/coreplugin.pri) + +QT += xml + +HEADERS += regexpwindow.h regexpplugin.h settings.h +SOURCES += regexpwindow.cpp regexpplugin.cpp settings.cpp diff --git a/src/plugins/regexp/regexpplugin.cpp b/src/plugins/regexp/regexpplugin.cpp new file mode 100644 index 00000000000..48b3e3590e7 --- /dev/null +++ b/src/plugins/regexp/regexpplugin.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "regexpplugin.h" +#include "settings.h" +#include "regexpwindow.h" + +#include <coreplugin/baseview.h> +#include <coreplugin/icore.h> +#include <coreplugin/uniqueidmanager.h> + +#include <QtCore/qplugin.h> + +using namespace RegExp::Internal; + +RegExpPlugin::RegExpPlugin() +{ +} + +RegExpPlugin::~RegExpPlugin() +{ + if (m_regexpWindow) { + m_regexpWindow->settings().toQSettings(m_core->settings()); + } +} + +void RegExpPlugin::extensionsInitialized() +{ +} + + +bool RegExpPlugin::initialize(const QStringList & /*arguments*/, QString *error_message) +{ + Q_UNUSED(error_message) + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + m_regexpWindow = new RegExpWindow; + Settings settings; + settings.fromQSettings(m_core->settings()); + m_regexpWindow->setSettings(settings); + const int plugId = m_core->uniqueIDManager()->uniqueIdentifier(QLatin1String("RegExpPlugin")); + addAutoReleasedObject(new Core::BaseView("TextEditor.RegExpWindow", + m_regexpWindow, + QList<int>() << plugId, + Qt::RightDockWidgetArea)); + return true; +} + +Q_EXPORT_PLUGIN(RegExpPlugin) diff --git a/src/plugins/regexp/regexpplugin.h b/src/plugins/regexp/regexpplugin.h new file mode 100644 index 00000000000..3b5703305b7 --- /dev/null +++ b/src/plugins/regexp/regexpplugin.h @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef REGEXPPLUGIN_H +#define REGEXPPLUGIN_H + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> +#include <QtCore/QPointer> + +namespace Core { +class ICore; +} + +namespace RegExp { +namespace Internal { + +class RegExpWindow; + +class RegExpPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + RegExpPlugin(); + virtual ~RegExpPlugin(); + + bool initialize(const QStringList &arguments, QString *error_message); + void extensionsInitialized(); + +private: + Core::ICore *m_core; + QPointer<RegExpWindow> m_regexpWindow; +}; + +} //namespace Internal +} //namespace RegExp + +#endif diff --git a/src/plugins/regexp/regexpwindow.cpp b/src/plugins/regexp/regexpwindow.cpp new file mode 100644 index 00000000000..a00a9137a0c --- /dev/null +++ b/src/plugins/regexp/regexpwindow.cpp @@ -0,0 +1,294 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "regexpwindow.h" +#include "settings.h" + +#include <QtGui/QCheckBox> +#include <QtGui/QComboBox> +#include <QtGui/QLabel> +#include <QtGui/QLayout> +#include <QtGui/QLineEdit> +#include <QtGui/QContextMenuEvent> +#include <QtGui/QMenu> +#include <QtGui/QInputDialog> + +using namespace RegExp::Internal; + +RegExpWindow::RegExpWindow(QWidget *parent) : + QWidget(parent), + patternLabel(new QLabel(tr("&Pattern:"))), + escapedPatternLabel(new QLabel(tr("&Escaped Pattern:"))), + syntaxLabel(new QLabel(tr("&Pattern Syntax:"))), + textLabel(new QLabel(tr("&Text:"))), + patternComboBox (new QComboBox), + escapedPatternLineEdit(new QLineEdit), + textComboBox(new QComboBox), + caseSensitiveCheckBox(new QCheckBox(tr("Case &Sensitive"))), + minimalCheckBox(new QCheckBox(tr("&Minimal"))), + syntaxComboBox(new QComboBox), + indexLabel(new QLabel(tr("Index of Match:"))), + matchedLengthLabel(new QLabel(tr("Matched Length:"))), + indexEdit(new QLineEdit), + matchedLengthEdit(new QLineEdit) +{ + QVBoxLayout *vboxLayout = new QVBoxLayout(this); + QGridLayout *mainLayout = new QGridLayout; + + patternComboBox->setEditable(true); + patternComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + patternLabel->setBuddy(patternComboBox); + + mainLayout->addWidget(patternLabel, 0, 0); + mainLayout->addWidget(patternComboBox, 0, 1); + + escapedPatternLineEdit->setReadOnly(true); + QPalette palette = escapedPatternLineEdit->palette(); + palette.setBrush(QPalette::Base, palette.brush(QPalette::Disabled, QPalette::Base)); + escapedPatternLineEdit->setPalette(palette); + + escapedPatternLabel->setBuddy(escapedPatternLineEdit); + + mainLayout->addWidget(escapedPatternLabel, 1, 0); + mainLayout->addWidget(escapedPatternLineEdit, 1, 1); + + syntaxComboBox->addItem(tr("Regular expression v1"), QRegExp::RegExp); + syntaxComboBox->addItem(tr("Regular expression v2"), QRegExp::RegExp2); + syntaxComboBox->addItem(tr("Wildcard"), QRegExp::Wildcard); + syntaxComboBox->addItem(tr("Fixed string"), QRegExp::FixedString); + + syntaxLabel->setBuddy(syntaxComboBox); + + mainLayout->addWidget(syntaxLabel, 2, 0); + mainLayout->addWidget(syntaxComboBox, 2, 1); + + QHBoxLayout *checkBoxLayout = new QHBoxLayout; + + caseSensitiveCheckBox->setChecked(true); + + checkBoxLayout->addWidget(caseSensitiveCheckBox); + checkBoxLayout->addWidget(minimalCheckBox); + checkBoxLayout->addStretch(1); + + mainLayout->addLayout(checkBoxLayout, 3, 0, 1, 2); + + textComboBox->setEditable(true); + textComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + textLabel->setBuddy(textComboBox); + + mainLayout->addWidget(textLabel, 4, 0); + mainLayout->addWidget(textComboBox, 4, 1); + + indexEdit->setReadOnly(true); + + mainLayout->addWidget(indexLabel, 5, 0); + mainLayout->addWidget(indexEdit, 5, 1); + + matchedLengthEdit->setReadOnly(true); + + mainLayout->addWidget(matchedLengthLabel, 6, 0); + mainLayout->addWidget(matchedLengthEdit, 6, 1); + + vboxLayout->addLayout(mainLayout); + vboxLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + for (int i = 0; i < MaxCaptures; ++i) { + captureLabels[i] = new QLabel(tr("Capture %1:").arg(i)); + captureEdits[i] = new QLineEdit; + captureEdits[i]->setReadOnly(true); + } + captureLabels[0]->setText(tr("Match:")); + + for (int j = 0; j < MaxCaptures; ++j) { + mainLayout->addWidget(captureLabels[j], 7 + j, 0); + mainLayout->addWidget(captureEdits[j], 7 + j, 1); + } + + connect(patternComboBox, SIGNAL(editTextChanged(const QString &)), this, SLOT(refresh())); + connect(textComboBox, SIGNAL(editTextChanged(const QString &)), this, SLOT(refresh())); + connect(caseSensitiveCheckBox, SIGNAL(toggled(bool)), this, SLOT(refresh())); + connect(minimalCheckBox, SIGNAL(toggled(bool)), this, SLOT(refresh())); + connect(syntaxComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(refresh())); + + setWindowTitle(tr("Regular Expression")); + refresh(); +} + +static const char *escapedBackSlash = "\\\\"; +static const char *escapedDoubleQuote = "\\\""; + +static QString escapePattern(const QString &pattern) +{ + QString escaped = pattern; + escaped.replace(QString(QLatin1Char('\\')) , QLatin1String(escapedBackSlash)); + const QChar doubleQuote(QLatin1Char('"')); + escaped.replace(doubleQuote, QString(QLatin1String(escapedDoubleQuote))); + escaped.prepend(doubleQuote); + escaped.append(doubleQuote); + return escaped; +} + +static QString unescapePattern(QString escaped) +{ + // remove quotes + const QChar doubleQuote(QLatin1Char('"')); + if (escaped.endsWith(doubleQuote)) + escaped.truncate(escaped.size() - 1); + if (escaped.startsWith(doubleQuote)) + escaped.remove(0, 1); + + const int size = escaped.size(); + if (!size) + return QString(); + + // parse out escapes. Do not just replace. + QString pattern; + const QChar backSlash = QLatin1Char('\\'); + bool escapeSeen = false; + for (int i = 0; i < size; i++) { + const QChar c = escaped.at(i); + if (c == backSlash && !escapeSeen) + escapeSeen = true; + else { + pattern.push_back(c); + escapeSeen = false; + } + } + return pattern; +} + +void RegExpWindow::refresh() +{ + setUpdatesEnabled(false); + + const QString pattern = patternComboBox->currentText(); + const QString text = textComboBox->currentText(); + + escapedPatternLineEdit->setText(escapePattern(pattern)); + + QRegExp rx(pattern); + const Qt::CaseSensitivity cs = caseSensitiveCheckBox->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive; + rx.setCaseSensitivity(cs); + rx.setMinimal(minimalCheckBox->isChecked()); + const QRegExp::PatternSyntax syntax = QRegExp::PatternSyntax( + syntaxComboBox->itemData(syntaxComboBox->currentIndex()).toInt()); + rx.setPatternSyntax(syntax); + + QPalette palette = patternComboBox->palette(); + if (rx.isValid()) { + palette.setColor(QPalette::Text, + textComboBox->palette().color(QPalette::Text)); + } else { + palette.setColor(QPalette::Text, Qt::red); + } + patternComboBox->setPalette(palette); + + indexEdit->setText(QString::number(rx.indexIn(text))); + matchedLengthEdit->setText(QString::number(rx.matchedLength())); + for (int i = 0; i < MaxCaptures; ++i) { + const bool enabled = i <= rx.numCaptures(); + captureLabels[i]->setEnabled(enabled); + captureEdits[i]->setEnabled(enabled); + captureEdits[i]->setText(rx.cap(i)); + } + + setUpdatesEnabled(true); +} + +static void saveTextCombo(const QComboBox *cb, QString ¤t, QStringList &items) +{ + current = cb->currentText(); + items.clear(); + if (const int count = cb->count()) + for (int i = 0;i < count; i++) { + const QString text = cb->itemText(i); + if (items.indexOf(text) == -1) + items += text; + } +} + +Settings RegExpWindow::settings() const +{ + Settings rc; + rc.m_patternSyntax = static_cast<QRegExp::PatternSyntax>(syntaxComboBox->itemData(syntaxComboBox->currentIndex()).toInt()); + rc.m_minimal = minimalCheckBox->checkState() == Qt::Checked; + rc.m_caseSensitive = caseSensitiveCheckBox->checkState() == Qt::Checked; + saveTextCombo(patternComboBox, rc.m_currentPattern, rc.m_patterns); + saveTextCombo(textComboBox, rc.m_currentMatch, rc.m_matches); + return rc; +} + +static void restoreTextCombo(const QString ¤t, const QStringList &items, QComboBox *cb) +{ + cb->clear(); + cb->addItems(items); + cb->lineEdit()->setText(current); +} + +void RegExpWindow::setSettings(const Settings &s) +{ + const int patternIndex = syntaxComboBox->findData(QVariant(s.m_patternSyntax)); + syntaxComboBox->setCurrentIndex(patternIndex); + minimalCheckBox->setCheckState(s.m_minimal ? Qt::Checked : Qt::Unchecked); + caseSensitiveCheckBox->setCheckState(s.m_caseSensitive ? Qt::Checked : Qt::Unchecked); + restoreTextCombo(s.m_currentPattern, s.m_patterns, patternComboBox); + restoreTextCombo(s.m_currentMatch, s.m_matches, textComboBox); +} + +void RegExpWindow::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu menu(this); + + QAction *enterQuotedAction = menu.addAction(tr("Enter pattern from code...")); + connect(enterQuotedAction, SIGNAL(triggered()), this, SLOT(enterEscaped())); + menu.addSeparator(); + + QAction *clearPatternsAction = menu.addAction(tr("Clear patterns")); + connect(clearPatternsAction, SIGNAL(triggered()), patternComboBox, SLOT(clear())); + + QAction *clearTextsAction = menu.addAction(tr("Clear texts")); + connect(clearTextsAction, SIGNAL(triggered()), textComboBox, SLOT(clear())); + + event->accept(); + menu.exec(event->globalPos()); +} + +void RegExpWindow::enterEscaped() +{ + const QString escapedPattern = QInputDialog::getText (this, tr("Enter pattern from code"), tr("Pattern")); + if ( escapedPattern.isEmpty()) + return; + patternComboBox->lineEdit()->setText(unescapePattern(escapedPattern)); + +} diff --git a/src/plugins/regexp/regexpwindow.h b/src/plugins/regexp/regexpwindow.h new file mode 100644 index 00000000000..066a7a44e8f --- /dev/null +++ b/src/plugins/regexp/regexpwindow.h @@ -0,0 +1,92 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef REGEXPWINDOW_H +#define REGEXPWINDOW_H + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE +class QLabel; +class QComboBox; +class QCheckBox; +class QLineEdit; +QT_END_NAMESPACE + +namespace RegExp { +namespace Internal { + +struct Settings; + +class RegExpWindow : public QWidget +{ + Q_OBJECT + +public: + RegExpWindow(QWidget *parent = 0); + + Settings settings() const; + void setSettings(const Settings &s); + +protected: + void contextMenuEvent(QContextMenuEvent *); + +private slots: + void refresh(); + void enterEscaped(); + +private: + QLabel *patternLabel; + QLabel *escapedPatternLabel; + QLabel *syntaxLabel; + QLabel *textLabel; + QComboBox *patternComboBox; + QLineEdit *escapedPatternLineEdit; + QComboBox *textComboBox; + QCheckBox *caseSensitiveCheckBox; + QCheckBox *minimalCheckBox; + QComboBox *syntaxComboBox; + + QLabel *indexLabel; + QLabel *matchedLengthLabel; + QLineEdit *indexEdit; + QLineEdit *matchedLengthEdit; + + enum { MaxCaptures = 6 }; + QLabel *captureLabels[MaxCaptures]; + QLineEdit *captureEdits[MaxCaptures]; +}; + +} //namespace Internal +} //namespace RegExp + +#endif diff --git a/src/plugins/regexp/settings.cpp b/src/plugins/regexp/settings.cpp new file mode 100644 index 00000000000..336bfc0878d --- /dev/null +++ b/src/plugins/regexp/settings.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "settings.h" +#include <QtCore/QSettings> + +static const char *syntaxKey = "Syntax"; +static const char *minimalKey = "Minimal"; +static const char *caseSensitiveKey = "CaseSensitive"; +static const char *patternKey = "Patterns"; +static const char *currentPatternKey = "CurrentPattern"; +static const char *matchKey = "Matches"; +static const char *currentMatchKey = "CurrentMatch"; +static const char *patternDefault = "[A-Za-z_]+([A-Za-z_0-9]*)"; +static const char *matchDefault = "(10 + delta4) * 32"; +static const char *settingsGroup = "RegExp"; + +namespace RegExp { +namespace Internal { + +Settings::Settings() : + m_patternSyntax(QRegExp::RegExp), + m_minimal(false), + m_caseSensitive(true), + m_patterns(QLatin1String(patternDefault)), + m_currentPattern(m_patterns.front()), + m_matches(QLatin1String(matchDefault)), + m_currentMatch(m_matches.front()) +{ +} + +void Settings::clear() +{ + *this = Settings(); +} + +void Settings::fromQSettings(QSettings *s) +{ + clear(); + s->beginGroup(QLatin1String(settingsGroup)); + m_patternSyntax = static_cast<QRegExp::PatternSyntax>(s->value(QLatin1String(syntaxKey), m_patternSyntax).toInt()); + m_minimal = s->value(QLatin1String(minimalKey), m_minimal).toBool(); + m_caseSensitive = s->value(QLatin1String(caseSensitiveKey), m_caseSensitive).toBool(); + m_patterns = s->value(QLatin1String(patternKey), m_patterns).toStringList(); + m_currentPattern = s->value(QLatin1String(currentPatternKey), m_currentPattern).toString(); + m_matches = s->value(QLatin1String(matchKey), m_matches).toStringList(); + m_currentMatch = s->value(QLatin1String(currentMatchKey), m_currentMatch).toString(); + s->endGroup(); +} + +void Settings::toQSettings(QSettings *s) const +{ + s->beginGroup(QLatin1String(settingsGroup)); + s->setValue(QLatin1String(syntaxKey), m_patternSyntax); + s->setValue(QLatin1String(minimalKey), m_minimal); + s->setValue(QLatin1String(caseSensitiveKey), m_caseSensitive); + s->setValue(QLatin1String(patternKey), m_patterns); + s->setValue(QLatin1String(currentPatternKey), m_currentPattern); + s->setValue(QLatin1String(matchKey), m_matches); + s->setValue(QLatin1String(currentMatchKey), m_currentMatch); + s->endGroup(); +} + +} +} diff --git a/src/plugins/regexp/settings.h b/src/plugins/regexp/settings.h new file mode 100644 index 00000000000..779ec8611e6 --- /dev/null +++ b/src/plugins/regexp/settings.h @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef REGEXP_SETTINGS_H +#define REGEXP_SETTINGS_H + +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QRegExp> + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace RegExp { +namespace Internal { + +// Settings of the Regexp plugin. +// Provides methods to serialize into QSettings. + +struct Settings { + Settings(); + void clear(); + void fromQSettings(QSettings *s); + void toQSettings(QSettings *s) const; + + QRegExp::PatternSyntax m_patternSyntax; + bool m_minimal; + bool m_caseSensitive; + + QStringList m_patterns; + QString m_currentPattern; + QStringList m_matches; + QString m_currentMatch; +}; + +} // namespace Internal +} // namespace RegExp + +#endif // REGEXP_SETTINGS_H diff --git a/src/plugins/resourceeditor/ResourceEditor.mimetypes.xml b/src/plugins/resourceeditor/ResourceEditor.mimetypes.xml new file mode 100644 index 00000000000..82a1797028d --- /dev/null +++ b/src/plugins/resourceeditor/ResourceEditor.mimetypes.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="application/vnd.nokia.xml.qt.resource"> + <sub-class-of type="text/xml"/> + <comment>Qt Resource file</comment> + <glob pattern="*.qrc"/> + </mime-type> +</mime-info> diff --git a/src/plugins/resourceeditor/ResourceEditor.pluginspec b/src/plugins/resourceeditor/ResourceEditor.pluginspec new file mode 100644 index 00000000000..cddde0b0d9d --- /dev/null +++ b/src/plugins/resourceeditor/ResourceEditor.pluginspec @@ -0,0 +1,10 @@ +<plugin name="ResourceEditor" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Editor for qrc files.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/resourceeditor/images/qt_qrc.png b/src/plugins/resourceeditor/images/qt_qrc.png Binary files differnew file mode 100644 index 00000000000..d22ca676103 --- /dev/null +++ b/src/plugins/resourceeditor/images/qt_qrc.png diff --git a/src/plugins/resourceeditor/resourceeditor.pro b/src/plugins/resourceeditor/resourceeditor.pro new file mode 100644 index 00000000000..e91099d9630 --- /dev/null +++ b/src/plugins/resourceeditor/resourceeditor.pro @@ -0,0 +1,24 @@ +TEMPLATE = lib +TARGET = ResourceEditor + +qtAddLibrary(QtDesigner) + +include(../../qworkbenchplugin.pri) +include(../../libs/utils/utils.pri) +include(../../plugins/coreplugin/coreplugin.pri) +include(../../../shared/qrceditor/qrceditor.pri) + +INCLUDEPATH += $$PWD/../../tools/utils + +HEADERS += resourceeditorfactory.h \ +resourceeditorplugin.h \ +resourcewizard.h \ +resourceeditorw.h \ +resourceeditorconstants.h + +SOURCES +=resourceeditorfactory.cpp \ +resourceeditorplugin.cpp \ +resourcewizard.cpp \ +resourceeditorw.cpp + +RESOURCES += resourceeditor.qrc diff --git a/src/plugins/resourceeditor/resourceeditor.qrc b/src/plugins/resourceeditor/resourceeditor.qrc new file mode 100644 index 00000000000..2265055f972 --- /dev/null +++ b/src/plugins/resourceeditor/resourceeditor.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/resourceeditor" > + <file>images/qt_qrc.png</file> + <file>ResourceEditor.mimetypes.xml</file> + </qresource> +</RCC> diff --git a/src/plugins/resourceeditor/resourceeditorconstants.h b/src/plugins/resourceeditor/resourceeditorconstants.h new file mode 100644 index 00000000000..9786a681e41 --- /dev/null +++ b/src/plugins/resourceeditor/resourceeditorconstants.h @@ -0,0 +1,44 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef RESOURCEEDITOR_CONSTANTS_H +#define RESOURCEEDITOR_CONSTANTS_H + +namespace ResourceEditor { +namespace Constants { +const char * const C_RESOURCEEDITOR = "Resource Editor"; +const char * const C_RESOURCEWINDOW = "Resourcewindow"; +const char * const C_RESOURCE_MIMETYPE = "application/vnd.nokia.xml.qt.resource"; +} +} + +#endif // RESOURCEEDITOR_CONSTANTS_H diff --git a/src/plugins/resourceeditor/resourceeditorfactory.cpp b/src/plugins/resourceeditor/resourceeditorfactory.cpp new file mode 100644 index 00000000000..8897b11524a --- /dev/null +++ b/src/plugins/resourceeditor/resourceeditorfactory.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "resourceeditorfactory.h" +#include "resourceeditorw.h" +#include "resourceeditorplugin.h" +#include "resourceeditorconstants.h" + +#include <coreplugin/icore.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/fileiconprovider.h> +#include <coreplugin/editormanager/editormanager.h> + +#include <QtCore/QFileInfo> +#include <QtCore/qdebug.h> + +using namespace ResourceEditor::Internal; +using namespace ResourceEditor::Constants; + +ResourceEditorFactory::ResourceEditorFactory(Core::ICore *core, ResourceEditorPlugin *plugin) : + Core::IEditorFactory(plugin), + m_mimeTypes(QStringList(QLatin1String("application/vnd.nokia.xml.qt.resource"))), + m_kind(QLatin1String(C_RESOURCEEDITOR)), + m_core(core), + m_plugin(plugin) +{ + m_context += m_core->uniqueIDManager() + ->uniqueIdentifier(QLatin1String(ResourceEditor::Constants::C_RESOURCEEDITOR)); + Core::FileIconProvider *iconProvider = Core::FileIconProvider::instance(); + iconProvider->registerIconForSuffix(QIcon(":/resourceeditor/images/qt_qrc.png"), + QLatin1String("qrc")); +} + +QString ResourceEditorFactory::kind() const +{ + return m_kind; +} + +Core::IFile *ResourceEditorFactory::open(const QString &fileName) +{ + Core::IEditor *iface = m_core->editorManager()->openEditor(fileName, kind()); + if (!iface) { + qWarning() << "ResourceEditorFactory::open: openEditor failed for " << fileName; + return 0; + } + return iface->file(); +} + +Core::IEditor *ResourceEditorFactory::createEditor(QWidget *parent) +{ + return new ResourceEditorW(m_context, m_core, m_plugin, parent); +} + +QStringList ResourceEditorFactory::mimeTypes() const +{ + return m_mimeTypes; +} diff --git a/src/plugins/resourceeditor/resourceeditorfactory.h b/src/plugins/resourceeditor/resourceeditorfactory.h new file mode 100644 index 00000000000..9f22743a357 --- /dev/null +++ b/src/plugins/resourceeditor/resourceeditorfactory.h @@ -0,0 +1,80 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef RRESOURCEEDITORFACTORY_H +#define RRESOURCEEDITORFACTORY_H + +#include <extensionsystem/ExtensionSystemInterfaces> +#include <coreplugin/editormanager/ieditorfactory.h> + +#include <QtCore/QStringList> + +namespace Core { +class ICore; +class IEditor; +class IFile; +} + +namespace ResourceEditor { +namespace Internal { + +class ResourceEditorPlugin; + +class ResourceEditorFactory : public Core::IEditorFactory +{ + Q_OBJECT + +public: + typedef QList<int> Context; + + ResourceEditorFactory(Core::ICore *core, ResourceEditorPlugin *plugin); + + virtual QStringList mimeTypes() const; + + //EditorFactory + QString kind() const; + Core::IFile *open(const QString &fileName); + Core::IEditor *createEditor(QWidget *parent); + +private: + const QStringList m_mimeTypes; + const QString m_kind; + Context m_context; + + Core::ICore *m_core; + ResourceEditorPlugin *m_plugin; +}; + +} // namespace Internal +} // namespace ResourceEditor + +#endif // RRESOURCEEDITORFACTORY_H diff --git a/src/plugins/resourceeditor/resourceeditorplugin.cpp b/src/plugins/resourceeditor/resourceeditorplugin.cpp new file mode 100644 index 00000000000..d0335db5a87 --- /dev/null +++ b/src/plugins/resourceeditor/resourceeditorplugin.cpp @@ -0,0 +1,131 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "resourceeditorplugin.h" +#include "resourceeditorw.h" +#include "resourceeditorconstants.h" +#include "resourcewizard.h" +#include "resourceeditorfactory.h" + +#include <coreplugin/icore.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> + +#include <QtCore/qplugin.h> +#include <QtGui/QAction> + +using namespace ResourceEditor::Internal; + +ResourceEditorPlugin::ResourceEditorPlugin() : + m_wizard(0), + m_editor(0), + m_core(NULL), + m_redoAction(NULL), + m_undoAction(NULL) +{ +} + +ResourceEditorPlugin::~ResourceEditorPlugin() +{ + removeObject(m_editor); + removeObject(m_wizard); +} + +bool ResourceEditorPlugin::initialize(const QStringList & /*arguments*/, QString *error_message) +{ + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + if (!m_core->mimeDatabase()->addMimeTypes(QLatin1String(":/resourceeditor/ResourceEditor.mimetypes.xml"), error_message)) + return false; + + m_editor = new ResourceEditorFactory(m_core, this); + addObject(m_editor); + + Core::BaseFileWizardParameters wizardParameters(Core::IWizard::FileWizard); + wizardParameters.setDescription(tr("Resource file")); + wizardParameters.setName(tr("Resource file")); + wizardParameters.setCategory(QLatin1String("Qt")); + wizardParameters.setTrCategory(tr("Qt")); + + m_wizard = new ResourceWizard(wizardParameters, m_core, this); + addObject(m_wizard); + + error_message->clear(); + + // Register undo and redo + Core::ActionManagerInterface * const actionManager = m_core->actionManager(); + int const pluginId = m_core->uniqueIDManager()->uniqueIdentifier( + Constants::C_RESOURCEEDITOR); + QList<int> const idList = QList<int>() << pluginId; + m_undoAction = new QAction(tr("&Undo"), this); + m_redoAction = new QAction(tr("&Redo"), this); + actionManager->registerAction(m_undoAction, Core::Constants::UNDO, idList); + actionManager->registerAction(m_redoAction, Core::Constants::REDO, idList); + connect(m_undoAction, SIGNAL(triggered()), this, SLOT(onUndo())); + connect(m_redoAction, SIGNAL(triggered()), this, SLOT(onRedo())); + + return true; +} + +void ResourceEditorPlugin::extensionsInitialized() +{ +} + +void ResourceEditorPlugin::onUndo() +{ + currentEditor()->onUndo(); +} + +void ResourceEditorPlugin::onRedo() +{ + currentEditor()->onRedo(); +} + +void ResourceEditorPlugin::onUndoStackChanged(ResourceEditorW const *editor, + bool canUndo, bool canRedo) +{ + if (editor == currentEditor()) { + m_undoAction->setEnabled(canUndo); + m_redoAction->setEnabled(canRedo); + } +} + +ResourceEditorW * ResourceEditorPlugin::currentEditor() const { + ResourceEditorW * const focusEditor = qobject_cast<ResourceEditorW *>( + m_core->editorManager()->currentEditor()); + Q_ASSERT(focusEditor); + return focusEditor; +} + +Q_EXPORT_PLUGIN(ResourceEditorPlugin) diff --git a/src/plugins/resourceeditor/resourceeditorplugin.h b/src/plugins/resourceeditor/resourceeditorplugin.h new file mode 100644 index 00000000000..6f75275da09 --- /dev/null +++ b/src/plugins/resourceeditor/resourceeditorplugin.h @@ -0,0 +1,84 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef RESOURCEEDITORPLUGIN_H +#define RESOURCEEDITORPLUGIN_H + +#include <extensionsystem/iplugin.h> + +QT_FORWARD_DECLARE_CLASS(QAction); + +namespace Core { + class ICore; +} + +namespace ResourceEditor { +namespace Internal { + +class ResourceEditorW; +class ResourceWizard; +class ResourceEditorFactory; + +class ResourceEditorPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + ResourceEditorPlugin(); + virtual ~ResourceEditorPlugin(); + + //Plugin + bool initialize(const QStringList &arguments, QString *error_message = 0); + void extensionsInitialized(); + +private slots: + void onUndo(); + void onRedo(); + +public: + void onUndoStackChanged(ResourceEditorW const *editor, bool canUndo, bool canRedo); + +private: + ResourceEditorW * currentEditor() const; + +private: + ResourceWizard *m_wizard; + ResourceEditorFactory *m_editor; + Core::ICore *m_core; + QAction *m_redoAction; + QAction *m_undoAction; +}; + +} // namespace Internal +} // namespace ResourceEditor + +#endif // RESOURCEEDITORPLUGIN_H diff --git a/src/plugins/resourceeditor/resourceeditorw.cpp b/src/plugins/resourceeditor/resourceeditorw.cpp new file mode 100644 index 00000000000..9eea18bce1f --- /dev/null +++ b/src/plugins/resourceeditor/resourceeditorw.cpp @@ -0,0 +1,271 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "resourceeditorw.h" +#include "resourceeditorplugin.h" +#include "resourceeditorconstants.h" + +#include <qrceditor.h> + +#include <QtCore/QTemporaryFile> +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/qdebug.h> +#include <QtGui/QMainWindow> +#include <QtGui/QHBoxLayout> + +#include <coreplugin/icore.h> +#include <utils/reloadpromptutils.h> + +namespace ResourceEditor { +namespace Internal { + +enum { debugResourceEditorW = 0 }; + + + +ResourceEditorFile::ResourceEditorFile(ResourceEditorW *parent) : + IFile(parent), + m_mimeType(QLatin1String(ResourceEditor::Constants::C_RESOURCE_MIMETYPE)), + m_parent(parent) +{ + if (debugResourceEditorW) + qDebug() << "ResourceEditorFile::ResourceEditorFile()"; +} + +QString ResourceEditorFile::mimeType() const +{ + return m_mimeType; +} + + +ResourceEditorW::ResourceEditorW(const QList<int> &context, + Core::ICore *core, + ResourceEditorPlugin *plugin, + QWidget *parent) + : m_context(context), + m_core(core), + m_resourceEditor(new SharedTools::QrcEditor(parent)), + m_resourceFile(new ResourceEditorFile(this)), + m_plugin(plugin) +{ + m_resourceEditor->setResourceDragEnabled(true); + m_resourceEditor->layout()->setMargin(9); + + connect(m_resourceEditor, SIGNAL(dirtyChanged(bool)), this, SLOT(dirtyChanged(bool))); + connect(m_resourceEditor, SIGNAL(undoStackChanged(bool, bool)), + this, SLOT(onUndoStackChanged(bool, bool))); + connect(m_resourceFile, SIGNAL(changed()), this, SIGNAL(changed())); + if (debugResourceEditorW) + qDebug() << "ResourceEditorW::ResourceEditorW()"; +} + +ResourceEditorW::~ResourceEditorW() +{ + if (m_resourceEditor) + m_resourceEditor->deleteLater(); +} + +bool ResourceEditorW::createNew(const QString &contents) +{ + QTemporaryFile tempFile(0); + tempFile.setAutoRemove(true); + if (!tempFile.open()) + return false; + const QString tempFileName = tempFile.fileName(); + tempFile.write(contents.toUtf8()); + tempFile.close(); + + const bool rc = m_resourceEditor->load(tempFileName); + m_resourceEditor->setFileName(QString()); + if (debugResourceEditorW) + qDebug() << "ResourceEditorW::createNew: " << contents << " (" << tempFileName << ") returns " << rc; + return rc; +} + +bool ResourceEditorW::open(const QString &fileName /*= QString()*/) +{ + if (debugResourceEditorW) + qDebug() << "ResourceEditorW::open: " << fileName; + + if (fileName.isEmpty()) { + setDisplayName(tr("untitled")); + return true; + } + + const QFileInfo fi(fileName); + + const QString absFileName = fi.absoluteFilePath(); + + if (!fi.isReadable()) + return false; + + if (!m_resourceEditor->load(absFileName)) + return false; + + setDisplayName(fi.fileName()); + + emit changed(); + return true; +} + +bool ResourceEditorFile::save(const QString &name /*= QString()*/) +{ + if (debugResourceEditorW) + qDebug() << ">ResourceEditorW::save: " << name; + + const QString oldFileName = fileName(); + const QString actualName = name.isEmpty() ? oldFileName : name; + if (actualName.isEmpty()) + return false; + + m_parent->m_resourceEditor->setFileName(actualName); + if (!m_parent->m_resourceEditor->save()) { + m_parent->m_resourceEditor->setFileName(oldFileName); + return false; + } + + m_parent->m_resourceEditor->setDirty(false); + m_parent->setDisplayName(QFileInfo(actualName).fileName()); + + emit changed(); + return true; +} + +const char *ResourceEditorW::kind() const { + return ResourceEditor::Constants::C_RESOURCEWINDOW; +} + +QString ResourceEditorFile::fileName() const +{ + return m_parent->m_resourceEditor->fileName(); +} + +bool ResourceEditorFile::isModified() const +{ + return m_parent->m_resourceEditor->isDirty(); +} + +bool ResourceEditorFile::isReadOnly() const +{ + const QString fileName = m_parent->m_resourceEditor->fileName(); + if (fileName.isEmpty()) + return false; + const QFileInfo fi(fileName); + return !fi.isWritable(); +} + +bool ResourceEditorFile::isSaveAsAllowed() const +{ + return true; +} + +void ResourceEditorFile::modified(Core::IFile::ReloadBehavior *behavior) +{ + const QString fileName = m_parent->m_resourceEditor->fileName(); + + switch (*behavior) { + case Core::IFile::ReloadNone: + return; + case Core::IFile::ReloadAll: + m_parent->open(fileName); + return; + case Core::IFile::ReloadPermissions: + emit changed(); + return; + case Core::IFile::AskForReload: + break; + } + + switch (Core::Utils::reloadPrompt(fileName, m_parent->m_core->mainWindow())) { + case Core::Utils::ReloadCurrent: + m_parent->open(fileName); + break; + case Core::Utils::ReloadAll: + m_parent->open(fileName); + *behavior = Core::IFile::ReloadAll; + break; + case Core::Utils::ReloadSkipCurrent: + break; + case Core::Utils::ReloadNone: + *behavior = Core::IFile::ReloadNone; + break; + } +} + +QString ResourceEditorFile::defaultPath() const +{ + return QString(); +} + +void ResourceEditorW::setSuggestedFileName(const QString &fileName) +{ + m_suggestedName = fileName; +} + +QString ResourceEditorFile::suggestedFileName() const +{ + return m_parent->m_suggestedName; +} + +void ResourceEditorW::dirtyChanged(bool dirty) +{ + if (debugResourceEditorW) + qDebug() << " ResourceEditorW::dirtyChanged" << dirty; + if (dirty) + emit changed(); +} + +QWidget *ResourceEditorW::widget() +{ + return m_resourceEditor; /* we know it's a subclass of QWidget...*/ +} + +void ResourceEditorW::onUndoStackChanged(bool canUndo, bool canRedo) +{ + m_plugin->onUndoStackChanged(this, canUndo, canRedo); +} + +void ResourceEditorW::onUndo() +{ + if (!m_resourceEditor.isNull()) + m_resourceEditor.data()->onUndo(); +} + +void ResourceEditorW::onRedo() +{ + if (!m_resourceEditor.isNull()) + m_resourceEditor.data()->onRedo(); +} + +} +} diff --git a/src/plugins/resourceeditor/resourceeditorw.h b/src/plugins/resourceeditor/resourceeditorw.h new file mode 100644 index 00000000000..b580f37d906 --- /dev/null +++ b/src/plugins/resourceeditor/resourceeditorw.h @@ -0,0 +1,143 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef RESOURCEDITORW_H +#define RESOURCEDITORW_H + +#include <coreplugin/ifile.h> +#include <coreplugin/editormanager/ieditor.h> + +#include <QtGui/QWidget> +#include <QtCore/QPointer> + + +namespace Core { + class ICore; +} + +namespace SharedTools { + class QrcEditor; +} + +namespace ResourceEditor { +namespace Internal { + +class ResourceEditorPlugin; +class ResourceEditorW; + +class ResourceEditorFile + : public virtual Core::IFile +{ + Q_OBJECT + +public: + typedef QList<int> Context; + + ResourceEditorFile(ResourceEditorW *parent = 0); + + //IFile + bool save(const QString &fileName = QString()); + QString fileName() const; + bool isModified() const; + bool isReadOnly() const; + bool isSaveAsAllowed() const; + void modified(Core::IFile::ReloadBehavior *behavior); + QString defaultPath() const; + QString suggestedFileName() const; + virtual QString mimeType() const; + +signals: + void changed(); + +private: + const QString m_mimeType; + ResourceEditorW *m_parent; +}; + +class ResourceEditorW : public Core::IEditor +{ + Q_OBJECT + +public: + typedef QList<int> Context; + + ResourceEditorW(const Context &context, + Core::ICore *core, + ResourceEditorPlugin *plugin, + QWidget *parent = 0); + ~ResourceEditorW(); + + // IEditor + bool createNew(const QString &contents); + bool open(const QString &fileName = QString()); + bool duplicateSupported() const { return false; } + Core::IEditor *duplicate(QWidget *) { return 0; } + Core::IFile *file() { return m_resourceFile; } + const char *kind() const; + QString displayName() const { return m_displayName; } + void setDisplayName(const QString &title) { m_displayName = title; } + QToolBar *toolBar() { return 0; } + QByteArray saveState() const { return QByteArray(); } + bool restoreState(const QByteArray &/*state*/) { return true; } + + // ContextInterface + Context context() const { return m_context; } + QWidget *widget(); + + void setSuggestedFileName(const QString &fileName); + +private slots: + void dirtyChanged(bool); + void onUndoStackChanged(bool canUndo, bool canRedo); + +private: + const QString m_extension; + const QString m_fileFilter; + QString m_displayName; + QString m_suggestedName; + const Context m_context; + Core::ICore *m_core; + QPointer<SharedTools::QrcEditor> m_resourceEditor; + ResourceEditorFile *m_resourceFile; + ResourceEditorPlugin *m_plugin; + +public: + void onUndo(); + void onRedo(); + + friend class ResourceEditorFile; +}; + +} // namespace Internal +} // namespace ResourceEditor + +#endif //RESOURCEDITORW_H diff --git a/src/plugins/resourceeditor/resourcewizard.cpp b/src/plugins/resourceeditor/resourcewizard.cpp new file mode 100644 index 00000000000..ce1b8eb8f18 --- /dev/null +++ b/src/plugins/resourceeditor/resourcewizard.cpp @@ -0,0 +1,59 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "resourcewizard.h" +#include "resourceeditorw.h" +#include "resourceeditorconstants.h" + +namespace ResourceEditor { +namespace Internal { + +ResourceWizard::ResourceWizard(const BaseFileWizardParameters ¶meters, Core::ICore *core, QObject *parent) : + Core::StandardFileWizard(parameters, core, parent) +{ +} + +Core::GeneratedFiles + ResourceWizard::generateFilesFromPath(const QString &path, + const QString &name, + QString * /*errorMessage*/) const +{ + const QString suffix = preferredSuffix(QLatin1String(Constants::C_RESOURCE_MIMETYPE)); + const QString fileName = Core::BaseFileWizard::buildFileName(path, name, suffix); + Core::GeneratedFile file(fileName); + file.setContents(QLatin1String("<RCC/>")); + file.setEditorKind(QLatin1String(Constants::C_RESOURCEEDITOR)); + return Core::GeneratedFiles() << file; +} + +} +} diff --git a/src/plugins/resourceeditor/resourcewizard.h b/src/plugins/resourceeditor/resourcewizard.h new file mode 100644 index 00000000000..40abe4612c7 --- /dev/null +++ b/src/plugins/resourceeditor/resourcewizard.h @@ -0,0 +1,59 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef RESOURCEWIZARD_H +#define RESOURCEWIZARD_H + +#include <coreplugin/basefilewizard.h> + +namespace ResourceEditor { +namespace Internal { + +class ResourceWizard : public Core::StandardFileWizard +{ + Q_OBJECT + +public: + + typedef Core::BaseFileWizardParameters BaseFileWizardParameters; + explicit ResourceWizard(const BaseFileWizardParameters ¶meters, Core::ICore *core, QObject *parent); + +protected: + virtual Core::GeneratedFiles + generateFilesFromPath(const QString &path, const QString &name, + QString *errorMessage) const; +}; + +} // namespace Internal +} // namespace ResourceEditor + +#endif //RESOURCEWIZARD_H diff --git a/src/plugins/snippets/Snippets.pluginspec b/src/plugins/snippets/Snippets.pluginspec new file mode 100644 index 00000000000..362791eb16f --- /dev/null +++ b/src/plugins/snippets/Snippets.pluginspec @@ -0,0 +1,12 @@ +<plugin name="Snippets" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Code snippet plugin.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> + <dependency name="TextEditor" version="0.9.1"/> + <dependency name="ProjectExplorer" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/snippets/images/dir.png b/src/plugins/snippets/images/dir.png Binary files differnew file mode 100644 index 00000000000..57cec6bcd31 --- /dev/null +++ b/src/plugins/snippets/images/dir.png diff --git a/src/plugins/snippets/images/diropen.png b/src/plugins/snippets/images/diropen.png Binary files differnew file mode 100644 index 00000000000..48fcb9b703b --- /dev/null +++ b/src/plugins/snippets/images/diropen.png diff --git a/src/plugins/snippets/images/file.png b/src/plugins/snippets/images/file.png Binary files differnew file mode 100644 index 00000000000..4a24ce3c2f4 --- /dev/null +++ b/src/plugins/snippets/images/file.png diff --git a/src/plugins/snippets/images/snippets.png b/src/plugins/snippets/images/snippets.png Binary files differnew file mode 100644 index 00000000000..b799041d618 --- /dev/null +++ b/src/plugins/snippets/images/snippets.png diff --git a/src/plugins/snippets/inputwidget.cpp b/src/plugins/snippets/inputwidget.cpp new file mode 100644 index 00000000000..782e1d1c01b --- /dev/null +++ b/src/plugins/snippets/inputwidget.cpp @@ -0,0 +1,134 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtCore/QDebug> +#include <QtGui/QApplication> +#include <QtGui/QLabel> +#include <QtGui/QLineEdit> +#include <QtGui/QHBoxLayout> +#include <QtGui/QBitmap> +#include <QtGui/QPainter> +#include <QtGui/QResizeEvent> + +#include "inputwidget.h" + +using namespace Snippets::Internal; + +InputWidget::InputWidget(const QString &text, const QString &value) + : QFrame(0, Qt::Popup) +{ + setAttribute(Qt::WA_DeleteOnClose); + + m_label = new QLabel(); + m_label->setTextFormat(Qt::RichText); + m_label->setText(text); + + m_lineEdit = new QLineEdit(); + m_lineEdit->setText(value); + m_lineEdit->setSelection(0, value.length()); + + qApp->installEventFilter(this); + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(m_label); + layout->addWidget(m_lineEdit); + layout->setMargin(3); + + setLayout(layout); + ensurePolished(); + + setAutoFillBackground(false); +} + +void InputWidget::resizeEvent(QResizeEvent *event) +{ + int height = event->size().height(); + int width = event->size().width(); + qDebug() << event->size(); + + QPalette pal = palette(); + QLinearGradient bg(0,0,0,height); + bg.setColorAt(0, QColor(195,195,255)); + bg.setColorAt(1, QColor(230,230,255)); + pal.setBrush(QPalette::Background, QBrush(bg)); + setPalette(pal); + + QBitmap bm(width, height); + bm.fill(Qt::color0); + QPainter p(&bm); + p.setBrush(QBrush(Qt::color1, Qt::SolidPattern)); + p.setPen(Qt::color1); + int rw = (25 * height) / width; + p.drawRoundRect(0,0,width,height, rw, 25); + setMask(bm); +} + +void InputWidget::showInputWidget(const QPoint &position) +{ + move(position); + show(); + m_lineEdit->setFocus(); +} + +bool InputWidget::eventFilter(QObject *o, QEvent *e) +{ + if (o != m_lineEdit) { + switch (e->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: + closeInputWidget(true); + default: + break; + } + } else if (e->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast<QKeyEvent *>(e); + switch (ke->key()) { + case Qt::Key_Escape: + qDebug() << "Escape"; + closeInputWidget(true); + break; + case Qt::Key_Enter: + case Qt::Key_Return: + qDebug() << "Enter"; + closeInputWidget(false); + return true; + } + } + + return false; +} + +void InputWidget::closeInputWidget(bool cancel) +{ + emit finished(cancel, m_lineEdit->text()); + close(); +} diff --git a/src/plugins/snippets/inputwidget.h b/src/plugins/snippets/inputwidget.h new file mode 100644 index 00000000000..ef0386a300d --- /dev/null +++ b/src/plugins/snippets/inputwidget.h @@ -0,0 +1,70 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef INPUTWIDGET_H +#define INPUTWIDGET_H + +#include <QtGui/QFrame> + +QT_BEGIN_NAMESPACE +class QLabel; +class QLineEdit; +QT_END_NAMESPACE + +namespace Snippets { +namespace Internal { + +class InputWidget : public QFrame +{ + Q_OBJECT + +public: + InputWidget(const QString &text, const QString &value); + void showInputWidget(const QPoint &position); + +signals: + void finished(bool canceled, const QString &value); + +protected: + bool eventFilter(QObject *, QEvent *); + void resizeEvent(QResizeEvent *event); + +private: + void closeInputWidget(bool cancel); + QLabel *m_label; + QLineEdit *m_lineEdit; +}; + +} //namespace Internal +} //namespace Snippets + +#endif // INPUTWIDGET_H diff --git a/src/plugins/snippets/snippets.pro b/src/plugins/snippets/snippets.pro new file mode 100644 index 00000000000..bbfb4b75b59 --- /dev/null +++ b/src/plugins/snippets/snippets.pro @@ -0,0 +1,24 @@ +TEMPLATE = lib +TARGET = Snippets +QT += xml + +include(../../qworkbenchplugin.pri) +include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/texteditor/texteditor.pri) + +INCLUDEPATH += ../projectexplorer + +HEADERS += snippetsplugin.h \ + snippetswindow.h \ + snippetspec.h \ + snippetscompletion.h \ + inputwidget.h + +SOURCES += snippetsplugin.cpp \ + snippetswindow.cpp \ + snippetspec.cpp \ + snippetscompletion.cpp \ + inputwidget.cpp + +RESOURCES += snippets.qrc diff --git a/src/plugins/snippets/snippets.qrc b/src/plugins/snippets/snippets.qrc new file mode 100644 index 00000000000..b36585dcc5e --- /dev/null +++ b/src/plugins/snippets/snippets.qrc @@ -0,0 +1,8 @@ +<RCC> + <qresource prefix="/snippets" > + <file>images/file.png</file> + <file>images/dir.png</file> + <file>images/diropen.png</file> + <file>images/snippets.png</file> + </qresource> +</RCC> diff --git a/src/plugins/snippets/snippetscompletion.cpp b/src/plugins/snippets/snippetscompletion.cpp new file mode 100644 index 00000000000..cd6bf6a7587 --- /dev/null +++ b/src/plugins/snippets/snippetscompletion.cpp @@ -0,0 +1,159 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "snippetscompletion.h" + +#include "snippetswindow.h" +#include "snippetspec.h" +#include "snippetsplugin.h" + +#include <texteditor/itexteditable.h> + +#include <QtCore/QDebug> +#include <QtCore/QMap> +#include <QtGui/QAction> +#include <QtGui/QKeyEvent> + +using namespace Snippets::Internal; + +const QIcon SnippetsCompletion::m_fileIcon = QIcon(":/snippets/images/file.png"); + +SnippetsCompletion::SnippetsCompletion(QObject *parent) + : ICompletionCollector(parent) +{ + m_core = SnippetsPlugin::core(); + m_snippetsWnd = SnippetsPlugin::snippetsWindow(); + + updateCompletions(); +} + +SnippetsCompletion::~SnippetsCompletion() +{ + qDeleteAll(m_autoCompletions.values()); + m_autoCompletions.clear(); +} + +void SnippetsCompletion::updateCompletions() +{ + qDeleteAll(m_autoCompletions.values()); + m_autoCompletions.clear(); + + int index = 0; + foreach (SnippetSpec *spec, m_snippetsWnd->snippets()) { + if (!spec->completionShortcut().isEmpty()) { + TextEditor::CompletionItem *item = new TextEditor::CompletionItem; + item->m_key = spec->name(); + item->m_collector = this; + item->m_index = index; + item->m_relevance = 0; + m_autoCompletions.insert(spec->completionShortcut(), item); + ++index; + } + } +} + +bool SnippetsCompletion::triggersCompletion(TextEditor::ITextEditable *editor) +{ + QString currentWord = editor->textAt(editor->position() - 3, 3); + currentWord = currentWord.trimmed(); + return currentWord.length() == 2 && m_autoCompletions.contains(currentWord) && + !editor->characterAt(editor->position() - 1).isSpace(); +} + +int SnippetsCompletion::startCompletion(TextEditor::ITextEditable *editor) +{ + m_editor = editor; + m_startPosition = findStartOfName(m_editor); + return m_startPosition; +} + +void SnippetsCompletion::completions(QList<TextEditor::CompletionItem *> *completions) +{ + const int length = m_editor->position() - m_startPosition; + if (length >= 2) { + QString key = m_editor->textAt(m_startPosition, length); + foreach (TextEditor::CompletionItem* item, m_autoCompletions.values()) { + if (item->m_key.startsWith(key, Qt::CaseInsensitive)) { + (*completions) << item; + } + } + } +} + +QString SnippetsCompletion::text(TextEditor::CompletionItem *item) const +{ + const SnippetSpec *spec = m_snippetsWnd->snippets().at(item->m_index); + return spec->name(); +} + +QString SnippetsCompletion::details(TextEditor::CompletionItem *item) const +{ + const SnippetSpec *spec = m_snippetsWnd->snippets().at(item->m_index); + return spec->description(); +} + +QIcon SnippetsCompletion::icon(TextEditor::CompletionItem *) const +{ + return m_fileIcon; +} + +void SnippetsCompletion::complete(TextEditor::CompletionItem *item) +{ + SnippetSpec *spec = m_snippetsWnd->snippets().at(item->m_index); + + int length = m_editor->position() - m_startPosition; + m_editor->setCurPos(m_startPosition); + m_editor->remove(length); + + m_snippetsWnd->insertSnippet(m_editor, spec); +} + +bool SnippetsCompletion::partiallyComplete() +{ + return false; +} + +void SnippetsCompletion::cleanup() +{ +} + +int SnippetsCompletion::findStartOfName(const TextEditor::ITextEditor *editor) +{ + int pos = editor->position() - 1; + QChar chr = editor->characterAt(pos); + + // Skip to the start of a name + while (!chr.isSpace() && !chr.isNull()) + chr = editor->characterAt(--pos); + + return pos + 1; +} diff --git a/src/plugins/snippets/snippetscompletion.h b/src/plugins/snippets/snippetscompletion.h new file mode 100644 index 00000000000..02dfdbc2579 --- /dev/null +++ b/src/plugins/snippets/snippetscompletion.h @@ -0,0 +1,99 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SNIPPETSCOMPLETION_H +#define SNIPPETSCOMPLETION_H + +#include <QtCore/QObject> +#include <QtCore/QMap> +#include <QtCore/QDir> +#include <QtGui/QIcon> + +#include <texteditor/icompletioncollector.h> + +namespace Core { +class ICore; +} + +namespace TextEditor { +class ITextEditable; +class ITextEditor; +} + +namespace Snippets { +namespace Internal { + +class SnippetsWindow; +class SnippetSpec; + +class SnippetsCompletion : public TextEditor::ICompletionCollector +{ + Q_OBJECT +public: + SnippetsCompletion(QObject *parent); + ~SnippetsCompletion(); + + // ICompletionCollector + bool triggersCompletion(TextEditor::ITextEditable *editor); + int startCompletion(TextEditor::ITextEditable *editor); + void completions(QList<TextEditor::CompletionItem *> *completions); + + QString text(TextEditor::CompletionItem *item) const; + QString details(TextEditor::CompletionItem *item) const; + QIcon icon(TextEditor::CompletionItem *item) const; + + void complete(TextEditor::CompletionItem *item); + bool partiallyComplete(); + void cleanup(); + +private slots: + void updateCompletions(); + +private: + static int findStartOfName(const TextEditor::ITextEditor *editor); + + TextEditor::ITextEditable *m_editor; + int m_startPosition; // Position of the cursor from which completion started + + SnippetsWindow *m_snippetsWnd; + Core::ICore *m_core; + + QMultiMap<QString, TextEditor::CompletionItem *> m_autoCompletions; + + static const QIcon m_fileIcon; +}; + +} // namespace Internal +} // namespace Snippets + +#endif // SNIPPETSCOMPLETION_H + diff --git a/src/plugins/snippets/snippetspec.cpp b/src/plugins/snippets/snippetspec.cpp new file mode 100644 index 00000000000..06820de3dc6 --- /dev/null +++ b/src/plugins/snippets/snippetspec.cpp @@ -0,0 +1,100 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "snippetspec.h" +#include "persistentsettings.h" + +using namespace Snippets::Internal; +using ProjectExplorer::PersistentSettingsReader; + +bool SnippetSpec::load(const QString &fileName) +{ + PersistentSettingsReader reader; + if (!reader.load(fileName)) + return false; + + m_contents = reader.restoreValue(QLatin1String("Contents")).toString(); + m_name = reader.restoreValue(QLatin1String("Name")).toString(); + m_description = reader.restoreValue(QLatin1String("Description")).toString(); + m_category = reader.restoreValue(QLatin1String("Category")).toString(); + m_completionShortcut = reader.restoreValue(QLatin1String("Shortcut")).toString(); + + QMap<QString, QVariant> temp = reader.restoreValue(QLatin1String("Arguments")).toMap(); + QMap<QString, QVariant>::const_iterator it, end; + end = temp.constEnd(); + for (it = temp.constBegin(); it != end; ++it) { + m_argumentDescription.insert( it.key().toInt(), it.value().toString()); + } + + temp = reader.restoreValue(QLatin1String("ArgumentDefaults")).toMap(); + end = temp.constEnd(); + for (it = temp.constBegin(); it != end; ++it) { + m_argumentDefault.insert(it.key().toInt(), it.value().toString()); + } + + return true; +} + +QString SnippetSpec::contents() const +{ + return m_contents; +} + +QString SnippetSpec::name() const +{ + return m_name; +} + +QString SnippetSpec::description() const +{ + return m_description; +} + +QString SnippetSpec::category() const +{ + return m_category; +} + +QString SnippetSpec::completionShortcut() const +{ + return m_completionShortcut; +} + +QString SnippetSpec::argumentDescription(int id) const +{ + return m_argumentDescription.value(id); +} + +QString SnippetSpec::argumentDefault(int id) const +{ + return m_argumentDefault.value(id); +} diff --git a/src/plugins/snippets/snippetspec.h b/src/plugins/snippets/snippetspec.h new file mode 100644 index 00000000000..8a9d9525b9a --- /dev/null +++ b/src/plugins/snippets/snippetspec.h @@ -0,0 +1,68 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SNIPPETSPEC_H +#define SNIPPETSPEC_H + +#include <QtCore/QMap> +#include <QtCore/QString> + +namespace Snippets { +namespace Internal { + +class SnippetSpec +{ +public: + bool load(const QString &fileName); + + QString contents() const; + QString name() const; + QString description() const; + QString category() const; + QString completionShortcut() const; + QString argumentDescription(int id) const; + QString argumentDefault(int id) const; + +private: + QString m_contents; + QString m_name; + QString m_description; + QString m_category; + QString m_completionShortcut; + QMap<int, QString> m_argumentDescription; + QMap<int, QString> m_argumentDefault; +}; + +} //namespace Internal +} //namespace Snippets + +#endif // SNIPPETSPEC_H diff --git a/src/plugins/snippets/snippetsplugin.cpp b/src/plugins/snippets/snippetsplugin.cpp new file mode 100644 index 00000000000..ddb8319819b --- /dev/null +++ b/src/plugins/snippets/snippetsplugin.cpp @@ -0,0 +1,118 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "snippetswindow.h" +#include "snippetscompletion.h" +#include "snippetsplugin.h" +#include "snippetspec.h" + +#include <QtCore/qplugin.h> +#include <QtCore/QDebug> +#include <QtGui/QShortcut> +#include <QtGui/QApplication> + +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/CoreTools> +#include <texteditor/itexteditable.h> +#include <texteditor/texteditorconstants.h> + +using namespace Snippets::Internal; + +SnippetsPlugin *SnippetsPlugin::m_instance = 0; + +SnippetsPlugin::SnippetsPlugin() +{ + m_instance = this; +} + +SnippetsPlugin::~SnippetsPlugin() +{ + removeObject(m_snippetsCompletion); + delete m_snippetsCompletion; +} + +void SnippetsPlugin::extensionsInitialized() +{ +} + +bool SnippetsPlugin::initialize(const QStringList & /*arguments*/, QString *) +{ + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + Core::ActionManagerInterface *am = m_core->actionManager(); + + QList<int> context; + context << m_core->uniqueIDManager()->uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR); + + m_snippetWnd = new SnippetsWindow(); + addAutoReleasedObject(new Core::BaseView("Snippets.SnippetsTree", + m_snippetWnd, + QList<int>() << m_core->uniqueIDManager()->uniqueIdentifier(QLatin1String("Snippets Window")) + << m_core->uniqueIDManager()->uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR), + Qt::RightDockWidgetArea)); + m_snippetsCompletion = new SnippetsCompletion(this); + addObject(m_snippetsCompletion); + + foreach (SnippetSpec *snippet, m_snippetWnd->snippets()) { + QShortcut *sc = new QShortcut(m_snippetWnd); + Core::ICommand *cmd = am->registerShortcut(sc, simplifySnippetName(snippet), context); + cmd->setCategory(tr("Snippets")); + connect(sc, SIGNAL(activated()), this, SLOT(snippetActivated())); + m_shortcuts.insert(sc, snippet); + } + + return true; +} + +QString SnippetsPlugin::simplifySnippetName(SnippetSpec *snippet) const +{ + return QLatin1String("Snippets.") + + snippet->category().simplified().replace(QLatin1String(" "), QLatin1String("")) + + QLatin1Char('.') + + snippet->name().simplified().replace(QLatin1String(" "), QLatin1String("")); +} + +void SnippetsPlugin::snippetActivated() +{ + SnippetSpec *snippet = m_shortcuts.value(sender()); + if (snippet && m_core->editorManager()->currentEditor()) { + TextEditor::ITextEditable *te = + qobject_cast<TextEditor::ITextEditable *>( + m_core->editorManager()->currentEditor()); + m_snippetWnd->insertSnippet(te, snippet); + } +} + +Q_EXPORT_PLUGIN(SnippetsPlugin) diff --git a/src/plugins/snippets/snippetsplugin.h b/src/plugins/snippets/snippetsplugin.h new file mode 100644 index 00000000000..ab7fdbd69c0 --- /dev/null +++ b/src/plugins/snippets/snippetsplugin.h @@ -0,0 +1,90 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SNIPPETS_H +#define SNIPPETS_H + +#include <QtCore/QMap> +#include <QtCore/QObject> +#include <QtGui/QShortcut> + +#include <extensionsystem/iplugin.h> + +namespace Core { +class ICore; +struct Application; +} + +namespace Snippets { +namespace Internal { + +class SnippetsWindow; +class SnippetSpec; +class SnippetsCompletion; + +class SnippetsPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + SnippetsPlugin(); + virtual ~SnippetsPlugin(); + + static SnippetsPlugin *instance() { return m_instance; } + static SnippetsWindow *snippetsWindow() { return m_instance->m_snippetWnd; } + static Core::ICore *core() { return m_instance->m_core; } + + bool initialize(const QStringList &arguments, QString *error_message); + void extensionsInitialized(); + +private slots: + void snippetActivated(); + +private: + static SnippetsPlugin *m_instance; + + QString simplifySnippetName(SnippetSpec *snippet) const; + Core::ICore *m_core; + SnippetsCompletion *m_snippetsCompletion; + SnippetsWindow *m_snippetWnd; + + int m_textContext; + int m_snippetsMode; + QShortcut *m_exitShortcut; + QShortcut *m_modeShortcut; + QMap<QObject*, SnippetSpec*> m_shortcuts; +}; + +} // namespace Internal +} // namespace Snippets + +#endif // SNIPPETS_H diff --git a/src/plugins/snippets/snippetswindow.cpp b/src/plugins/snippets/snippetswindow.cpp new file mode 100644 index 00000000000..e44f21182d9 --- /dev/null +++ b/src/plugins/snippets/snippetswindow.cpp @@ -0,0 +1,434 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "snippetswindow.h" +#include "snippetspec.h" +#include "inputwidget.h" +#include "snippetsplugin.h" + +#include <coreplugin/icore.h> +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/itexteditable.h> +#include <texteditor/itexteditor.h> + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtGui/QDragEnterEvent> +#include <QtGui/QApplication> +#include <QtGui/QLabel> +#include <QtCore/QMimeData> +#include <QtGui/QHeaderView> + +using namespace Snippets::Internal; + +const QIcon SnippetsWindow::m_fileIcon = QIcon(":/snippets/images/file.png"); +const QIcon SnippetsWindow::m_dirIcon = QIcon(":/snippets/images/dir.png"); +const QIcon SnippetsWindow::m_dirOpenIcon = QIcon(":/snippets/images/diropen.png"); + +Q_DECLARE_METATYPE(Snippets::Internal::SnippetSpec *) + +SnippetsWindow::SnippetsWindow() +{ + m_core = SnippetsPlugin::core(); + + setWindowTitle(tr("Snippets")); + setWindowIcon(QIcon(":/snippets/images/snippets.png")); + setOrientation(Qt::Vertical); + + m_snippetsTree = new SnippetsTree(this); + addWidget(m_snippetsTree); + + m_descLabel = new QLabel(this); + m_descLabel->setAlignment(Qt::AlignTop|Qt::AlignLeft); + m_descLabel->setFrameShape(QFrame::Panel); + m_descLabel->setFrameShadow(QFrame::Raised); + m_descLabel->setWordWrap(true); + addWidget(m_descLabel); + + m_snippetsDir = QDir::home(); + if (!initSnippetsDir()) + setDisabled(true); + else { + QDir defaultDir(m_core->resourcePath() + QLatin1String("/snippets")); + if (defaultDir.exists()) + initSnippets(defaultDir); + initSnippets(m_snippetsDir); + } + + connect(m_snippetsTree, SIGNAL(itemCollapsed(QTreeWidgetItem *)), + this, SLOT(setClosedIcon(QTreeWidgetItem *))); + + connect(m_snippetsTree, SIGNAL(itemExpanded(QTreeWidgetItem *)), + this, SLOT(setOpenIcon(QTreeWidgetItem *))); + + connect(m_snippetsTree, SIGNAL(itemActivated(QTreeWidgetItem *, int)), + this, SLOT(activateSnippet(QTreeWidgetItem *, int))); + + connect(m_snippetsTree, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + this, SLOT(updateDescription(QTreeWidgetItem *))); +} + +SnippetsWindow::~SnippetsWindow() +{ + qDeleteAll(m_snippets); +} + + +void SnippetsWindow::activateSnippet(QTreeWidgetItem *item, int column) +{ + if (!item->parent()) + return; + + TextEditor::ITextEditable *editor = 0; + if (m_core->editorManager()->currentEditor()) + editor = qobject_cast<TextEditor::ITextEditable *>( + m_core->editorManager()->currentEditor()); + if (editor) { + SnippetSpec* spec = qVariantValue<SnippetSpec*>(item->data(0, Qt::UserRole)); + insertSnippet(editor, spec); + } + + Q_UNUSED(column); +} + +const QList<SnippetSpec *> &SnippetsWindow::snippets() const +{ + return m_snippets; +} + +void SnippetsWindow::initSnippets(const QDir &dir) +{ + QString name; + QString category; + + QMap<QString, QTreeWidgetItem *> categories; + for (int i = 0; i < m_snippetsTree->topLevelItemCount(); ++i) { + categories.insert(m_snippetsTree->topLevelItem(i)->text(0), + m_snippetsTree->topLevelItem(i)); + } + + foreach (const QString &snippet, dir.entryList(QStringList("*.snp"))) { + SnippetSpec *spec = new SnippetSpec(); + if (spec->load(dir.filePath(snippet))) { + if (!categories.contains(spec->category())) { + QTreeWidgetItem *citem = new QTreeWidgetItem(m_snippetsTree); + citem->setText(0, spec->category()); + citem->setIcon(0, m_dirIcon); + categories.insert(spec->category(), citem); + } + + QTreeWidgetItem *item = new QTreeWidgetItem( + categories.value(spec->category())); + item->setText(0, spec->name()); + item->setIcon(0, m_fileIcon); + QVariant v; + qVariantSetValue<SnippetSpec *>(v, spec); + item->setData(0, Qt::UserRole, v); + + m_snippets.append(spec); + } + } +} + +QString SnippetsWindow::createUniqueFileName() +{ + int fileNumber = 0; + QString baseName = "snippet"; + while (m_snippetsDir.exists(baseName + QString::number(fileNumber) + ".snp")) { + ++fileNumber; + } + return baseName + QString::number(fileNumber) + ".snp"; +} + +void SnippetsWindow::writeSnippet(const QMimeData *) +{ +} + +bool SnippetsWindow::initSnippetsDir() +{ + if (!m_snippetsDir.exists(".qworkbench")) + m_snippetsDir.mkdir(".qworkbench"); + if (!m_snippetsDir.cd(".qworkbench")) + return false; + + if (!m_snippetsDir.exists("snippets")) + m_snippetsDir.mkdir("snippets"); + return m_snippetsDir.cd("snippets"); +} + +void SnippetsWindow::getArguments() +{ + QString contents = m_currentSnippet->contents(); + int index = 0; + bool pc = false; + QString nrstr; + + QSet<int> requiredArgs; + m_requiredArgs.clear(); + m_args.clear(); + + while (index < contents.length()) { + QChar c = contents.at(index); + if (c == QLatin1Char('%')) { + pc = !pc; + } else if (pc) { + if (c.isNumber()) { + nrstr += c; + } else { + pc = false; + } + } + + if (!pc && !nrstr.isEmpty()) { + requiredArgs << nrstr.toInt(); + nrstr.clear(); + } + + ++index; + } + + m_requiredArgs = requiredArgs.toList(); + m_requiredArgs.prepend(-1); + + showInputWidget(false, QString()); +} + +void SnippetsWindow::showInputWidget(bool canceled, const QString &value) +{ + if (canceled) + return; + + TextEditor::ITextEditor *te = 0; + if (m_core->editorManager()->currentEditor()) + te = qobject_cast<TextEditor::ITextEditor*>( + m_core->editorManager()->currentEditor()); + + int arg = m_requiredArgs.takeFirst(); + if (arg != -1) + m_args << value; + + if (!te || m_requiredArgs.isEmpty()) { + qDebug("replaceAndInsert"); + replaceAndInsert(); + } else { + QString desc = m_currentSnippet->argumentDescription(m_requiredArgs.first()); + QString def = m_currentSnippet->argumentDefault(m_requiredArgs.first()); + foreach(QString arg, m_args) { + desc = desc.arg(arg); + def = def.arg(arg); + } + + InputWidget *iw = new InputWidget(desc, def); + connect(iw, SIGNAL(finished(bool, const QString &)), + this, SLOT(showInputWidget(bool, const QString &))); + iw->showInputWidget(te->cursorRect().bottomRight()); + } +} + +void SnippetsWindow::replaceAndInsert() +{ + QString result; + QString keyWord; + int setAnchor = -1; + int setCursor = -1; + int selLength = 0; + + //clean up selection + int startPos = m_currentEditor->position(TextEditor::ITextEditable::Anchor); + int endPos = m_currentEditor->position(); + + if (startPos < 0) { + startPos = endPos; + } else { + if (startPos > endPos) { + int tmp = startPos; + startPos = endPos; + endPos = tmp; + } + selLength = endPos - startPos; + } + + //parse the contents + m_currentEditor->setCurPos(startPos); + QString editorIndent = getCurrentIndent(m_currentEditor); + QString content = m_currentSnippet->contents(); + foreach (const QString &arg, m_args) { + content = content.arg(arg); + } + + int startOfKey = -1; + for (int i = 0; i<content.length(); ++i) { + //handle windows,mac and linux new lines... + if (content.at(i) == QLatin1Char('\n')) { + if ((i <= 0) || content.at(i-1) != QLatin1Char('\r')) + result += QLatin1Char('\n') + editorIndent; + continue; + } else if (content.at(i) == QLatin1Char('\r')) { + result += QLatin1Char('\n') + editorIndent; + continue; + } + + if (content.at(i) == QChar('$')) { + if (startOfKey != -1) { + m_currentEditor->insert(result); + if (keyWord == QLatin1String("selection")) { + const QString &indent = indentOfString(content, i); + int selStartPos = m_currentEditor->position(); + m_currentEditor->setCurPos(selStartPos + selLength); + insertIdents(m_currentEditor, indent, selStartPos, m_currentEditor->position()); + } else if (keyWord == QLatin1String("anchor")) { + setAnchor = m_currentEditor->position(); + } else if (keyWord == QLatin1String("cursor")) { + setCursor = m_currentEditor->position(); + } + result.clear(); + keyWord.clear(); + startOfKey = -1; + } else { + startOfKey = i; + } + } else { + if (startOfKey != -1) + keyWord += content.at(i).toLower(); + else + result += content.at(i); + } + } + + m_currentEditor->insert(result); + + if (setAnchor != -1) { + m_currentEditor->setCurPos(setAnchor); + m_currentEditor->select(setCursor); + } else if (setCursor != -1) { + m_currentEditor->setCurPos(setCursor); + } +} + +void SnippetsWindow::insertSnippet(TextEditor::ITextEditable *editor, SnippetSpec *snippet) +{ + m_currentEditor = editor; + m_currentSnippet = snippet; + getArguments(); +} + +QString SnippetsWindow::getCurrentIndent(TextEditor::ITextEditor *editor) +{ + const int startPos = editor->position(TextEditor::ITextEditor::StartOfLine); + const int endPos = editor->position(TextEditor::ITextEditor::EndOfLine); + if (startPos < endPos) + return indentOfString(editor->textAt(startPos, endPos - startPos)); + return QString(); +} + +void SnippetsWindow::insertIdents(TextEditor::ITextEditable *editor, + const QString &indent, int fromPos, int toPos) +{ + int offset = 0; + const int startPos = editor->position(); + editor->setCurPos(toPos); + int currentLinePos = editor->position(TextEditor::ITextEditor::StartOfLine); + while (currentLinePos > fromPos) { + editor->setCurPos(currentLinePos); + editor->insert(indent); + offset += indent.length(); + editor->setCurPos(currentLinePos-1); + currentLinePos = editor->position(TextEditor::ITextEditor::StartOfLine); + } + editor->setCurPos(startPos + offset); +} + +QString SnippetsWindow::indentOfString(const QString &str, int at) +{ + QString result; + int startAt = at; + if (startAt < 0) + startAt = str.length() - 1; + + // find start position + while (startAt >= 0 && str.at(startAt) != QChar('\n') + && str.at(startAt) != QChar('\r')) --startAt; + + for (int i = (startAt + 1); i < str.length(); ++i) { + if (str.at(i) == QChar(' ') || str.at(i) == QChar('\t')) + result += str.at(i); + else + break; + } + + return result; +} + +void SnippetsWindow::setOpenIcon(QTreeWidgetItem *item) +{ + item->setIcon(0, m_dirOpenIcon); +} + +void SnippetsWindow::setClosedIcon(QTreeWidgetItem *item) +{ + item->setIcon(0, m_dirIcon); +} + +void SnippetsWindow::updateDescription(QTreeWidgetItem *item) +{ + const SnippetSpec* spec = qVariantValue<SnippetSpec*>(item->data(0, Qt::UserRole)); + if (spec) { + m_descLabel->setText(QLatin1String("<b>") + spec->name() + QLatin1String("</b><br>") + + spec->description()); + } else { + m_descLabel->setText(QLatin1String("<b>") + item->text(0) + QLatin1String("</b><br>")); + } +} + +SnippetsTree::SnippetsTree(QWidget *parent) + : QTreeWidget(parent) +{ + setColumnCount(1); + header()->setVisible(false); + setAlternatingRowColors(true); + setAcceptDrops(true); +} + +void SnippetsTree::dropEvent(QDropEvent *) +{ + //writeSnippet(event->mimeData()); +} + +void SnippetsTree::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasText()) + event->acceptProposedAction(); +} + +void SnippetsTree::dragMoveEvent(QDragMoveEvent *) +{ +} diff --git a/src/plugins/snippets/snippetswindow.h b/src/plugins/snippets/snippetswindow.h new file mode 100644 index 00000000000..9b9f54c376d --- /dev/null +++ b/src/plugins/snippets/snippetswindow.h @@ -0,0 +1,128 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SNIPPETSWINDOW_H +#define SNIPPETSWINDOW_H + +#include <QtCore/QDir> +#include <QtGui/QTreeWidget> +#include <QtGui/QSplitter> +#include <QtGui/QIcon> + +QT_BEGIN_NAMESPACE +class QDir; +class QLabel; +QT_END_NAMESPACE + +namespace Core { +class ICore; +} + +namespace TextEditor { +class ITextEditable; +class ITextEditor; +} + +namespace Snippets { +namespace Internal { + +class SnippetSpec; +class SnippetsTree; +class InputWidget; + +class SnippetsWindow : public QSplitter +{ + Q_OBJECT + +public: + SnippetsWindow(); + ~SnippetsWindow(); + const QList<SnippetSpec *> &snippets() const; + void insertSnippet(TextEditor::ITextEditable *editor, SnippetSpec *snippet); + +private slots: + void updateDescription(QTreeWidgetItem *item); + void activateSnippet(QTreeWidgetItem *item, int column); + void setOpenIcon(QTreeWidgetItem *item); + void setClosedIcon(QTreeWidgetItem *item); + + void showInputWidget(bool canceled, const QString &value); + +private: + void getArguments(); + void replaceAndInsert(); + QString indentOfString(const QString &str, int at = -1); + void insertIdents(TextEditor::ITextEditable *editor, + const QString &indent, int fromPos, int toPos); + QString getCurrentIndent(TextEditor::ITextEditor *editor); + + QList<SnippetSpec *> m_snippets; + QString createUniqueFileName(); + void writeSnippet(const QMimeData *mData); + bool initSnippetsDir(); + void initSnippets(const QDir &dir); + + QList<int> m_requiredArgs; + QStringList m_args; + SnippetSpec *m_currentSnippet; + TextEditor::ITextEditable *m_currentEditor; + + Core::ICore *m_core; + QDir m_snippetsDir; + + SnippetsTree *m_snippetsTree; + + QLabel *m_descLabel; + + static const QIcon m_fileIcon; + static const QIcon m_dirIcon; + static const QIcon m_dirOpenIcon; +}; + +class SnippetsTree : public QTreeWidget +{ + Q_OBJECT + +public: + SnippetsTree(QWidget *parent); + +protected: + void dragMoveEvent(QDragMoveEvent * event); + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *event); +}; + +} // namespace Internal +} // namespace Snippets + +#endif // SNIPPETSWINDOW_H + diff --git a/src/plugins/subversion/Subversion.mimetypes.xml b/src/plugins/subversion/Subversion.mimetypes.xml new file mode 100644 index 00000000000..6aa0c3eda9b --- /dev/null +++ b/src/plugins/subversion/Subversion.mimetypes.xml @@ -0,0 +1,7 @@ +<?xml version="1.0"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="application/vnd.nokia.text.subversion.submit"> + <comment>Subversion submit template</comment> + <sub-class-of type="text/plain"/> + </mime-type> +</mime-info> diff --git a/src/plugins/subversion/Subversion.pluginspec b/src/plugins/subversion/Subversion.pluginspec new file mode 100644 index 00000000000..f503ff1340f --- /dev/null +++ b/src/plugins/subversion/Subversion.pluginspec @@ -0,0 +1,13 @@ +<plugin name="Subversion" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Subversion integration.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="TextEditor" version="0.9.1"/> + <dependency name="ProjectExplorer" version="0.9.1"/> + <dependency name="Core" version="0.9.1"/> + <dependency name="VCSBase" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/subversion/annotationhighlighter.cpp b/src/plugins/subversion/annotationhighlighter.cpp new file mode 100644 index 00000000000..e6269ef0617 --- /dev/null +++ b/src/plugins/subversion/annotationhighlighter.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "annotationhighlighter.h" + +namespace Subversion { +namespace Internal { + +SubversionAnnotationHighlighter::SubversionAnnotationHighlighter(const ChangeNumbers &changeNumbers, + QTextDocument *document) : + VCSBase::BaseAnnotationHighlighter(changeNumbers, document), + m_blank(QLatin1Char(' ')) +{ +} + +QString SubversionAnnotationHighlighter::changeNumber(const QString &block) const +{ + const int pos = block.indexOf(m_blank); + return pos > 1 ? block.left(pos) : QString(); +} + +} +} diff --git a/src/plugins/subversion/annotationhighlighter.h b/src/plugins/subversion/annotationhighlighter.h new file mode 100644 index 00000000000..4078ea477e2 --- /dev/null +++ b/src/plugins/subversion/annotationhighlighter.h @@ -0,0 +1,58 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ANNOTATIONHIGHLIGHTER_H +#define ANNOTATIONHIGHLIGHTER_H + +#include <vcsbase/baseannotationhighlighter.h> + +namespace Subversion { +namespace Internal { + +// Annotation highlighter for subversion triggering on 'changenumber ' +class SubversionAnnotationHighlighter : public VCSBase::BaseAnnotationHighlighter +{ + Q_OBJECT +public: + explicit SubversionAnnotationHighlighter(const ChangeNumbers &changeNumbers, + QTextDocument *document = 0); + +private: + virtual QString changeNumber(const QString &block) const; + + const QChar m_blank; +}; + +} //namespace Subversion +} //namespace Internal + +#endif diff --git a/src/plugins/subversion/changenumberdialog.cpp b/src/plugins/subversion/changenumberdialog.cpp new file mode 100644 index 00000000000..4cdb6b05e14 --- /dev/null +++ b/src/plugins/subversion/changenumberdialog.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtGui/QIntValidator> + +#include "changenumberdialog.h" + +using namespace Subversion::Internal; + +ChangeNumberDialog::ChangeNumberDialog(QWidget *parent) + : QDialog(parent) +{ + m_ui.setupUi(this); + m_ui.numberLineEdit->setValidator(new QIntValidator(0, 1000000, this)); +} + +int ChangeNumberDialog::number() const +{ + if (m_ui.numberLineEdit->text().isEmpty()) + return -1; + bool ok; + return m_ui.numberLineEdit->text().toInt(&ok); +} diff --git a/src/plugins/subversion/changenumberdialog.h b/src/plugins/subversion/changenumberdialog.h new file mode 100644 index 00000000000..ebc2d87cb8e --- /dev/null +++ b/src/plugins/subversion/changenumberdialog.h @@ -0,0 +1,60 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CHANGENUMBERDIALOG_H +#define CHANGENUMBERDIALOG_H + +#include <QtGui/QDialog> + +#include "ui_changenumberdialog.h" + +namespace Subversion { +namespace Internal { + +class ChangeNumberDialog : public QDialog +{ + Q_OBJECT +public: + ChangeNumberDialog(QWidget *parent = 0); + int number() const; + +private: + Ui::ChangeNumberDialog m_ui; + +}; + +} //namespace Subversion +} //namespace Internal + +#endif + + diff --git a/src/plugins/subversion/changenumberdialog.ui b/src/plugins/subversion/changenumberdialog.ui new file mode 100644 index 00000000000..ff3e6517508 --- /dev/null +++ b/src/plugins/subversion/changenumberdialog.ui @@ -0,0 +1,79 @@ +<ui version="4.0" > + <class>Subversion::Internal::ChangeNumberDialog</class> + <widget class="QDialog" name="Subversion::Internal::ChangeNumberDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>319</width> + <height>76</height> + </rect> + </property> + <property name="windowTitle" > + <string>Change Number</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="0" column="1" > + <widget class="QLineEdit" name="numberLineEdit" /> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Change Number:</string> + </property> + </widget> + </item> + <item row="1" column="0" 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> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Subversion::Internal::ChangeNumberDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>59</x> + <y>24</y> + </hint> + <hint type="destinationlabel" > + <x>160</x> + <y>38</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Subversion::Internal::ChangeNumberDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>59</x> + <y>24</y> + </hint> + <hint type="destinationlabel" > + <x>160</x> + <y>38</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/subversion/images/diff.png b/src/plugins/subversion/images/diff.png Binary files differnew file mode 100644 index 00000000000..b3597f9ff85 --- /dev/null +++ b/src/plugins/subversion/images/diff.png diff --git a/src/plugins/subversion/images/submit.png b/src/plugins/subversion/images/submit.png Binary files differnew file mode 100644 index 00000000000..4f302302b9e --- /dev/null +++ b/src/plugins/subversion/images/submit.png diff --git a/src/plugins/subversion/settingspage.cpp b/src/plugins/subversion/settingspage.cpp new file mode 100644 index 00000000000..16ed6484836 --- /dev/null +++ b/src/plugins/subversion/settingspage.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "settingspage.h" +#include "subversionsettings.h" +#include "subversionplugin.h" + +#include <coreplugin/icore.h> +#include <extensionsystem/pluginmanager.h> + +#include <QtGui/QFileDialog> + +using namespace Subversion::Internal; + +SettingsPageWidget::SettingsPageWidget(QWidget *parent) : + QWidget(parent) +{ + m_ui.setupUi(this); + connect(m_ui.browseButton, SIGNAL(clicked()), this, SLOT(browseForCommand())); +} + +SubversionSettings SettingsPageWidget::settings() const +{ + SubversionSettings rc; + rc.svnCommand = m_ui.svnCmdLineEdit->text(); + rc.useAuthentication = m_ui.userGroupBox->isChecked(); + rc.user = m_ui.usernameLineEdit->text(); + rc.password = m_ui.passwordLineEdit->text(); + if (rc.user.isEmpty()) + rc.useAuthentication = false; + return rc; +} + +void SettingsPageWidget::setSettings(const SubversionSettings &s) +{ + m_ui.svnCmdLineEdit->setText(s.svnCommand); + m_ui.usernameLineEdit->setText(s.user); + m_ui.passwordLineEdit->setText(s.password); + m_ui.userGroupBox->setChecked(s.useAuthentication); +} + +void SettingsPageWidget::browseForCommand() +{ + QString cmd = QFileDialog::getOpenFileName(window(), tr("Subversion Command")); + if (!cmd.isEmpty()) + m_ui.svnCmdLineEdit->setText(cmd); +} + +SettingsPage::SettingsPage() +{ +} + +QString SettingsPage::name() const +{ + return tr("General"); +} + +QString SettingsPage::category() const +{ + return QLatin1String("Subversion"); +} + +QString SettingsPage::trCategory() const +{ + return tr("Subversion"); +} + +QWidget *SettingsPage::createPage(QWidget *parent) +{ + if (!m_widget) + m_widget = new SettingsPageWidget(parent); + m_widget->setSettings(SubversionPlugin::subversionPluginInstance()->settings()); + return m_widget; +} + +void SettingsPage::finished(bool accepted) +{ + if (!accepted || !m_widget) + return; + SubversionPlugin::subversionPluginInstance()->setSettings(m_widget->settings()); +} diff --git a/src/plugins/subversion/settingspage.h b/src/plugins/subversion/settingspage.h new file mode 100644 index 00000000000..6bed2236b08 --- /dev/null +++ b/src/plugins/subversion/settingspage.h @@ -0,0 +1,90 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SETTINGSPAGE_H +#define SETTINGSPAGE_H + +#include <QtGui/QWidget> +#include <QtCore/QPointer> +#include <QtCore/QString> + +#include <coreplugin/dialogs/ioptionspage.h> + +#include "ui_settingspage.h" + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace Subversion { + namespace Internal { + +struct SubversionSettings; + +class SettingsPageWidget : public QWidget { + Q_OBJECT +public: + explicit SettingsPageWidget(QWidget *parent = 0); + + SubversionSettings settings() const; + void setSettings(const SubversionSettings &); + +private slots:; + void browseForCommand(); + +private: + Ui::SettingsPage m_ui; +}; + + +class SettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + SettingsPage(); + + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + +private: + QPointer<SettingsPageWidget> m_widget; +}; + + } //namespace Subversion +} //namespace Internal + +#endif // SETTINGSPAGE_H diff --git a/src/plugins/subversion/settingspage.ui b/src/plugins/subversion/settingspage.ui new file mode 100644 index 00000000000..14e5ebce87e --- /dev/null +++ b/src/plugins/subversion/settingspage.ui @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Subversion::Internal::SettingsPage</class> + <widget class="QWidget" name="Subversion::Internal::SettingsPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>427</width> + <height>280</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="commandHBox"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="commandLabel"> + <property name="text"> + <string>Subversion Command:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="svnCmdLineEdit"/> + </item> + <item> + <widget class="QToolButton" name="browseButton"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QGroupBox" name="userGroupBox"> + <property name="title"> + <string>Authentication</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="usernameLabel"> + <property name="text"> + <string>User name:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="usernameLineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="passwordLabel"> + <property name="text"> + <string>Password:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="passwordLineEdit"> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + </layout> + </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> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>105</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <tabstops> + <tabstop>svnCmdLineEdit</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/subversion/subversion.pro b/src/plugins/subversion/subversion.pro new file mode 100644 index 00000000000..7de5499fa76 --- /dev/null +++ b/src/plugins/subversion/subversion.pro @@ -0,0 +1,34 @@ +TEMPLATE = lib +TARGET = Subversion + +include(../../qworkbenchplugin.pri) +include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/texteditor/texteditor.pri) +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/vcsbase/vcsbase.pri) +include(../../libs/utils/utils.pri) + +HEADERS += annotationhighlighter.h \ + subversionplugin.h \ + subversioncontrol.h \ + subversionoutputwindow.h \ + settingspage.h \ + subversioneditor.h \ + changenumberdialog.h \ + subversionsubmiteditor.h \ + subversionsettings.h + +SOURCES += annotationhighlighter.cpp \ + subversionplugin.cpp \ + subversioncontrol.cpp \ + subversionoutputwindow.cpp \ + settingspage.cpp \ + subversioneditor.cpp \ + changenumberdialog.cpp \ + subversionsubmiteditor.cpp \ + subversionsettings.cpp + +FORMS += settingspage.ui \ + changenumberdialog.ui + +RESOURCES += subversion.qrc diff --git a/src/plugins/subversion/subversion.qrc b/src/plugins/subversion/subversion.qrc new file mode 100644 index 00000000000..7fa3a2a31e1 --- /dev/null +++ b/src/plugins/subversion/subversion.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/trolltech.subversion" > + <file>images/diff.png</file> + <file>images/submit.png</file> + <file>Subversion.mimetypes.xml</file> + </qresource> +</RCC> diff --git a/src/plugins/subversion/subversionconstants.h b/src/plugins/subversion/subversionconstants.h new file mode 100644 index 00000000000..eaf25755080 --- /dev/null +++ b/src/plugins/subversion/subversionconstants.h @@ -0,0 +1,51 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SUBVERSION_CONSTANTS_H +#define SUBVERSION_CONSTANTS_H + +namespace Subversion { + namespace Constants { + const char * const SUBVERSION_SUBMIT_MIMETYPE = "application/vnd.nokia.text.subversion.submit"; + const char * const SUBVERSIONEDITOR = "Subversion Editor"; + const char * const SUBVERSIONEDITOR_KIND = "Subversion Editor"; + const char * const SUBVERSIONCOMMITEDITOR = "Subversion Commit Editor"; + const char * const SUBVERSIONCOMMITEDITOR_KIND = "Subversion Commit Editor"; + const char * const ICON_SUBMIT = ":/trolltech.subversion/images/submit.png"; + const char * const ICON_DIFF = ":/trolltech.subversion/images/diff.png"; + const char * const SUBMIT_CURRENT = "Nokia.Subversion.SubmitCurrentLog"; + const char * const DIFF_SELECTED = "Nokia.Subversion.DiffSelectedFilesInLog"; + enum { debug = 0 }; + } +} + +#endif diff --git a/src/plugins/subversion/subversioncontrol.cpp b/src/plugins/subversion/subversioncontrol.cpp new file mode 100644 index 00000000000..0357eccf5ba --- /dev/null +++ b/src/plugins/subversion/subversioncontrol.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "subversioncontrol.h" +#include "subversionplugin.h" + +namespace Subversion { +namespace Internal { + +SubversionControl::SubversionControl(SubversionPlugin *plugin) : + m_plugin(plugin) +{ +} + +bool SubversionControl::vcsOpen(const QString & /* fileName */) +{ + // Open for edit: N/A + return true; +} + +bool SubversionControl::vcsAdd(const QString &fileName) +{ + return m_plugin->vcsAdd(fileName); +} + +bool SubversionControl::vcsDelete(const QString &fileName) +{ + return m_plugin->vcsDelete(fileName); +} + +bool SubversionControl::managesDirectory(const QString &directory) const +{ + return m_plugin->managesDirectory(directory); +} + +QString SubversionControl::findTopLevelForDirectory(const QString &directory) const +{ + return m_plugin->findTopLevelForDirectory(directory); +} +} +} diff --git a/src/plugins/subversion/subversioncontrol.h b/src/plugins/subversion/subversioncontrol.h new file mode 100644 index 00000000000..7be525275f2 --- /dev/null +++ b/src/plugins/subversion/subversioncontrol.h @@ -0,0 +1,60 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SUBVERSIONCONTROL_H +#define SUBVERSIONCONTROL_H + +#include <coreplugin/iversioncontrol.h> + +namespace Subversion { +namespace Internal { +class SubversionPlugin; + +// Just a proxy for SubversionPlugin +class SubversionControl : public Core::IVersionControl +{ + Q_OBJECT +public: + explicit SubversionControl(SubversionPlugin *plugin); + virtual bool managesDirectory(const QString &directory) const; + virtual QString findTopLevelForDirectory(const QString &directory) const; + virtual bool vcsOpen(const QString &fileName); + virtual bool vcsAdd(const QString &fileName); + virtual bool vcsDelete(const QString &filename); + +private: + SubversionPlugin *m_plugin; +}; + +} +} +#endif diff --git a/src/plugins/subversion/subversioneditor.cpp b/src/plugins/subversion/subversioneditor.cpp new file mode 100644 index 00000000000..d87138aae14 --- /dev/null +++ b/src/plugins/subversion/subversioneditor.cpp @@ -0,0 +1,140 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "subversioneditor.h" +#include "annotationhighlighter.h" +#include "subversionconstants.h" + +#include <vcsbase/diffhighlighter.h> + +#include <QtCore/QDebug> +#include <QtGui/QTextCursor> + +namespace Subversion { +namespace Internal { + +SubversionEditor::SubversionEditor(const VCSBase::VCSBaseEditorParameters *type, + QWidget *parent) : + VCSBase::VCSBaseEditor(type, parent), + m_changeNumberPattern(QLatin1String("^\\d+$")), + m_revisionNumberPattern(QLatin1String("^r\\d+$")) +{ + Q_ASSERT(m_changeNumberPattern.isValid()); + Q_ASSERT(m_revisionNumberPattern.isValid()); +} + +QSet<QString> SubversionEditor::annotationChanges() const +{ + QSet<QString> changes; + const QString txt = toPlainText(); + if (txt.isEmpty()) + return changes; + // Hunt for first change number in annotation: "<change>:" + QRegExp r(QLatin1String("^(\\d+):")); + Q_ASSERT(r.isValid()); + if (r.indexIn(txt) != -1) { + changes.insert(r.cap(1)); + r.setPattern(QLatin1String("\n(\\d+):")); + Q_ASSERT(r.isValid()); + int pos = 0; + while ((pos = r.indexIn(txt, pos)) != -1) { + pos += r.matchedLength(); + changes.insert(r.cap(1)); + } + } + if (Subversion::Constants::debug) + qDebug() << "SubversionEditor::annotationChanges() returns #" << changes.size(); + return changes; +} + +QString SubversionEditor::changeUnderCursor(const QTextCursor &c) const +{ + QTextCursor cursor = c; + // Any number is regarded as change number. + cursor.select(QTextCursor::WordUnderCursor); + if (!cursor.hasSelection()) + return QString(); + QString change = cursor.selectedText(); + // Annotation output has number, log output has revision numbers + // as r1, r2... + if (m_changeNumberPattern.exactMatch(change)) + return change; + if (m_revisionNumberPattern.exactMatch(change)) { + change.remove(0, 1); + return change; + } + return QString(); +} + +/* code: + Index: main.cpp +=================================================================== +--- main.cpp (revision 2) ++++ main.cpp (working copy) +@@ -6,6 +6,5 @@ +\endcode +*/ + +VCSBase::DiffHighlighter *SubversionEditor::createDiffHighlighter() const +{ + const QRegExp filePattern(QLatin1String("^[-+][-+][-+] .*|^Index: .*|^==*$")); + Q_ASSERT(filePattern.isValid()); + return new VCSBase::DiffHighlighter(filePattern); +} + +VCSBase::BaseAnnotationHighlighter *SubversionEditor::createAnnotationHighlighter(const QSet<QString> &changes) const +{ + return new SubversionAnnotationHighlighter(changes); +} + +QString SubversionEditor::fileNameFromDiffSpecification(const QTextBlock &inBlock) const +{ + // "+++ /depot/.../mainwindow.cpp<tab>(revision 3)" + // Go back chunks + const QString diffIndicator = QLatin1String("+++ "); + for (QTextBlock block = inBlock; block.isValid() ; block = block.previous()) { + QString diffFileName = block.text(); + if (diffFileName.startsWith(diffIndicator)) { + diffFileName.remove(0, diffIndicator.size()); + const int tabIndex = diffFileName.lastIndexOf(QLatin1Char('\t')); + if (tabIndex != -1) + diffFileName.truncate(tabIndex); + if (Subversion::Constants::debug) + qDebug() << Q_FUNC_INFO << diffFileName; + return diffFileName; + } + } + return QString(); +} + +} +} diff --git a/src/plugins/subversion/subversioneditor.h b/src/plugins/subversion/subversioneditor.h new file mode 100644 index 00000000000..9dee4a9d5a3 --- /dev/null +++ b/src/plugins/subversion/subversioneditor.h @@ -0,0 +1,65 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SUBVERSIONEDITOR_H +#define SUBVERSIONEDITOR_H + +#include <vcsbase/vcsbaseeditor.h> + +#include <QtCore/QRegExp> + +namespace Subversion { +namespace Internal { + +class SubversionEditor : public VCSBase::VCSBaseEditor +{ + Q_OBJECT + +public: + explicit SubversionEditor(const VCSBase::VCSBaseEditorParameters *type, + QWidget *parent); + +private: + virtual QSet<QString> annotationChanges() const; + virtual QString changeUnderCursor(const QTextCursor &) const; + virtual VCSBase::DiffHighlighter *createDiffHighlighter() const; + virtual VCSBase::BaseAnnotationHighlighter *createAnnotationHighlighter(const QSet<QString> &changes) const; + virtual QString fileNameFromDiffSpecification(const QTextBlock &diffFileName) const; + + const QRegExp m_changeNumberPattern; + const QRegExp m_revisionNumberPattern; +}; + +} // namespace Internal +} // namespace Subversion + +#endif // SUBVERSIONEDITOR_H diff --git a/src/plugins/subversion/subversionoutputwindow.cpp b/src/plugins/subversion/subversionoutputwindow.cpp new file mode 100644 index 00000000000..f3d8fd8ac1a --- /dev/null +++ b/src/plugins/subversion/subversionoutputwindow.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "subversionoutputwindow.h" +#include "subversionplugin.h" + +#include <QtGui/QListWidget> +#include <QtCore/QDebug> + +using namespace Subversion::Internal; + +SubversionOutputWindow::SubversionOutputWindow(SubversionPlugin *svnPlugin) + : m_svnPlugin(svnPlugin) +{ + m_outputListWidget = new QListWidget; + m_outputListWidget->setFrameStyle(QFrame::NoFrame); + m_outputListWidget->setWindowTitle(tr("Subversion Output")); + m_outputListWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); +} + +SubversionOutputWindow::~SubversionOutputWindow() +{ + delete m_outputListWidget; +} + +QWidget *SubversionOutputWindow::outputWidget(QWidget *parent) +{ + m_outputListWidget->setParent(parent); + return m_outputListWidget; +} + +QString SubversionOutputWindow::name() const +{ + return tr("Subversion"); +} + +void SubversionOutputWindow::clearContents() +{ + m_outputListWidget->clear(); +} + +int SubversionOutputWindow::priorityInStatusBar() const +{ + return -1; +} + +void SubversionOutputWindow::visibilityChanged(bool b) +{ + if (b) + m_outputListWidget->setFocus(); +} + +void SubversionOutputWindow::append(const QString &txt, bool doPopup) +{ + const QStringList lines = txt.split(QLatin1Char('\n')); + foreach (const QString &s, lines) + m_outputListWidget->addItem(s); + m_outputListWidget->scrollToBottom(); + + if (doPopup) + popup(); +} + +bool SubversionOutputWindow::canFocus() +{ + return false; +} + +bool SubversionOutputWindow::hasFocus() +{ + return m_outputListWidget->hasFocus(); +} + +void SubversionOutputWindow::setFocus() +{ +} diff --git a/src/plugins/subversion/subversionoutputwindow.h b/src/plugins/subversion/subversionoutputwindow.h new file mode 100644 index 00000000000..cd1bda5e9db --- /dev/null +++ b/src/plugins/subversion/subversionoutputwindow.h @@ -0,0 +1,81 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SUBVERSIONOUTPUTWINDOW_H +#define SUBVERSIONOUTPUTWINDOW_H + +#include <coreplugin/ioutputpane.h> + +QT_BEGIN_NAMESPACE +class QListWidget; +QT_END_NAMESPACE + +namespace Subversion { + namespace Internal { + +class SubversionPlugin; + +class SubversionOutputWindow : public Core::IOutputPane +{ + Q_OBJECT + +public: + SubversionOutputWindow(SubversionPlugin *svnPlugin); + ~SubversionOutputWindow(); + + QWidget *outputWidget(QWidget *parent); + QList<QWidget*> toolBarWidgets(void) const { + return QList<QWidget *>(); + } + + QString name() const; + void clearContents(); + int priorityInStatusBar() const; + void visibilityChanged(bool visible); + + virtual bool canFocus(); + virtual bool hasFocus(); + virtual void setFocus(); + +public slots: + void append(const QString &txt, bool popup = false); + +private: + + SubversionPlugin *m_svnPlugin; + QListWidget *m_outputListWidget; +}; + +} // namespace Subversion +} // namespace Internal + +#endif diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp new file mode 100644 index 00000000000..7c44e87fea0 --- /dev/null +++ b/src/plugins/subversion/subversionplugin.cpp @@ -0,0 +1,1087 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "subversionplugin.h" + +#include "settingspage.h" +#include "subversioneditor.h" + +#include "subversionoutputwindow.h" +#include "subversionsubmiteditor.h" +#include "changenumberdialog.h" +#include "subversionconstants.h" +#include "subversioncontrol.h" + +#include <vcsbase/basevcseditorfactory.h> +#include <vcsbase/vcsbaseeditor.h> +#include <vcsbase/basevcssubmiteditorfactory.h> +#include <utils/synchronousprocess.h> + +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/filemanager.h> +#include <coreplugin/messagemanager.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> +#include <projectexplorer/ProjectExplorerInterfaces> + +#include <QtCore/qplugin.h> +#include <QtCore/QDebug> +#include <QtCore/QTextCodec> +#include <QtCore/QFileInfo> +#include <QtCore/QTemporaryFile> +#include <QtCore/QDir> +#include <QtGui/QAction> +#include <QtGui/QMenu> +#include <QtGui/QMessageBox> +#include <QtGui/QMainWindow> +#include <QtGui/QFileDialog> + +using namespace Subversion::Internal; + +// Timeout for normal output commands +enum { subversionShortTimeOut = 10000 }; +// Timeout for submit, update +enum { subversionLongTimeOut = 120000 }; + +// #pragma mark -- SubversionPlugin + +const char * const SubversionPlugin::SUBVERSION_MENU = "Subversion.Menu"; +const char * const SubversionPlugin::ADD = "Subversion.Add"; +const char * const SubversionPlugin::DELETE_FILE = "Subversion.Delete"; +const char * const SubversionPlugin::REVERT = "Subversion.Revert"; +const char * const SubversionPlugin::SEPARATOR0 = "Subversion.Separator0"; +const char * const SubversionPlugin::DIFF_PROJECT = "Subversion.DiffAll"; +const char * const SubversionPlugin::DIFF_CURRENT = "Subversion.DiffCurrent"; +const char * const SubversionPlugin::SEPARATOR1 = "Subversion.Separator1"; +const char * const SubversionPlugin::COMMIT_ALL = "Subversion.CommitAll"; +const char * const SubversionPlugin::COMMIT_CURRENT = "Subversion.CommitCurrent"; +const char * const SubversionPlugin::SEPARATOR2 = "Subversion.Separator2"; +const char * const SubversionPlugin::FILELOG_CURRENT = "Subversion.FilelogCurrent"; +const char * const SubversionPlugin::ANNOTATE_CURRENT = "Subversion.AnnotateCurrent"; +const char * const SubversionPlugin::SEPARATOR3 = "Subversion.Separator3"; +const char * const SubversionPlugin::STATUS = "Subversion.Status"; +const char * const SubversionPlugin::UPDATE = "Subversion.Update"; + +static const VCSBase::VCSBaseEditorParameters editorParameters[] = { +{ + VCSBase::RegularCommandOutput, + "Subversion Command Log Editor", + Core::Constants::C_GLOBAL, + "application/vnd.nokia.text.scs_svn_commandlog", + "scslog"}, +{ VCSBase::LogOutput, + "Subversion File Log Editor", + Core::Constants::C_GLOBAL, + "application/vnd.nokia.text.scs_svn_filelog", + "scsfilelog"}, +{ VCSBase::AnnotateOutput, + "Subversion Annotation Editor", + Core::Constants::C_GLOBAL, + "application/vnd.nokia.text.scs_svn_annotation", + "scsannotate"}, +{ VCSBase::DiffOutput, + "Subversion Diff Editor", + Core::Constants::C_GLOBAL, + "text/x-patch","diff"} +}; + +// Utility to find a parameter set by type +static inline const VCSBase::VCSBaseEditorParameters *findType(int ie) +{ + const VCSBase::EditorContentType et = static_cast<VCSBase::EditorContentType>(ie); + return VCSBase::VCSBaseEditor::findType(editorParameters, sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters), et); +} + +static inline QString debugCodec(const QTextCodec *c) +{ + return c ? QString::fromAscii(c->name()) : QString::fromAscii("Null codec"); +} + +inline Core::IEditor* locateEditor(const Core::ICore *core, const char *property, const QString &entry) +{ + foreach (Core::IEditor *ed, core->editorManager()->openedEditors()) + if (ed->property(property).toString() == entry) + return ed; + return 0; +} + +// ------------- SubversionPlugin +Core::ICore *SubversionPlugin::m_coreInstance = 0; +SubversionPlugin *SubversionPlugin::m_subversionPluginInstance = 0; + +SubversionPlugin::SubversionPlugin() : + m_svnDotDirectory(QLatin1String(".svn")), + m_versionControl(0), + m_coreListener(0), + m_settingsPage(0), + m_changeTmpFile(0), + m_submitEditorFactory(0), + m_subversionOutputWindow(0), + m_projectExplorer(0), + m_addAction(0), + m_deleteAction(0), + m_revertAction(0), + m_diffProjectAction(0), + m_diffCurrentAction(0), + m_commitAllAction(0), + m_commitCurrentAction(0), + m_filelogCurrentAction(0), + m_annotateCurrentAction(0), + m_statusAction(0), + m_updateProjectAction(0), + m_submitCurrentLogAction(0), + m_submitDiffAction(0), + m_submitUndoAction(0), + m_submitRedoAction(0) +{ +} + +SubversionPlugin::~SubversionPlugin() +{ + if (m_versionControl) { + removeObject(m_versionControl); + delete m_versionControl; + m_versionControl = 0; + } + + if (m_settingsPage) { + removeObject(m_settingsPage); + delete m_settingsPage; + m_settingsPage = 0; + } + if (m_subversionOutputWindow) { + removeObject(m_subversionOutputWindow); + delete m_subversionOutputWindow; + m_subversionOutputWindow = 0; + } + if (m_submitEditorFactory) { + removeObject(m_submitEditorFactory); + delete m_submitEditorFactory; + m_submitEditorFactory = 0; + } + + if (!m_editorFactories.empty()) { + foreach(Core::IEditorFactory* pf, m_editorFactories) + removeObject(pf); + qDeleteAll(m_editorFactories); + m_editorFactories.clear(); + } + + if (m_coreListener) { + removeObject(m_coreListener); + delete m_coreListener; + m_coreListener = 0; + } + cleanChangeTmpFile(); +} + +void SubversionPlugin::cleanChangeTmpFile() +{ + if (m_changeTmpFile) { + if (m_changeTmpFile->isOpen()) + m_changeTmpFile->close(); + delete m_changeTmpFile; + m_changeTmpFile = 0; + } +} + +static const VCSBase::VCSBaseSubmitEditorParameters submitParameters = { + Subversion::Constants::SUBVERSION_SUBMIT_MIMETYPE, + Subversion::Constants::SUBVERSIONCOMMITEDITOR_KIND, + Subversion::Constants::SUBVERSIONCOMMITEDITOR, + Core::Constants::UNDO, + Core::Constants::REDO, + Subversion::Constants::SUBMIT_CURRENT, + Subversion::Constants::DIFF_SELECTED +}; + +bool SubversionPlugin::initialize(const QStringList & /*arguments*/, QString *errorMessage) +{ + typedef VCSBase::VCSSubmitEditorFactory<SubversionSubmitEditor> SubversionSubmitEditorFactory; + typedef VCSBase::VCSEditorFactory<SubversionEditor> SubversionEditorFactory; + using namespace Constants; + + using namespace Core::Constants; + using namespace ExtensionSystem; + + m_subversionPluginInstance = this; + m_coreInstance = PluginManager::instance()->getObject<Core::ICore>(); + + if (!m_coreInstance->mimeDatabase()->addMimeTypes(QLatin1String(":/trolltech.subversion/Subversion.mimetypes.xml"), errorMessage)) + return false; + + m_versionControl = new SubversionControl(this); + addObject(m_versionControl); + + if (QSettings *settings = m_coreInstance->settings()) + m_settings.fromSettings(settings); + + m_coreListener = new CoreListener(this); + addObject(m_coreListener); + + m_settingsPage = new SettingsPage; + addObject(m_settingsPage); + + m_submitEditorFactory = new SubversionSubmitEditorFactory(&submitParameters); + addObject(m_submitEditorFactory); + + static const char *describeSlot = SLOT(describe(QString,QString)); + const int editorCount = sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters); + for (int i = 0; i < editorCount; i++) { + m_editorFactories.push_back(new SubversionEditorFactory(editorParameters + i, m_coreInstance, this, describeSlot)); + addObject(m_editorFactories.back()); + } + + m_subversionOutputWindow = new SubversionOutputWindow(this); + addObject(m_subversionOutputWindow); + + //register actions + Core::ActionManagerInterface *ami = m_coreInstance->actionManager(); + Core::IActionContainer *toolsContainer = ami->actionContainer(M_TOOLS); + + Core::IActionContainer *subversionMenu = + ami->createMenu(QLatin1String(SUBVERSION_MENU)); + subversionMenu->menu()->setTitle(tr("&Subversion")); + toolsContainer->addMenu(subversionMenu); + + QList<int> globalcontext; + globalcontext << m_coreInstance->uniqueIDManager()->uniqueIdentifier(C_GLOBAL); + + Core::ICommand *command; + m_addAction = new QAction(tr("Add"), this); + command = ami->registerAction(m_addAction, SubversionPlugin::ADD, + globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+A"))); + connect(m_addAction, SIGNAL(triggered()), this, SLOT(addCurrentFile())); + subversionMenu->addAction(command); + + m_deleteAction = new QAction(tr("Delete"), this); + command = ami->registerAction(m_deleteAction, SubversionPlugin::DELETE_FILE, + globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(deleteCurrentFile())); + subversionMenu->addAction(command); + + m_revertAction = new QAction(tr("Revert"), this); + command = ami->registerAction(m_revertAction, SubversionPlugin::REVERT, + globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_revertAction, SIGNAL(triggered()), this, SLOT(revertCurrentFile())); + subversionMenu->addAction(command); + + QAction *tmpaction = new QAction(this); + tmpaction->setSeparator(true); + subversionMenu->addAction(ami->registerAction(tmpaction, + SubversionPlugin::SEPARATOR0, globalcontext)); + + m_diffProjectAction = new QAction(tr("Diff Project"), this); + command = ami->registerAction(m_diffProjectAction, SubversionPlugin::DIFF_PROJECT, + globalcontext); + connect(m_diffProjectAction, SIGNAL(triggered()), this, SLOT(diffProject())); + subversionMenu->addAction(command); + + m_diffCurrentAction = new QAction(tr("Diff Current File"), this); + command = ami->registerAction(m_diffCurrentAction, + SubversionPlugin::DIFF_CURRENT, globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+D"))); + connect(m_diffCurrentAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile())); + subversionMenu->addAction(command); + + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + subversionMenu->addAction(ami->registerAction(tmpaction, + SubversionPlugin::SEPARATOR1, globalcontext)); + + m_commitAllAction = new QAction(tr("Commit All Files"), this); + command = ami->registerAction(m_commitAllAction, SubversionPlugin::COMMIT_ALL, + globalcontext); + connect(m_commitAllAction, SIGNAL(triggered()), this, SLOT(startCommitAll())); + subversionMenu->addAction(command); + + m_commitCurrentAction = new QAction(tr("Commit Current File"), this); + command = ami->registerAction(m_commitCurrentAction, + SubversionPlugin::COMMIT_CURRENT, globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + command->setDefaultKeySequence(QKeySequence(tr("Alt+S,Alt+C"))); + connect(m_commitCurrentAction, SIGNAL(triggered()), this, SLOT(startCommitCurrentFile())); + subversionMenu->addAction(command); + + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + subversionMenu->addAction(ami->registerAction(tmpaction, + SubversionPlugin::SEPARATOR2, globalcontext)); + + m_filelogCurrentAction = new QAction(tr("Filelog Current File"), this); + command = ami->registerAction(m_filelogCurrentAction, + SubversionPlugin::FILELOG_CURRENT, globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_filelogCurrentAction, SIGNAL(triggered()), this, + SLOT(filelogCurrentFile())); + subversionMenu->addAction(command); + + m_annotateCurrentAction = new QAction(tr("Annotate Current File"), this); + command = ami->registerAction(m_annotateCurrentAction, + SubversionPlugin::ANNOTATE_CURRENT, globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_annotateCurrentAction, SIGNAL(triggered()), this, + SLOT(annotateCurrentFile())); + subversionMenu->addAction(command); + + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + subversionMenu->addAction(ami->registerAction(tmpaction, + SubversionPlugin::SEPARATOR3, globalcontext)); + + m_statusAction = new QAction(tr("Project Status"), this); + command = ami->registerAction(m_statusAction, SubversionPlugin::STATUS, + globalcontext); + connect(m_statusAction, SIGNAL(triggered()), this, SLOT(projectStatus())); + subversionMenu->addAction(command); + + m_updateProjectAction = new QAction(tr("Update Project"), this); + command = ami->registerAction(m_updateProjectAction, SubversionPlugin::UPDATE, globalcontext); + connect(m_updateProjectAction, SIGNAL(triggered()), this, SLOT(updateProject())); + subversionMenu->addAction(command); + + // Actions of the submit editor + QList<int> svncommitcontext; + svncommitcontext << m_coreInstance->uniqueIDManager()->uniqueIdentifier(Constants::SUBVERSIONCOMMITEDITOR); + + m_submitCurrentLogAction = new QAction(QIcon(Constants::ICON_SUBMIT), tr("Commit"), this); + command = ami->registerAction(m_submitCurrentLogAction, Constants::SUBMIT_CURRENT, svncommitcontext); + connect(m_submitCurrentLogAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog())); + + m_submitDiffAction = new QAction(QIcon(Constants::ICON_DIFF), tr("Diff Selected Files"), this); + command = ami->registerAction(m_submitDiffAction , Constants::DIFF_SELECTED, svncommitcontext); + + m_submitUndoAction = new QAction(tr("&Undo"), this); + command = ami->registerAction(m_submitUndoAction, Core::Constants::UNDO, svncommitcontext); + + m_submitRedoAction = new QAction(tr("&Redo"), this); + command = ami->registerAction(m_submitRedoAction, Core::Constants::REDO, svncommitcontext); + + connect(m_coreInstance, SIGNAL(contextChanged(Core::IContext *)), this, SLOT(updateActions())); + + return true; +} + +void SubversionPlugin::extensionsInitialized() +{ + using namespace ExtensionSystem; + using namespace ProjectExplorer; + + m_projectExplorer = PluginManager::instance()->getObject<ProjectExplorerPlugin>(); + if (m_projectExplorer) { + connect(m_projectExplorer, + SIGNAL(currentProjectChanged(ProjectExplorer::Project*)), + m_subversionPluginInstance, SLOT(updateActions())); + } + updateActions(); +} + +bool SubversionPlugin::editorAboutToClose(Core::IEditor *iEditor) +{ + if (!m_changeTmpFile || !iEditor || qstrcmp(Constants::SUBVERSIONCOMMITEDITOR, iEditor->kind())) + return true; + + Core::IFile *fileIFace = iEditor->file(); + const SubversionSubmitEditor *editor = qobject_cast<SubversionSubmitEditor *>(iEditor); + if (!fileIFace || !editor) + return true; + + // Submit editor closing. Make it write out the commit message + // and retrieve files + const QFileInfo editorFile(fileIFace->fileName()); + const QFileInfo changeFile(m_changeTmpFile->fileName()); + if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath()) + return true; // Oops?! + + // Prompt user. + const QMessageBox::StandardButton answer = QMessageBox::question( + m_coreInstance->mainWindow(), tr("Closing Subversion Editor"), + tr("Do you want to commit the change?"), + QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes); + switch (answer) { + case QMessageBox::Cancel: + return false; // Keep editing and change file + case QMessageBox::No: + cleanChangeTmpFile(); + return true; // Cancel all + default: + break; + } + + const QStringList fileList = editor->checkedFiles(); + if (!fileList.empty()) { + // get message & commit + m_coreInstance->fileManager()->blockFileChange(fileIFace); + fileIFace->save(); + m_coreInstance->fileManager()->unblockFileChange(fileIFace); + commit(m_changeTmpFile->fileName(), fileList); + } + cleanChangeTmpFile(); + return true; +} + +void SubversionPlugin::diffFiles(const QStringList &files) +{ + svnDiff(files); +} + +void SubversionPlugin::svnDiff(const QStringList &files, QString diffname) +{ + if (Subversion::Constants::debug) + qDebug() << Q_FUNC_INFO << files << diffname; + const QString source = files.empty() ? QString() : files.front(); + QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VCSBase::VCSBaseEditor::getCodec(m_coreInstance, source); + + if (files.count() == 1 && diffname.isEmpty()) + diffname = QFileInfo(files.front()).fileName(); + + QStringList args(QLatin1String("diff")); + args << files; + + const SubversionResponse response = runSvn(args, subversionShortTimeOut, false, codec); + if (response.error) + return; + + // diff of a single file? re-use an existing view if possible to support + // the common usage pattern of continuously changing and diffing a file + if (files.count() == 1) { + // Show in the same editor if diff has been executed before + if (Core::IEditor *editor = locateEditor(m_coreInstance, "originalFileName", files.front())) { + editor->createNew(response.stdOut); + m_coreInstance->editorManager()->setCurrentEditor(editor); + return; + } + } + const QString title = tr("svn diff %1").arg(diffname); + Core::IEditor *editor = showOutputInEditor(title, response.stdOut, VCSBase::DiffOutput, source, codec); + if (files.count() == 1) + editor->setProperty("originalFileName", files.front()); +} + +SubversionSubmitEditor *SubversionPlugin::openSubversionSubmitEditor(const QString &fileName) +{ + Core::IEditor *editor = m_coreInstance->editorManager()->openEditor(fileName, QLatin1String(Constants::SUBVERSIONCOMMITEDITOR_KIND)); + SubversionSubmitEditor *submitEditor = qobject_cast<SubversionSubmitEditor*>(editor); + Q_ASSERT(submitEditor); + // The actions are for some reason enabled by the context switching + // mechanism. Disable them correctly. + m_submitDiffAction->setEnabled(false); + m_submitUndoAction->setEnabled(false); + m_submitRedoAction->setEnabled(false); + connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(diffFiles(QStringList))); + + return submitEditor; +} + +void SubversionPlugin::updateActions() +{ + QString fileName = currentFileName(); + const bool hasFile = !fileName.isEmpty(); + + m_addAction->setEnabled(hasFile); + m_deleteAction->setEnabled(hasFile); + m_revertAction->setEnabled(hasFile); + m_diffProjectAction->setEnabled(true); + m_diffCurrentAction->setEnabled(hasFile); + m_commitAllAction->setEnabled(true); + m_commitCurrentAction->setEnabled(hasFile); + m_filelogCurrentAction->setEnabled(hasFile); + m_annotateCurrentAction->setEnabled(hasFile); + m_statusAction->setEnabled(true); + + QString baseName; + if (hasFile) + baseName = QFileInfo(fileName).fileName(); + + m_addAction->setText(tr("Add %1").arg(baseName)); + m_deleteAction->setText(tr("Delete %1").arg(baseName)); + m_revertAction->setText(tr("Revert %1").arg(baseName)); + m_diffCurrentAction->setText(tr("Diff %1").arg(baseName)); + m_commitCurrentAction->setText(tr("Commit %1").arg(baseName)); + m_filelogCurrentAction->setText(tr("Filelog %1").arg(baseName)); + m_annotateCurrentAction->setText(tr("Annotate %1").arg(baseName)); +} + +void SubversionPlugin::addCurrentFile() +{ + const QString file = currentFileName(); + if (!file.isEmpty()) + vcsAdd(file); +} + +void SubversionPlugin::deleteCurrentFile() +{ + const QString file = currentFileName(); + if (!file.isEmpty()) + vcsDelete(file); +} + +void SubversionPlugin::revertCurrentFile() +{ + const QString file = QDir::toNativeSeparators(currentFileName()); + if (file.isEmpty()) + return; + + QStringList args(QLatin1String("diff")); + args.push_back(file); + + const SubversionResponse diffResponse = runSvn(args, subversionShortTimeOut, false); + if (diffResponse.error) + return; + + if (diffResponse.stdOut.isEmpty()) + return; + if (QMessageBox::warning(0, tr("svn revert"), tr("The file has been changed. Do you want to revert it?"), + QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) + return; + + Core::FileManager *fm = m_coreInstance->fileManager(); + QList<Core::IFile *> files = fm->managedFiles(file); + foreach (Core::IFile *file, files) { + fm->blockFileChange(file); + } + + // revert + args.clear(); + args.push_back(QLatin1String("revert")); + args.append(file); + + const SubversionResponse revertResponse = runSvn(args, subversionShortTimeOut, true); + if (revertResponse.error) { + foreach (Core::IFile *file, files) + fm->unblockFileChange(file); + return; + } + + Core::IFile::ReloadBehavior tempBehavior = Core::IFile::ReloadAll; + foreach (Core::IFile *file, files) { + file->modified(&tempBehavior); + fm->unblockFileChange(file); + } +} + +// Get a unique set of toplevel directories for the current projects. +// To be used for "diff all" or "commit all". +QStringList SubversionPlugin::currentProjectsTopLevels(QString *name) const +{ + typedef QList<ProjectExplorer::Project *> ProjectList; + ProjectList projects; + // Compile list of projects + if (ProjectExplorer::Project *currentProject = m_projectExplorer->currentProject()) { + projects.push_back(currentProject); + } else { + if (const ProjectExplorer::SessionManager *session = m_projectExplorer->session()) + projects.append(session->projects()); + } + // Get unique set of toplevels and concat project names + QStringList toplevels; + const QChar blank(QLatin1Char(' ')); + foreach (const ProjectExplorer::Project *p, projects) { + if (name) { + if (!name->isEmpty()) + name->append(blank); + name->append(p->name()); + } + + const QString projectPath = QFileInfo(p->file()->fileName()).absolutePath(); + const QString topLevel = findTopLevelForDirectory(projectPath); + if (!topLevel.isEmpty() && !toplevels.contains(topLevel)) + toplevels.push_back(topLevel); + } + return toplevels; +} + +void SubversionPlugin::diffProject() +{ + QString diffName; + const QStringList topLevels = currentProjectsTopLevels(&diffName); + if (!topLevels.isEmpty()) + svnDiff(topLevels, diffName); +} + +void SubversionPlugin::diffCurrentFile() +{ + svnDiff(QStringList(currentFileName())); +} + +void SubversionPlugin::startCommitCurrentFile() +{ + const QString file = QDir::toNativeSeparators(currentFileName()); + if (!file.isEmpty()) + startCommit(QStringList(file)); +} + +void SubversionPlugin::startCommitAll() +{ + // Make sure we have only repository for commit + const QStringList files = currentProjectsTopLevels(); + switch (files.size()) { + case 0: + break; + case 1: + startCommit(files); + break; + default: { + const QString msg = tr("The commit list spans several respositories (%1). Please commit them one by one."). + arg(files.join(QString(QLatin1Char(' ')))); + QMessageBox::warning(0, tr("svn commit"), msg, QMessageBox::Ok); + } + break; + } +} + +/* Start commit of files of a single repository by displaying + * template and files in a submit editor. On closing, the real + * commit will start. */ +void SubversionPlugin::startCommit(const QStringList &files) +{ + if (files.empty()) + return; + + if (m_changeTmpFile) { + showOutput(tr("Another commit is currently being executed.")); + return; + } + + QStringList args(QLatin1String("status")); + args += files; + if (args.size() == 1) + return; + + const SubversionResponse response = runSvn(args, subversionShortTimeOut, false); + if (response.error) + return; + // Get list of added/modified/deleted files + const QStringList statusOutput = parseStatusOutput(response.stdOut); + if (statusOutput.empty()) { + showOutput(tr("There are no modified files."), true); + return; + } + + // Create a new submit change file containing the submit template + QTemporaryFile *changeTmpFile = new QTemporaryFile(this); + changeTmpFile->setAutoRemove(true); + if (!changeTmpFile->open()) { + showOutput(tr("Cannot create temporary file: %1").arg(changeTmpFile->errorString())); + delete changeTmpFile; + return; + } + m_changeTmpFile = changeTmpFile; + // TODO: Retrieve submit template from + const QString submitTemplate; + // Create a submit + m_changeTmpFile->write(submitTemplate.toUtf8()); + m_changeTmpFile->flush(); + m_changeTmpFile->seek(0); + // Create a submit editor and set file list + SubversionSubmitEditor *editor = openSubversionSubmitEditor(m_changeTmpFile->fileName()); + editor->setFileList(statusOutput); +} + +// Parse "status" output for added/modified/deleted files +QStringList SubversionPlugin::parseStatusOutput(const QString &output) const +{ + QStringList changeSet; + const QString newLine = QString(QLatin1Char('\n')); + const QStringList list = output.split(newLine, QString::SkipEmptyParts); + foreach (const QString& l, list) { + QString line(l.trimmed()); + if (line.startsWith(QLatin1Char('A')) || line.startsWith(QLatin1Char('D')) + || line.startsWith(QLatin1Char('M'))) + changeSet.append(line); + } + return changeSet; +} + +bool SubversionPlugin::commit(const QString &messageFile, + const QStringList &subVersionFileList) +{ + if (Subversion::Constants::debug) + qDebug() << Q_FUNC_INFO << messageFile << subVersionFileList; + // Transform the status list which is sth + // "[ADM]<blanks>file" into an args list. The files of the status log + // can be relative or absolute depending on where the command was run. + QStringList args = QStringList(QLatin1String("commit")); + args << QLatin1String("--non-interactive") << QLatin1String("--file") << messageFile; + args.append(subVersionFileList); + const SubversionResponse response = runSvn(args, subversionLongTimeOut, true); + return !response.error ; +} + +void SubversionPlugin::filelogCurrentFile() +{ + const QString file = currentFileName(); + if (!file.isEmpty()) + filelog(file); +} + +void SubversionPlugin::filelog(const QString &file) +{ + QTextCodec *codec = VCSBase::VCSBaseEditor::getCodec(m_coreInstance, file); + // no need for temp file + QStringList args(QLatin1String("log")); + args.append(QDir::toNativeSeparators(file)); + + const SubversionResponse response = runSvn(args, subversionShortTimeOut, false, codec); + if (response.error) + return; + + // Re-use an existing view if possible to support + // the common usage pattern of continuously changing and diffing a file + + if (Core::IEditor *editor = locateEditor(m_coreInstance, "logFileName", file)) { + editor->createNew(response.stdOut); + m_coreInstance->editorManager()->setCurrentEditor(editor); + } else { + const QString title = tr("svn log %1").arg(QFileInfo(file).fileName()); + Core::IEditor *newEditor = showOutputInEditor(title, response.stdOut, VCSBase::LogOutput, file, codec); + newEditor->setProperty("logFileName", file); + } +} + +void SubversionPlugin::updateProject() +{ + const QStringList topLevels = currentProjectsTopLevels(); + if (topLevels.empty()) + return; + + QStringList args(QLatin1String("update")); + args.append(topLevels); + runSvn(args, subversionLongTimeOut, false); +} + +void SubversionPlugin::annotateCurrentFile() +{ + const QString file = currentFileName(); + if (!file.isEmpty()) + annotate(file); +} + +void SubversionPlugin::annotate(const QString &file) +{ + QTextCodec *codec = VCSBase::VCSBaseEditor::getCodec(m_coreInstance, file); + + QStringList args(QLatin1String("annotate")); + args.push_back(QLatin1String("-v")); + args.append(QDir::toNativeSeparators(file)); + + const SubversionResponse response = runSvn(args, subversionShortTimeOut, false, codec); + if (response.error) + return; + + // Re-use an existing view if possible to support + // the common usage pattern of continuously changing and diffing a file + + if (Core::IEditor *editor = locateEditor(m_coreInstance, "annotateFileName", file)) { + editor->createNew(response.stdOut); + m_coreInstance->editorManager()->setCurrentEditor(editor); + } else { + const QString title = tr("svn annotate %1").arg(QFileInfo(file).fileName()); + Core::IEditor *newEditor = showOutputInEditor(title, response.stdOut, VCSBase::AnnotateOutput, file, codec); + newEditor->setProperty("annotateFileName", file); + } +} + +void SubversionPlugin::projectStatus() +{ + if (!m_projectExplorer) + return; + + QStringList args(QLatin1String("status")); + args += currentProjectsTopLevels(); + + if (args.size() == 1) + return; + + runSvn(args, subversionShortTimeOut, true); +} + +void SubversionPlugin::describe(const QString &source, const QString &changeNr) +{ + // To describe a complete change, find the top level and then do + //svn diff -r 472958:472959 <top level> + const QFileInfo fi(source); + const QString topLevel = findTopLevelForDirectory(fi.isDir() ? source : fi.absolutePath()); + if (topLevel.isEmpty()) + return; + if (Subversion::Constants::debug) + qDebug() << Q_FUNC_INFO << source << topLevel << changeNr; + // Number must be > 1 + bool ok; + const int number = changeNr.toInt(&ok); + if (!ok || number < 2) + return; + QStringList args(QLatin1String("diff")); + args.push_back(QLatin1String("-r")); + QString diffArg; + QTextStream(&diffArg) << (number - 1) << ':' << number; + args.push_back(diffArg); + args.push_back(topLevel); + + QTextCodec *codec = VCSBase::VCSBaseEditor::getCodec(m_coreInstance, source); + const SubversionResponse response = runSvn(args, subversionShortTimeOut, false, codec); + if (response.error) + return; + + // Re-use an existing view if possible to support + // the common usage pattern of continuously changing and diffing a file + const QString id = diffArg + source; + if (Core::IEditor *editor = locateEditor(m_coreInstance, "describeChange", id)) { + editor->createNew(response.stdOut); + m_coreInstance->editorManager()->setCurrentEditor(editor); + } else { + const QString title = tr("svn describe %1#%2").arg(QFileInfo(source).fileName(), changeNr); + Core::IEditor *newEditor = showOutputInEditor(title, response.stdOut, VCSBase::DiffOutput, source, codec); + newEditor->setProperty("describeChange", id); + } +} + +void SubversionPlugin::submitCurrentLog() +{ + m_coreInstance->editorManager()->closeEditors(QList<Core::IEditor*>() + << m_coreInstance->editorManager()->currentEditor()); +} + +QString SubversionPlugin::currentFileName() const +{ + const QString fileName = m_coreInstance->fileManager()->currentFile(); + if (!fileName.isEmpty()) { + const QFileInfo fi(fileName); + if (fi.exists()) + return fi.canonicalFilePath(); + } + return QString(); +} + +static inline QString processStdErr(QProcess &proc) +{ + return QString::fromLocal8Bit(proc.readAllStandardError()).remove(QLatin1Char('\r')); +} + +static inline QString processStdOut(QProcess &proc, QTextCodec *outputCodec = 0) +{ + const QByteArray stdOutData = proc.readAllStandardOutput(); + QString stdOut = outputCodec ? outputCodec->toUnicode(stdOutData) : QString::fromLocal8Bit(stdOutData); + return stdOut.remove(QLatin1Char('\r')); +} + +SubversionResponse SubversionPlugin::runSvn(const QStringList &arguments, + int timeOut, + bool showStdOutInOutputWindow, + QTextCodec *outputCodec) +{ + const QString executable = m_settings.svnCommand; + SubversionResponse response; + if (executable.isEmpty()) { + response.error = true; + response.message =tr("No subversion executable specified!"); + return response; + } + const QStringList allArgs = m_settings.addOptions(arguments); + + // Hide passwords, etc in the log window + const QString timeStamp = QTime::currentTime().toString(QLatin1String("HH:mm")); + const QString outputText = tr("%1 Executing: %2 %3\n").arg(timeStamp, executable, SubversionSettings::formatArguments(allArgs)); + showOutput(outputText, false); + + if (Subversion::Constants::debug) + qDebug() << "runSvn" << timeOut << outputText; + + // Run, connect stderr to the output window + Core::Utils::SynchronousProcess process; + process.setTimeout(timeOut); + process.setStdOutCodec(outputCodec); + + process.setStdErrBufferedSignalsEnabled(true); + connect(&process, SIGNAL(stdErrBuffered(QString,bool)), m_subversionOutputWindow, SLOT(append(QString,bool))); + + // connect stdout to the output window if desired + if (showStdOutInOutputWindow) { + process.setStdOutBufferedSignalsEnabled(true); + connect(&process, SIGNAL(stdOutBuffered(QString,bool)), m_subversionOutputWindow, SLOT(append(QString,bool))); + } + + const Core::Utils::SynchronousProcessResponse sp_resp = process.run(executable, allArgs); + response.error = true; + response.stdErr = sp_resp.stdErr; + response.stdOut = sp_resp.stdOut; + switch (sp_resp.result) { + case Core::Utils::SynchronousProcessResponse::Finished: + response.error = false; + break; + case Core::Utils::SynchronousProcessResponse::FinishedError: + response.message = tr("The process terminated with exit code %1.").arg(sp_resp.exitCode); + break; + case Core::Utils::SynchronousProcessResponse::TerminatedAbnormally: + response.message = tr("The process terminated abnormally."); + break; + case Core::Utils::SynchronousProcessResponse::StartFailed: + response.message = tr("Could not start subversion '%1'. Please check your settings in the preferences.").arg(executable); + break; + case Core::Utils::SynchronousProcessResponse::Hang: + response.message = tr("Subversion did not respond within timeout limit (%1 ms).").arg(timeOut); + break; + } + if (response.error) + m_subversionOutputWindow->append(response.message, true); + + return response; +} + +void SubversionPlugin::showOutput(const QString &output, bool bringToForeground) +{ + m_subversionOutputWindow->append(output); + if (bringToForeground) + m_subversionOutputWindow->popup(); +} + +Core::IEditor * SubversionPlugin::showOutputInEditor(const QString& title, const QString &output, + int editorType, const QString &source, + QTextCodec *codec) +{ + const VCSBase::VCSBaseEditorParameters *params = findType(editorType); + Q_ASSERT(params); + const QString kind = QLatin1String(params->kind); + if (Subversion::Constants::debug) + qDebug() << "SubversionPlugin::showOutputInEditor" << title << kind << "Size= " << output.size() << " Type=" << editorType << debugCodec(codec); + QString s = title; + Core::IEditor *ediface = m_coreInstance->editorManager()->newFile(kind, &s, output.toLocal8Bit()); + SubversionEditor *e = qobject_cast<SubversionEditor*>(ediface->widget()); + if (!e) + return 0; + s.replace(QLatin1Char(' '), QLatin1Char('_')); + e->setSuggestedFileName(s); + if (!source.isEmpty()) + e->setSource(source); + if (codec) + e->setCodec(codec); + return e->editableInterface(); +} + +SubversionSettings SubversionPlugin::settings() const +{ + return m_settings; +} + +void SubversionPlugin::setSettings(const SubversionSettings &s) +{ + if (s != m_settings) { + m_settings = s; + if (QSettings *settings = m_coreInstance->settings()) + m_settings.toSettings(settings); + } +} + +Core::ICore *SubversionPlugin::coreInstance() +{ + Q_ASSERT(m_coreInstance); + return m_coreInstance; +} + +SubversionPlugin *SubversionPlugin::subversionPluginInstance() +{ + Q_ASSERT(m_subversionPluginInstance); + return m_subversionPluginInstance; +} + +bool SubversionPlugin::vcsAdd(const QString &rawFileName) +{ + const QString file = QDir::toNativeSeparators(rawFileName); + QStringList args(QLatin1String("add")); + args.push_back(file); + + const SubversionResponse response = runSvn(args, subversionShortTimeOut, true); + return !response.error; +} + +bool SubversionPlugin::vcsDelete(const QString &rawFileName) +{ + const QString file = QDir::toNativeSeparators(rawFileName); + + QStringList args(QLatin1String("delete")); + args.push_back(file); + + const SubversionResponse response = runSvn(args, subversionShortTimeOut, true); + return !response.error; +} + +/* Subversion has ".svn" directory in each directory + * it manages. The top level is the first directory + * under the directory that does not have a ".svn". */ +bool SubversionPlugin::managesDirectory(const QString &directory) const +{ + const QDir dir(directory); + const bool rc = dir.exists() && managesDirectory(dir); + if (Subversion::Constants::debug) + qDebug() << "SubversionPlugin::managesDirectory" << directory << rc; + return rc; +} + +bool SubversionPlugin::managesDirectory(const QDir &directory) const +{ + const QString svnDir = directory.absoluteFilePath(m_svnDotDirectory); + return QFileInfo(svnDir).isDir(); +} + +QString SubversionPlugin::findTopLevelForDirectory(const QString &directory) const +{ + // Debug wrapper + const QString rc = findTopLevelForDirectoryI(directory); + if (Subversion::Constants::debug) + qDebug() << "SubversionPlugin::findTopLevelForDirectory" << directory << rc; + return rc; +} + +QString SubversionPlugin::findTopLevelForDirectoryI(const QString &directory) const +{ + /* Recursing up, the top level is a child of the first directory that does + * not have a ".svn" directory. The starting directory must be a managed + * one. Go up and try to find the first unmanaged parent dir. */ + QDir lastDirectory = QDir(directory); + if (!lastDirectory.exists() || !managesDirectory(lastDirectory)) + return QString(); + for (QDir parentDir = lastDirectory; parentDir.cdUp() ; lastDirectory = parentDir) { + if (!managesDirectory(parentDir)) + return QDir::toNativeSeparators(lastDirectory.absolutePath()); + } + return QString(); +} + +Q_EXPORT_PLUGIN(SubversionPlugin) diff --git a/src/plugins/subversion/subversionplugin.h b/src/plugins/subversion/subversionplugin.h new file mode 100644 index 00000000000..d01d0a05bbd --- /dev/null +++ b/src/plugins/subversion/subversionplugin.h @@ -0,0 +1,223 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SUBVERSIONPLUGIN_H +#define SUBVERSIONPLUGIN_H + +#include "subversionsettings.h" + +#include <coreplugin/editormanager/ieditorfactory.h> +#include <coreplugin/iversioncontrol.h> +#include <coreplugin/icorelistener.h> +#include <extensionsystem/iplugin.h> +#include <coreplugin/icorelistener.h> + +#include <QtCore/QObject> +#include <QtCore/QProcess> + +QT_BEGIN_NAMESPACE +class QFile; +class QDir; +class QAction; +class QTemporaryFile; +class QTextCodec; +QT_END_NAMESPACE + +namespace Core { + class ICore; + class IEditorFactory; + class IVersionControl; +} + +namespace ProjectExplorer { + class ProjectExplorerPlugin; +} + +namespace Subversion { +namespace Internal { + +class CoreListener; +class SettingsPage; +class SubversionOutputWindow; +class SubversionSubmitEditor; + +struct SubversionResponse +{ + SubversionResponse() : error(false) {} + bool error; + QString stdOut; + QString stdErr; + QString message; +}; + +class SubversionPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + SubversionPlugin(); + ~SubversionPlugin(); + + bool initialize(const QStringList &arguments, QString *error_message); + void extensionsInitialized(); + bool editorAboutToClose(Core::IEditor *editor); + + void svnDiff(const QStringList &files, QString diffname = QString()); + + SubversionSubmitEditor *openSubversionSubmitEditor(const QString &fileName); + + SubversionSettings settings() const; + void setSettings(const SubversionSettings &s); + + // IVersionControl + bool vcsAdd(const QString &fileName); + bool vcsDelete(const QString &fileName); + bool managesDirectory(const QString &directory) const; + QString findTopLevelForDirectory(const QString &directory) const; + + static Core::ICore *coreInstance(); + static SubversionPlugin *subversionPluginInstance(); + +private slots: + void updateActions(); + void addCurrentFile(); + void deleteCurrentFile(); + void revertCurrentFile(); + void diffProject(); + void diffCurrentFile(); + void startCommitAll(); + void startCommitCurrentFile(); + void filelogCurrentFile(); + void annotateCurrentFile(); + void projectStatus(); + void describe(const QString &source, const QString &changeNr); + void updateProject(); + void submitCurrentLog(); + void diffFiles(const QStringList &); + +private: + QString currentFileName() const; + Core::IEditor * showOutputInEditor(const QString& title, const QString &output, + int editorType, const QString &source, + QTextCodec *codec); + SubversionResponse runSvn(const QStringList &arguments, int timeOut, + bool showStdOutInOutputWindow, QTextCodec *outputCodec = 0); + void showOutput(const QString &output, bool bringToForeground = true); + QStringList parseStatusOutput(const QString &output) const; + void annotate(const QString &file); + void filelog(const QString &file); + bool managesDirectory(const QDir &directory) const; + QString findTopLevelForDirectoryI(const QString &directory) const; + QStringList currentProjectsTopLevels(QString *name = 0) const; + void startCommit(const QStringList &files); + bool commit(const QString &messageFile, const QStringList &subVersionFileList); + void cleanChangeTmpFile(); + + const QString m_svnDotDirectory; + + SubversionSettings m_settings; + Core::IVersionControl *m_versionControl; + CoreListener *m_coreListener; + SettingsPage *m_settingsPage; + QTemporaryFile *m_changeTmpFile; + + Core::IEditorFactory *m_submitEditorFactory; + QList<Core::IEditorFactory*> m_editorFactories; + + SubversionOutputWindow *m_subversionOutputWindow; + ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer; + + QAction *m_addAction; + QAction *m_deleteAction; + QAction *m_revertAction; + QAction *m_diffProjectAction; + QAction *m_diffCurrentAction; + QAction *m_commitAllAction; + QAction *m_commitCurrentAction; + QAction *m_filelogCurrentAction; + QAction *m_annotateCurrentAction; + QAction *m_statusAction; + QAction *m_updateProjectAction; + + QAction *m_submitCurrentLogAction; + QAction *m_submitDiffAction; + QAction *m_submitUndoAction; + QAction *m_submitRedoAction; + + static const char * const SUBVERSION_MENU; + static const char * const ADD; + static const char * const DELETE_FILE; + static const char * const REVERT; + static const char * const SEPARATOR0; + static const char * const DIFF_PROJECT; + static const char * const DIFF_CURRENT; + static const char * const SEPARATOR1; + static const char * const COMMIT_ALL; + static const char * const COMMIT_CURRENT; + static const char * const SEPARATOR2; + static const char * const FILELOG_CURRENT; + static const char * const ANNOTATE_CURRENT; + static const char * const SEPARATOR3; + static const char * const STATUS; + static const char * const UPDATE; + + static Core::ICore *m_coreInstance; + static SubversionPlugin *m_subversionPluginInstance; + + friend class SubversionOutputWindow; +}; + +// Just a proxy for SubversionPlugin +class CoreListener : public Core::ICoreListener +{ + Q_OBJECT +public: + CoreListener(SubversionPlugin *plugin) : m_plugin(plugin) { } + + // Start commit when submit editor closes + bool editorAboutToClose(Core::IEditor *editor) { + return m_plugin->editorAboutToClose(editor); + } + + // TODO: how to handle that ??? + bool coreAboutToClose() { + return true; + } + +private: + SubversionPlugin *m_plugin; +}; + +} // namespace Subversion +} // namespace Internal + +#endif // SUBVERSIONPLUGIN_H diff --git a/src/plugins/subversion/subversionsettings.cpp b/src/plugins/subversion/subversionsettings.cpp new file mode 100644 index 00000000000..ca4a946c7c0 --- /dev/null +++ b/src/plugins/subversion/subversionsettings.cpp @@ -0,0 +1,133 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "subversionsettings.h" + +#include <QtCore/QSettings> +#include <QtCore/QTextStream> + +static const char *groupC = "Subversion"; +static const char *commandKeyC = "Command"; +static const char *userKeyC = "User"; +static const char *passwordKeyC = "Password"; +static const char *authenticationKeyC = "Authentication"; + +static const char *userNameOptionC = "--username"; +static const char *passwordOptionC = "--password"; + +static QString defaultCommand() +{ + QString rc; + rc = QLatin1String("svn"); +#if defined(Q_OS_WIN32) + rc.append(QLatin1String(".exe")); +#endif + return rc; +} + +namespace Subversion { +namespace Internal { + +SubversionSettings::SubversionSettings() : + svnCommand(defaultCommand()), + useAuthentication(false) +{ +} + +void SubversionSettings::fromSettings(QSettings *settings) +{ + settings->beginGroup(QLatin1String(groupC)); + svnCommand = settings->value(QLatin1String(commandKeyC), defaultCommand()).toString(); + useAuthentication = settings->value(QLatin1String(authenticationKeyC), QVariant(false)).toBool(); + user = settings->value(QLatin1String(userKeyC), QString()).toString(); + password = settings->value(QLatin1String(passwordKeyC), QString()).toString(); + settings->endGroup(); +} + +void SubversionSettings::toSettings(QSettings *settings) const +{ + settings->beginGroup(QLatin1String(groupC)); + settings->setValue(QLatin1String(commandKeyC), svnCommand); + settings->setValue(QLatin1String(authenticationKeyC), QVariant(useAuthentication)); + settings->setValue(QLatin1String(userKeyC), user); + settings->setValue(QLatin1String(passwordKeyC), password); + settings->endGroup(); +} + +bool SubversionSettings::equals(const SubversionSettings &s) const +{ + return svnCommand == s.svnCommand + && useAuthentication == s.useAuthentication + && user == s.user + && password == s.password; +} + +QStringList SubversionSettings::addOptions(const QStringList &args) const +{ + if (!useAuthentication || user.isEmpty()) + return args; + + QStringList rc; + rc.push_back(QLatin1String(userNameOptionC)); + rc.push_back(user); + if (!password.isEmpty()) { + rc.push_back(QLatin1String(passwordOptionC)); + rc.push_back(password); + } + rc.append(args); + return rc; +} + +// Format arguments for log windows hiding passwords, etc. +QString SubversionSettings::formatArguments(const QStringList &args) +{ + QString rc; + QTextStream str(&rc); + const int size = args.size(); + // Skip authentication options + for (int i = 0; i < size; i++) { + const QString &arg = args.at(i); + if (i) + str << ' '; + str << arg; + if (arg == QLatin1String(userNameOptionC) || arg == QLatin1String(passwordOptionC)) { + str << " ********"; + i++; + } + } + return rc; +} + + +} +} + diff --git a/src/plugins/subversion/subversionsettings.h b/src/plugins/subversion/subversionsettings.h new file mode 100644 index 00000000000..32c0c301ff5 --- /dev/null +++ b/src/plugins/subversion/subversionsettings.h @@ -0,0 +1,73 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SUBVERSIONSETTINGS_H +#define SUBVERSIONSETTINGS_H + +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace Subversion { +namespace Internal { + +// Todo: Add user name and password? +struct SubversionSettings { + SubversionSettings(); + + void fromSettings(QSettings *); + void toSettings(QSettings *) const; + + // Add authentication and (maybe future) options to the + // command line + QStringList addOptions(const QStringList &args) const; + // Format arguments for log windows hiding passwords, etc. + static QString formatArguments(const QStringList &args); + + bool equals(const SubversionSettings &s) const; + + QString svnCommand; + bool useAuthentication; + QString user; + QString password; +}; + +inline bool operator==(const SubversionSettings &p1, const SubversionSettings &p2) + { return p1.equals(p2); } +inline bool operator!=(const SubversionSettings &p1, const SubversionSettings &p2) + { return !p1.equals(p2); } +} +} + +#endif diff --git a/src/plugins/subversion/subversionsubmiteditor.cpp b/src/plugins/subversion/subversionsubmiteditor.cpp new file mode 100644 index 00000000000..13010413781 --- /dev/null +++ b/src/plugins/subversion/subversionsubmiteditor.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "subversionsubmiteditor.h" + +#include <utils/submiteditorwidget.h> + +namespace Subversion { +namespace Internal { + +SubversionSubmitEditor::SubversionSubmitEditor(const VCSBase::VCSBaseSubmitEditorParameters *parameters, + QWidget *parentWidget) : + VCSBase::VCSBaseSubmitEditor(parameters, new Core::Utils::SubmitEditorWidget(parentWidget)) +{ + setDisplayName(tr("Subversion Submit")); +} + +QStringList SubversionSubmitEditor::vcsFileListToFileList(const QStringList &rl) const +{ + QStringList files; + const QStringList::const_iterator cend = rl.constEnd(); + for (QStringList::const_iterator it = rl.constBegin(); it != cend; ++it) + files.push_back(SubversionSubmitEditor::fileFromStatusLine(*it)); + return files; +} + +QString SubversionSubmitEditor::fileFromStatusLine(const QString &statusLine) +{ + enum { filePos = 7 }; + return statusLine.mid(filePos, statusLine.size() - filePos); +} + +} +} diff --git a/src/plugins/subversion/subversionsubmiteditor.h b/src/plugins/subversion/subversionsubmiteditor.h new file mode 100644 index 00000000000..3ba3335e49d --- /dev/null +++ b/src/plugins/subversion/subversionsubmiteditor.h @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SUBVERSIONSUBMITEDITOR_H +#define SUBVERSIONSUBMITEDITOR_H + +#include <vcsbase/vcsbasesubmiteditor.h> + +namespace Subversion { +namespace Internal { + +class SubversionSubmitEditor : public VCSBase::VCSBaseSubmitEditor +{ + Q_OBJECT +public: + SubversionSubmitEditor(const VCSBase::VCSBaseSubmitEditorParameters *parameters, + QWidget *parentWidget = 0); + + static QString fileFromStatusLine(const QString &statusLine); + +private: + virtual QStringList vcsFileListToFileList(const QStringList &) const; +}; + +} // namespace Internal +} // namespace Subversion + +#endif // SUBVERSIONSUBMITEDITOR_H diff --git a/src/plugins/texteditor/TextEditor.mimetypes.xml b/src/plugins/texteditor/TextEditor.mimetypes.xml new file mode 100644 index 00000000000..e78754da68d --- /dev/null +++ b/src/plugins/texteditor/TextEditor.mimetypes.xml @@ -0,0 +1,47 @@ +<?xml version="1.0"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="text/plain"> + <comment>Plain text document</comment> + <sub-class-of type="application/octet-stream"/> + <comment xml:lang="bg">Документ с неформатиран текст</comment> + <comment xml:lang="ca">document de text pla</comment> + <comment xml:lang="cs">Prostý textový dokument</comment> + <comment xml:lang="da">ren tekst-dokument</comment> + <comment xml:lang="de">einfaches Textdokument</comment> + <comment xml:lang="el">έγγραφο απλού κειμένου</comment> + <comment xml:lang="eo">plata teksta dokumento</comment> + <comment xml:lang="es">documento de texto sencillo</comment> + <comment xml:lang="eu">testu soileko dokumentua</comment> + <comment xml:lang="fi">perustekstiasiakirja</comment> + <comment xml:lang="fr">document plein texte</comment> + <comment xml:lang="hu">egyszerű szöveg</comment> + <comment xml:lang="it">Documento in testo semplice</comment> + <comment xml:lang="ja">平文テキストドキュメント</comment> + <comment xml:lang="ko">보통 text 문서</comment> + <comment xml:lang="lt">paprastas tekstinis dokumentas</comment> + <comment xml:lang="ms">Dokumen teks jernih</comment> + <comment xml:lang="nb">vanlig tekstdokument</comment> + <comment xml:lang="nl">platte tekst document</comment> + <comment xml:lang="nn">vanleg tekstdokument</comment> + <comment xml:lang="pl">zwykły dokument tekstowy</comment> + <comment xml:lang="pt">documento em texto simples</comment> + <comment xml:lang="pt_BR">Documento somente texto</comment> + <comment xml:lang="sq">dokument teksti i thjeshtë</comment> + <comment xml:lang="sr">обичан текстуални документ</comment> + <comment xml:lang="sv">vanligt textdokument</comment> + <comment xml:lang="uk">звичайний текстовий документ</comment> + <comment xml:lang="vi">thư nhập thô (văn bản không có kiểu dáng)</comment> + <comment xml:lang="zh_CN">纯文本文档</comment> + <comment xml:lang="zh_TW">普通文本檔</comment> + <glob pattern="*.txt"/> + </mime-type> + <mime-type type="application/xml"> + <sub-class-of type="text/plain"/> + <comment>XML document</comment> + <glob pattern="*.xml"/> + <glob pattern="*.xsl"/> + <glob pattern="*.xslt"/> + <glob pattern="*.xbl"/> + <alias type="text/xml"/> + </mime-type> +</mime-info> diff --git a/src/plugins/texteditor/TextEditor.pluginspec b/src/plugins/texteditor/TextEditor.pluginspec new file mode 100644 index 00000000000..aaad622ea4f --- /dev/null +++ b/src/plugins/texteditor/TextEditor.pluginspec @@ -0,0 +1,12 @@ +<plugin name="TextEditor" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Text editor framework and the implementation of the basic text editor.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> + <dependency name="Find" version="0.9.1"/> + <dependency name="QuickOpen" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp new file mode 100644 index 00000000000..4c11a626dbf --- /dev/null +++ b/src/plugins/texteditor/basefilefind.cpp @@ -0,0 +1,233 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "basefilefind.h" + +#include <coreplugin/stylehelper.h> +#include <coreplugin/progressmanager/progressmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> +#include <find/textfindconstants.h> +#include <texteditor/itexteditor.h> +#include <texteditor/basetexteditor.h> + +#include <QtDebug> +#include <QtCore/QDirIterator> +#include <QtGui/QPushButton> +#include <QtGui/QFileDialog> + +using namespace Core::Utils; +using namespace Find; +using namespace TextEditor; + +BaseFileFind::BaseFileFind(Core::ICore *core, SearchResultWindow *resultWindow) + : m_core(core), + m_resultWindow(resultWindow), + m_isSearching(false), + m_resultLabel(0), + m_filterCombo(0), + m_useRegExp(false), + m_useRegExpCheckBox(0) +{ + m_watcher.setPendingResultsLimit(1); + connect(&m_watcher, SIGNAL(resultReadyAt(int)), this, SLOT(displayResult(int))); + connect(&m_watcher, SIGNAL(finished()), this, SLOT(searchFinished())); +} + +bool BaseFileFind::isEnabled() const +{ + return !m_isSearching; +} + +QStringList BaseFileFind::fileNameFilters() const +{ + QStringList filters; + if (m_filterCombo && !m_filterCombo->currentText().isEmpty()) { + QStringList parts = m_filterCombo->currentText().split(","); + foreach (const QString &part, parts) { + QString filter = part.trimmed(); + if (!filter.isEmpty()) { + filters << filter; + } + } + } + return filters; +} + +void BaseFileFind::findAll(const QString &txt, QTextDocument::FindFlags findFlags) +{ + m_isSearching = true; + emit changed(); + updateComboEntries(m_filterCombo, false); + m_watcher.setFuture(QFuture<FileSearchResult>()); + m_resultWindow->clearContents(); + m_resultWindow->popup(true); + if (m_useRegExp) + m_watcher.setFuture(Core::Utils::findInFilesRegExp(txt, files(), findFlags)); + else + m_watcher.setFuture(Core::Utils::findInFiles(txt, files(), findFlags)); + Core::FutureProgress *progress = m_core->progressManager()->addTask(m_watcher.future(), + "Search", + Constants::TASK_SEARCH); + progress->setWidget(createProgressWidget()); + connect(progress, SIGNAL(clicked()), m_resultWindow, SLOT(popup())); +} + +void BaseFileFind::displayResult(int index) { + Core::Utils::FileSearchResult result = m_watcher.future().resultAt(index); + ResultWindowItem *item = m_resultWindow->addResult(result.fileName, + result.lineNumber, + result.matchingLine, + result.matchStart, + result.matchLength); + if (item) + connect(item, SIGNAL(activated(const QString&,int,int)), this, SLOT(openEditor(const QString&,int,int))); + + if (m_resultLabel) + m_resultLabel->setText(tr("%1 found").arg(m_resultWindow->numberOfResults())); +} + +void BaseFileFind::searchFinished() +{ + m_isSearching = false; + m_resultLabel = 0; + emit changed(); +} + +QWidget *BaseFileFind::createProgressWidget() +{ + m_resultLabel = new QLabel; + // ### TODO this setup should be done by style + QFont f = m_resultLabel->font(); + f.setBold(true); + f.setPointSizeF(StyleHelper::sidebarFontSize()); + m_resultLabel->setFont(f); + m_resultLabel->setPalette(StyleHelper::sidebarFontPalette(m_resultLabel->palette())); + m_resultLabel->setText(tr("%1 found").arg(m_resultWindow->numberOfResults())); + return m_resultLabel; +} + +QWidget *BaseFileFind::createPatternWidget() +{ +/* + QWidget *widget = new QWidget; + QHBoxLayout *hlayout = new QHBoxLayout(widget); + hlayout->setMargin(0); + widget->setLayout(hlayout); +*/ + QString filterToolTip = tr("List of comma separated wildcard filters"); +/* + QLabel *label = new QLabel(tr("File pattern:")); + label->setToolTip(filterToolTip); +*/ +/* + hlayout->addWidget(label); +*/ + m_filterCombo = new QComboBox; + m_filterCombo->setEditable(true); + m_filterCombo->setModel(&m_filterStrings); + m_filterCombo->setMaxCount(10); + m_filterCombo->setMinimumContentsLength(10); + m_filterCombo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + m_filterCombo->setInsertPolicy(QComboBox::InsertAtBottom); + m_filterCombo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + m_filterCombo->setToolTip(filterToolTip); + syncComboWithSettings(m_filterCombo, m_filterSetting); +/* + hlayout->addWidget(m_filterCombo); +*/ + return m_filterCombo; +} + +QWidget *BaseFileFind::createRegExpWidget() +{ + m_useRegExpCheckBox = new QCheckBox(tr("Use Regular Expressions")); + m_useRegExpCheckBox->setChecked(m_useRegExp); + connect(m_useRegExpCheckBox, SIGNAL(toggled(bool)), this, SLOT(syncRegExpSetting(bool))); + return m_useRegExpCheckBox; +} + +void BaseFileFind::writeCommonSettings(QSettings *settings) +{ + settings->setValue("filters", m_filterStrings.stringList()); + if (m_filterCombo) + settings->setValue("currentFilter", m_filterCombo->currentText()); + settings->setValue("useRegExp", m_useRegExp); +} + +void BaseFileFind::readCommonSettings(QSettings *settings, const QString &defaultFilter) +{ + QStringList filters = settings->value("filters").toStringList(); + m_filterSetting = settings->value("currentFilter").toString(); + m_useRegExp = settings->value("useRegExp", false).toBool(); + if (m_useRegExpCheckBox) + m_useRegExpCheckBox->setChecked(m_useRegExp); + if (filters.isEmpty()) + filters << defaultFilter; + if (m_filterSetting.isEmpty()) + m_filterSetting = filters.first(); + m_filterStrings.setStringList(filters); + syncComboWithSettings(m_filterCombo, m_filterSetting); +} + +void BaseFileFind::syncComboWithSettings(QComboBox *combo, const QString &setting) +{ + if (!combo) + return; + int index = combo->findText(setting); + if (index < 0) + combo->setEditText(setting); + else + combo->setCurrentIndex(index); +} + +void BaseFileFind::updateComboEntries(QComboBox *combo, bool onTop) +{ + int index = combo->findText(combo->currentText()); + if (index < 0) { + if (onTop) { + combo->insertItem(0, combo->currentText()); + } else { + combo->addItem(combo->currentText()); + } + combo->setCurrentIndex(combo->findText(combo->currentText())); + } +} + +void BaseFileFind::syncRegExpSetting(bool useRegExp) +{ + m_useRegExp = useRegExp; +} + +void BaseFileFind::openEditor(const QString &fileName, int line, int column) +{ + TextEditor::BaseTextEditor::openEditorAt(fileName, line, column); +} diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h new file mode 100644 index 00000000000..562a4487ccd --- /dev/null +++ b/src/plugins/texteditor/basefilefind.h @@ -0,0 +1,95 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BASEFILEFIND_H +#define BASEFILEFIND_H + +#include "texteditor_global.h" + +#include <coreplugin/icore.h> +#include <find/ifindfilter.h> +#include <find/searchresultwindow.h> +#include <utils/filesearch.h> + +#include <QtCore/QFutureWatcher> +#include <QtCore/QPointer> +#include <QtGui/QLabel> +#include <QtGui/QComboBox> +#include <QtGui/QStringListModel> +#include <QtGui/QCheckBox> + +namespace TextEditor { + +class TEXTEDITOR_EXPORT BaseFileFind : public Find::IFindFilter +{ + Q_OBJECT + +public: + BaseFileFind(Core::ICore *core, Find::SearchResultWindow *resultWindow); + + bool isEnabled() const; + void findAll(const QString &txt, QTextDocument::FindFlags findFlags); + +protected: + virtual QStringList files() = 0; + void writeCommonSettings(QSettings *settings); + void readCommonSettings(QSettings *settings, const QString &defaultFilter); + QWidget *createPatternWidget(); + QWidget *createRegExpWidget(); + void syncComboWithSettings(QComboBox *combo, const QString &setting); + void updateComboEntries(QComboBox *combo, bool onTop); + QStringList fileNameFilters() const; + +private slots: + void displayResult(int index); + void searchFinished(); + void openEditor(const QString &fileName, int line, int column); + void syncRegExpSetting(bool useRegExp); + +private: + QWidget *createProgressWidget(); + + Core::ICore *m_core; + Find::SearchResultWindow *m_resultWindow; + QFutureWatcher<Core::Utils::FileSearchResult> m_watcher; + bool m_isSearching; + QLabel *m_resultLabel; + QStringListModel m_filterStrings; + QString m_filterSetting; + QPointer<QComboBox> m_filterCombo; + bool m_useRegExp; + QCheckBox *m_useRegExpCheckBox; +}; + +} // namespace TextEditor + +#endif // BASEFILEFIND_H diff --git a/src/plugins/texteditor/basetextdocument.cpp b/src/plugins/texteditor/basetextdocument.cpp new file mode 100644 index 00000000000..916bc11f2f2 --- /dev/null +++ b/src/plugins/texteditor/basetextdocument.cpp @@ -0,0 +1,349 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "basetextdocument.h" +#include "basetexteditor.h" +#include "storagesettings.h" + +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QTextStream> +#include <QtCore/QTextCodec> +#include <QtGui/QMainWindow> +#include <QtGui/QSyntaxHighlighter> +#include <QtGui/QApplication> + +#ifndef TEXTEDITOR_STANDALONE +#include <utils/reloadpromptutils.h> +#include "coreplugin/icore.h" +#endif + +using namespace TextEditor; + +#if defined (Q_OS_WIN) +# define NATIVE_LINE_TERMINATOR CRLFLineTerminator +#else +# define NATIVE_LINE_TERMINATOR LFLineTerminator +#endif + +DocumentMarker::DocumentMarker(QTextDocument *doc) + : ITextMarkable(doc), document(doc) +{ +} + +BaseTextDocument::BaseTextDocument() + : m_document(new QTextDocument(this)), + m_highlighter(0) +{ + m_documentMarker = new DocumentMarker(m_document); + m_lineTerminatorMode = NativeLineTerminator; + m_isBinaryData = false; + m_codec = QTextCodec::codecForLocale(); + m_hasDecodingError = false; +} + +BaseTextDocument::~BaseTextDocument() +{ + QTextBlock block = m_document->begin(); + while (block.isValid()) { + if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData())) + data->documentClosing(); + block = block.next(); + } + delete m_document; + m_document = 0; +} + +QString BaseTextDocument::mimeType() const +{ + return m_mimeType; +} + +void BaseTextDocument::setMimeType(const QString &mt) +{ + m_mimeType = mt; +} + +bool BaseTextDocument::save(const QString &fileName) +{ + QTextCursor cursor(m_document); + + cursor.beginEditBlock(); + if (m_storageSettings.m_cleanWhitespace) + cleanWhitespace(cursor, m_storageSettings.m_inEntireDocument); + if (m_storageSettings.m_addFinalNewLine) + ensureFinalNewLine(cursor); + cursor.endEditBlock(); + + QString fName = m_fileName; + if (!fileName.isEmpty()) + fName = fileName; + + QFile file(fName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + return false; + + QString plainText = m_document->toPlainText(); + + if (m_lineTerminatorMode == CRLFLineTerminator) + plainText.replace(QLatin1Char('\n'), QLatin1String("\r\n")); + + file.write(m_codec->fromUnicode(plainText)); + if (!file.flush()) + return false; + file.close(); + + const QFileInfo fi(fName); + m_fileName = fi.absoluteFilePath(); + + m_document->setModified(false); + emit titleChanged(fi.fileName()); + emit changed(); + + m_isBinaryData = false; + m_hasDecodingError = false; + m_decodingErrorSample.clear(); + + return true; +} + +bool BaseTextDocument::isReadOnly() const +{ + if (m_isBinaryData || m_hasDecodingError) + return true; + if (m_fileName.isEmpty()) //have no corresponding file, so editing is ok + return false; + const QFileInfo fi(m_fileName); + return !fi.isWritable(); +} + +bool BaseTextDocument::isModified() const +{ + return m_document->isModified(); +} + +bool BaseTextDocument::open(const QString &fileName) +{ + QString title = tr("untitled"); + if (!fileName.isEmpty()) { + const QFileInfo fi(fileName); + m_fileName = fi.absoluteFilePath(); + + QFile file(fileName); + if (!file.exists()) + return false; + + if (!fi.isReadable()) + return false; + + if (!fi.isWritable()) { + if (!file.open(QIODevice::ReadOnly)) + return false; + } else { + if (!file.open(QIODevice::ReadWrite)) + return false; + } + title = fi.fileName(); + + QByteArray buf = file.readAll(); + int bytesRead = buf.size(); + + QTextCodec *codec = m_codec; + + // code taken from qtextstream + if (bytesRead >= 4 && ((uchar(buf[0]) == 0xff && uchar(buf[1]) == 0xfe && uchar(buf[2]) == 0 && uchar(buf[3]) == 0) + || (uchar(buf[0]) == 0 && uchar(buf[1]) == 0 && uchar(buf[2]) == 0xfe && uchar(buf[3]) == 0xff))) { + codec = QTextCodec::codecForName("UTF-32"); + } else if (bytesRead >= 2 && ((uchar(buf[0]) == 0xff && uchar(buf[1]) == 0xfe) + || (uchar(buf[0]) == 0xfe && uchar(buf[1]) == 0xff))) { + codec = QTextCodec::codecForName("UTF-16"); + } else if (!codec) { + codec = QTextCodec::codecForLocale(); + } + // end code taken from qtextstream + + m_codec = codec; + +#if 0 // should work, but does not, Qt bug with "system" codec + QTextDecoder *decoder = m_codec->makeDecoder(); + QString text = decoder->toUnicode(buf); + m_hasDecodingError = (decoder->hasFailure()); + delete decoder; +#else + QString text = m_codec->toUnicode(buf); + QByteArray verifyBuf = m_codec->fromUnicode(text); // slow + // the minSize trick lets us ignore unicode headers + int minSize = qMin(verifyBuf.size(), buf.size()); + m_hasDecodingError = (minSize < buf.size()- 4 + || memcmp(verifyBuf.constData() + verifyBuf.size() - minSize, + buf.constData() + buf.size() - minSize, minSize)); +#endif + + if (m_hasDecodingError) { + int p = buf.indexOf('\n', 16384); + if (p < 0) + m_decodingErrorSample = buf; + else + m_decodingErrorSample = buf.left(p); + } else { + m_decodingErrorSample.clear(); + } + + int lf = text.indexOf('\n'); + if (lf > 0 && text.at(lf-1) == QLatin1Char('\r')) { + m_lineTerminatorMode = CRLFLineTerminator; + } else if (lf >= 0) { + m_lineTerminatorMode = LFLineTerminator; + } else { + m_lineTerminatorMode = NativeLineTerminator; + } + + m_document->setModified(false); + m_document->setUndoRedoEnabled(false); + if (m_isBinaryData) + m_document->setHtml(tr("<em>Binary data</em>")); + else + m_document->setPlainText(text); + m_document->setUndoRedoEnabled(true); + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(m_document->documentLayout()); + Q_ASSERT(documentLayout); + documentLayout->lastSaveRevision = 0; + m_document->setModified(false); + emit titleChanged(title); + emit changed(); + } + return true; +} + +void BaseTextDocument::reload(QTextCodec *codec) +{ + Q_ASSERT(codec); + m_codec = codec; + reload(); +} + +void BaseTextDocument::reload() +{ + emit aboutToReload(); + if (open(m_fileName)) { + emit reloaded(); + } +} + +void BaseTextDocument::modified(Core::IFile::ReloadBehavior *behavior) +{ + switch (*behavior) { + case Core::IFile::ReloadNone: + return; + case Core::IFile::ReloadAll: + reload(); + return; + case Core::IFile::ReloadPermissions: + emit changed(); + return; + case Core::IFile::AskForReload: + break; + } + +#ifndef TEXTEDITOR_STANDALONE + switch (Core::Utils::reloadPrompt(m_fileName, QApplication::activeWindow())) { + case Core::Utils::ReloadCurrent: + reload(); + break; + case Core::Utils::ReloadAll: + reload(); + *behavior = Core::IFile::ReloadAll; + break; + case Core::Utils::ReloadSkipCurrent: + break; + case Core::Utils::ReloadNone: + *behavior = Core::IFile::ReloadNone; + break; + } +#endif +} + +void BaseTextDocument::setSyntaxHighlighter(QSyntaxHighlighter *highlighter) +{ + if (m_highlighter) + delete m_highlighter; + m_highlighter = highlighter; + m_highlighter->setParent(this); + m_highlighter->setDocument(m_document); +} + +void BaseTextDocument::cleanWhitespace(QTextCursor& cursor, bool inEntireDocument) +{ + + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(m_document->documentLayout()); + + QTextBlock block = m_document->firstBlock(); + while (block.isValid()) { + + if (inEntireDocument || block.revision() > documentLayout->lastSaveRevision) { + + QString blockText = block.text(); + if (int trailing = m_tabSettings.trailingWhitespaces(blockText)) { + cursor.setPosition(block.position() + block.length() - 1); + cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, trailing); + cursor.removeSelectedText(); + } + if (!m_tabSettings.isIndentationClean(blockText)) { + cursor.setPosition(block.position()); + int firstNonSpace = m_tabSettings.firstNonSpace(blockText); + if (firstNonSpace == blockText.length()) { + cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + } else { + int column = m_tabSettings.columnAt(blockText, firstNonSpace); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, firstNonSpace); + QString indentationString = m_tabSettings.indentationString(0, column); + cursor.insertText(indentationString); + } + } + } + + block = block.next(); + } +} + +void BaseTextDocument::ensureFinalNewLine(QTextCursor& cursor) +{ + cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); + bool emptyFile = !cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); + + if (!emptyFile && cursor.selectedText().at(0) != QChar::ParagraphSeparator) + { + cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); + cursor.insertText(QLatin1String("\n")); + } +} diff --git a/src/plugins/texteditor/basetextdocument.h b/src/plugins/texteditor/basetextdocument.h new file mode 100644 index 00000000000..8dbfda2ea88 --- /dev/null +++ b/src/plugins/texteditor/basetextdocument.h @@ -0,0 +1,161 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BASETEXTDOCUMENT_H +#define BASETEXTDOCUMENT_H + +#include "texteditor_global.h" +#include "storagesettings.h" +#include "itexteditor.h" +#include "tabsettings.h" + +QT_BEGIN_NAMESPACE +class QTextCursor; +class QTextDocument; +class QSyntaxHighlighter; +QT_END_NAMESPACE + + +namespace Core { class ICore; } + +namespace TextEditor { + + +class DocumentMarker : public ITextMarkable +{ + Q_OBJECT +public: + DocumentMarker(QTextDocument *); + + // ITextMarkable + bool addMark(ITextMark *mark, int line); + TextMarks marksAt(int line) const; + void removeMark(ITextMark *mark); + bool hasMark(ITextMark *mark) const; + void updateMark(ITextMark *mark); + +private: + QTextDocument *document; +}; + + + +class TEXTEDITOR_EXPORT BaseTextDocument + : public Core::IFile +{ + Q_OBJECT + +public: + BaseTextDocument(); + ~BaseTextDocument(); + void setStorageSettings(const StorageSettings &storageSettings) { m_storageSettings = storageSettings; } + void setTabSettings(const TabSettings &tabSettings) { m_tabSettings = tabSettings; } + + inline const StorageSettings &storageSettings() const { return m_storageSettings; } + inline const TabSettings &tabSettings() const { return m_tabSettings; } + + DocumentMarker *documentMarker() const {return m_documentMarker; } + + //IFile + virtual bool save(const QString &fileName = QString()); + virtual QString fileName() const { return m_fileName; } + virtual bool isReadOnly() const; + virtual bool isModified() const; + virtual bool isSaveAsAllowed() const { return true; } + virtual void modified(Core::IFile::ReloadBehavior *behavior); + virtual QString mimeType() const; + void setMimeType(const QString &mt); + + virtual QString defaultPath() const { return m_defaultPath; } + virtual QString suggestedFileName() const { return m_suggestedFileName; } + + void setDefaultPath(const QString &defaultPath) { m_defaultPath = defaultPath; } + void setSuggestedFileName(const QString &suggestedFileName) { m_suggestedFileName = suggestedFileName; } + + virtual bool open(const QString &fileName = QString()); + virtual void reload(); + + QTextDocument *document() const { return m_document; } + void setSyntaxHighlighter(QSyntaxHighlighter *highlighter); + QSyntaxHighlighter *syntaxHighlighter() const { return m_highlighter; } + + + inline bool isBinaryData() const { return m_isBinaryData; } + inline bool hasDecodingError() const { return m_hasDecodingError || m_isBinaryData; } + inline QTextCodec *codec() const { return m_codec; } + inline void setCodec(QTextCodec *c) { m_codec = c; } + inline QByteArray decodingErrorSample() const { return m_decodingErrorSample; } + + void reload(QTextCodec *codec); + +signals: + void titleChanged(QString title); + void changed(); + void aboutToReload(); + void reloaded(); + +private: + QString m_fileName; + QString m_defaultPath; + QString m_suggestedFileName; + QString m_mimeType; + StorageSettings m_storageSettings; + TabSettings m_tabSettings; + Core::ICore *m_core; + QTextDocument *m_document; + DocumentMarker *m_documentMarker; + QSyntaxHighlighter *m_highlighter; + + enum LineTerminatorMode { + LFLineTerminator, + CRLFLineTerminator, + NativeLineTerminator = +#if defined (Q_OS_WIN) + CRLFLineTerminator +#else + LFLineTerminator +#endif + }; + LineTerminatorMode m_lineTerminatorMode; + QTextCodec *m_codec; + + bool m_isBinaryData; + bool m_hasDecodingError; + QByteArray m_decodingErrorSample; + + void cleanWhitespace(QTextCursor& cursor, bool onlyInModifiedLines); + void ensureFinalNewLine(QTextCursor& cursor); +}; + +} // namespace TextEditor + +#endif diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp new file mode 100644 index 00000000000..a112770f934 --- /dev/null +++ b/src/plugins/texteditor/basetexteditor.cpp @@ -0,0 +1,3453 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "texteditor_global.h" +#include "texteditorconstants.h" +#ifndef TEXTEDITOR_STANDALONE +#include "texteditorplugin.h" +#include "completionsupport.h" +#endif +#include "basetextdocument.h" +#include "basetexteditor_p.h" +#include "codecselector.h" + +#ifndef TEXTEDITOR_STANDALONE +#include <coreplugin/icore.h> +#include <coreplugin/manhattanstyle.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/editormanager/editormanager.h> +#include <find/basetextfind.h> +#include <texteditor/fontsettings.h> +#include <utils/reloadpromptutils.h> +#include <aggregation/aggregate.h> +#endif +#include <utils/linecolumnlabel.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QTextCodec> +#include <QtCore/QFile> +#include <QtCore/QDebug> +#include <QtCore/QTimer> +#include <QtGui/QAbstractTextDocumentLayout> +#include <QtGui/QApplication> +#include <QtGui/QKeyEvent> +#include <QtGui/QLabel> +#include <QtGui/QLayout> +#include <QtGui/QPainter> +#include <QtGui/QPrinter> +#include <QtGui/QPrintDialog> +#include <QtGui/QPainter> +#include <QtGui/QScrollBar> +#include <QtGui/QShortcut> +#include <QtGui/QScrollBar> +#include <QtGui/QStyle> +#include <QtGui/QSyntaxHighlighter> +#include <QtGui/QTextCursor> +#include <QtGui/QTextBlock> +#include <QtGui/QTextLayout> +#include <QtGui/QToolBar> +#include <QtGui/QToolTip> +#include <QtGui/QInputDialog> + +using namespace TextEditor; +using namespace TextEditor::Internal; + + +namespace TextEditor { + namespace Internal { + +class TextEditExtraArea : public QWidget { + BaseTextEditor *textEdit; +public: + TextEditExtraArea(BaseTextEditor *edit):QWidget(edit) { + textEdit = edit; + setAutoFillBackground(true); + } +public: + + QSize sizeHint() const { + return QSize(textEdit->extraAreaWidth(), 0); + } +protected: + void paintEvent(QPaintEvent *event){ + textEdit->extraAreaPaintEvent(event); + } + void mousePressEvent(QMouseEvent *event){ + textEdit->extraAreaMouseEvent(event); + } + void mouseMoveEvent(QMouseEvent *event){ + textEdit->extraAreaMouseEvent(event); + } + void mouseReleaseEvent(QMouseEvent *event){ + textEdit->extraAreaMouseEvent(event); + } + void leaveEvent(QEvent *event){ + textEdit->extraAreaLeaveEvent(event); + } + + void wheelEvent(QWheelEvent *event) { + QCoreApplication::sendEvent(textEdit->viewport(), event); + } +}; + + } +} + +ITextEditor *BaseTextEditor::openEditorAt(const QString &fileName, + int line, + int column) +{ + Core::EditorManager *editorManager = + ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()->editorManager(); + editorManager->addCurrentPositionToNavigationHistory(true); + Core::IEditor *editor = editorManager->openEditor(fileName, QString(), true); + TextEditor::ITextEditor *texteditor = qobject_cast<TextEditor::ITextEditor *>(editor); + if (texteditor) { + texteditor->gotoLine(line, column); + editorManager->addCurrentPositionToNavigationHistory(); + return texteditor; + } + return 0; +} + +BaseTextEditor::BaseTextEditor(QWidget *parent) + : QPlainTextEdit(parent) +{ + d = new BaseTextEditorPrivate(); + d->q = this; + d->m_extraArea = new TextEditExtraArea(this); + d->m_extraArea->setMouseTracking(true); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + + d->setupDocumentSignals(d->m_document); + d->setupDocumentSignals(d->m_document); + + d->m_lastScrollPos = -1; + setCursorWidth(2); + + + // from RESEARCH + + setLayoutDirection(Qt::LeftToRight); + viewport()->setMouseTracking(true); + d->extraAreaSelectionAnchorBlockNumber + = d->extraAreaToggleMarkBlockNumber + = d->extraAreaHighlightCollapseBlockNumber + = d->extraAreaHighlightFadingBlockNumber + = -1; + d->extraAreaCollapseAlpha = 255; + + d->visibleCollapsedBlockNumber = d->suggestedVisibleCollapsedBlockNumber = -1; + + connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(slotUpdateExtraAreaWidth())); + connect(this, SIGNAL(modificationChanged(bool)), this, SLOT(slotModificationChanged(bool))); + connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(slotCursorPositionChanged())); + connect(this, SIGNAL(updateRequest(QRect, int)), this, SLOT(slotUpdateRequest(QRect, int))); + connect(this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged())); + +// (void) new QShortcut(tr("CTRL+L"), this, SLOT(centerCursor()), 0, Qt::WidgetShortcut); +// (void) new QShortcut(tr("F9"), this, SLOT(slotToggleMark()), 0, Qt::WidgetShortcut); +// (void) new QShortcut(tr("F11"), this, SLOT(slotToggleBlockVisible())); + + + // parentheses matcher + d->m_parenthesesMatchingEnabled = false; + d->m_formatRange = true; + d->m_matchFormat.setForeground(Qt::red); + d->m_rangeFormat.setBackground(QColor(0xb4, 0xee, 0xb4)); + d->m_mismatchFormat.setBackground(Qt::magenta); + d->m_parenthesesMatchingTimer = new QTimer(this); + d->m_parenthesesMatchingTimer->setSingleShot(true); + connect(d->m_parenthesesMatchingTimer, SIGNAL(timeout()), this, SLOT(_q_matchParentheses())); + + + d->m_searchResultFormat.setBackground(QColor(0xffef0b)); + + slotUpdateExtraAreaWidth(); + slotCursorPositionChanged(); + setFrameStyle(QFrame::NoFrame); + + + d->extraAreaTimeLine = new QTimeLine(150, this); + d->extraAreaTimeLine->setFrameRange(0, 255); + connect(d->extraAreaTimeLine, SIGNAL(frameChanged(int)), this, + SLOT(setCollapseIndicatorAlpha(int))); + + + connect(Core::EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)), + this, SLOT(currentEditorChanged(Core::IEditor*))); +} + +BaseTextEditor::~BaseTextEditor() +{ + delete d; + d = 0; +} + +QString BaseTextEditor::mimeType() const +{ + return d->m_document->mimeType(); +} + +void BaseTextEditor::setMimeType(const QString &mt) +{ + d->m_document->setMimeType(mt); +} + +void BaseTextEditor::print(QPrinter *printer) +{ + const bool oldFullPage = printer->fullPage(); + printer->setFullPage(true); + QPrintDialog *dlg = new QPrintDialog(printer, this); + dlg->setWindowTitle(tr("Print Document")); + if (dlg->exec() == QDialog::Accepted) { + d->print(printer); + } + printer->setFullPage(oldFullPage); + delete dlg; +} + +static void printPage(int index, QPainter *painter, const QTextDocument *doc, + const QRectF &body, const QRectF &titleBox, + const QString &title) +{ + painter->save(); + + painter->translate(body.left(), body.top() - (index - 1) * body.height()); + QRectF view(0, (index - 1) * body.height(), body.width(), body.height()); + + QAbstractTextDocumentLayout *layout = doc->documentLayout(); + QAbstractTextDocumentLayout::PaintContext ctx; + + painter->setFont(QFont(doc->defaultFont())); + QRectF box = titleBox.translated(0, view.top()); + int dpix = painter->device()->logicalDpiX(); + int dpiy = painter->device()->logicalDpiY(); + int mx = 5 * dpix / 72.0; + int my = 2 * dpiy / 72.0; + painter->fillRect(box.adjusted(-mx, -my, mx, my), QColor(210, 210, 210)); + if (!title.isEmpty()) + painter->drawText(box, Qt::AlignCenter, title); + const QString pageString = QString::number(index); + painter->drawText(box, Qt::AlignRight, pageString); + + painter->setClipRect(view); + ctx.clip = view; + // don't use the system palette text as default text color, on HP/UX + // for example that's white, and white text on white paper doesn't + // look that nice + ctx.palette.setColor(QPalette::Text, Qt::black); + + layout->draw(painter, ctx); + + painter->restore(); +} + +void BaseTextEditorPrivate::print(QPrinter *printer) +{ + + QTextDocument *doc = q->document(); + + QString title = q->displayName(); + if (title.isEmpty()) + printer->setDocName(title); + + + QPainter p(printer); + + // Check that there is a valid device to print to. + if (!p.isActive()) + return; + + doc = doc->clone(doc); + + QTextOption opt = doc->defaultTextOption(); + opt.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + doc->setDefaultTextOption(opt); + + (void)doc->documentLayout(); // make sure that there is a layout + + + QColor background = q->palette().color(QPalette::Base); + bool backgroundIsDark = background.value() < 128; + + for (QTextBlock srcBlock = q->document()->firstBlock(), dstBlock = doc->firstBlock(); + srcBlock.isValid() && dstBlock.isValid(); + srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) { + + + QList<QTextLayout::FormatRange> formatList = srcBlock.layout()->additionalFormats(); + if (backgroundIsDark) { + // adjust syntax highlighting colors for better contrast + for (int i = formatList.count() - 1; i >=0; --i) { + QTextCharFormat &format = formatList[i].format; + if (format.background().color() == background) { + QBrush brush = format.foreground(); + QColor color = brush.color(); + int h,s,v,a; + color.getHsv(&h, &s, &v, &a); + color.setHsv(h, s, qMin(128, v), a); + brush.setColor(color); + format.setForeground(brush); + } + format.setBackground(Qt::white); + } + } + + dstBlock.layout()->setAdditionalFormats(formatList); + } + + QAbstractTextDocumentLayout *layout = doc->documentLayout(); + layout->setPaintDevice(p.device()); + + int dpiy = p.device()->logicalDpiY(); + int margin = (int) ((2/2.54)*dpiy); // 2 cm margins + + QTextFrameFormat fmt = doc->rootFrame()->frameFormat(); + fmt.setMargin(margin); + doc->rootFrame()->setFrameFormat(fmt); + + QRectF pageRect(printer->pageRect()); + QRectF body = QRectF(0, 0, pageRect.width(), pageRect.height()); + QFontMetrics fontMetrics(doc->defaultFont(), p.device()); + + QRectF titleBox(margin, + body.top() + margin + - fontMetrics.height() + - 6 * dpiy / 72.0, + body.width() - 2*margin, + fontMetrics.height()); + doc->setPageSize(body.size()); + + int docCopies; + int pageCopies; + if (printer->collateCopies() == true){ + docCopies = 1; + pageCopies = printer->numCopies(); + } else { + docCopies = printer->numCopies(); + pageCopies = 1; + } + + int fromPage = printer->fromPage(); + int toPage = printer->toPage(); + bool ascending = true; + + if (fromPage == 0 && toPage == 0) { + fromPage = 1; + toPage = doc->pageCount(); + } + // paranoia check + fromPage = qMax(1, fromPage); + toPage = qMin(doc->pageCount(), toPage); + + if (printer->pageOrder() == QPrinter::LastPageFirst) { + int tmp = fromPage; + fromPage = toPage; + toPage = tmp; + ascending = false; + } + + for (int i = 0; i < docCopies; ++i) { + + int page = fromPage; + while (true) { + for (int j = 0; j < pageCopies; ++j) { + if (printer->printerState() == QPrinter::Aborted + || printer->printerState() == QPrinter::Error) + goto UserCanceled; + printPage(page, &p, doc, body, titleBox, title); + if (j < pageCopies - 1) + printer->newPage(); + } + + if (page == toPage) + break; + + if (ascending) + ++page; + else + --page; + + printer->newPage(); + } + + if ( i < docCopies - 1) + printer->newPage(); + } + +UserCanceled: + delete doc; +} + + +bool DocumentMarker::addMark(TextEditor::ITextMark *mark, int line) +{ + Q_ASSERT(line >= 1); + int blockNumber = line - 1; + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(document->documentLayout()); + Q_ASSERT(documentLayout); + QTextBlock block = document->findBlockByNumber(blockNumber); + + if (block.isValid()) { + TextBlockUserData *userData = TextEditDocumentLayout::userData(block); + userData->addMark(mark); + mark->updateLineNumber(blockNumber + 1); + mark->updateBlock(block); + documentLayout->hasMarks = true; + documentLayout->requestUpdate(); + return true; + } + return false; +} + + +TextEditor::TextMarks DocumentMarker::marksAt(int line) const +{ + Q_ASSERT(line >= 1); + int blockNumber = line - 1; + QTextBlock block = document->findBlockByNumber(blockNumber); + + if (block.isValid()) { + if (TextBlockUserData *userData = TextEditDocumentLayout::testUserData(block)) + return userData->marks(); + } + return TextMarks(); +} + +void DocumentMarker::removeMark(TextEditor::ITextMark *mark) +{ + bool needUpdate = false; + QTextBlock block = document->begin(); + while (block.isValid()) { + if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData())) { + needUpdate |= data->removeMark(mark); + } + block = block.next(); + } + if (needUpdate) + updateMark(0); +} + + +bool DocumentMarker::hasMark(TextEditor::ITextMark *mark) const +{ + QTextBlock block = document->begin(); + while (block.isValid()) { + if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData())) { + if (data->hasMark(mark)) + return true; + } + block = block.next(); + } + return false; +} + +ITextMarkable *BaseTextEditor::markableInterface() const +{ + return baseTextDocument()->documentMarker(); +} + +ITextEditable *BaseTextEditor::editableInterface() const +{ + if (!d->m_editable) { + d->m_editable = const_cast<BaseTextEditor*>(this)->createEditableInterface(); + connect(this, SIGNAL(textChanged()), + d->m_editable, SIGNAL(contentsChanged())); + connect(this, SIGNAL(changed()), + d->m_editable, SIGNAL(changed())); + connect(this, + SIGNAL(markRequested(TextEditor::ITextEditor *, int)), + d->m_editable, + SIGNAL(markRequested(TextEditor::ITextEditor *, int))); + } + return d->m_editable; +} + + +void BaseTextEditor::currentEditorChanged(Core::IEditor *editor) +{ + if (editor == d->m_editable) { + if (d->m_document->hasDecodingError()) { + Core::EditorManager::instance()->showEditorInfoBar(QLatin1String(Constants::SELECT_ENCODING), + tr("<b>Error:</b> Could not decode \"%1\" with \"%2\"-encoding. Editing not possible.").arg(displayName()).arg(QString::fromLatin1(d->m_document->codec()->name())), + tr("Select Encoding"), + this, SLOT(selectEncoding())); + } + } +} + +void BaseTextEditor::selectEncoding() +{ + BaseTextDocument *doc = d->m_document; + CodecSelector codecSelector(this, doc); + + switch (codecSelector.exec()) { + case CodecSelector::Reload: + doc->reload(codecSelector.selectedCodec()); + setReadOnly(d->m_document->hasDecodingError()); + if (doc->hasDecodingError()) + currentEditorChanged(Core::EditorManager::instance()->currentEditor()); + else + Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String(Constants::SELECT_ENCODING)); + break; + case CodecSelector::Save: + doc->setCodec(codecSelector.selectedCodec()); + Core::EditorManager::instance()->saveEditor(editableInterface()); + break; + case CodecSelector::Cancel: + break; + } +} + + +void DocumentMarker::updateMark(ITextMark *mark) +{ + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(document->documentLayout()); + Q_ASSERT(documentLayout); + Q_UNUSED(mark); + documentLayout->requestUpdate(); +} + + +void BaseTextEditor::triggerCompletions() +{ + emit requestAutoCompletion(editableInterface(), true); +} + +bool BaseTextEditor::createNew(const QString &contents) +{ + setPlainText(contents); + document()->setModified(false); + return true; +} + +bool BaseTextEditor::open(const QString &fileName) +{ + if (d->m_document->open(fileName)) { + moveCursor(QTextCursor::Start); + setReadOnly(d->m_document->hasDecodingError()); + return true; + } + return false; +} + +Core::IFile * BaseTextEditor::file() +{ + return d->m_document; +} + +void BaseTextEditor::editorContentsChange(int position, int charsRemoved, int charsAdded) +{ + d->m_contentsChanged = true; + + // Keep the line numbers and the block information for the text marks updated + if (charsRemoved != 0) { + d->updateMarksLineNumber(); + d->updateMarksBlock(document()->findBlock(position)); + } else { + const QTextBlock posBlock = document()->findBlock(position); + const QTextBlock nextBlock = document()->findBlock(position + charsAdded); + if (posBlock != nextBlock) { + d->updateMarksLineNumber(); + d->updateMarksBlock(posBlock); + d->updateMarksBlock(nextBlock); + } else { + d->updateMarksBlock(posBlock); + } + } +} + + +void BaseTextEditor::slotSelectionChanged() +{ + bool changed = (d->m_inBlockSelectionMode != d->m_lastEventWasBlockSelectionEvent); + d->m_inBlockSelectionMode = d->m_lastEventWasBlockSelectionEvent; + if (changed || d->m_inBlockSelectionMode) + viewport()->update(); + if (!d->m_inBlockSelectionMode) + d->m_blockSelectionExtraX = 0; +} + +void BaseTextEditor::keyPressEvent(QKeyEvent *e) +{ + + d->clearVisibleCollapsedBlock(); + + QKeyEvent *original_e = e; + d->m_lastEventWasBlockSelectionEvent = false; + + if (e->key() == Qt::Key_Escape) { + e->accept(); + QTextCursor cursor = textCursor(); + cursor.clearSelection(); + setTextCursor(cursor); + return; + } + + d->m_contentsChanged = false; + + bool ro = isReadOnly(); + + if (d->m_inBlockSelectionMode) { + if (e == QKeySequence::Cut) { + if (!ro) { + cut(); + e->accept(); + return; + } + } else if (e == QKeySequence::Delete) { + if (!ro) { + d->removeBlockSelection(); + e->accept(); + return; + } + } + } + + + if (!ro + && (e == QKeySequence::InsertParagraphSeparator + || (!d->m_lineSeparatorsAllowed && e == QKeySequence::InsertLineSeparator)) + ) { + + QTextCursor cursor = textCursor(); + if (d->m_inBlockSelectionMode) + cursor.clearSelection(); + cursor.insertBlock(); + if (d->m_document->tabSettings().m_autoIndent) { + indent(document(), cursor, QChar::Null); + } + e->accept(); + setTextCursor(cursor); + return; + } else switch (e->key()) { + + +#if 0 + case Qt::Key_sterling: { + + static bool toggle = false; + if ((toggle = !toggle)) { + QList<BaseTextEditor::BlockRange> rangeList; + rangeList += BaseTextEditor::BlockRange(4, 12); + rangeList += BaseTextEditor::BlockRange(15, 19); + setIfdefedOutBlocks(rangeList); + } else { + setIfdefedOutBlocks(QList<BaseTextEditor::BlockRange>()); + } + e->accept(); + return; + + } break; +#endif + case Qt::Key_Tab: + case Qt::Key_Backtab: + if (ro) break; + indentOrUnindent(e->key() == Qt::Key_Tab); + e->accept(); + return; + case Qt::Key_Backspace: + if (ro) break; + if (d->m_document->tabSettings().m_smartBackspace + && (e->modifiers() & (Qt::ControlModifier + | Qt::ShiftModifier + | Qt::AltModifier + | Qt::MetaModifier)) == Qt::NoModifier + && !textCursor().hasSelection()) { + handleBackspaceKey(); + e->accept(); + return; + } + break; + case Qt::Key_Home: + if (!(e == QKeySequence::MoveToStartOfDocument) && !(e == QKeySequence::SelectStartOfDocument)) { + handleHomeKey(e->modifiers() & Qt::ShiftModifier); + e->accept(); + return; + } + break; + case Qt::Key_Up: + case Qt::Key_Down: + if (e->modifiers() & Qt::ControlModifier) { + verticalScrollBar()->triggerAction( + e->key() == Qt::Key_Up ? QAbstractSlider::SliderSingleStepSub : + QAbstractSlider::SliderSingleStepAdd); + e->accept(); + return; + } + // fall through + case Qt::Key_Right: + case Qt::Key_Left: +#ifndef Q_OS_MAC + if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) { + + d->m_lastEventWasBlockSelectionEvent = true; + + if (d->m_inBlockSelectionMode) { + if (e->key() == Qt::Key_Right && textCursor().atBlockEnd()) { + d->m_blockSelectionExtraX++; + viewport()->update(); + e->accept(); + return; + } else if (e->key() == Qt::Key_Left && d->m_blockSelectionExtraX > 0) { + d->m_blockSelectionExtraX--; + e->accept(); + viewport()->update(); + return; + } + } + + e = new QKeyEvent( + e->type(), + e->key(), + e->modifiers() & ~Qt::AltModifier, + e->text(), + e->isAutoRepeat(), + e->count() + ); + } +#endif + break; + case Qt::Key_PageUp: + case Qt::Key_PageDown: + if (e->modifiers() == Qt::ControlModifier) { + verticalScrollBar()->triggerAction( + e->key() == Qt::Key_PageUp ? QAbstractSlider::SliderPageStepSub : + QAbstractSlider::SliderPageStepAdd); + e->accept(); + return; + } + break; + + default: + if (! ro && d->m_document->tabSettings().m_autoIndent + && ! e->text().isEmpty() && isElectricCharacter(e->text().at(0))) { + QTextCursor cursor = textCursor(); + const QString text = e->text(); + cursor.insertText(text); + const QString leftText = cursor.block().text().left(cursor.position() - 1 - cursor.block().position()); + if (leftText.simplified().isEmpty()) { + const QChar typedChar = e->text().at(0); + indent(document(), cursor, typedChar); + } +#if 0 + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(document()->documentLayout()); + Q_ASSERT(documentLayout); + documentLayout->requestUpdate(); // a bit drastic + e->accept(); +#endif + setTextCursor(cursor); + return; + } + break; + } + + if (d->m_inBlockSelectionMode) { + QString text = e->text(); + if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'))) { + d->removeBlockSelection(text); + goto skip_event; + } + } + + QPlainTextEdit::keyPressEvent(e); + +skip_event: + if (!ro && e->key() == Qt::Key_Delete && d->m_parenthesesMatchingEnabled) + slotCursorPositionChanged(); // parentheses matching + + if (!ro && d->m_contentsChanged && !e->text().isEmpty() && e->text().at(0).isPrint()) + emit requestAutoCompletion(editableInterface(), false); + + if (e != original_e) + delete e; +} + +void BaseTextEditor::gotoLine(int line, int column) +{ + const int blockNumber = line - 1; + const QTextBlock &block = document()->findBlockByNumber(blockNumber); + if (block.isValid()) { + QTextCursor cursor(block); + if (column > 0) { + cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, column); + } else { + int pos = cursor.position(); + while (characterAt(pos).category() == QChar::Separator_Space) { + ++pos; + } + cursor.setPosition(pos); + } + setTextCursor(cursor); + centerCursor(); + } +} + +int BaseTextEditor::position(ITextEditor::PositionOperation posOp, int at) const +{ + QTextCursor tc = textCursor(); + + if (at != -1) + tc.setPosition(at); + + if (posOp == ITextEditor::Current) + return tc.position(); + + switch (posOp) { + case ITextEditor::EndOfLine: + tc.movePosition(QTextCursor::EndOfLine); + return tc.position(); + case ITextEditor::StartOfLine: + tc.movePosition(QTextCursor::StartOfLine); + return tc.position(); + case ITextEditor::Anchor: + if (tc.hasSelection()) + return tc.anchor(); + break; + case ITextEditor::EndOfDoc: + tc.movePosition(QTextCursor::End); + return tc.position(); + default: + break; + } + + return -1; +} + +void BaseTextEditor::convertPosition(int pos, int *line, int *column) const +{ + QTextBlock block = document()->findBlock(pos); + if (!block.isValid()) { + (*line) = -1; + (*column) = -1; + } else { + (*line) = block.blockNumber() + 1; + (*column) = pos - block.position(); + } +} + +QChar BaseTextEditor::characterAt(int pos) const +{ + return document()->characterAt(pos); +} + +bool BaseTextEditor::event(QEvent *e) +{ + switch (e->type()) { + case QEvent::ShortcutOverride: + e->ignore(); // we are a really nice citizen + return true; + default: + break; + } + + return QPlainTextEdit::event(e); +} + +void BaseTextEditor::duplicateFrom(BaseTextEditor *editor) +{ + if (this == editor) + return; + setDisplayName(editor->displayName()); + d->m_revisionsVisible = editor->d->m_revisionsVisible; + if (d->m_document == editor->d->m_document) + return; + d->setupDocumentSignals(editor->d->m_document); + d->m_document = editor->d->m_document; +} + +QString BaseTextEditor::displayName() const +{ + return d->m_displayName; +} + +void BaseTextEditor::setDisplayName(const QString &title) +{ + d->m_displayName = title; +} + +BaseTextDocument *BaseTextEditor::baseTextDocument() const +{ + return d->m_document; +} + +void BaseTextEditor::setBaseTextDocument(BaseTextDocument *doc) +{ + if (doc) { + d->setupDocumentSignals(doc); + d->m_document = doc; + } +} + +void BaseTextEditor::memorizeCursorPosition() +{ + d->m_tempState = saveState(); +} + +void BaseTextEditor::restoreCursorPosition() +{ + restoreState(d->m_tempState); +} + +QByteArray BaseTextEditor::saveState() const +{ + QByteArray state; + QDataStream stream(&state, QIODevice::WriteOnly); + stream << 0; // version number + stream << verticalScrollBar()->value(); + stream << horizontalScrollBar()->value(); + int line, column; + convertPosition(textCursor().position(), &line, &column); + stream << line; + stream << column; + return state; +} + +bool BaseTextEditor::restoreState(const QByteArray &state) +{ + int version; + int vval; + int hval; + int lval; + int cval; + QDataStream stream(state); + stream >> version; + stream >> vval; + stream >> hval; + stream >> lval; + stream >> cval; + gotoLine(lval, cval); + verticalScrollBar()->setValue(vval); + horizontalScrollBar()->setValue(hval); + return true; +} + +void BaseTextEditor::setDefaultPath(const QString &defaultPath) +{ + baseTextDocument()->setDefaultPath(defaultPath); +} + +void BaseTextEditor::setSuggestedFileName(const QString &suggestedFileName) +{ + baseTextDocument()->setSuggestedFileName(suggestedFileName); +} + +void BaseTextEditor::setParenthesesMatchingEnabled(bool b) +{ + d->m_parenthesesMatchingEnabled = b; +} + +bool BaseTextEditor::isParenthesesMatchingEnabled() const +{ + return d->m_parenthesesMatchingEnabled; +} + +void BaseTextEditor::setHighlightCurrentLine(bool b) +{ + d->m_highlightCurrentLine = b; + slotCursorPositionChanged(); +} + +bool BaseTextEditor::highlightCurrentLine() const +{ + return d->m_highlightCurrentLine; +} + +void BaseTextEditor::setLineNumbersVisible(bool b) +{ + d->m_lineNumbersVisible = b; + slotUpdateExtraAreaWidth(); +} + +bool BaseTextEditor::lineNumbersVisible() const +{ + return d->m_lineNumbersVisible; +} + +void BaseTextEditor::setMarksVisible(bool b) +{ + d->m_marksVisible = b; + slotUpdateExtraAreaWidth(); +} + +bool BaseTextEditor::marksVisible() const +{ + return d->m_marksVisible; +} + +void BaseTextEditor::setRequestMarkEnabled(bool b) +{ + d->m_requestMarkEnabled = b; +} + +bool BaseTextEditor::requestMarkEnabled() const +{ + return d->m_requestMarkEnabled; +} + +void BaseTextEditor::setLineSeparatorsAllowed(bool b) +{ + d->m_lineSeparatorsAllowed = b; +} + +bool BaseTextEditor::lineSeparatorsAllowed() const +{ + return d->m_lineSeparatorsAllowed; +} + + +void BaseTextEditor::setCodeFoldingVisible(bool b) +{ + d->m_codeFoldingVisible = b; + slotUpdateExtraAreaWidth(); +} + +bool BaseTextEditor::codeFoldingVisible() const +{ + return d->m_codeFoldingVisible; +} + +void BaseTextEditor::setRevisionsVisible(bool b) +{ + d->m_revisionsVisible = b; + slotUpdateExtraAreaWidth(); +} + +bool BaseTextEditor::revisionsVisible() const +{ + return d->m_revisionsVisible; +} + +void BaseTextEditor::setVisibleWrapColumn(int column) +{ + d->m_visibleWrapColumn = column; + viewport()->update(); +} + +int BaseTextEditor::visibleWrapColumn() const +{ + return d->m_visibleWrapColumn; +} + +void BaseTextEditor::setFontSettings(const TextEditor::FontSettings &fs) +{ + const QTextCharFormat textFormat = fs.toTextCharFormat(QLatin1String(Constants::C_TEXT)); + const QTextCharFormat selectionFormat = fs.toTextCharFormat(QLatin1String(Constants::C_SELECTION)); + const QTextCharFormat lineNumberFormat = fs.toTextCharFormat(QLatin1String(Constants::C_LINE_NUMBER)); + const QTextCharFormat searchResultFormat = fs.toTextCharFormat(QLatin1String(Constants::C_SEARCH_RESULT)); + const QTextCharFormat searchScopeFormat = fs.toTextCharFormat(QLatin1String(Constants::C_SEARCH_SCOPE)); + const QTextCharFormat parenthesesFormat = fs.toTextCharFormat(QLatin1String(Constants::C_PARENTHESES)); + const QTextCharFormat currentLineFormat = fs.toTextCharFormat(QLatin1String(Constants::C_CURRENT_LINE)); + const QTextCharFormat ifdefedOutFormat = fs.toTextCharFormat(QLatin1String(Constants::C_DISABLED_CODE)); + QFont font(textFormat.font()); + + const QColor foreground = textFormat.foreground().color(); + const QColor background = textFormat.background().color(); + QPalette p = palette(); + p.setColor(QPalette::Text, foreground); + p.setColor(QPalette::Foreground, foreground); + p.setColor(QPalette::Base, background); + p.setColor(QPalette::Highlight, (selectionFormat.background().style() != Qt::NoBrush) ? + selectionFormat.background().color() : + QApplication::palette().color(QPalette::Highlight)); + p.setColor(QPalette::HighlightedText, selectionFormat.foreground().color()); + p.setBrush(QPalette::Inactive, QPalette::Highlight, p.highlight()); + p.setBrush(QPalette::Inactive, QPalette::HighlightedText, p.highlightedText()); + setPalette(p); + setFont(font); + setTabSettings(d->m_document->tabSettings()); // update tabs, they depend on the font + + // Line numbers + QPalette ep = d->m_extraArea->palette(); + ep.setColor(QPalette::Dark, lineNumberFormat.foreground().color()); + ep.setColor(QPalette::Background, lineNumberFormat.background().style() != Qt::NoBrush ? + lineNumberFormat.background().color() : background); + d->m_extraArea->setPalette(ep); + + // Search results + d->m_searchResultFormat.setBackground(searchResultFormat.background()); + d->m_searchScopeFormat.setBackground(searchScopeFormat.background()); + d->m_currentLineFormat.setBackground(currentLineFormat.background()); + + // Matching braces + d->m_matchFormat.setForeground(parenthesesFormat.foreground()); + d->m_rangeFormat.setBackground(parenthesesFormat.background()); + + // Disabled code + d->m_ifdefedOutFormat.setForeground(ifdefedOutFormat.foreground()); + + slotUpdateExtraAreaWidth(); +} + +void BaseTextEditor::setStorageSettings(const StorageSettings &storageSettings) +{ + d->m_document->setStorageSettings(storageSettings); +} + +//--------- BaseTextEditorPrivate ----------- + +BaseTextEditorPrivate::BaseTextEditorPrivate() + : + m_contentsChanged(false), + m_document(new BaseTextDocument()), + m_parenthesesMatchingEnabled(false), + m_extraArea(0), + m_marksVisible(false), + m_codeFoldingVisible(false), + m_revisionsVisible(false), + m_lineNumbersVisible(true), + m_highlightCurrentLine(true), + m_requestMarkEnabled(true), + m_lineSeparatorsAllowed(false), + m_visibleWrapColumn(0), + m_editable(0), + m_actionHack(0), + m_inBlockSelectionMode(false), + m_lastEventWasBlockSelectionEvent(false), + m_blockSelectionExtraX(0) +{ +} + +BaseTextEditorPrivate::~BaseTextEditorPrivate() +{ +} + +void BaseTextEditorPrivate::setupDocumentSignals(BaseTextDocument *document) +{ + BaseTextDocument *oldDocument = q->baseTextDocument(); + if (oldDocument) { + q->disconnect(oldDocument->document(), 0, q, 0); + q->disconnect(oldDocument, 0, q, 0); + } + + QTextDocument *doc = document->document(); + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(doc->documentLayout()); + if (!documentLayout) { + QTextOption opt = doc->defaultTextOption(); + opt.setTextDirection(Qt::LeftToRight); + opt.setFlags(opt.flags() | QTextOption::IncludeTrailingSpaces + | QTextOption::AddSpaceForLineAndParagraphSeparators + ); + doc->setDefaultTextOption(opt); + documentLayout = new TextEditDocumentLayout(doc); + doc->setDocumentLayout(documentLayout); + } + + + q->setDocument(doc); + QObject::connect(documentLayout, SIGNAL(updateBlock(QTextBlock)), q, SLOT(slotUpdateBlockNotify(QTextBlock))); + QObject::connect(q, SIGNAL(requestBlockUpdate(QTextBlock)), documentLayout, SIGNAL(updateBlock(QTextBlock))); + QObject::connect(doc, SIGNAL(modificationChanged(bool)), q, SIGNAL(changed())); + QObject::connect(doc, SIGNAL(contentsChange(int,int,int)), q, + SLOT(editorContentsChange(int,int,int)), Qt::DirectConnection); + QObject::connect(document, SIGNAL(changed()), q, SIGNAL(changed())); + QObject::connect(document, SIGNAL(titleChanged(QString)), q, SLOT(setDisplayName(const QString &))); + QObject::connect(document, SIGNAL(aboutToReload()), q, SLOT(memorizeCursorPosition())); + QObject::connect(document, SIGNAL(reloaded()), q, SLOT(restoreCursorPosition())); + q->slotUpdateExtraAreaWidth(); +} + +#ifndef TEXTEDITOR_STANDALONE +bool BaseTextEditorPrivate::needMakeWritableCheck() const +{ + return !m_document->isModified() + && !m_document->fileName().isEmpty() + && m_document->isReadOnly(); +} +#endif + +bool Parenthesis::hasClosingCollapse(const Parentheses &parentheses) +{ + return closeCollapseAtPos(parentheses) >= 0; +} + + +int Parenthesis::closeCollapseAtPos(const Parentheses &parentheses) +{ + int depth = 0; + for (int i = 0; i < parentheses.size(); ++i) { + const Parenthesis &p = parentheses.at(i); + if (p.chr == QLatin1Char('{') || p.chr == QLatin1Char('+')) { + ++depth; + } else if (p.chr == QLatin1Char('}') || p.chr == QLatin1Char('-')) { + if (--depth < 0) + return p.pos; + } + } + return -1; +} + +int Parenthesis::collapseAtPos(const Parentheses &parentheses, QChar *character) +{ + int result = -1; + QChar c; + + int depth = 0; + for (int i = 0; i < parentheses.size(); ++i) { + const Parenthesis &p = parentheses.at(i); + if (p.chr == QLatin1Char('{') || p.chr == QLatin1Char('+')) { + if (depth == 0) { + result = p.pos; + c = p.chr; + } + ++depth; + } else if (p.chr == QLatin1Char('}') || p.chr == QLatin1Char('-')) { + if (--depth < 0) + depth = 0; + result = -1; + } + } + if (result >= 0 && character) + *character = c; + return result; +} + + +int TextBlockUserData::collapseAtPos() const +{ + return Parenthesis::collapseAtPos(m_parentheses); +} + + +void TextEditDocumentLayout::setParentheses(const QTextBlock &block, const Parentheses &parentheses) +{ + if (parentheses.isEmpty()) { + if (TextBlockUserData *userData = testUserData(block)) + userData->clearParentheses(); + } else { + userData(block)->setParentheses(parentheses); + } +} + +Parentheses TextEditDocumentLayout::parentheses(const QTextBlock &block) +{ + if (TextBlockUserData *userData = testUserData(block)) + return userData->parentheses(); + return Parentheses(); +} + +bool TextEditDocumentLayout::hasParentheses(const QTextBlock &block) +{ + if (TextBlockUserData *userData = testUserData(block)) + return userData->hasParentheses(); + return false; +} + + +bool TextEditDocumentLayout::setIfdefedOut(const QTextBlock &block) +{ + return userData(block)->setIfdefedOut(); +} + +bool TextEditDocumentLayout::clearIfdefedOut(const QTextBlock &block) +{ + if (TextBlockUserData *userData = testUserData(block)) + return userData->clearIfdefedOut(); + return false; +} + +bool TextEditDocumentLayout::ifdefedOut(const QTextBlock &block) +{ + if (TextBlockUserData *userData = testUserData(block)) + return userData->ifdefedOut(); + return false; +} + + +TextEditDocumentLayout::TextEditDocumentLayout(QTextDocument *doc) + :QPlainTextDocumentLayout(doc) { + lastSaveRevision = 0; + hasMarks = 0; +} + +TextEditDocumentLayout::~TextEditDocumentLayout() +{ +} + +QRectF TextEditDocumentLayout::blockBoundingRect(const QTextBlock &block) const +{ + QRectF r = QPlainTextDocumentLayout::blockBoundingRect(block); + return r; +} + + +bool BaseTextEditor::viewportEvent(QEvent *event) +{ + if (event->type() == QEvent::ContextMenu) { + const QContextMenuEvent *ce = static_cast<QContextMenuEvent*>(event); + if (ce->reason() == QContextMenuEvent::Mouse && !textCursor().hasSelection()) + setTextCursor(cursorForPosition(ce->pos())); + } else if (event->type() == QEvent::ToolTip) { + const QHelpEvent *he = static_cast<QHelpEvent*>(event); + const QPoint &pos = he->pos(); + + // Allow plugins to show tooltips + const QTextCursor &c = cursorForPosition(pos); + QPoint cursorPos = mapToGlobal(cursorRect(c).bottomRight() + QPoint(1,1)); + cursorPos.setX(cursorPos.x() + d->m_extraArea->width()); + + editableInterface(); // create if necessary + + emit d->m_editable->tooltipRequested(editableInterface(), cursorPos, c.position()); + return true; + } + return QPlainTextEdit::viewportEvent(event); +} + + +void BaseTextEditor::resizeEvent(QResizeEvent *e) +{ + QPlainTextEdit::resizeEvent(e); + QRect cr = viewport()->rect(); + d->m_extraArea->setGeometry( + QStyle::visualRect(layoutDirection(), cr, + QRect(cr.left(), cr.top(), extraAreaWidth(), cr.height()))); +} + +QRect BaseTextEditor::collapseBox(const QTextBlock &block) +{ + QRectF br = blockBoundingGeometry(block).translated(contentOffset()); + int collapseBoxWidth = fontMetrics().lineSpacing() + 1; + return QRect(d->m_extraArea->width() - collapseBoxWidth + collapseBoxWidth/4, + int(br.top()) + collapseBoxWidth/4, + 2 * (collapseBoxWidth/4) + 1, 2 * (collapseBoxWidth/4) + 1); + +} + +QTextBlock BaseTextEditor::collapsedBlockAt(const QPoint &pos, QRect *box) const { + QPointF offset(contentOffset()); + QTextBlock block = firstVisibleBlock(); + int top = (int)blockBoundingGeometry(block).translated(offset).top(); + int bottom = top + (int)blockBoundingRect(block).height(); + + int viewportHeight = viewport()->height(); + + while (block.isValid() && top <= viewportHeight) { + QTextBlock nextBlock = block.next(); + if (block.isVisible() && bottom >= 0) { + if (nextBlock.isValid() && !nextBlock.isVisible()) { + QTextLayout *layout = block.layout(); + QTextLine line = layout->lineAt(layout->lineCount()-1); + QRectF lineRect = line.naturalTextRect().translated(offset.x(), top); + lineRect.adjust(0, 0, -1, -1); + + QRectF collapseRect(lineRect.right() + 12, + lineRect.top(), + fontMetrics().width(QLatin1String(" {...}; ")), + lineRect.height()); + if (collapseRect.contains(pos)) { + QTextBlock result = block; + if (box) + *box = collapseRect.toAlignedRect(); + return result; + } else { + block = nextBlock; + while (nextBlock.isValid() && !nextBlock.isVisible()) { + block = nextBlock; + nextBlock = block.next(); + } + } + } + } + + block = nextBlock; + top = bottom; + bottom = top + (int)blockBoundingRect(block).height(); + } + return QTextBlock(); +} + +void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block, + QVector<QTextLayout::FormatRange> *selections) +{ + if (m_searchExpr.isEmpty()) + return; + + QString text = block.text(); + text.replace(QChar::Nbsp, QLatin1Char(' ')); + int idx = -1; + while (idx < text.length()) { + idx = m_searchExpr.indexIn(text, idx + 1); + if (idx < 0) + break; + int l = m_searchExpr.matchedLength(); + if ((m_findFlags & QTextDocument::FindWholeWords) + && ((idx && text.at(idx-1).isLetterOrNumber()) + || (idx + l < text.length() && text.at(idx + l).isLetterOrNumber()))) + continue; + + if (m_findScope.isNull() + || (block.position() + idx >= m_findScope.selectionStart() + && block.position() + idx + l <= m_findScope.selectionEnd())) { + QTextLayout::FormatRange selection; + selection.start = idx; + selection.length = l; + selection.format = m_searchResultFormat; + selections->append(selection); + } + } +} + + +namespace TextEditor { + namespace Internal { + struct BlockSelectionData { + int selectionIndex; + int selectionStart; + int selectionEnd; + int firstColumn; + int lastColumn; + }; + + } +} + +void BaseTextEditorPrivate::clearBlockSelection() +{ + if (m_inBlockSelectionMode) { + m_inBlockSelectionMode = false; + QTextCursor cursor = q->textCursor(); + cursor.clearSelection(); + q->setTextCursor(cursor); + } +} + +QString BaseTextEditorPrivate::copyBlockSelection() +{ + QString text; + + QTextCursor cursor = q->textCursor(); + if (!cursor.hasSelection()) + return text; + + QTextDocument *doc = q->document(); + int start = cursor.selectionStart(); + int end = cursor.selectionEnd(); + QTextBlock startBlock = doc->findBlock(start); + int columnA = start - startBlock.position(); + QTextBlock endBlock = doc->findBlock(end); + int columnB = end - endBlock.position(); + int firstColumn = qMin(columnA, columnB); + int lastColumn = qMax(columnA, columnB) + m_blockSelectionExtraX; + + QTextBlock block = startBlock; + for (;;) { + + cursor.setPosition(block.position() + qMin(block.length()-1, firstColumn)); + cursor.setPosition(block.position() + qMin(block.length()-1, lastColumn), QTextCursor::KeepAnchor); + text += cursor.selectedText(); + if (block == endBlock) + break; + text += QLatin1Char('\n'); + block = block.next(); + } + + return text; +} + +void BaseTextEditorPrivate::removeBlockSelection(const QString &text) +{ + QTextCursor cursor = q->textCursor(); + if (!cursor.hasSelection()) + return; + + QTextDocument *doc = q->document(); + int start = cursor.selectionStart(); + int end = cursor.selectionEnd(); + QTextBlock startBlock = doc->findBlock(start); + int columnA = start - startBlock.position(); + QTextBlock endBlock = doc->findBlock(end); + int columnB = end - endBlock.position(); + int firstColumn = qMin(columnA, columnB); + int lastColumn = qMax(columnA, columnB) + m_blockSelectionExtraX; + + cursor.clearSelection(); + cursor.beginEditBlock(); + + QTextBlock block = startBlock; + for (;;) { + + cursor.setPosition(block.position() + qMin(block.length()-1, firstColumn)); + cursor.setPosition(block.position() + qMin(block.length()-1, lastColumn), QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + if (block == endBlock) + break; + block = block.next(); + } + + cursor.setPosition(start); + if (!text.isEmpty()) + cursor.insertText(text); + cursor.endEditBlock(); + q->setTextCursor(cursor); +} + +void BaseTextEditor::paintEvent(QPaintEvent *e) +{ + /* + Here comes an almost verbatim copy of + QPlainTextEdit::paintEvent() so we can adjust the extra + selections dynamically to indicate all search results. + */ + //begin QPlainTextEdit::paintEvent() + + QPainter painter(viewport()); + QTextDocument *doc = document(); + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(doc->documentLayout()); + Q_ASSERT(documentLayout); + + QPointF offset(contentOffset()); + + QRect er = e->rect(); + QRect viewportRect = viewport()->rect(); + + // keep right margin clean from full-width selection + int maxX = offset.x() + qMax((qreal)viewportRect.width(), documentLayout->documentSize().width()) + - doc->documentMargin(); + er.setRight(qMin(er.right(), maxX)); + painter.setClipRect(er); + + bool editable = !isReadOnly(); + + QTextBlock block = firstVisibleBlock(); + + QAbstractTextDocumentLayout::PaintContext context = getPaintContext(); + + + if (!d->m_findScope.isNull()) { + QAbstractTextDocumentLayout::Selection selection; + selection.format.setBackground(d->m_searchScopeFormat.background()); + selection.cursor = d->m_findScope; + context.selections.prepend(selection); + } + + BlockSelectionData *blockSelection = 0; + + if (d->m_inBlockSelectionMode + && context.selections.count() && context.selections.last().cursor == textCursor()) { + blockSelection = new BlockSelectionData; + blockSelection->selectionIndex = context.selections.size()-1; + const QAbstractTextDocumentLayout::Selection &selection = context.selections[blockSelection->selectionIndex]; + int start = blockSelection->selectionStart = selection.cursor.selectionStart(); + int end = blockSelection->selectionEnd = selection.cursor.selectionEnd(); + QTextBlock block = doc->findBlock(start); + int columnA = start - block.position(); + block = doc->findBlock(end); + int columnB = end - block.position(); + blockSelection->firstColumn = qMin(columnA, columnB); + blockSelection->lastColumn = qMax(columnA, columnB) + d->m_blockSelectionExtraX; + } + + const QColor baseColor = palette().base().color(); + const int blendBase = (baseColor.value() > 128) ? 0 : 255; + // Darker backgrounds may need a bit more contrast + // (this calculation is temporary solution until we have a setting for this color) + const int blendFactor = (baseColor.value() > 128) ? 8 : 16; + const QColor blendColor( + (blendBase * blendFactor + baseColor.blue() * (256 - blendFactor)) / 256, + (blendBase * blendFactor + baseColor.green() * (256 - blendFactor)) / 256, + (blendBase * blendFactor + baseColor.blue() * (256 - blendFactor)) / 256); + if (d->m_visibleWrapColumn > 0) { + qreal lineX = fontMetrics().averageCharWidth() * d->m_visibleWrapColumn + offset.x() + 4; + painter.fillRect(QRectF(lineX, 0, viewportRect.width() - lineX, viewportRect.height()), blendColor); + } + + QTextBlock visibleCollapsedBlock; + QPointF visibleCollapsedBlockOffset; + + while (block.isValid()) { + + if (!block.isVisible()) { + block = block.next(); + continue; + } + + QRectF r = blockBoundingRect(block).translated(offset); + QTextLayout *layout = block.layout(); + + QTextOption option = layout->textOption(); + if (TextEditDocumentLayout::ifdefedOut(block)) { + option.setFlags(option.flags() | QTextOption::SuppressColors); + painter.setPen(d->m_ifdefedOutFormat.foreground().color()); + } else { + option.setFlags(option.flags() & ~QTextOption::SuppressColors); + painter.setPen(context.palette.text().color()); + } + layout->setTextOption(option); + + if (r.bottom() >= er.top() && r.top() <= er.bottom()) { + + int blpos = block.position(); + int bllen = block.length(); + + QVector<QTextLayout::FormatRange> selections; + QVector<QTextLayout::FormatRange> selectionsWithText; + + for (int i = 0; i < context.selections.size(); ++i) { + const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i); + const int selStart = range.cursor.selectionStart() - blpos; + const int selEnd = range.cursor.selectionEnd() - blpos; + if (selStart < bllen && selEnd > 0 + && selEnd > selStart) { + QTextLayout::FormatRange o; + o.start = selStart; + o.length = selEnd - selStart; + o.format = range.format; + if (blockSelection && blockSelection->selectionIndex == i) { + o.start = qMin(blockSelection->firstColumn, bllen-1); + o.length = qMin(blockSelection->lastColumn, bllen-1) - o.start; + } + if (o.format.foreground().style() != Qt::NoBrush) + selectionsWithText.append(o); + else + selections.append(o); + } else if (!range.cursor.hasSelection() && range.format.hasProperty(QTextFormat::FullWidthSelection) + && block.contains(range.cursor.position())) { + // for full width selections we don't require an actual selection, just + // a position to specify the line. that's more convenience in usage. + QTextLayout::FormatRange o; + QTextLine l = layout->lineForTextPosition(range.cursor.position() - blpos); + o.start = l.textStart(); + o.length = l.textLength(); + if (o.start + o.length == bllen - 1) + ++o.length; // include newline + o.format = range.format; + if (o.format.foreground().style() != Qt::NoBrush) + selectionsWithText.append(o); + else + selections.append(o); + } + } + d->highlightSearchResults(block, &selections); + selections += selectionsWithText; + + bool drawCursor = ((editable || true) // we want the cursor in read-only mode + && context.cursorPosition >= blpos + && context.cursorPosition < blpos + bllen); + + bool drawCursorAsBlock = drawCursor && overwriteMode() ; + + if (drawCursorAsBlock) { + if (context.cursorPosition == blpos + bllen - 1) { + drawCursorAsBlock = false; + } else { + QTextLayout::FormatRange o; + o.start = context.cursorPosition - blpos; + o.length = 1; + o.format.setForeground(palette().base()); + o.format.setBackground(palette().text()); + selections.append(o); + } + } + + + layout->draw(&painter, offset, selections, er); + + if ((drawCursor && !drawCursorAsBlock) + || (editable && context.cursorPosition < -1 + && !layout->preeditAreaText().isEmpty())) { + int cpos = context.cursorPosition; + if (cpos < -1) + cpos = layout->preeditAreaPosition() - (cpos + 2); + else + cpos -= blpos; + layout->drawCursor(&painter, offset, cpos, cursorWidth()); + } + } + + offset.ry() += r.height(); + + if (offset.y() > viewportRect.height()) + break; + block = block.next(); + if (!block.isVisible()) { + if (block.blockNumber() == d->visibleCollapsedBlockNumber) { + visibleCollapsedBlock = block; + visibleCollapsedBlockOffset = offset; + } + + // invisible blocks do have zero line count + block = doc->findBlockByLineNumber(block.firstLineNumber()); + } + + } + + if (backgroundVisible() && !block.isValid() && offset.y() <= er.bottom() + && (centerOnScroll() || verticalScrollBar()->maximum() == verticalScrollBar()->minimum())) { + painter.fillRect(QRect(QPoint((int)er.left(), (int)offset.y()), er.bottomRight()), palette().background()); + } + + //end QPlainTextEdit::paintEvent() + + delete blockSelection; + + offset = contentOffset(); + block = firstVisibleBlock(); + + int top = (int)blockBoundingGeometry(block).translated(offset).top(); + int bottom = top + (int)blockBoundingRect(block).height(); + + QTextCursor cursor = textCursor(); + bool hasSelection = cursor.hasSelection(); + int selectionStart = cursor.selectionStart(); + int selectionEnd = cursor.selectionEnd(); + + + while (block.isValid() && top <= e->rect().bottom()) { + QTextBlock nextBlock = block.next(); + QTextBlock nextVisibleBlock = nextBlock; + + if (!nextVisibleBlock.isVisible()) + // invisible blocks do have zero line count + nextVisibleBlock = doc->findBlockByLineNumber(nextVisibleBlock.firstLineNumber()); + if (block.isVisible() && bottom >= e->rect().top()) { + if (d->m_displaySettings.m_visualizeWhitespace) { + QTextLayout *layout = block.layout(); + int lineCount = layout->lineCount(); + if (lineCount >= 2 || !nextBlock.isValid()) { + painter.save(); + painter.setPen(Qt::lightGray); + for (int i = 0; i < lineCount-1; ++i) { // paint line wrap indicator + QTextLine line = layout->lineAt(i); + QRectF lineRect = line.naturalTextRect().translated(offset.x(), top); + QChar visualArrow((ushort)0x21b5); + painter.drawText(static_cast<int>(lineRect.right()), + static_cast<int>(lineRect.top() + line.ascent()), visualArrow); + } + if (!nextBlock.isValid()) { // paint EOF symbol + QTextLine line = layout->lineAt(lineCount-1); + QRectF lineRect = line.naturalTextRect().translated(offset.x(), top); + int h = 4; + lineRect.adjust(0, 0, -1, -1); + QPainterPath path; + QPointF pos(lineRect.topRight() + QPointF(h+4, line.ascent())); + path.moveTo(pos); + path.lineTo(pos + QPointF(-h, -h)); + path.lineTo(pos + QPointF(0, -2*h)); + path.lineTo(pos + QPointF(h, -h)); + path.closeSubpath(); + painter.setBrush(painter.pen().color()); + painter.drawPath(path); + } + painter.restore(); + } + } + + if (nextBlock.isValid() && !nextBlock.isVisible()) { + + bool selectThis = (hasSelection + && nextBlock.position() >= selectionStart + && nextBlock.position() < selectionEnd); + if (selectThis) { + painter.save(); + painter.setBrush(palette().highlight()); + } + + QTextLayout *layout = block.layout(); + QTextLine line = layout->lineAt(layout->lineCount()-1); + QRectF lineRect = line.naturalTextRect().translated(offset.x(), top); + lineRect.adjust(0, 0, -1, -1); + + QRectF collapseRect(lineRect.right() + 12, + lineRect.top(), + fontMetrics().width(QLatin1String(" {...}; ")), + lineRect.height()); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.translate(.5, .5); + painter.drawRoundedRect(collapseRect.adjusted(0, 0, 0, -1), 3, 3); + painter.setRenderHint(QPainter::Antialiasing, false); + painter.translate(-.5, -.5); + + QString replacement = QLatin1String("..."); + + QTextBlock info = block; + if (block.userData() + && static_cast<TextBlockUserData*>(block.userData())->collapseMode() == TextBlockUserData::CollapseAfter) + ; + else if (block.next().userData() + && static_cast<TextBlockUserData*>(block.next().userData())->collapseMode() + == TextBlockUserData::CollapseThis) { + replacement.prepend(nextBlock.text().trimmed().left(1)); + info = nextBlock; + } + + + block = nextVisibleBlock.previous(); + if (!block.isValid()) + block = doc->lastBlock(); + + if (info.userData() + && static_cast<TextBlockUserData*>(info.userData())->collapseIncludesClosure()) { + QString right = block.text().trimmed(); + if (right.endsWith(QLatin1Char(';'))) { + right.chop(1); + right = right.trimmed(); + replacement.append(right.right(right.endsWith(QLatin1Char('/')) ? 2 : 1)); + replacement.append(QLatin1Char(';')); + } else { + replacement.append(right.right(right.endsWith(QLatin1Char('/')) ? 2 : 1)); + } + } + if (selectThis) + painter.setPen(palette().highlightedText().color()); + painter.drawText(collapseRect, Qt::AlignCenter, replacement); + if (selectThis) + painter.restore(); + } + } + + block = nextVisibleBlock; + top = bottom; + bottom = top + (int)blockBoundingRect(block).height(); + } + + if (visibleCollapsedBlock.isValid() ) { + int margin = doc->documentMargin(); + qreal maxWidth = 0; + qreal blockHeight = 0; + QTextBlock b = visibleCollapsedBlock; + + while (!b.isVisible() && visibleCollapsedBlockOffset.y() + blockHeight <= e->rect().bottom()) { + b.setVisible(true); // make sure block bounding rect works + QRectF r = blockBoundingRect(b).translated(visibleCollapsedBlockOffset); + + QTextLayout *layout = b.layout(); + for (int i = layout->lineCount()-1; i >= 0; --i) + maxWidth = qMax(maxWidth, layout->lineAt(i).naturalTextWidth() + margin); + + blockHeight += r.height(); + + b.setVisible(false); // restore previous state + b = b.next(); + } + + painter.save(); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.translate(.5, .5); + QColor color = blendColor; +// color.setAlpha(240); // someone thinks alpha blending looks messy + painter.setBrush(color); + painter.drawRoundedRect(QRectF(visibleCollapsedBlockOffset.x(), + visibleCollapsedBlockOffset.y(), + maxWidth, blockHeight).adjusted(0, 0, 1, 1), 3, 3); + painter.restore(); + + QTextBlock end = b; + b = visibleCollapsedBlock; + while (b != end) { + b.setVisible(true); // make sure block bounding rect works + QRectF r = blockBoundingRect(b).translated(visibleCollapsedBlockOffset); + QTextLayout *layout = b.layout(); + QVector<QTextLayout::FormatRange> selections; + d->highlightSearchResults(b, &selections); + layout->draw(&painter, visibleCollapsedBlockOffset, selections, er); + + b.setVisible(false); // restore previous state + visibleCollapsedBlockOffset.ry() += r.height(); + b = b.next(); + } + } + + + if (d->m_visibleWrapColumn > 0) { + qreal lineX = fontMetrics().width('x') * d->m_visibleWrapColumn + offset.x() + 4; + const QColor bg = palette().base().color(); + QColor col = (bg.value() > 128) ? Qt::black : Qt::white; + col.setAlpha(32); + painter.setPen(QPen(col, 0)); + painter.drawLine(QPointF(lineX, 0), QPointF(lineX, viewport()->height())); + } +} + +void BaseTextEditor::slotUpdateExtraAreaWidth() +{ + if (isLeftToRight()) + setViewportMargins(extraAreaWidth(), 0, 0, 0); + else + setViewportMargins(0, 0, extraAreaWidth(), 0); +} + + +QWidget *BaseTextEditor::extraArea() const +{ + return d->m_extraArea; +} + +int BaseTextEditor::extraAreaWidth(int *markWidthPtr) const +{ + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(document()->documentLayout()); + if (!documentLayout) + return 0; + + if (!d->m_marksVisible && documentLayout->hasMarks) + d->m_marksVisible = true; + + int space = 0; + const QFontMetrics fm(d->m_extraArea->fontMetrics()); + + if (d->m_lineNumbersVisible) { + int digits = 2; + int max = qMax(1, blockCount()); + while (max >= 100) { + max /= 10; + ++digits; + } + space += fm.width(QLatin1Char('9')) * digits; + } + int markWidth = 0; + + if (d->m_marksVisible) { + markWidth += fm.lineSpacing(); +// if (documentLayout->doubleMarkCount) +// markWidth += fm.lineSpacing() / 3; + space += markWidth; + } else { + space += 2; + } + + if (markWidthPtr) + *markWidthPtr = markWidth; + + space += 4; + + if (d->m_codeFoldingVisible) + space += fm.lineSpacing(); + return space; +} + +void BaseTextEditor::slotModificationChanged(bool m) +{ + if (m) + return; + + QTextDocument *doc = document(); + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(doc->documentLayout()); + Q_ASSERT(documentLayout); + int oldLastSaveRevision = documentLayout->lastSaveRevision; + documentLayout->lastSaveRevision = doc->revision(); + + if (oldLastSaveRevision != documentLayout->lastSaveRevision) { + QTextBlock block = doc->begin(); + while (block.isValid()) { + if (block.revision() < 0 || block.revision() != oldLastSaveRevision) { + block.setRevision(-documentLayout->lastSaveRevision - 1); + } else { + block.setRevision(documentLayout->lastSaveRevision); + } + block = block.next(); + } + } + d->m_extraArea->update(); +} + +void BaseTextEditor::slotUpdateBlockNotify(const QTextBlock &block) +{ + static bool blockRecursion = false; + if (blockRecursion) + return; + if (block.previous().isValid() && block.userState() != block.previous().userState()) { + /* The syntax highlighting state changes. This opens up for + the possibility that the paragraph has braces that support + code folding. In this case, do the save thing and also + update the previous block, which might contain a collapse + box which now is invalid.*/ + blockRecursion = true; + emit requestBlockUpdate(block.previous()); + blockRecursion = false; + } +} + +void BaseTextEditor::slotUpdateRequest(const QRect &r, int dy) +{ + if (dy) + d->m_extraArea->scroll(0, dy); + else if (r.width() > 4) { // wider than cursor width, not just cursor blinking + d->m_extraArea->update(0, r.y(), d->m_extraArea->width(), r.height()); + } + + if (r.contains(viewport()->rect())) + slotUpdateExtraAreaWidth(); +} + + +void BaseTextEditor::setCollapseIndicatorAlpha(int alpha) +{ + d->extraAreaCollapseAlpha = alpha; + d->m_extraArea->update(); +} + +void BaseTextEditor::extraAreaPaintEvent(QPaintEvent *e) +{ + QTextDocument *doc = document(); + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(doc->documentLayout()); + Q_ASSERT(documentLayout); + + QPalette pal = d->m_extraArea->palette(); + pal.setCurrentColorGroup(QPalette::Active); + QPainter painter(d->m_extraArea); + QFontMetrics fm(painter.fontMetrics()); + int fmLineSpacing = fm.lineSpacing(); + + int markWidth = 0; + if (d->m_marksVisible) + markWidth += fm.lineSpacing(); +// if (documentLayout->doubleMarkCount) +// markWidth += fm.lineSpacing() / 3; + + const int collapseBoxWidth = d->m_codeFoldingVisible ? fmLineSpacing + 1: 0; + const int extraAreaWidth = d->m_extraArea->width() - collapseBoxWidth; + + painter.fillRect(e->rect(), pal.color(QPalette::Base)); + painter.fillRect(e->rect().intersected(QRect(0, 0, extraAreaWidth, INT_MAX)), + pal.color(QPalette::Background)); + + + QTextBlock block = firstVisibleBlock(); + int blockNumber = block.blockNumber(); + int top = (int)blockBoundingGeometry(block).translated(contentOffset()).top(); + int bottom = top; + + int extraAreaHighlightCollapseEndBlockNumber = -1; + + int extraAreaHighlightCollapseBlockNumber = d->extraAreaHighlightCollapseBlockNumber; + if (extraAreaHighlightCollapseBlockNumber < 0) { + extraAreaHighlightCollapseBlockNumber = d->extraAreaHighlightFadingBlockNumber; + } + + if (extraAreaHighlightCollapseBlockNumber >= 0 ) { + QTextBlock highlightBlock = doc->findBlockByNumber(extraAreaHighlightCollapseBlockNumber); + if (highlightBlock.isValid() && highlightBlock.next().isValid() && highlightBlock.next().isVisible()) + extraAreaHighlightCollapseEndBlockNumber = TextBlockUserData::testCollapse(highlightBlock).blockNumber(); + else + extraAreaHighlightCollapseEndBlockNumber = extraAreaHighlightCollapseBlockNumber; + } + + + while (block.isValid() && top <= e->rect().bottom()) { + + bool collapseThis = false; + bool collapseAfter = false; + bool hasClosingCollapse = false; + + + top = bottom; + bottom = top + (int)blockBoundingRect(block).height(); + QTextBlock nextBlock = block.next(); + + QTextBlock nextVisibleBlock = nextBlock; + int nextVisibleBlockNumber = blockNumber + 1; + + if (!nextVisibleBlock.isVisible()) { + // invisible blocks do have zero line count + nextVisibleBlock = doc->findBlockByLineNumber(nextVisibleBlock.firstLineNumber()); + nextVisibleBlockNumber = nextVisibleBlock.blockNumber(); + } + + painter.setPen(pal.color(QPalette::Dark)); + + if (d->m_codeFoldingVisible || d->m_marksVisible) { + painter.save(); + painter.setRenderHint(QPainter::Antialiasing, false); + + int previousBraceDepth = block.previous().userState(); + if (previousBraceDepth >= 0) + previousBraceDepth >>= 8; + else + previousBraceDepth = 0; + + int braceDepth = block.userState(); + if (!nextBlock.isVisible()) { + QTextBlock lastInvisibleBlock = nextVisibleBlock.previous(); + if (!lastInvisibleBlock.isValid()) + lastInvisibleBlock = doc->lastBlock(); + braceDepth = lastInvisibleBlock.userState(); + } + if (braceDepth >= 0) + braceDepth >>= 8; + else + braceDepth = 0; + + if (TextBlockUserData *userData = static_cast<TextBlockUserData*>(block.userData())) { + if (d->m_marksVisible) { + int xoffset = 0; + foreach (ITextMark *mrk, userData->marks()) { + int x = 0; + int radius = fmLineSpacing - 1; + QRect r(x + xoffset, top, radius, radius); + mrk->icon().paint(&painter, r, Qt::AlignCenter); + xoffset += 2; + } + } + + collapseAfter = (userData->collapseMode() == TextBlockUserData::CollapseAfter); + collapseThis = (userData->collapseMode() == TextBlockUserData::CollapseThis); + hasClosingCollapse = userData->hasClosingCollapse() && (previousBraceDepth > 0); + } + + if (d->m_codeFoldingVisible) { + const QRect box(extraAreaWidth + collapseBoxWidth/4, top + collapseBoxWidth/4, + 2 * (collapseBoxWidth/4) + 1, 2 * (collapseBoxWidth/4) + 1); + const QPoint boxCenter = box.center(); + + QColor textColorAlpha = pal.text().color(); + textColorAlpha.setAlpha(d->extraAreaCollapseAlpha); + QColor textColorInactive = pal.text().color(); + textColorInactive.setAlpha(100); + QColor textColor = pal.text().color(); + textColor.setAlpha(qMax(textColorInactive.alpha(), d->extraAreaCollapseAlpha)); + + const QPen pen( (blockNumber >= extraAreaHighlightCollapseBlockNumber + && blockNumber <= extraAreaHighlightCollapseEndBlockNumber) ? + textColorAlpha : pal.base().color()); + const QPen boxPen((blockNumber == extraAreaHighlightCollapseBlockNumber) ? + textColor : textColorInactive); + const QPen endPen((blockNumber == extraAreaHighlightCollapseEndBlockNumber) ? + textColorAlpha : pal.base().color()); + const QPen previousPen((blockNumber-1 >= extraAreaHighlightCollapseBlockNumber + && blockNumber-1 < extraAreaHighlightCollapseEndBlockNumber) ? + textColorAlpha : pal.base().color()); + const QPen nextPen((blockNumber+1 > extraAreaHighlightCollapseBlockNumber + && blockNumber+1 <= extraAreaHighlightCollapseEndBlockNumber) ? + textColorAlpha : pal.base().color()); + + TextBlockUserData *nextBlockUserData = TextEditDocumentLayout::testUserData(nextBlock); + + bool collapseNext = nextBlockUserData + && nextBlockUserData->collapseMode() + == TextBlockUserData::CollapseThis; + + bool nextHasClosingCollapse = nextBlockUserData + && nextBlockUserData->hasClosingCollapseInside(); + + bool drawBox = ((collapseAfter || collapseNext) && !nextHasClosingCollapse); + + if (braceDepth || (collapseNext && nextBlock.isVisible())) { + painter.setPen((hasClosingCollapse || !nextBlock.isVisible())? nextPen : pen); + painter.drawLine(boxCenter.x(), boxCenter.y(), boxCenter.x(), bottom - 1); + } + + if (previousBraceDepth || collapseThis) { + painter.setPen((collapseAfter || collapseNext) ? previousPen : pen); + painter.drawLine(boxCenter.x(), top, boxCenter.x(), boxCenter.y()); + } + + if (drawBox) { + painter.setPen(boxPen); + painter.setBrush(pal.base()); + painter.drawRect(box.adjusted(0, 0, -1, -1)); + if (!nextBlock.isVisible()) + painter.drawLine(boxCenter.x(), box.top() + 2, boxCenter.x(), box.bottom() - 2); + painter.drawLine(box.left() + 2, boxCenter.y(), box.right() - 2, boxCenter.y()); + } else if (hasClosingCollapse || collapseAfter || collapseNext) { + painter.setPen(endPen); + painter.drawLine(boxCenter.x() + 1, boxCenter.y(), box.right() - 1, boxCenter.y()); + } + + } + + painter.restore(); + } + + + if (d->m_revisionsVisible && block.revision() != documentLayout->lastSaveRevision) { + painter.save(); + painter.setRenderHint(QPainter::Antialiasing, false); + if (block.revision() < 0) + painter.setPen(QPen(Qt::darkGreen, 2)); + else + painter.setPen(QPen(Qt::red, 2)); + painter.drawLine(extraAreaWidth-1, top, extraAreaWidth-1, bottom-1); + painter.restore(); + } + + if (d->m_lineNumbersVisible) { + const QString &number = QString::number(blockNumber + 1); + painter.drawText(markWidth, top, extraAreaWidth - markWidth - 4, fm.height(), Qt::AlignRight, number); + } + + block = nextVisibleBlock; + blockNumber = nextVisibleBlockNumber; + } +} + +void BaseTextEditor::timerEvent(QTimerEvent *e) +{ + if (e->timerId() == d->autoScrollTimer.timerId()) { + const QPoint globalPos = QCursor::pos(); + const QPoint pos = d->m_extraArea->mapFromGlobal(globalPos); + QRect visible = d->m_extraArea->rect(); + verticalScrollBar()->triggerAction( pos.y() < visible.center().y() ? + QAbstractSlider::SliderSingleStepSub + : QAbstractSlider::SliderSingleStepAdd); + QMouseEvent ev(QEvent::MouseMove, pos, globalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + extraAreaMouseEvent(&ev); + int delta = qMax(pos.y() - visible.top(), visible.bottom() - pos.y()) - visible.height(); + if (delta < 7) + delta = 7; + int timeout = 4900 / (delta * delta); + d->autoScrollTimer.start(timeout, this); + + } else if (e->timerId() == d->collapsedBlockTimer.timerId()) { + d->visibleCollapsedBlockNumber = d->suggestedVisibleCollapsedBlockNumber; + d->suggestedVisibleCollapsedBlockNumber = -1; + d->collapsedBlockTimer.stop(); + viewport()->update(); + } + QPlainTextEdit::timerEvent(e); +} + + +void BaseTextEditorPrivate::clearVisibleCollapsedBlock() +{ + if (suggestedVisibleCollapsedBlockNumber) { + suggestedVisibleCollapsedBlockNumber = -1; + collapsedBlockTimer.stop(); + } + if (visibleCollapsedBlockNumber >= 0) { + visibleCollapsedBlockNumber = -1; + q->viewport()->update(); + } +} + + +void BaseTextEditor::mouseMoveEvent(QMouseEvent *e) +{ + d->m_lastEventWasBlockSelectionEvent = (e->modifiers() & Qt::AltModifier); + if (e->buttons() == 0) { + QTextBlock collapsedBlock = collapsedBlockAt(e->pos()); + int blockNumber = collapsedBlock.next().blockNumber(); + if (blockNumber < 0) { + d->clearVisibleCollapsedBlock(); + } else if (blockNumber != d->visibleCollapsedBlockNumber) { + d->suggestedVisibleCollapsedBlockNumber = blockNumber; + d->collapsedBlockTimer.start(40, this); + } + viewport()->setCursor(collapsedBlock.isValid() ? Qt::PointingHandCursor : Qt::IBeamCursor); + } else { + QPlainTextEdit::mouseMoveEvent(e); + } + if (d->m_lastEventWasBlockSelectionEvent && d->m_inBlockSelectionMode) { + if (textCursor().atBlockEnd()) { + d->m_blockSelectionExtraX = qMax(0, e->pos().x() - cursorRect().center().x()) / fontMetrics().width(QLatin1Char('x')); + } else { + d->m_blockSelectionExtraX = 0; + } + } +} + +void BaseTextEditor::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == Qt::LeftButton) { + d->clearBlockSelection(); // just in case, otherwise we might get strange drag and drop + + QTextBlock collapsedBlock = collapsedBlockAt(e->pos()); + if (collapsedBlock.isValid()) { + toggleBlockVisible(collapsedBlock); + viewport()->setCursor(Qt::IBeamCursor); + } + } + QPlainTextEdit::mousePressEvent(e); +} + +void BaseTextEditor::extraAreaLeaveEvent(QEvent *) +{ + if (d->extraAreaHighlightCollapseBlockNumber >= 0) { + d->extraAreaHighlightFadingBlockNumber = d->extraAreaHighlightCollapseBlockNumber; + d->extraAreaHighlightCollapseBlockNumber = -1; // missing mouse move event from Qt + d->extraAreaTimeLine->setDirection(QTimeLine::Backward); + if (d->extraAreaTimeLine->state() != QTimeLine::Running) + d->extraAreaTimeLine->start(); + } +} + +void BaseTextEditor::extraAreaMouseEvent(QMouseEvent *e) +{ + QTextCursor cursor = cursorForPosition(QPoint(0, e->pos().y())); + cursor.setPosition(cursor.block().position()); + + int markWidth; + extraAreaWidth(&markWidth); + + if (e->type() == QEvent::MouseMove && e->buttons() == 0) { // mouse tracking + int highlightBlockNumber = d->extraAreaHighlightCollapseBlockNumber; + d->extraAreaHighlightCollapseBlockNumber = -1; + if (TextBlockUserData::canCollapse(cursor.block()) + && !TextBlockUserData::hasClosingCollapseInside(cursor.block().next()) + && collapseBox(cursor.block()).contains(e->pos())) + d->extraAreaHighlightCollapseBlockNumber = cursor.blockNumber(); + + bool hand = (e->pos().x() <= markWidth || d->extraAreaHighlightCollapseBlockNumber >= 0); + if (hand != (d->m_extraArea->cursor().shape() == Qt::PointingHandCursor)) + d->m_extraArea->setCursor(hand ? Qt::PointingHandCursor : Qt::ArrowCursor); + + if (highlightBlockNumber != d->extraAreaHighlightCollapseBlockNumber) { + d->extraAreaTimeLine->stop(); + d->extraAreaTimeLine->setDirection(d->extraAreaHighlightCollapseBlockNumber >= 0? + QTimeLine::Forward : QTimeLine::Backward); + if (d->extraAreaTimeLine->direction() == QTimeLine::Backward) + d->extraAreaHighlightFadingBlockNumber = highlightBlockNumber; + else + d->extraAreaHighlightFadingBlockNumber = -1; + if (d->extraAreaTimeLine->state() != QTimeLine::Running) + d->extraAreaTimeLine->start(); + } + } + + + if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonDblClick) { + if (e->button() == Qt::LeftButton) { + if (TextBlockUserData::canCollapse(cursor.block()) + && !TextBlockUserData::hasClosingCollapseInside(cursor.block().next()) + && collapseBox(cursor.block()).contains(e->pos())) { + setTextCursor(cursor); + toggleBlockVisible(cursor.block()); + } else if (e->pos().x() > markWidth) { + QTextCursor selection = cursor; + selection.setVisualNavigation(true); + d->extraAreaSelectionAnchorBlockNumber = selection.blockNumber(); + selection.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + selection.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); + setTextCursor(selection); + } else { + d->extraAreaToggleMarkBlockNumber = cursor.blockNumber(); + } + } + } else if (d->extraAreaSelectionAnchorBlockNumber >= 0) { + QTextCursor selection = cursor; + selection.setVisualNavigation(true); + if (e->type() == QEvent::MouseMove) { + QTextBlock anchorBlock = document()->findBlockByNumber(d->extraAreaSelectionAnchorBlockNumber); + selection.setPosition(anchorBlock.position()); + if (cursor.blockNumber() < d->extraAreaSelectionAnchorBlockNumber) { + selection.movePosition(QTextCursor::EndOfBlock); + selection.movePosition(QTextCursor::Right); + } + selection.setPosition(cursor.block().position(), QTextCursor::KeepAnchor); + if (cursor.blockNumber() >= d->extraAreaSelectionAnchorBlockNumber) { + selection.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + selection.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); + } + + if (e->pos().y() >= 0 && e->pos().y() <= d->m_extraArea->height()) + d->autoScrollTimer.stop(); + else if (!d->autoScrollTimer.isActive()) + d->autoScrollTimer.start(100, this); + + } else { + d->autoScrollTimer.stop(); + d->extraAreaSelectionAnchorBlockNumber = -1; + return; + } + setTextCursor(selection); + } else if (d->extraAreaToggleMarkBlockNumber >= 0 && d->m_marksVisible && d->m_requestMarkEnabled) { + if (e->type() == QEvent::MouseButtonRelease && e->button() == Qt::LeftButton) { + int n = d->extraAreaToggleMarkBlockNumber; + d->extraAreaToggleMarkBlockNumber = -1; + if (cursor.blockNumber() == n) { + int line = n + 1; + emit markRequested(editableInterface(), line); + } + } + } +} + +void BaseTextEditor::slotCursorPositionChanged() +{ + QList<QTextEdit::ExtraSelection> extraSelections; + + if (d->m_highlightCurrentLine) { + QTextEdit::ExtraSelection sel; + sel.format.setBackground(d->m_currentLineFormat.background()); + sel.format.setProperty(QTextFormat::FullWidthSelection, true); + sel.cursor = textCursor(); + sel.cursor.clearSelection(); + extraSelections.append(sel); + } + + if (d->m_parenthesesMatchingEnabled) + d->m_parenthesesMatchingTimer->start(50); + + d->m_extraSelections = extraSelections; + setExtraSelections(d->m_extraSelections + d->m_extraExtraSelections); +} + +QTextBlock TextBlockUserData::testCollapse(const QTextBlock& block) +{ + QTextBlock info = block; + if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->collapseMode() == CollapseAfter) + ; + else if (block.next().userData() + && static_cast<TextBlockUserData*>(block.next().userData())->collapseMode() + == TextBlockUserData::CollapseThis) + info = block.next(); + else + return QTextBlock(); + int pos = static_cast<TextBlockUserData*>(info.userData())->collapseAtPos(); + if (pos < 0) + return QTextBlock(); + QTextCursor cursor(info); + cursor.setPosition(cursor.position() + pos); + matchCursorForward(&cursor); + return cursor.block(); +} + +void TextBlockUserData::doCollapse(const QTextBlock& block, bool visible) +{ + QTextBlock info = block; + if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->collapseMode() == CollapseAfter) + ; + else if (block.next().userData() + && static_cast<TextBlockUserData*>(block.next().userData())->collapseMode() + == TextBlockUserData::CollapseThis) + info = block.next(); + else { + if (visible && !block.next().isVisible()) { + // no match, at least unfold! + QTextBlock b = block.next(); + while (b.isValid() && !b.isVisible()) { + b.setVisible(true); + b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); + b = b.next(); + } + } + return; + } + int pos = static_cast<TextBlockUserData*>(info.userData())->collapseAtPos(); + if (pos < 0) + return; + QTextCursor cursor(info); + cursor.setPosition(cursor.position() + pos); + if (matchCursorForward(&cursor) != Match) { + if (visible) { + // no match, at least unfold! + QTextBlock b = block.next(); + while (b.isValid() && !b.isVisible()) { + b.setVisible(true); + b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); + b = b.next(); + } + } + return; + } + + QTextBlock b = block.next(); + while (b < cursor.block()) { + b.setVisible(visible); + b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); + if (visible) { + TextBlockUserData *data = canCollapse(b); + if (data && data->collapsed()) { + QTextBlock end = testCollapse(b); + if (data->collapseIncludesClosure()) + end = end.next(); + if (end.isValid()) { + b = end; + continue; + } + } + } + b = b.next(); + } + + bool collapseIncludesClosure = hasClosingCollapseAtEnd(b); + if (collapseIncludesClosure) { + b.setVisible(visible); + b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); + } + static_cast<TextBlockUserData*>(info.userData())->setCollapseIncludesClosure(collapseIncludesClosure); + static_cast<TextBlockUserData*>(info.userData())->setCollapsed(!block.next().isVisible()); + +} + + +void BaseTextEditor::ensureCursorVisible() +{ + QTextBlock block = textCursor().block(); + if (!block.isVisible()) { + while (!block.isVisible() && block.previous().isValid()) + block = block.previous(); + toggleBlockVisible(block); + } + QPlainTextEdit::ensureCursorVisible(); +} + +void BaseTextEditor::toggleBlockVisible(const QTextBlock &block) +{ + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(document()->documentLayout()); + Q_ASSERT(documentLayout); + + bool visible = block.next().isVisible(); + TextBlockUserData::doCollapse(block, !visible); + documentLayout->requestUpdate(); + documentLayout->emitDocumentSizeChanged(); +} + + +const TabSettings &BaseTextEditor::tabSettings() const +{ + return d->m_document->tabSettings(); +} + +const DisplaySettings &BaseTextEditor::displaySettings() const +{ + return d->m_displaySettings; +} + + + +void BaseTextEditor::indentOrUnindent(bool doIndent) +{ + QTextCursor cursor = textCursor(); + cursor.beginEditBlock(); + + int pos = cursor.position(); + const TextEditor::TabSettings &tabSettings = d->m_document->tabSettings(); + + + QTextDocument *doc = document(); + if (!cursor.hasSelection() + || (doc->findBlock(cursor.selectionStart()) == doc->findBlock(cursor.selectionEnd()) )) { + cursor.removeSelectedText(); + QTextBlock block = cursor.block(); + QString text = block.text(); + int indentPosition = (cursor.position() - block.position());; + int spaces = tabSettings.spacesLeftFromPosition(text, indentPosition); + int startColumn = tabSettings.columnAt(text, indentPosition - spaces); + int targetColumn = tabSettings.indentedColumn(tabSettings.columnAt(text, indentPosition), doIndent); + + cursor.setPosition(block.position() + indentPosition); + cursor.setPosition(block.position() + indentPosition - spaces, QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + cursor.insertText(tabSettings.indentationString(startColumn, targetColumn)); + } else { + int anchor = cursor.anchor(); + int start = qMin(anchor, pos); + int end = qMax(anchor, pos); + + QTextBlock startBlock = doc->findBlock(start); + QTextBlock endBlock = doc->findBlock(end-1).next(); + + for (QTextBlock block = startBlock; block != endBlock; block = block.next()) { + QString text = block.text(); + int indentPosition = tabSettings.lineIndentPosition(text); + if (!doIndent && !indentPosition) + indentPosition = tabSettings.firstNonSpace(text); + int targetColumn = tabSettings.indentedColumn(tabSettings.columnAt(text, indentPosition), doIndent); + cursor.setPosition(block.position() + indentPosition); + cursor.insertText(tabSettings.indentationString(0, targetColumn)); + cursor.setPosition(block.position()); + cursor.setPosition(block.position() + indentPosition, QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + } + } + + cursor.endEditBlock(); +} + +void BaseTextEditor::handleHomeKey(bool anchor) +{ + QTextCursor cursor = textCursor(); + QTextCursor::MoveMode mode = QTextCursor::MoveAnchor; + + if (anchor) + mode = QTextCursor::KeepAnchor; + + const int initpos = cursor.position(); + int pos = cursor.block().position(); + QChar character = characterAt(pos); + const QLatin1Char tab = QLatin1Char('\t'); + + while (character == tab || character.category() == QChar::Separator_Space) { + ++pos; + character = characterAt(pos); + } + + // Go to the start of the block when we're already at the start of the text + if (pos == initpos) + pos = cursor.block().position(); + + cursor.setPosition(pos, mode); + setTextCursor(cursor); +} + +void BaseTextEditor::handleBackspaceKey() +{ + QTextCursor cursor = textCursor(); + Q_ASSERT(!cursor.hasSelection()); + + const TextEditor::TabSettings &tabSettings = d->m_document->tabSettings(); + QTextBlock currentBlock = cursor.block(); + int positionInBlock = cursor.position() - currentBlock.position(); + const QString blockText = currentBlock.text(); + if (cursor.atBlockStart() || tabSettings.firstNonSpace(blockText) < positionInBlock) { + cursor.deletePreviousChar(); + return; + } + + int previousIndent = 0; + const int indent = tabSettings.columnAt(blockText, positionInBlock); + + for (QTextBlock previousNonEmptyBlock = currentBlock.previous(); + previousNonEmptyBlock.isValid(); + previousNonEmptyBlock = previousNonEmptyBlock.previous()) { + QString previousNonEmptyBlockText = previousNonEmptyBlock.text(); + if (previousNonEmptyBlockText.trimmed().isEmpty()) + continue; + previousIndent = tabSettings.columnAt(previousNonEmptyBlockText, + tabSettings.firstNonSpace(previousNonEmptyBlockText)); + if (previousIndent < indent) + break; + } + + if (previousIndent >= indent) + previousIndent = 0; + + cursor.beginEditBlock(); + cursor.setPosition(currentBlock.position(), QTextCursor::KeepAnchor); + cursor.insertText(tabSettings.indentationString(0, previousIndent)); + cursor.endEditBlock(); +} + + +void BaseTextEditor::format() +{ + QTextCursor cursor = textCursor(); + indent(document(), cursor, QChar::Null); +} + +void BaseTextEditor::unCommentSelection() +{ +} + +void BaseTextEditor::setTabSettings(const TabSettings &ts) +{ + d->m_document->setTabSettings(ts); + int charWidth = QFontMetrics(font()).width(QChar(' ')); + setTabStopWidth(charWidth * ts.m_tabSize); +} + +void BaseTextEditor::setDisplaySettings(const DisplaySettings &ds) +{ + setLineWrapMode(ds.m_textWrapping ? QPlainTextEdit::WidgetWidth : QPlainTextEdit::NoWrap); + setLineNumbersVisible(ds.m_displayLineNumbers); + setVisibleWrapColumn(ds.m_showWrapColumn ? ds.m_wrapColumn : 0); + setCodeFoldingVisible(ds.m_displayFoldingMarkers); + setHighlightCurrentLine(ds.m_highlightCurrentLine); + + if (d->m_displaySettings.m_visualizeWhitespace != ds.m_visualizeWhitespace) { + if (QSyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter()) + highlighter->rehighlight(); + QTextOption option = document()->defaultTextOption(); + if (ds.m_visualizeWhitespace) + option.setFlags(option.flags() | QTextOption::ShowTabsAndSpaces); + else + option.setFlags(option.flags() & ~QTextOption::ShowTabsAndSpaces); + option.setFlags(option.flags() | QTextOption::AddSpaceForLineAndParagraphSeparators); + document()->setDefaultTextOption(option); + } + + d->m_displaySettings = ds; +} + + +void BaseTextEditor::wheelEvent(QWheelEvent *e) +{ + d->clearVisibleCollapsedBlock(); + if (e->modifiers() & Qt::ControlModifier) { + const int delta = e->delta(); + if (delta < 0) + zoomOut(); + else if (delta > 0) + zoomIn(); + return; + } + QPlainTextEdit::wheelEvent(e); +} + +void BaseTextEditor::zoomIn(int range) +{ + d->clearVisibleCollapsedBlock(); + QFont f = font(); + const int newSize = f.pointSize() + range; + if (newSize <= 0) + return; + f.setPointSize(newSize); + setFont(f); +} + +void BaseTextEditor::zoomOut(int range) +{ + zoomIn(-range); +} + +bool BaseTextEditor::isElectricCharacter(const QChar &) const +{ + return false; +} + +void BaseTextEditor::indentBlock(QTextDocument *, QTextBlock, QChar) +{ +} + +void BaseTextEditor::indent(QTextDocument *doc, const QTextCursor &cursor, QChar typedChar) +{ + if (cursor.hasSelection()) { + QTextBlock block = doc->findBlock(qMin(cursor.selectionStart(), cursor.selectionEnd())); + const QTextBlock end = doc->findBlock(qMax(cursor.selectionStart(), cursor.selectionEnd())).next(); + do { + indentBlock(doc, block, typedChar); + block = block.next(); + } while (block.isValid() && block != end); + } else { + indentBlock(doc, cursor.block(), typedChar); + } +} + +void BaseTextEditorPrivate::updateMarksBlock(const QTextBlock &block) +{ + if (const TextBlockUserData *userData = TextEditDocumentLayout::testUserData(block)) + foreach (ITextMark *mrk, userData->marks()) { + mrk->updateBlock(block); + } +} + +void BaseTextEditorPrivate::updateMarksLineNumber() +{ + QTextDocument *doc = q->document(); + QTextBlock block = doc->begin(); + int blockNumber = 0; + while (block.isValid()) { + if (const TextBlockUserData *userData = TextEditDocumentLayout::testUserData(block)) + foreach (ITextMark *mrk, userData->marks()) { + mrk->updateLineNumber(blockNumber + 1); + } + block = block.next(); + ++blockNumber; + } +} + +void BaseTextEditor::markBlocksAsChanged(QList<int> blockNumbers) { + QTextBlock block = document()->begin(); + while (block.isValid()) { + if (block.revision() < 0) + block.setRevision(-block.revision() - 1); + block = block.next(); + } + foreach (const int blockNumber, blockNumbers) { + QTextBlock block = document()->findBlockByNumber(blockNumber); + if (block.isValid()) + block.setRevision(-block.revision() - 1); + } +} + + + +TextBlockUserData::MatchType TextBlockUserData::checkOpenParenthesis(QTextCursor *cursor, QChar c) +{ + if (!TextEditDocumentLayout::hasParentheses(cursor->block())) + return NoMatch; + + Parentheses parenList = TextEditDocumentLayout::parentheses(cursor->block()); + Parenthesis openParen, closedParen; + QTextBlock closedParenParag = cursor->block(); + + const int cursorPos = cursor->position() - closedParenParag.position(); + int i = 0; + int ignore = 0; + bool foundOpen = false; + for (;;) { + if (!foundOpen) { + if (i >= parenList.count()) + return NoMatch; + openParen = parenList.at(i); + if (openParen.pos != cursorPos) { + ++i; + continue; + } else { + foundOpen = true; + ++i; + } + } + + if (i >= parenList.count()) { + for (;;) { + closedParenParag = closedParenParag.next(); + if (!closedParenParag.isValid()) + return NoMatch; + if (TextEditDocumentLayout::hasParentheses(closedParenParag)) { + parenList = TextEditDocumentLayout::parentheses(closedParenParag); + break; + } + } + i = 0; + } + + closedParen = parenList.at(i); + if (closedParen.type == Parenthesis::Opened) { + ignore++; + ++i; + continue; + } else { + if (ignore > 0) { + ignore--; + ++i; + continue; + } + + cursor->clearSelection(); + cursor->setPosition(closedParenParag.position() + closedParen.pos + 1, QTextCursor::KeepAnchor); + + if ((c == QLatin1Char('{') && closedParen.chr != QLatin1Char('}')) + || (c == QLatin1Char('(') && closedParen.chr != QLatin1Char(')')) + || (c == QLatin1Char('[') && closedParen.chr != QLatin1Char(']')) + || (c == QLatin1Char('+') && closedParen.chr != QLatin1Char('-')) + ) + return Mismatch; + + return Match; + } + } +} + +TextBlockUserData::MatchType TextBlockUserData::checkClosedParenthesis(QTextCursor *cursor, QChar c) +{ + if (!TextEditDocumentLayout::hasParentheses(cursor->block())) + return NoMatch; + + Parentheses parenList = TextEditDocumentLayout::parentheses(cursor->block()); + Parenthesis openParen, closedParen; + QTextBlock openParenParag = cursor->block(); + + const int cursorPos = cursor->position() - openParenParag.position(); + int i = parenList.count() - 1; + int ignore = 0; + bool foundClosed = false; + for (;;) { + if (!foundClosed) { + if (i < 0) + return NoMatch; + closedParen = parenList.at(i); + if (closedParen.pos != cursorPos - 1) { + --i; + continue; + } else { + foundClosed = true; + --i; + } + } + + if (i < 0) { + for (;;) { + openParenParag = openParenParag.previous(); + if (!openParenParag.isValid()) + return NoMatch; + + if (TextEditDocumentLayout::hasParentheses(openParenParag)) { + parenList = TextEditDocumentLayout::parentheses(openParenParag); + break; + } + } + i = parenList.count() - 1; + } + + openParen = parenList.at(i); + if (openParen.type == Parenthesis::Closed) { + ignore++; + --i; + continue; + } else { + if (ignore > 0) { + ignore--; + --i; + continue; + } + + cursor->clearSelection(); + cursor->setPosition(openParenParag.position() + openParen.pos, QTextCursor::KeepAnchor); + + if ((c == '}' && openParen.chr != '{') || + (c == ')' && openParen.chr != '(') || + (c == ']' && openParen.chr != '[') || + (c == '-' && openParen.chr != '+')) + return Mismatch; + + return Match; + } + } +} + +TextBlockUserData::MatchType TextBlockUserData::matchCursorBackward(QTextCursor *cursor) +{ + cursor->clearSelection(); + const QTextBlock block = cursor->block(); + + if (!TextEditDocumentLayout::hasParentheses(block)) + return NoMatch; + + const int relPos = cursor->position() - block.position(); + + Parentheses parentheses = TextEditDocumentLayout::parentheses(block); + const Parentheses::const_iterator cend = parentheses.constEnd(); + for (Parentheses::const_iterator it = parentheses.constBegin();it != cend; ++it) { + const Parenthesis &paren = *it; + if (paren.pos == relPos - 1 + && paren.type == Parenthesis::Closed) { + return checkClosedParenthesis(cursor, paren.chr); + } + } + return NoMatch; +} + +TextBlockUserData::MatchType TextBlockUserData::matchCursorForward(QTextCursor *cursor) +{ + cursor->clearSelection(); + const QTextBlock block = cursor->block(); + + if (!TextEditDocumentLayout::hasParentheses(block)) + return NoMatch; + + const int relPos = cursor->position() - block.position(); + + Parentheses parentheses = TextEditDocumentLayout::parentheses(block); + const Parentheses::const_iterator cend = parentheses.constEnd(); + for (Parentheses::const_iterator it = parentheses.constBegin();it != cend; ++it) { + const Parenthesis &paren = *it; + if (paren.pos == relPos + && paren.type == Parenthesis::Opened) { + return checkOpenParenthesis(cursor, paren.chr); + } + } + return NoMatch; +} + + +void BaseTextEditor::highlightSearchResults(const QString &txt, QTextDocument::FindFlags findFlags) +{ + if (d->m_searchExpr.pattern() == txt) + return; + d->m_searchExpr.setPattern(txt); + d->m_searchExpr.setPatternSyntax(QRegExp::FixedString); + d->m_searchExpr.setCaseSensitivity((findFlags & QTextDocument::FindCaseSensitively) ? + Qt::CaseSensitive : Qt::CaseInsensitive); + d->m_findFlags = findFlags; + viewport()->update(); +} + + +void BaseTextEditor::setFindScope(const QTextCursor &scope) +{ + if (scope.isNull() != d->m_findScope.isNull()) { + d->m_findScope = scope; + viewport()->update(); + } +} + +void BaseTextEditor::_q_matchParentheses() +{ + if (isReadOnly()) + return; + + QTextCursor backwardMatch = textCursor(); + QTextCursor forwardMatch = textCursor(); + const TextBlockUserData::MatchType backwardMatchType = TextBlockUserData::matchCursorBackward(&backwardMatch); + const TextBlockUserData::MatchType forwardMatchType = TextBlockUserData::matchCursorForward(&forwardMatch); + + if (backwardMatchType == TextBlockUserData::NoMatch && forwardMatchType == TextBlockUserData::NoMatch) + return; + + QList<QTextEdit::ExtraSelection> extraSelections = d->m_extraSelections; + + if (backwardMatch.hasSelection()) { + QTextEdit::ExtraSelection sel; + if (backwardMatchType == TextBlockUserData::Mismatch) { + sel.cursor = backwardMatch; + sel.format = d->m_mismatchFormat; + } else { + + if (d->m_formatRange) { + sel.cursor = backwardMatch; + sel.format = d->m_rangeFormat; + extraSelections.append(sel); + } + + sel.cursor = backwardMatch; + sel.format = d->m_matchFormat; + + sel.cursor.setPosition(backwardMatch.selectionStart()); + sel.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); + extraSelections.append(sel); + + sel.cursor.setPosition(backwardMatch.selectionEnd()); + sel.cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); + } + extraSelections.append(sel); + } + + if (forwardMatch.hasSelection()) { + QTextEdit::ExtraSelection sel; + if (forwardMatchType == TextBlockUserData::Mismatch) { + sel.cursor = forwardMatch; + sel.format = d->m_mismatchFormat; + } else { + + if (d->m_formatRange) { + sel.cursor = forwardMatch; + sel.format = d->m_rangeFormat; + extraSelections.append(sel); + } + + sel.cursor = forwardMatch; + sel.format = d->m_matchFormat; + + sel.cursor.setPosition(forwardMatch.selectionStart()); + sel.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); + extraSelections.append(sel); + + sel.cursor.setPosition(forwardMatch.selectionEnd()); + sel.cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); + } + extraSelections.append(sel); + } + d->m_extraSelections = extraSelections; + setExtraSelections(d->m_extraSelections + d->m_extraExtraSelections); +} + +void BaseTextEditor::setActionHack(QObject *hack) +{ + d->m_actionHack = hack; +} + +QObject *BaseTextEditor::actionHack() const +{ + return d->m_actionHack; +} + +void BaseTextEditor::changeEvent(QEvent *e) +{ + QPlainTextEdit::changeEvent(e); + if (e->type() == QEvent::ApplicationFontChange + || e->type() == QEvent::FontChange) { + if (d->m_extraArea) { + QFont f = d->m_extraArea->font(); + f.setPointSize(font().pointSize()); + d->m_extraArea->setFont(f); + slotUpdateExtraAreaWidth(); + d->m_extraArea->update(); + } + } +} + +// shift+del +void BaseTextEditor::deleteLine() +{ + QTextCursor cursor = textCursor(); + if (!cursor.hasSelection()) { + const QTextBlock &block = cursor.block(); + if (block.next().isValid()) { + cursor.setPosition(block.position()); + cursor.setPosition(block.next().position(), QTextCursor::KeepAnchor); + } else { + cursor.movePosition(QTextCursor::EndOfBlock); + cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); + cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); + } + setTextCursor(cursor); + } + cut(); +} + +void BaseTextEditor::setExtraExtraSelections(const QList<QTextEdit::ExtraSelection> &selections) +{ + d->m_extraExtraSelections = selections; + setExtraSelections(d->m_extraSelections + d->m_extraExtraSelections); +} + +QList<QTextEdit::ExtraSelection> BaseTextEditor::extraExtraSelections() const +{ + return d->m_extraExtraSelections; +} + + +// the blocks list must be sorted +void BaseTextEditor::setIfdefedOutBlocks(const QList<BaseTextEditor::BlockRange> &blocks) +{ + QTextDocument *doc = document(); + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(doc->documentLayout()); + Q_ASSERT(documentLayout); + + bool needUpdate = false; + + QTextBlock block = doc->firstBlock(); + + int rangeNumber = 0; + while (block.isValid()) { + if (rangeNumber < blocks.size()) { + const BlockRange &range = blocks.at(rangeNumber); + + if (block.position() >= range.first && (block.position() <= range.last || !range.last)) { + needUpdate += TextEditDocumentLayout::setIfdefedOut(block); + } else { + needUpdate += TextEditDocumentLayout::clearIfdefedOut(block); + } + if (block.contains(range.last)) + ++rangeNumber; + } else { + needUpdate |= TextEditDocumentLayout::clearIfdefedOut(block); + } + + block = block.next(); + } + + if (needUpdate) + documentLayout->requestUpdate(); +} + + +void BaseTextEditorPrivate::moveCursorVisible() +{ + QTextCursor cursor = q->textCursor(); + if (!cursor.block().isVisible()) { + cursor.setVisualNavigation(true); + cursor.movePosition(QTextCursor::PreviousBlock); + q->setTextCursor(cursor); + } + q->ensureCursorVisible(); +} + +void BaseTextEditor::collapse() +{ + QTextDocument *doc = document(); + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(doc->documentLayout()); + Q_ASSERT(documentLayout); + QTextBlock block = textCursor().block(); + while (block.isValid()) { + if (TextBlockUserData::canCollapse(block)) { + if ((block.next().userState()) >> 8 == (textCursor().block().userState() >> 8)) + break; + } + block = block.previous(); + } + if (block.isValid()) { + TextBlockUserData::doCollapse(block, false); + d->moveCursorVisible(); + documentLayout->requestUpdate(); + documentLayout->emitDocumentSizeChanged(); + } +} + +void BaseTextEditor::expand() +{ + QTextDocument *doc = document(); + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(doc->documentLayout()); + Q_ASSERT(documentLayout); + QTextBlock block = textCursor().block(); + while (block.isValid() && !block.isVisible()) + block = block.previous(); + TextBlockUserData::doCollapse(block, true); + d->moveCursorVisible(); + documentLayout->requestUpdate(); + documentLayout->emitDocumentSizeChanged(); +} + +void BaseTextEditor::unCollapseAll() +{ + QTextDocument *doc = document(); + TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(doc->documentLayout()); + Q_ASSERT(documentLayout); + + QTextBlock block = doc->firstBlock(); + bool makeVisible = true; + while (block.isValid()) { + if (block.isVisible() && TextBlockUserData::canCollapse(block) && block.next().isVisible()) { + makeVisible = false; + break; + } + block = block.next(); + } + + block = doc->firstBlock(); + + while (block.isValid()) { + if (TextBlockUserData::canCollapse(block)) + TextBlockUserData::doCollapse(block, makeVisible); + block = block.next(); + + } + + d->moveCursorVisible(); + documentLayout->requestUpdate(); + documentLayout->emitDocumentSizeChanged(); +} + +void BaseTextEditor::setTextCodec(QTextCodec *codec) +{ + baseTextDocument()->setCodec(codec); +} + +QTextCodec *BaseTextEditor::textCodec() const +{ + return baseTextDocument()->codec(); +} + +void BaseTextEditor::setReadOnly(bool b) +{ + QPlainTextEdit::setReadOnly(b); + if (b) + setTextInteractionFlags(textInteractionFlags() | Qt::TextSelectableByKeyboard); +} + +void BaseTextEditor::cut() +{ + if (d->m_inBlockSelectionMode) { + copy(); + d->removeBlockSelection(); + return; + } + QPlainTextEdit::cut(); +} + +QMimeData *BaseTextEditor::createMimeDataFromSelection() const +{ + if (d->m_inBlockSelectionMode) { + QMimeData *mimeData = new QMimeData; + QString text = d->copyBlockSelection(); + mimeData->setData(QLatin1String("application/vnd.nokia.qtcreator.blocktext"), text.toUtf8()); + mimeData->setText(text); // for exchangeability + return mimeData; + } + return QPlainTextEdit::createMimeDataFromSelection(); +} + +bool BaseTextEditor::canInsertFromMimeData(const QMimeData *source) const +{ + return QPlainTextEdit::canInsertFromMimeData(source); +} + +void BaseTextEditor::insertFromMimeData(const QMimeData *source) +{ + if (!isReadOnly() && source->hasFormat(QLatin1String("application/vnd.nokia.qtcreator.blocktext"))) { + QString text = QString::fromUtf8(source->data(QLatin1String("application/vnd.nokia.qtcreator.blocktext"))); + if (text.isEmpty()) + return; + QStringList lines = text.split(QLatin1Char('\n')); + QTextCursor cursor = textCursor(); + cursor.beginEditBlock(); + int initialCursorPosition = cursor.position(); + int column = cursor.position() - cursor.block().position(); + cursor.insertText(lines.first()); + for (int i = 1; i < lines.count(); ++i) { + QTextBlock next = cursor.block().next(); + if (next.isValid()) { + cursor.setPosition(next.position() + qMin(column, next.length()-1)); + } else { + cursor.movePosition(QTextCursor::EndOfBlock); + cursor.insertBlock(); + } + + int actualColumn = cursor.position() - cursor.block().position(); + if (actualColumn < column) + cursor.insertText(QString(column - actualColumn, QLatin1Char(' '))); + cursor.insertText(lines.at(i)); + } + cursor.setPosition(initialCursorPosition); + cursor.endEditBlock(); + setTextCursor(cursor); + ensureCursorVisible(); + return; + } + QPlainTextEdit::insertFromMimeData(source); +} + +BaseTextEditorEditable::BaseTextEditorEditable(BaseTextEditor *editor) + : e(editor) +{ +#ifndef TEXTEDITOR_STANDALONE + using namespace Find; + Aggregation::Aggregate *aggregate = new Aggregation::Aggregate; + BaseTextFind *baseTextFind = new BaseTextFind(editor); + connect(baseTextFind, SIGNAL(highlightAll(QString, QTextDocument::FindFlags)), + editor, SLOT(highlightSearchResults(QString, QTextDocument::FindFlags))); + connect(baseTextFind, SIGNAL(findScopeChanged(QTextCursor)), editor, SLOT(setFindScope(QTextCursor))); + aggregate->add(baseTextFind); + aggregate->add(editor); +#endif + + m_cursorPositionLabel = new Core::Utils::LineColumnLabel; + + QHBoxLayout *l = new QHBoxLayout; + QWidget *w = new QWidget; + l->setMargin(0); + l->setContentsMargins(0, 0, 5, 0); + l->addStretch(1); + l->addWidget(m_cursorPositionLabel); + w->setLayout(l); + + m_toolBar = new QToolBar; + m_toolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + m_toolBar->addWidget(w); + + connect(editor, SIGNAL(cursorPositionChanged()), this, SLOT(updateCursorPosition())); +} + +BaseTextEditorEditable::~BaseTextEditorEditable() +{ + delete m_toolBar; + delete e; +} + +QToolBar *BaseTextEditorEditable::toolBar() +{ + return m_toolBar; +} + +int BaseTextEditorEditable::find(const QString &) const +{ + return 0; +} + +int BaseTextEditorEditable::currentLine() const +{ + return e->textCursor().blockNumber() + 1; +} + +int BaseTextEditorEditable::currentColumn() const +{ + QTextCursor cursor = e->textCursor(); + return cursor.position() - cursor.block().position() + 1; +} + +QRect BaseTextEditorEditable::cursorRect(int pos) const +{ + QTextCursor tc = e->textCursor(); + if (pos >= 0) + tc.setPosition(pos); + QRect result = e->cursorRect(tc); + result.moveTo(e->viewport()->mapToGlobal(result.topLeft())); + return result; +} + +QString BaseTextEditorEditable::contents() const +{ + return e->toPlainText(); +} + +QString BaseTextEditorEditable::selectedText() const +{ + if (e->textCursor().hasSelection()) + return e->textCursor().selectedText(); + return QString(); +} + +QString BaseTextEditorEditable::textAt(int pos, int length) const +{ + QTextCursor c = e->textCursor(); + + if (pos < 0) + pos = 0; + c.movePosition(QTextCursor::End); + if (pos + length > c.position()) + length = c.position() - pos; + + c.setPosition(pos); + c.setPosition(pos + length, QTextCursor::KeepAnchor); + + return c.selectedText(); +} + +void BaseTextEditorEditable::remove(int length) +{ + QTextCursor tc = e->textCursor(); + tc.setPosition(tc.position() + length, QTextCursor::KeepAnchor); + tc.removeSelectedText(); +} + +void BaseTextEditorEditable::insert(const QString &string) +{ + QTextCursor tc = e->textCursor(); + tc.insertText(string); +} + +void BaseTextEditorEditable::replace(int length, const QString &string) +{ + QTextCursor tc = e->textCursor(); + tc.setPosition(tc.position() + length, QTextCursor::KeepAnchor); + tc.insertText(string); +} + +void BaseTextEditorEditable::setCurPos(int pos) +{ + QTextCursor tc = e->textCursor(); + tc.setPosition(pos); + e->setTextCursor(tc); +} + +void BaseTextEditorEditable::select(int toPos) +{ + QTextCursor tc = e->textCursor(); + tc.setPosition(toPos, QTextCursor::KeepAnchor); + e->setTextCursor(tc); +} + +void BaseTextEditorEditable::updateCursorPosition() +{ + const QTextCursor cursor = e->textCursor(); + const QTextBlock block = cursor.block(); + const int line = block.blockNumber() + 1; + const int column = cursor.position() - block.position() + 1; + m_cursorPositionLabel->setText(QString("Line: %1, Col: %2").arg(line).arg(column), + QString("Line: %1, Col: 999").arg(e->blockCount())); + m_contextHelpId.clear(); + + if (!block.isVisible()) + e->ensureCursorVisible(); + +} + +QString BaseTextEditorEditable::contextHelpId() const +{ + if (m_contextHelpId.isEmpty()) + emit const_cast<BaseTextEditorEditable*>(this)->contextHelpIdRequested(e->editableInterface(), + e->textCursor().position()); + return m_contextHelpId; +} + + +TextBlockUserData::~TextBlockUserData() +{ + TextMarks marks = m_marks; + m_marks.clear(); + foreach (ITextMark *mrk, marks) { + mrk->removedFromEditor(); + } +} + diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h new file mode 100644 index 00000000000..f41e26b2b0a --- /dev/null +++ b/src/plugins/texteditor/basetexteditor.h @@ -0,0 +1,513 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BASETEXTEDITOR_H +#define BASETEXTEDITOR_H + +#include "displaysettings.h" +#include "tabsettings.h" +#include "itexteditable.h" + +#include <QtGui/QPlainTextEdit> +#include <QtGui/QLabel> +#include <QtGui/QKeyEvent> + +QT_BEGIN_NAMESPACE +class QLabel; +class QTextCharFormat; +class QToolBar; +QT_END_NAMESPACE + +namespace Core { + namespace Utils { + class LineColumnLabel; + } +} + +namespace TextEditor { + +namespace Internal { + class BaseTextEditorPrivate; +} + +class ITextMark; +class ITextMarkable; + +class TextEditorActionHandler; +class BaseTextDocument; +class FontSettings; +struct StorageSettings; + +struct Parenthesis; +typedef QVector<Parenthesis> Parentheses; +struct TEXTEDITOR_EXPORT Parenthesis +{ + enum Type { Opened, Closed }; + + inline Parenthesis() : type(Opened), pos(-1) {} + inline Parenthesis(Type t, QChar c, int position) + : type(t), chr(c), pos(position) {} + Type type; + QChar chr; + int pos; + static int collapseAtPos(const Parentheses &parentheses, QChar *character = 0); + static int closeCollapseAtPos(const Parentheses &parentheses); + static bool hasClosingCollapse(const Parentheses &parentheses); +}; + + + +class TEXTEDITOR_EXPORT TextBlockUserData : public QTextBlockUserData { +public: + + enum CollapseMode { NoCollapse , CollapseThis, CollapseAfter }; + enum ClosingCollapseMode { NoClosingCollapse, ClosingCollapse, ClosingCollapseAtEnd }; + + inline TextBlockUserData() + : m_collapseIncludesClosure(false), + m_collapseMode(NoCollapse), + m_closingCollapseMode(NoClosingCollapse), + m_collapsed(false), + m_ifdefedOut(false) {} + ~TextBlockUserData(); + + inline TextMarks marks() const { return m_marks; } + inline void addMark(ITextMark *mark) { m_marks += mark; } + inline bool removeMark(ITextMark *mark) { return m_marks.removeAll(mark); } + inline bool hasMark(ITextMark *mark) const { return m_marks.contains(mark); } + inline void clearMarks() { m_marks.clear(); } + inline void documentClosing() { Q_FOREACH(ITextMark *tm, m_marks) { tm->documentClosing(); } m_marks.clear();} + + inline CollapseMode collapseMode() const { return (CollapseMode)m_collapseMode; } + inline void setCollapseMode(CollapseMode c) { m_collapseMode = c; } + + inline void setClosingCollapseMode(ClosingCollapseMode c) { m_closingCollapseMode = c; } + inline ClosingCollapseMode closingCollapseMode() const { return (ClosingCollapseMode) m_closingCollapseMode; } + + inline bool hasClosingCollapse() const { return closingCollapseMode() != NoClosingCollapse; } + inline bool hasClosingCollapseAtEnd() const { return closingCollapseMode() == ClosingCollapseAtEnd; } + inline bool hasClosingCollapseInside() const { return closingCollapseMode() == ClosingCollapse; } + + inline void setCollapsed(bool b) { m_collapsed = b; } + inline bool collapsed() const { return m_collapsed; } + + inline void setCollapseIncludesClosure(bool b) { m_collapseIncludesClosure = b; } + inline bool collapseIncludesClosure() const { return m_collapseIncludesClosure; } + + inline void setParentheses(const Parentheses &parentheses) { m_parentheses = parentheses; } + inline void clearParentheses() { m_parentheses.clear(); } + inline const Parentheses &parentheses() const { return m_parentheses; } + inline bool hasParentheses() const { return !m_parentheses.isEmpty(); } + + inline bool setIfdefedOut() { bool result = m_ifdefedOut; m_ifdefedOut = true; return !result; } + inline bool clearIfdefedOut() { bool result = m_ifdefedOut; m_ifdefedOut = false; return result;} + inline bool ifdefedOut() const { return m_ifdefedOut; } + + inline static TextBlockUserData *canCollapse(const QTextBlock& block) { + TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); + if (!data || data->collapseMode() != CollapseAfter) { + data = static_cast<TextBlockUserData*>(block.next().userData()); + if (!data || data->collapseMode() != TextBlockUserData::CollapseThis) + data = 0; + } + return data; + } + + inline static bool hasClosingCollapse(const QTextBlock &block) { + TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); + return (data && data->hasClosingCollapse()); + } + + inline static bool hasClosingCollapseAtEnd(const QTextBlock &block) { + TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); + return (data && data->hasClosingCollapseAtEnd()); + } + + inline static bool hasClosingCollapseInside(const QTextBlock &block) { + TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); + return (data && data->hasClosingCollapseInside()); + } + + static QTextBlock testCollapse(const QTextBlock& block); + static void doCollapse(const QTextBlock& block, bool visible); + + int collapseAtPos() const; + + enum MatchType { NoMatch, Match, Mismatch }; + static MatchType checkOpenParenthesis(QTextCursor *cursor, QChar c); + static MatchType checkClosedParenthesis(QTextCursor *cursor, QChar c); + static MatchType matchCursorBackward(QTextCursor *cursor); + static MatchType matchCursorForward(QTextCursor *cursor); + + +private: + TextMarks m_marks; + uint m_collapseIncludesClosure : 1; + uint m_collapseMode : 4; + uint m_closingCollapseMode : 4; + uint m_collapsed : 1; + uint m_ifdefedOut : 1; + Parentheses m_parentheses; +}; + + +class TEXTEDITOR_EXPORT TextEditDocumentLayout : public QPlainTextDocumentLayout +{ + Q_OBJECT + +public: + TextEditDocumentLayout(QTextDocument *doc); + ~TextEditDocumentLayout(); + + QRectF blockBoundingRect(const QTextBlock &block) const; + + static void setParentheses(const QTextBlock &block, const Parentheses &parentheses); + static void clearParentheses(const QTextBlock &block) { setParentheses(block, Parentheses());} + static Parentheses parentheses(const QTextBlock &block); + static bool hasParentheses(const QTextBlock &block); + static bool setIfdefedOut(const QTextBlock &block); + static bool clearIfdefedOut(const QTextBlock &block); + static bool ifdefedOut(const QTextBlock &block); + + static TextBlockUserData *testUserData(const QTextBlock &block) { + return static_cast<TextBlockUserData*>(block.userData()); + } + static TextBlockUserData *userData(const QTextBlock &block) { + TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData()); + if (!data && block.isValid()) + const_cast<QTextBlock &>(block).setUserData((data = new TextBlockUserData)); + return data; + } + + + void emitDocumentSizeChanged() { emit documentSizeChanged(documentSize()); } + int lastSaveRevision; + bool hasMarks; +}; + + +class BaseTextEditorEditable; + +class TEXTEDITOR_EXPORT BaseTextEditor + : public QPlainTextEdit +{ + Q_OBJECT + +public: + BaseTextEditor(QWidget *parent); + ~BaseTextEditor(); + + static ITextEditor *openEditorAt(const QString &fileName, int line, int column = 0); + + // EditorInterface + Core::IFile * file(); + bool createNew(const QString &contents); + bool open(const QString &fileName = QString()); + QByteArray saveState() const; + bool restoreState(const QByteArray &state); + QString displayName() const; + + // ITextEditor + + void gotoLine(int line, int column = 0); + + int position( + ITextEditor::PositionOperation posOp = ITextEditor::Current + , int at = -1) const; + void convertPosition(int pos, int *line, int *column) const; + + ITextEditable *editableInterface() const; + ITextMarkable *markableInterface() const; + + virtual void triggerCompletions(); + + QChar characterAt(int pos) const; + + void print(QPrinter *); + + void setSuggestedFileName(const QString &suggestedFileName); + QString mimeType() const; + void setMimeType(const QString &mt); + + + // Works only in conjunction with a syntax highlighter that puts + // parentheses into text block user data + void setParenthesesMatchingEnabled(bool b); + bool isParenthesesMatchingEnabled() const; + + void setHighlightCurrentLine(bool b); + bool highlightCurrentLine() const; + + void setLineNumbersVisible(bool b); + bool lineNumbersVisible() const; + + void setMarksVisible(bool b); + bool marksVisible() const; + + void setRequestMarkEnabled(bool b); + bool requestMarkEnabled() const; + + void setLineSeparatorsAllowed(bool b); + bool lineSeparatorsAllowed() const; + + void setCodeFoldingVisible(bool b); + bool codeFoldingVisible() const; + + void setRevisionsVisible(bool b); + bool revisionsVisible() const; + + void setVisibleWrapColumn(int column); + int visibleWrapColumn() const; + + void setActionHack(QObject *); + QObject *actionHack() const; + + void setTextCodec(QTextCodec *codec); + QTextCodec *textCodec() const; + + void setReadOnly(bool b); + +public slots: + void setDisplayName(const QString &title); + virtual void setFontSettings(const TextEditor::FontSettings &); + virtual void format(); + virtual void unCommentSelection(); + virtual void setStorageSettings(const TextEditor::StorageSettings &); + + void cut(); + + void zoomIn(int range = 1); + void zoomOut(int range = 1); + + void deleteLine(); + void unCollapseAll(); + void collapse(); + void expand(); + void selectEncoding(); + +signals: + void changed(); + + // ITextEditor + void contentsChanged(); + +protected: + bool event(QEvent *e); + void keyPressEvent(QKeyEvent *e); + void wheelEvent(QWheelEvent *e); + void changeEvent(QEvent *e); + + // reimplemented to support block selection + QMimeData *createMimeDataFromSelection() const; + bool canInsertFromMimeData(const QMimeData *source) const; + void insertFromMimeData(const QMimeData *source); + +public: + void duplicateFrom(BaseTextEditor *editor); +protected: + BaseTextDocument *baseTextDocument() const; + void setBaseTextDocument(BaseTextDocument *doc); + + void setDefaultPath(const QString &defaultPath); + + virtual BaseTextEditorEditable *createEditableInterface() = 0; + +private slots: + void editorContentsChange(int position, int charsRemoved, int charsAdded); + void memorizeCursorPosition(); + void restoreCursorPosition(); + void highlightSearchResults(const QString &txt, QTextDocument::FindFlags findFlags); + void setFindScope(const QTextCursor &); + void setCollapseIndicatorAlpha(int); + void currentEditorChanged(Core::IEditor *editor); + +private: + Internal::BaseTextEditorPrivate *d; + friend class Internal::BaseTextEditorPrivate; + + +public: + QWidget *extraArea() const; + virtual int extraAreaWidth(int *markWidthPtr = 0) const; + virtual void extraAreaPaintEvent(QPaintEvent *); + virtual void extraAreaMouseEvent(QMouseEvent *); + virtual void extraAreaLeaveEvent(QEvent *); + + + const TabSettings &tabSettings() const; + const DisplaySettings &displaySettings() const; + + void markBlocksAsChanged(QList<int> blockNumbers); + + void ensureCursorVisible(); + + void setExtraExtraSelections(const QList<QTextEdit::ExtraSelection> &selections); + QList<QTextEdit::ExtraSelection> extraExtraSelections() const; + + struct BlockRange { + BlockRange():first(0), last(-1){} + BlockRange(int first_position, int last_position):first(first_position), last(last_position){} + int first; + int last; + inline bool isNull() const { return last < first; } + }; + + // the blocks list must be sorted + void setIfdefedOutBlocks(const QList<BaseTextEditor::BlockRange> &blocks); + + +public slots: + virtual void setTabSettings(const TextEditor::TabSettings &); + virtual void setDisplaySettings(const TextEditor::DisplaySettings &); + +protected: + bool viewportEvent(QEvent *event); + + void resizeEvent(QResizeEvent *); + void paintEvent(QPaintEvent *); + void timerEvent(QTimerEvent *); + void mouseMoveEvent(QMouseEvent *); + void mousePressEvent(QMouseEvent *); + + // Rertuns true if key triggers an indent. + virtual bool isElectricCharacter(const QChar &ch) const; + // Indent a text block based on previous line. Default does nothing + virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar); + // Indent at cursor. Calls indentBlock for selection or current line. + virtual void indent(QTextDocument *doc, const QTextCursor &cursor, QChar typedChar); + + +protected slots: + virtual void slotUpdateExtraAreaWidth(); + virtual void slotModificationChanged(bool); + virtual void slotUpdateRequest(const QRect &r, int dy); + virtual void slotCursorPositionChanged(); + virtual void slotUpdateBlockNotify(const QTextBlock &); + + + +signals: + void markRequested(TextEditor::ITextEditor *editor, int line); + void requestBlockUpdate(const QTextBlock &); + void requestAutoCompletion(ITextEditable *editor, bool forced); + +private: + void indentOrUnindent(bool doIndent); + void handleHomeKey(bool anchor); + void handleBackspaceKey(); + + void toggleBlockVisible(const QTextBlock &block); + QRect collapseBox(const QTextBlock &block); + + QTextBlock collapsedBlockAt(const QPoint &pos, QRect *box = 0) const; + + // parentheses matcher +private slots: + void _q_matchParentheses(); + void slotSelectionChanged(); +}; + + +class TEXTEDITOR_EXPORT BaseTextEditorEditable + : public ITextEditable +{ + Q_OBJECT + friend class BaseTextEditor; +public: + BaseTextEditorEditable(BaseTextEditor *editor); + ~BaseTextEditorEditable(); + + inline BaseTextEditor *editor() const { return e; } + + // EditorInterface + inline QWidget *widget() { return e; } + inline Core::IFile * file() { return e->file(); } + inline bool createNew(const QString &contents) { return e->createNew(contents); } + inline bool open(const QString &fileName = QString()) + { + return e->open(fileName); + } + inline QString displayName() const { return e->displayName(); } + inline void setDisplayName(const QString &title) { e->setDisplayName(title); } + + inline QByteArray saveState() const { return e->saveState(); } + inline bool restoreState(const QByteArray &state) { return e->restoreState(state); } + QToolBar *toolBar(); + + // ITextEditor + int find(const QString &string) const; + + int currentLine() const; + int currentColumn() const; + inline void gotoLine(int line, int column = 0) { e->gotoLine(line, column); } + + inline int position( + ITextEditor::PositionOperation posOp = ITextEditor::Current + , int at = -1) const { return e->position(posOp, at); } + inline void convertPosition(int pos, int *line, int *column) const { e->convertPosition(pos, line, column); } + QRect cursorRect(int pos = -1) const; + + QString contents() const; + QString selectedText() const; + QString textAt(int pos, int length) const; + inline QChar characterAt(int pos) const { return e->characterAt(pos); } + + inline void triggerCompletions() { e->triggerCompletions(); } // slot? + inline ITextMarkable *markableInterface() { return e->markableInterface(); } + + void setContextHelpId(const QString &id) { m_contextHelpId = id; } + QString contextHelpId() const; // from IContext + + inline void setTextCodec(QTextCodec *codec) { e->setTextCodec(codec); } + inline QTextCodec *textCodec() const { return e->textCodec(); } + + + // ITextEditable + void remove(int length); + void insert(const QString &string); + void replace(int length, const QString &string); + void setCurPos(int pos); + void select(int toPos); + +private slots: + void updateCursorPosition(); + +private: + BaseTextEditor *e; + mutable QString m_contextHelpId; + QToolBar *m_toolBar; + Core::Utils::LineColumnLabel *m_cursorPositionLabel; +}; + +} // namespace TextEditor + +#endif // BASETEXTEDITOR_H diff --git a/src/plugins/texteditor/basetexteditor_p.h b/src/plugins/texteditor/basetexteditor_p.h new file mode 100644 index 00000000000..4bb0de70701 --- /dev/null +++ b/src/plugins/texteditor/basetexteditor_p.h @@ -0,0 +1,225 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BASETEXTEDITOR_P_H +#define BASETEXTEDITOR_P_H + +#include "basetexteditor.h" + +#include <QtCore/QBasicTimer> +#include <QtCore/QTimeLine> +#include <QtCore/QSharedData> + +#include <QtGui/QTextEdit> +#include <QtGui/QPixmap> + +namespace TextEditor { + +class BaseTextDocument; + +namespace Internal { + +//========== Pointers with reference count ========== + +template <class T> class QRefCountData : public QSharedData +{ +public: + QRefCountData(T *data) { m_data = data; } + + ~QRefCountData() { delete m_data; } + + T *m_data; +}; + +/* MOSTLY COPIED FROM QSHAREDDATA(-POINTER) */ +template <class T> class QRefCountPointer +{ +public: + inline T &operator*() { return d ? *(d->m_data) : 0; } + inline const T &operator*() const { return d ? *(d->m_data) : 0; } + inline T *operator->() { return d ? d->m_data : 0; } + inline const T *operator->() const { return d ? d->m_data : 0; } + inline operator T *() { return d ? d->m_data : 0; } + inline operator const T *() const { return d ? d->m_data : 0; } + + inline bool operator==(const QRefCountPointer<T> &other) const { return d == other.d; } + inline bool operator!=(const QRefCountPointer<T> &other) const { return d != other.d; } + + inline QRefCountPointer() { d = 0; } + inline ~QRefCountPointer() { if (d && !d->ref.deref()) delete d; } + + explicit QRefCountPointer(T *data) { + if (data) { + d = new QRefCountData<T>(data); + d->ref.ref(); + } + else { + d = 0; + } + } + inline QRefCountPointer(const QRefCountPointer<T> &o) : d(o.d) { if (d) d->ref.ref(); } + inline QRefCountPointer<T> & operator=(const QRefCountPointer<T> &o) { + if (o.d != d) { + if (d && !d->ref.deref()) + delete d; + //todo: atomic assign of pointers + d = o.d; + if (d) + d->ref.ref(); + } + return *this; + } + inline QRefCountPointer &operator=(T *o) { + if (d == 0 || d->m_data != o) { + if (d && !d->ref.deref()) + delete d; + d = new QRefCountData<T>(o); + if (d) + d->ref.ref(); + } + return *this; + } + + inline bool operator!() const { return !d; } + +private: + QRefCountData<T> *d; +}; + +//================BaseTextEditorPrivate============== + +class BaseTextEditorPrivate +{ + BaseTextEditorPrivate(const BaseTextEditorPrivate &); + BaseTextEditorPrivate &operator=(const BaseTextEditorPrivate &); + +public: + BaseTextEditorPrivate(); + ~BaseTextEditorPrivate(); + +#ifndef TEXTEDITOR_STANDALONE + void setupBasicEditActions(TextEditorActionHandler *actionHandler); +#endif + void setupDocumentSignals(BaseTextDocument *document); + void updateLineSelectionColor(); +#ifndef TEXTEDITOR_STANDALONE + bool needMakeWritableCheck() const; +#endif + + void print(QPrinter *printer); + + QTextBlock m_firstVisible; + int m_lastScrollPos; + int m_lineNumber; + + BaseTextEditor *q; + bool m_contentsChanged; + + QList<QTextEdit::ExtraSelection> m_syntaxHighlighterSelections; + QTextEdit::ExtraSelection m_lineSelection; + + QRefCountPointer<BaseTextDocument> m_document; + QByteArray m_tempState; + + QString m_displayName; + bool m_parenthesesMatchingEnabled; + QTimer *m_updateTimer; + + // parentheses matcher + bool m_formatRange; + QTextCharFormat m_matchFormat; + QTextCharFormat m_mismatchFormat; + QTextCharFormat m_rangeFormat; + QTimer *m_parenthesesMatchingTimer; + // end parentheses matcher + + QWidget *m_extraArea; + DisplaySettings m_displaySettings; + + int extraAreaSelectionAnchorBlockNumber; + int extraAreaToggleMarkBlockNumber; + int extraAreaHighlightCollapseBlockNumber; + int extraAreaCollapseAlpha; + int extraAreaHighlightFadingBlockNumber; + QTimeLine *extraAreaTimeLine; + + QBasicTimer collapsedBlockTimer; + int visibleCollapsedBlockNumber; + int suggestedVisibleCollapsedBlockNumber; + void clearVisibleCollapsedBlock(); + + QBasicTimer autoScrollTimer; + void updateMarksLineNumber(); + void updateMarksBlock(const QTextBlock &block); + uint m_marksVisible : 1; + uint m_codeFoldingVisible : 1; + uint m_revisionsVisible : 1; + uint m_lineNumbersVisible : 1; + uint m_highlightCurrentLine : 1; + uint m_requestMarkEnabled : 1; + uint m_lineSeparatorsAllowed : 1; + int m_visibleWrapColumn; + + QTextCharFormat m_ifdefedOutFormat; + + QRegExp m_searchExpr; + QTextDocument::FindFlags m_findFlags; + QTextCharFormat m_searchResultFormat; + QTextCharFormat m_searchScopeFormat; + QTextCharFormat m_currentLineFormat; + void highlightSearchResults(const QTextBlock &block, + QVector<QTextLayout::FormatRange> *selections); + + BaseTextEditorEditable *m_editable; + + QObject *m_actionHack; + + QList<QTextEdit::ExtraSelection> m_extraSelections; + QList<QTextEdit::ExtraSelection> m_extraExtraSelections; + + // block selection mode + bool m_inBlockSelectionMode; + bool m_lastEventWasBlockSelectionEvent; + int m_blockSelectionExtraX; + void clearBlockSelection(); + QString copyBlockSelection(); + void removeBlockSelection(const QString &text = QString()); + + QTextCursor m_findScope; + + void moveCursorVisible(); +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // BASETEXTEDITOR_P_H diff --git a/src/plugins/texteditor/basetextmark.cpp b/src/plugins/texteditor/basetextmark.cpp new file mode 100644 index 00000000000..4110480332e --- /dev/null +++ b/src/plugins/texteditor/basetextmark.cpp @@ -0,0 +1,136 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "basetextmark.h" +#include <coreplugin/editormanager/editormanager.h> +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/icore.h> +#include <QtCore/QTimer> + +using namespace TextEditor; +using namespace TextEditor::Internal; + +BaseTextMark::BaseTextMark() + : m_markableInterface(0), m_internalMark(0), m_init(false) +{ + +} + +BaseTextMark::BaseTextMark(const QString &filename, int line) + : m_markableInterface(0), m_internalMark(0), m_fileName(filename), m_line(line), m_init(false) +{ + // Why is this? + QTimer::singleShot(0, this, SLOT(init())); +} + +void BaseTextMark::init() +{ + m_init = true; + Core::EditorManager *em = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()->editorManager(); + connect(em, SIGNAL(editorOpened(Core::IEditor *)), this, SLOT(editorOpened(Core::IEditor *))); + + foreach(Core::IEditor *editor, em->openedEditors()) { + editorOpened(editor); + } +} + +void BaseTextMark::editorOpened(Core::IEditor *editor) +{ + if (editor->file()->fileName() != m_fileName) + return; + if (ITextEditor *textEditor = qobject_cast<ITextEditor *>(editor)) { + if (m_markableInterface == 0) { // We aren't added to something + m_markableInterface = textEditor->markableInterface(); + m_internalMark = new InternalMark(this); + m_markableInterface->addMark(m_internalMark, m_line); + } + } +} + +void BaseTextMark::childRemovedFromEditor(InternalMark *mark) +{ + Q_UNUSED(mark) + // m_internalMark was removed from the editor + delete m_internalMark; + m_markableInterface = 0; + m_internalMark = 0; + removedFromEditor(); +} + +void BaseTextMark::documentClosingFor(InternalMark *mark) +{ + Q_UNUSED(mark) + // the document is closing + delete m_internalMark; + m_markableInterface = 0; + m_internalMark = 0; +} + +BaseTextMark::~BaseTextMark() +{ + // oha we are deleted + if (m_markableInterface) + m_markableInterface->removeMark(m_internalMark); + delete m_internalMark; + m_internalMark = 0; + m_markableInterface = 0; +} + +//#include <QDebug> + +void BaseTextMark::updateMarker() +{ + //qDebug()<<"BaseTextMark::updateMarker()"<<m_markableInterface<<m_internalMark; + if (m_markableInterface) + m_markableInterface->updateMark(m_internalMark); +} + +void BaseTextMark::moveMark(const QString & /* filename */, int /* line */) +{ + Core::EditorManager *em = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()->editorManager(); + if (!m_init) { + connect(em, SIGNAL(editorOpened(Core::IEditor *)), this, SLOT(editorOpened(Core::IEditor *))); + m_init = true; + } + + + if (m_markableInterface) + m_markableInterface->removeMark(m_internalMark); + m_markableInterface = 0; + // This is only necessary since m_internalMark is created in ediorOpened + delete m_internalMark; + m_internalMark = 0; + + foreach(Core::IEditor *editor, em->openedEditors()) { + editorOpened(editor); + } +} diff --git a/src/plugins/texteditor/basetextmark.h b/src/plugins/texteditor/basetextmark.h new file mode 100644 index 00000000000..763e3eec47d --- /dev/null +++ b/src/plugins/texteditor/basetextmark.h @@ -0,0 +1,132 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BASETEXTMARK_H +#define BASETEXTMARK_H + +#include "itexteditor.h" + +namespace TextEditor { + +class ITextMarkable; + +namespace Internal { +class InternalMark; +} + +class TEXTEDITOR_EXPORT BaseTextMark : public QObject +{ + friend class Internal::InternalMark; + Q_OBJECT +public: + BaseTextMark(); + BaseTextMark(const QString &filename, int line); + ~BaseTextMark(); + + // return your icon here + virtual QIcon icon() const = 0; + + // called if the linenumber changes + virtual void updateLineNumber(int lineNumber) = 0; + + // called whenever the text of the block for the marker changed + virtual void updateBlock(const QTextBlock &block) = 0; + + // called if the block containing this mark has been removed + // if this also removes your mark call this->deleteLater(); + virtual void removedFromEditor() = 0; + // call this if the icon has changed. + void updateMarker(); + // access to internal data + QString fileName() const { return m_fileName; } + int lineNumber() const { return m_line; } + + void moveMark(const QString &filename, int line); +private slots: + void editorOpened(Core::IEditor *editor); + void init(); +private: + void childRemovedFromEditor(Internal::InternalMark *mark); + void documentClosingFor(Internal::InternalMark *mark); + + ITextMarkable *m_markableInterface; + Internal::InternalMark *m_internalMark; + + QString m_fileName; + int m_line; + bool m_init; +}; + +namespace Internal { + +class InternalMark : public ITextMark +{ +public: + InternalMark(BaseTextMark *parent) + : m_parent(parent) + { + } + + ~InternalMark() + { + } + + virtual QIcon icon() const + { + return m_parent->icon(); + } + + virtual void updateLineNumber(int lineNumber) + { + return m_parent->updateLineNumber(lineNumber); + } + + virtual void updateBlock(const QTextBlock &block) + { + return m_parent->updateBlock(block); + } + + virtual void removedFromEditor() + { + m_parent->childRemovedFromEditor(this); + } + + virtual void documentClosing() + { + m_parent->documentClosingFor(this); + } +private: + BaseTextMark *m_parent; +}; +} +} +#endif // BASETEXTMARK_H diff --git a/src/plugins/texteditor/codecselector.cpp b/src/plugins/texteditor/codecselector.cpp new file mode 100644 index 00000000000..5893fa5b71d --- /dev/null +++ b/src/plugins/texteditor/codecselector.cpp @@ -0,0 +1,179 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "codecselector.h" +#include "basetextdocument.h" + + +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtCore/QTextCodec> +#include <QtGui/QPushButton> +#include <QtGui/QScrollBar> +#include <QtGui/QVBoxLayout> + +using namespace TextEditor; +using namespace TextEditor::Internal; + + +namespace TextEditor { + namespace Internal { + + /* custom class to make sure the width is wide enough for the + * contents. Should be easier with Qt. */ + class CodecListWidget : public QListWidget { + public: + CodecListWidget(QWidget *parent):QListWidget(parent){} + QSize sizeHint() const { + return QListWidget::sizeHint().expandedTo( + QSize(sizeHintForColumn(0) + verticalScrollBar()->sizeHint().width() + 4, 0)); + } + }; + } +} + +CodecSelector::CodecSelector(QWidget *parent, BaseTextDocument *doc) + : QDialog(parent) +{ + m_hasDecodingError = doc->hasDecodingError(); + m_isModified = doc->isModified(); + + QByteArray buf; + if (m_hasDecodingError) + buf = doc->decodingErrorSample(); + + setWindowTitle(tr("Text Encoding")); + m_label = new QLabel(this); + QString decodingErrorHint; + if (m_hasDecodingError) + decodingErrorHint = tr("\nThe following encodings are likely to fit:"); + m_label->setText(tr("Select encoding for \"%1\".%2").arg(QFileInfo(doc->fileName()).fileName()).arg(decodingErrorHint)); + + m_listWidget = new CodecListWidget(this); + + QStringList encodings; + + QList<int> mibs = QTextCodec::availableMibs(); + qSort(mibs); + QList<int> sortedMibs; + foreach(int mib, mibs) + if (mib >= 0) + sortedMibs += mib; + foreach(int mib, mibs) + if (mib < 0) + sortedMibs += mib; + + int currentIndex = -1; + foreach(int mib, sortedMibs) { + QTextCodec *c = QTextCodec::codecForMib(mib); + if (!buf.isEmpty()) { + + // slow, should use a feature from QTextCodec or QTextDecoder (but those are broken currently) + QByteArray verifyBuf = c->fromUnicode(c->toUnicode(buf)); + // the minSize trick lets us ignore unicode headers + int minSize = qMin(verifyBuf.size(), buf.size()); + if (minSize < buf.size() - 4 + || memcmp(verifyBuf.constData() + verifyBuf.size() - minSize, + buf.constData() + buf.size() - minSize, minSize)) + continue; + } + QString names = QString::fromLatin1(c->name()); + foreach(QByteArray alias, c->aliases()) { + names += QLatin1String(" / ") + QString::fromLatin1(alias); + } + if (doc->codec() == c) + currentIndex = encodings.count(); + encodings << names; + } + m_listWidget->addItems(encodings); + if (currentIndex >= 0) + m_listWidget->setCurrentRow(currentIndex); + + connect(m_listWidget, SIGNAL(itemSelectionChanged()), this, SLOT(updateButtons())); + + m_dialogButtonBox = new QDialogButtonBox(this); + m_reloadButton = m_dialogButtonBox->addButton(tr("Reload with Encoding"), QDialogButtonBox::DestructiveRole); + m_saveButton = m_dialogButtonBox->addButton(tr("Save with Encoding"), QDialogButtonBox::DestructiveRole); + m_dialogButtonBox->addButton(QDialogButtonBox::Cancel); + connect(m_dialogButtonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*))); + + QVBoxLayout *vbox = new QVBoxLayout(this); + vbox->addWidget(m_label); + vbox->addWidget(m_listWidget); + vbox->addWidget(m_dialogButtonBox); + + updateButtons(); +} + + + +CodecSelector::~CodecSelector() +{ +} + +void CodecSelector::updateButtons() +{ + bool hasCodec = (selectedCodec() != 0); + m_reloadButton->setEnabled(!m_isModified && hasCodec); + m_saveButton->setEnabled(!m_hasDecodingError && hasCodec); +} + +QTextCodec *CodecSelector::selectedCodec() const +{ + if (QListWidgetItem *item = m_listWidget->currentItem()) { + if (!item->isSelected()) + return 0; + QString codecName = item->text(); + if (codecName.contains(QLatin1String(" / "))) + codecName = codecName.left(codecName.indexOf(QLatin1String(" / "))); + return QTextCodec::codecForName(codecName.toLatin1()); + } + return 0; +} + + +CodecSelector::Result CodecSelector::exec() +{ + return (Result) QDialog::exec(); +} + + +void CodecSelector::buttonClicked(QAbstractButton *button) +{ + Result result = Cancel; + if (button == m_reloadButton) + result = Reload; + if (button == m_saveButton) + result = Save; + done(result); +} + diff --git a/src/plugins/texteditor/codecselector.h b/src/plugins/texteditor/codecselector.h new file mode 100644 index 00000000000..aa873b3a2b1 --- /dev/null +++ b/src/plugins/texteditor/codecselector.h @@ -0,0 +1,85 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CODECSELECTOR_H +#define CODECSELECTOR_H + +#include <QtGui/QDialog> +#include <QtGui/QLabel> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QListWidget> + +namespace TextEditor { + +class BaseTextDocument; + +namespace Internal { + +class CodecSelector : public QDialog +{ + Q_OBJECT + +public: + + CodecSelector(QWidget *parent, BaseTextDocument *doc); + ~CodecSelector(); + + QTextCodec *selectedCodec() const; + + enum Result { + Cancel, Reload, Save + }; + + Result exec(); + +private slots: + void updateButtons(); + void buttonClicked(QAbstractButton *button); + +private: + bool m_hasDecodingError; + bool m_isModified; + QLabel *m_label; + QListWidget *m_listWidget; + QDialogButtonBox *m_dialogButtonBox; + QAbstractButton *m_reloadButton; + QAbstractButton *m_saveButton; +}; + + + + + +} +} + +#endif // CODECSELECTOR_H diff --git a/src/plugins/texteditor/completionsupport.cpp b/src/plugins/texteditor/completionsupport.cpp new file mode 100644 index 00000000000..6c9972d7ebe --- /dev/null +++ b/src/plugins/texteditor/completionsupport.cpp @@ -0,0 +1,171 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "completionsupport.h" +#include "completionwidget.h" +#include "icompletioncollector.h" + +#include <coreplugin/icore.h> +#include <texteditor/itexteditable.h> + +#include <QString> +#include <QList> + +using namespace TextEditor; +using namespace TextEditor::Internal; + + +CompletionSupport *CompletionSupport::instance(Core::ICore *core) +{ + static CompletionSupport *m_instance = 0; + if (!m_instance) { + m_instance = new CompletionSupport(core); + } + return m_instance; +} + +CompletionSupport::CompletionSupport(Core::ICore *core) + : QObject(core), + m_completionList(0), + m_startPosition(0), + m_checkCompletionTrigger(false), + m_editor(0) +{ + m_completionCollector = core->pluginManager()->getObject<ICompletionCollector>(); +} + +void CompletionSupport::performCompletion(const CompletionItem &item) +{ + item.m_collector->complete(item); + m_checkCompletionTrigger = true; +} + +void CompletionSupport::cleanupCompletions() +{ + if (m_completionList) + disconnect(m_completionList, SIGNAL(destroyed(QObject*)), + this, SLOT(cleanupCompletions())); + + m_completionList = 0; + m_completionCollector->cleanup(); + + if (m_checkCompletionTrigger) { + m_checkCompletionTrigger = false; + + // Only check for completion trigger when some text was entered + if (m_editor->position() > m_startPosition) + autoComplete(m_editor, false); + } +} + +void CompletionSupport::autoComplete(ITextEditable *editor, bool forced) +{ + if (!m_completionCollector) + return; + + m_editor = editor; + QList<CompletionItem> completionItems; + + if (!m_completionList) { + if (!forced && !m_completionCollector->triggersCompletion(editor)) + return; + + m_startPosition = m_completionCollector->startCompletion(editor); + completionItems = getCompletions(); + + Q_ASSERT(m_startPosition != -1 || completionItems.size() == 0); + + if (completionItems.isEmpty()) { + cleanupCompletions(); + return; + } + + m_completionList = new CompletionWidget(this, editor); + + connect(m_completionList, SIGNAL(itemSelected(TextEditor::CompletionItem)), + this, SLOT(performCompletion(TextEditor::CompletionItem))); + connect(m_completionList, SIGNAL(completionListClosed()), + this, SLOT(cleanupCompletions())); + + // Make sure to clean up the completions if the list is destroyed without + // emitting completionListClosed (can happen when no focus out event is received, + // for example when switching applications on the Mac) + connect(m_completionList, SIGNAL(destroyed(QObject*)), + this, SLOT(cleanupCompletions())); + } else { + completionItems = getCompletions(); + + if (completionItems.isEmpty()) { + m_completionList->closeList(); + return; + } + } + + m_completionList->setCompletionItems(completionItems); + + // Partially complete when completion was forced + if (forced && m_completionCollector->partiallyComplete(completionItems)) { + m_checkCompletionTrigger = true; + m_completionList->closeList(); + } else { + m_completionList->showCompletions(m_startPosition); + } +} + +static bool completionItemLessThan(const CompletionItem &i1, const CompletionItem &i2) +{ + // The order is case-insensitive in principle, but case-sensitive when this would otherwise mean equality + const int c = i1.m_text.compare(i2.m_text, Qt::CaseInsensitive); + return c ? c < 0 : i1.m_text < i2.m_text; +} + +QList<CompletionItem> CompletionSupport::getCompletions() const +{ + QList<CompletionItem> completionItems; + + m_completionCollector->completions(&completionItems); + + qStableSort(completionItems.begin(), completionItems.end(), completionItemLessThan); + + // Remove duplicates + QString lastKey; + QList<CompletionItem> uniquelist; + + foreach (const CompletionItem item, completionItems) { + if (item.m_text != lastKey) { + uniquelist.append(item); + lastKey = item.m_text; + } + } + + return uniquelist; +} diff --git a/src/plugins/texteditor/completionsupport.h b/src/plugins/texteditor/completionsupport.h new file mode 100644 index 00000000000..d3083630b66 --- /dev/null +++ b/src/plugins/texteditor/completionsupport.h @@ -0,0 +1,83 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef COMPLETIONSUPPORT_H +#define COMPLETIONSUPPORT_H + +#include <texteditor/texteditor_global.h> +#include <QtCore/QObject> + +namespace Core { class ICore; } + +namespace TextEditor { + +struct CompletionItem; +class ICompletionCollector; +class ITextEditable; + +namespace Internal { + +class CompletionWidget; + +/* Completion support is responsible for querying the list of completion collectors + and popping up the CompletionWidget with the available completions. + */ +class TEXTEDITOR_EXPORT CompletionSupport : public QObject +{ + Q_OBJECT + +public: + CompletionSupport(Core::ICore *core); + + static CompletionSupport *instance(Core::ICore *core); +public slots: + void autoComplete(ITextEditable *editor, bool forced); + +private slots: + void performCompletion(const TextEditor::CompletionItem &item); + void cleanupCompletions(); + +private: + QList<CompletionItem> getCompletions() const; + + CompletionWidget *m_completionList; + int m_startPosition; + bool m_checkCompletionTrigger; // Whether to check for completion trigger after cleanup + ITextEditable *m_editor; + ICompletionCollector *m_completionCollector; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // COMPLETIONSUPPORT_H + diff --git a/src/plugins/texteditor/completionwidget.cpp b/src/plugins/texteditor/completionwidget.cpp new file mode 100644 index 00000000000..939123a5cf1 --- /dev/null +++ b/src/plugins/texteditor/completionwidget.cpp @@ -0,0 +1,264 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "completionwidget.h" +#include "completionsupport.h" +#include "icompletioncollector.h" + +#include <texteditor/itexteditable.h> + +#include <QtCore/QEvent> +#include <QtGui/QKeyEvent> +#include <QtGui/QApplication> +#include <QtGui/QVBoxLayout> + +#include <limits.h> + +using namespace TextEditor; +using namespace TextEditor::Internal; + +#define NUMBER_OF_VISIBLE_ITEMS 10 + +class AutoCompletionModel : public QAbstractListModel +{ +public: + AutoCompletionModel(QObject *parent, const QList<CompletionItem> &items); + + inline const CompletionItem &itemAt(const QModelIndex &index) const + { return m_items.at(index.row()); } + + void setItems(const QList<CompletionItem> &items); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +private: + QList<CompletionItem> m_items; +}; + +AutoCompletionModel::AutoCompletionModel(QObject *parent, const QList<CompletionItem> &items) + : QAbstractListModel(parent) +{ + m_items = items; +} + +void AutoCompletionModel::setItems(const QList<CompletionItem> &items) +{ + m_items = items; + reset(); +} + +int AutoCompletionModel::rowCount(const QModelIndex &) const +{ + return m_items.count(); +} + +QVariant AutoCompletionModel::data(const QModelIndex &index, int role) const +{ + if (index.row() >= m_items.count()) + return QVariant(); + + if (role == Qt::DisplayRole) { + return itemAt(index).m_text; + } else if (role == Qt::DecorationRole) { + return itemAt(index).m_icon; + } else if (role == Qt::ToolTipRole) { + return itemAt(index).m_details; + } + + return QVariant(); +} + +CompletionWidget::CompletionWidget(CompletionSupport *support, ITextEditable *editor) + : QListView(), + m_blockFocusOut(false), + m_editor(editor), + m_editorWidget(editor->widget()), + m_model(0), + m_support(support) +{ + Q_ASSERT(m_editorWidget); + + setUniformItemSizes(true); + setSelectionBehavior(QAbstractItemView::SelectItems); + setSelectionMode(QAbstractItemView::SingleSelection); + + connect(this, SIGNAL(activated(const QModelIndex &)), + this, SLOT(completionActivated(const QModelIndex &))); + + // We disable the frame on this list view and use a QFrame around it instead. + // This fixes the missing frame on Mac and improves the look with QGTKStyle. + m_popupFrame = new QFrame(0, Qt::Popup); + m_popupFrame->setFrameStyle(frameStyle()); + setFrameStyle(QFrame::NoFrame); + setParent(m_popupFrame); + m_popupFrame->setObjectName("m_popupFrame"); + m_popupFrame->setAttribute(Qt::WA_DeleteOnClose); + QVBoxLayout *layout = new QVBoxLayout(m_popupFrame); + layout->setMargin(0); + layout->addWidget(this); + + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +} + +bool CompletionWidget::event(QEvent *e) +{ + if (m_blockFocusOut) + return QListView::event(e); + + bool forwardKeys = true; + if (e->type() == QEvent::FocusOut) { + closeList(); + return true; + } else if (e->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast<QKeyEvent *>(e); + switch (ke->key()) { + case Qt::Key_Escape: + closeList(); + return true; + case Qt::Key_Right: + case Qt::Key_Left: + break; + case Qt::Key_Tab: + case Qt::Key_Return: + //independently from style, accept current entry if return is pressed + closeList(currentIndex()); + return true; + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Enter: + case Qt::Key_PageDown: + case Qt::Key_PageUp: + forwardKeys = false; + break; + default: + break; + } + + if (forwardKeys) { + m_blockFocusOut = true; + QApplication::sendEvent(m_editorWidget, e); + m_blockFocusOut = false; + + // Have the completion support update the list of items + m_support->autoComplete(m_editor, false); + + return true; + } + } + return QListView::event(e); +} + +void CompletionWidget::keyboardSearch(const QString &search) +{ + Q_UNUSED(search); +} + +void CompletionWidget::closeList(const QModelIndex &index) +{ + m_blockFocusOut = true; + if (index.isValid()) + emit itemSelected(m_model->itemAt(index)); + + close(); + if (m_popupFrame) { + m_popupFrame->close(); + m_popupFrame = 0; + } + + emit completionListClosed(); + + m_blockFocusOut = false; +} + +void CompletionWidget::setCompletionItems(const QList<TextEditor::CompletionItem> &completionItems) +{ + if (!m_model) { + m_model = new AutoCompletionModel(this, completionItems); + setModel(m_model); + } else { + m_model->setItems(completionItems); + } + + // Select the first of the most relevant completion items + int relevance = INT_MIN; + int mostRelevantIndex = 0; + for (int i = 0; i < completionItems.size(); ++i) { + const CompletionItem &item = completionItems.at(i); + if (item.m_relevance > relevance) { + relevance = item.m_relevance; + mostRelevantIndex = i; + } + } + + setCurrentIndex(m_model->index(mostRelevantIndex)); +} + +void CompletionWidget::showCompletions(int startPos) +{ + const QPoint &pos = m_editor->cursorRect(startPos).bottomLeft(); + m_popupFrame->move(pos.x() - 16, pos.y()); + m_popupFrame->setMinimumSize(1, 1); + setMinimumSize(1, 1); + + updateSize(); + + m_popupFrame->show(); + show(); + setFocus(); +} + +void CompletionWidget::updateSize() +{ + int visibleItems = m_model->rowCount(); + if (visibleItems > NUMBER_OF_VISIBLE_ITEMS) + visibleItems = NUMBER_OF_VISIBLE_ITEMS; + + const QStyleOptionViewItem &option = viewOptions(); + + QSize shint; + for (int i = 0; i < visibleItems; ++i) { + QSize tmp = itemDelegate()->sizeHint(option, m_model->index(i)); + if (shint.width() < tmp.width()) + shint = tmp; + } + + const int width = (shint.width() + (m_popupFrame->frameWidth() * 2) + 30); + const int height = (shint.height() * visibleItems) + m_popupFrame->frameWidth() * 2; + + m_popupFrame->resize(width, height); +} + +void CompletionWidget::completionActivated(const QModelIndex &index) +{ + closeList(index); +} diff --git a/src/plugins/texteditor/completionwidget.h b/src/plugins/texteditor/completionwidget.h new file mode 100644 index 00000000000..4a41ffad344 --- /dev/null +++ b/src/plugins/texteditor/completionwidget.h @@ -0,0 +1,90 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef COMPLETIONWIDGET_H +#define COMPLETIONWIDGET_H + +#include <QtGui/QListView> +#include <QPointer> + +class AutoCompletionModel; + +namespace TextEditor { + +struct CompletionItem; +class ITextEditable; + +namespace Internal { + +class CompletionSupport; + +/* The completion widget is responsible for showing a list of possible completions. + It is only used by the CompletionSupport. + */ +class CompletionWidget : public QListView +{ + Q_OBJECT + +public: + CompletionWidget(CompletionSupport *support, ITextEditable *editor); + + void setCompletionItems(const QList<TextEditor::CompletionItem> &completionitems); + void showCompletions(int startPos); + void keyboardSearch(const QString &search); + void closeList(const QModelIndex &index = QModelIndex()); + +protected: + bool event(QEvent *e); + +signals: + void itemSelected(const TextEditor::CompletionItem &item); + void completionListClosed(); + +private slots: + void completionActivated(const QModelIndex &index); + +private: + void updateSize(); + + QPointer<QFrame> m_popupFrame; + bool m_blockFocusOut; + ITextEditable *m_editor; + QWidget *m_editorWidget; + AutoCompletionModel *m_model; + CompletionSupport *m_support; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // COMPLETIONWIDGET_H + diff --git a/src/plugins/texteditor/displaysettings.cpp b/src/plugins/texteditor/displaysettings.cpp new file mode 100644 index 00000000000..af74e4b2ab8 --- /dev/null +++ b/src/plugins/texteditor/displaysettings.cpp @@ -0,0 +1,108 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "displaysettings.h" + +#include <QtCore/QSettings> +#include <QtCore/QString> +#include <QtGui/QTextCursor> +#include <QtGui/QTextDocument> + +static const char * const displayLineNumbersKey = "DisplayLineNumbers"; +static const char * const textWrappingKey = "TextWrapping"; +static const char * const showWrapColumnKey = "ShowWrapColumn"; +static const char * const wrapColumnKey = "WrapColumn"; +static const char * const visualizeWhitespaceKey = "VisualizeWhitespace"; +static const char * const displayFoldingMarkersKey = "DisplayFoldingMarkers"; +static const char * const highlightCurrentLineKey = "HighlightCurrentLineKey"; +static const char * const groupPostfix = "DisplaySettings"; + +namespace TextEditor { + +DisplaySettings::DisplaySettings() : + m_displayLineNumbers(true), + m_textWrapping(false), + m_showWrapColumn(false), + m_wrapColumn(80), + m_visualizeWhitespace(false), + m_displayFoldingMarkers(true), + m_highlightCurrentLine(true) +{ +} + +void DisplaySettings::toSettings(const QString &category, QSettings *s) const +{ + QString group = QLatin1String(groupPostfix); + if (!category.isEmpty()) + group.insert(0, category); + s->beginGroup(group); + s->setValue(QLatin1String(displayLineNumbersKey), m_displayLineNumbers); + s->setValue(QLatin1String(textWrappingKey), m_textWrapping); + s->setValue(QLatin1String(showWrapColumnKey), m_showWrapColumn); + s->setValue(QLatin1String(wrapColumnKey), m_wrapColumn); + s->setValue(QLatin1String(visualizeWhitespaceKey), m_visualizeWhitespace); + s->setValue(QLatin1String(displayFoldingMarkersKey), m_displayFoldingMarkers); + s->setValue(QLatin1String(highlightCurrentLineKey), m_highlightCurrentLine); + s->endGroup(); +} + +void DisplaySettings::fromSettings(const QString &category, const QSettings *s) +{ + QString group = QLatin1String(groupPostfix); + if (!category.isEmpty()) + group.insert(0, category); + group += QLatin1Char('/'); + + *this = DisplaySettings(); // Assign defaults + + m_displayLineNumbers = s->value(group + QLatin1String(displayLineNumbersKey), m_displayLineNumbers).toBool(); + m_textWrapping = s->value(group + QLatin1String(textWrappingKey), m_textWrapping).toBool(); + m_showWrapColumn = s->value(group + QLatin1String(showWrapColumnKey), m_showWrapColumn).toBool(); + m_wrapColumn = s->value(group + QLatin1String(wrapColumnKey), m_wrapColumn).toInt(); + m_visualizeWhitespace = s->value(group + QLatin1String(visualizeWhitespaceKey), m_visualizeWhitespace).toBool(); + m_displayFoldingMarkers = s->value(group + QLatin1String(displayFoldingMarkersKey), m_displayFoldingMarkers).toBool(); + m_highlightCurrentLine = s->value(group + QLatin1String(highlightCurrentLineKey), m_highlightCurrentLine).toBool(); +} + +bool DisplaySettings::equals(const DisplaySettings &ds) const +{ + return m_displayLineNumbers == ds.m_displayLineNumbers + && m_textWrapping == ds.m_textWrapping + && m_showWrapColumn == ds.m_showWrapColumn + && m_wrapColumn == ds.m_wrapColumn + && m_visualizeWhitespace == ds.m_visualizeWhitespace + && m_displayFoldingMarkers == ds.m_displayFoldingMarkers + && m_highlightCurrentLine == ds.m_highlightCurrentLine + ; +} + +} // namespace TextEditor diff --git a/src/plugins/texteditor/displaysettings.h b/src/plugins/texteditor/displaysettings.h new file mode 100644 index 00000000000..7c70126e5d2 --- /dev/null +++ b/src/plugins/texteditor/displaysettings.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DISPLAYSETTINGS_H +#define DISPLAYSETTINGS_H + +#include "texteditor_global.h" + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace TextEditor { + +struct TEXTEDITOR_EXPORT DisplaySettings +{ + DisplaySettings(); + + void toSettings(const QString &category, QSettings *s) const; + void fromSettings(const QString &category, const QSettings *s); + + bool m_displayLineNumbers; + bool m_textWrapping; + bool m_showWrapColumn; + int m_wrapColumn; + bool m_visualizeWhitespace; + bool m_displayFoldingMarkers; + bool m_highlightCurrentLine; + + bool equals(const DisplaySettings &ds) const; +}; + +inline bool operator==(const DisplaySettings &t1, const DisplaySettings &t2) { return t1.equals(t2); } +inline bool operator!=(const DisplaySettings &t1, const DisplaySettings &t2) { return !t1.equals(t2); } + +} // namespace TextEditor + +#endif // DISPLAYSETTINGS_H diff --git a/src/plugins/texteditor/findinfiles.cpp b/src/plugins/texteditor/findinfiles.cpp new file mode 100644 index 00000000000..6e11e400171 --- /dev/null +++ b/src/plugins/texteditor/findinfiles.cpp @@ -0,0 +1,144 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "findinfiles.h" + +#include <QtDebug> +#include <QtCore/QDirIterator> +#include <QtGui/QPushButton> +#include <QtGui/QFileDialog> +#include <QtGui/QVBoxLayout> + +using namespace Find; +using namespace TextEditor::Internal; + +FindInFiles::FindInFiles(Core::ICore *core, SearchResultWindow *resultWindow) + : BaseFileFind(core, resultWindow), + m_configWidget(0), + m_directory(0) +{ +} + +QString FindInFiles::name() const +{ + return tr("Files on Disk"); +} + +QKeySequence FindInFiles::defaultShortcut() const +{ + return QKeySequence(); +} + +void FindInFiles::findAll(const QString &txt, QTextDocument::FindFlags findFlags) +{ + updateComboEntries(m_directory, true); + BaseFileFind::findAll(txt, findFlags); +} + +QStringList FindInFiles::files() +{ + QStringList fileList; + QDirIterator it(m_directory->currentText(), + fileNameFilters(), + QDir::Files|QDir::Readable, + QDirIterator::Subdirectories); + while (it.hasNext()) { + it.next(); + fileList << it.filePath(); + } + return fileList; +} + +QWidget *FindInFiles::createConfigWidget() +{ + if (!m_configWidget) { + m_configWidget = new QWidget; + QGridLayout * const gridLayout = new QGridLayout(m_configWidget); + gridLayout->setMargin(0); + m_configWidget->setLayout(gridLayout); + gridLayout->addWidget(createRegExpWidget(), 0, 1, 1, 2); + + gridLayout->addWidget(new QLabel(tr("Directory:")), 1, 0, Qt::AlignRight); + m_directory = new QComboBox; + m_directory->setEditable(true); + m_directory->setMaxCount(30); + m_directory->setMinimumContentsLength(10); + m_directory->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + m_directory->setInsertPolicy(QComboBox::InsertAtTop); + m_directory->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + m_directory->setModel(&m_directoryStrings); + syncComboWithSettings(m_directory, m_directorySetting); + gridLayout->addWidget(m_directory, 1, 1); + QPushButton *browseButton = new QPushButton(tr("Browse")); + gridLayout->addWidget(browseButton, 1, 2); + connect(browseButton, SIGNAL(clicked()), this, SLOT(openFileBrowser())); + + QLabel * const filePatternLabel = new QLabel(tr("File pattern:")); + filePatternLabel->setMinimumWidth(80); + filePatternLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + filePatternLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + gridLayout->addWidget(filePatternLabel, 2, 0); + gridLayout->addWidget(createPatternWidget(), 2, 1, 1, 2); + m_configWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + } + return m_configWidget; +} + +void FindInFiles::openFileBrowser() +{ + if (!m_directory) + return; + QString dir = QFileDialog::getExistingDirectory(m_configWidget, + tr("Directory to search")); + if (!dir.isEmpty()) + m_directory->setEditText(dir); +} + +void FindInFiles::writeSettings(QSettings *settings) +{ + settings->beginGroup("FindInFiles"); + writeCommonSettings(settings); + settings->setValue("directories", m_directoryStrings.stringList()); + if (m_directory) + settings->setValue("currentDirectory", m_directory->currentText()); + settings->endGroup(); +} + +void FindInFiles::readSettings(QSettings *settings) +{ + settings->beginGroup("FindInFiles"); + readCommonSettings(settings, "*.cpp,*.h"); + m_directoryStrings.setStringList(settings->value("directories").toStringList()); + m_directorySetting = settings->value("currentDirectory").toString(); + settings->endGroup(); + syncComboWithSettings(m_directory, m_directorySetting); +} diff --git a/src/plugins/texteditor/findinfiles.h b/src/plugins/texteditor/findinfiles.h new file mode 100644 index 00000000000..3d5bf0c940e --- /dev/null +++ b/src/plugins/texteditor/findinfiles.h @@ -0,0 +1,83 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FINDINFILES_H +#define FINDINFILES_H + +#include "basefilefind.h" + +#include <coreplugin/icore.h> +#include <find/ifindfilter.h> +#include <find/searchresultwindow.h> + +#include <QtCore/QPointer> +#include <QtGui/QLabel> +#include <QtGui/QComboBox> +#include <QtGui/QStringListModel> + + +namespace TextEditor { +namespace Internal { + +class FindInFiles : public BaseFileFind +{ + Q_OBJECT + +public: + FindInFiles(Core::ICore *core, Find::SearchResultWindow *resultWindow); + + QString name() const; + + QKeySequence defaultShortcut() const; + + void findAll(const QString &txt, QTextDocument::FindFlags findFlags); + QWidget *createConfigWidget(); + void writeSettings(QSettings *settings); + void readSettings(QSettings *settings); + +protected: + QStringList files(); + +private slots: + void openFileBrowser(); + +private: + QStringListModel m_directoryStrings; + QString m_directorySetting; + QPointer<QWidget> m_configWidget; + QPointer<QComboBox> m_directory; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // FINDINFILES_H diff --git a/src/plugins/texteditor/fontsettings.cpp b/src/plugins/texteditor/fontsettings.cpp new file mode 100644 index 00000000000..1ced2cafd69 --- /dev/null +++ b/src/plugins/texteditor/fontsettings.cpp @@ -0,0 +1,277 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "fontsettings.h" +#include "fontsettingspage.h" + +#include <QtCore/QSettings> +#include <QtGui/QTextCharFormat> + +static const char *fontFamilyKey = "FontFamily"; +static const char *fontSizeKey = "FontSize"; +static const char *trueString = "true"; +static const char *falseString = "false"; + +namespace { +#ifdef Q_WS_MAC + enum { DEFAULT_FONT_SIZE = 12 }; + static const char *DEFAULT_FONT_FAMILY = "Monaco"; +#else +#ifdef Q_OS_UNIX + enum { DEFAULT_FONT_SIZE = 9 }; + static const char *DEFAULT_FONT_FAMILY = "Monospace"; +#else + enum { DEFAULT_FONT_SIZE = 10 }; + static const char *DEFAULT_FONT_FAMILY = "Courier"; +#endif +#endif +} // anonymous namespace + +namespace TextEditor { + +// Format -- +Format::Format() : + m_foreground(Qt::black), + m_background(Qt::white), + m_bold(false), + m_italic(false) +{ +} + +void Format::setForeground(const QColor &foreground) +{ + m_foreground = foreground; +} + +void Format::setBackground(const QColor &background) +{ + m_background = background; +} + +void Format::setBold(bool bold) +{ + m_bold = bold; +} + +void Format::setItalic(bool italic) +{ + m_italic = italic; +} + +static QString colorToString(const QColor &color) { + if (color.isValid()) + return color.name(); + return QLatin1String("invalid"); +} + +static QColor stringToColor(const QString &string) { + if (string == QLatin1String("invalid")) + return QColor(); + return QColor(string); +} + +QString Format::toString() const +{ + const QChar delimiter = QLatin1Char(';'); + QString s = colorToString(m_foreground); + s += delimiter; + s += colorToString(m_background); + s += delimiter; + s += m_bold ? QLatin1String(trueString) : QLatin1String(falseString); + s += delimiter; + s += m_italic ? QLatin1String(trueString) : QLatin1String(falseString); + return s; +} + +bool Format::fromString(const QString &str) +{ + *this = Format(); + + const QStringList lst = str.split(QLatin1Char(';')); + if (lst.count() != 4) + return false; + + m_foreground = stringToColor(lst.at(0)); + m_background = stringToColor(lst.at(1)); + m_bold = lst.at(2) == QLatin1String(trueString); + m_italic = lst.at(3) == QLatin1String(trueString); + return true; +} + +bool Format::equals(const Format &f) const +{ + return m_foreground == f.m_foreground && m_background == f.m_background && + m_bold == f.m_bold && m_italic == f.m_italic; +} +// -- FontSettings +FontSettings::FontSettings(const FormatDescriptions &fd) : + m_family(defaultFixedFontFamily()), + m_fontSize(DEFAULT_FONT_SIZE) +{ +} + +void FontSettings::clear() +{ + m_family = defaultFixedFontFamily(); + m_fontSize = DEFAULT_FONT_SIZE; + qFill(m_formats.begin(), m_formats.end(), Format()); +} + +void FontSettings::toSettings(const QString &category, + const FormatDescriptions &descriptions, + QSettings *s) const +{ + const int numFormats = m_formats.size(); + Q_ASSERT(descriptions.size() == numFormats); + s->beginGroup(category); + if (m_family != defaultFixedFontFamily() || s->contains(QLatin1String(fontFamilyKey))) + s->setValue(QLatin1String(fontFamilyKey), m_family); + + if (m_fontSize != DEFAULT_FONT_SIZE || s->contains(QLatin1String(fontSizeKey))) + s->setValue(QLatin1String(fontSizeKey), m_fontSize); + + const Format defaultFormat; + + foreach (const FormatDescription &desc, descriptions) { + QMap<QString, Format>::const_iterator i = m_formats.find(desc.name()); + if (i != m_formats.end() && ((*i) != defaultFormat || s->contains(desc.name()))) { + s->setValue(desc.name(), (*i).toString()); + } + } + s->endGroup(); +} + +bool FontSettings::fromSettings(const QString &category, + const FormatDescriptions &descriptions, + const QSettings *s) +{ + clear(); + + if (!s->childGroups().contains(category)) + return false; + + QString group = category; + group += QLatin1Char('/'); + + m_family = s->value(group + QLatin1String(fontFamilyKey), defaultFixedFontFamily()).toString(); + m_fontSize = s->value(group + QLatin1String(QLatin1String(fontSizeKey)), m_fontSize).toInt(); + + foreach (const FormatDescription &desc, descriptions) { + const QString name = desc.name(); + const QString fmt = s->value(group + name, QString()).toString(); + if (fmt.isEmpty()) { + m_formats[name].setForeground(desc.foreground()); + m_formats[name].setBackground(desc.background()); + } else { + m_formats[name].fromString(fmt); + } + } + return true; +} + +bool FontSettings::equals(const FontSettings &f) const +{ + return m_family == f.m_family + && m_fontSize == f.m_fontSize + && m_formats == f.m_formats; +} + +QTextCharFormat FontSettings::toTextCharFormat(const QString &category) const +{ + const Format f = m_formats.value(category); + QTextCharFormat tf; + if (category == QLatin1String("Text")) { + tf.setFontFamily(m_family); + tf.setFontPointSize(m_fontSize); + } + + if (f.foreground().isValid()) + tf.setForeground(f.foreground()); + if (f.background().isValid() && (category == QLatin1String("Text") || f.background() != m_formats.value(QLatin1String("Text")).background())) + tf.setBackground(f.background()); + tf.setFontWeight(f.bold() ? QFont::Bold : QFont::Normal); + tf.setFontItalic(f.italic()); + return tf; +} + +QVector<QTextCharFormat> FontSettings::toTextCharFormats(const QVector<QString> &categories) const +{ + QVector<QTextCharFormat> rc; + const int size = categories.size(); + rc.reserve(size); + for (int i = 0; i < size; i++) + rc.push_back(toTextCharFormat(categories.at(i))); + return rc; +} + +QString FontSettings::family() const +{ + return m_family; +} + +void FontSettings::setFamily(const QString &family) +{ + m_family = family; +} + +int FontSettings::fontSize() const +{ + return m_fontSize; +} + +void FontSettings::setFontSize(int size) +{ + m_fontSize = size; +} + +Format &FontSettings::formatFor(const QString &category) +{ + return m_formats[category]; +} + +QString FontSettings::defaultFixedFontFamily() +{ + static QString rc; + if (rc.isEmpty()) { + QFont f(DEFAULT_FONT_FAMILY); + f.setStyleHint(QFont::TypeWriter); + rc = f.family(); + } + return rc; +} + +int FontSettings::defaultFontSize() +{ + return DEFAULT_FONT_SIZE; +} + +} // namespace TextEditor diff --git a/src/plugins/texteditor/fontsettings.h b/src/plugins/texteditor/fontsettings.h new file mode 100644 index 00000000000..1b9088b6219 --- /dev/null +++ b/src/plugins/texteditor/fontsettings.h @@ -0,0 +1,149 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FONTSETTINGS_H +#define FONTSETTINGS_H + +#include "texteditor_global.h" + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QVector> +#include <QtGui/QColor> + +QT_BEGIN_NAMESPACE +class QTextCharFormat; +class QSettings; +QT_END_NAMESPACE + +namespace TextEditor { + +class FormatDescription; + +// Format for a particular piece of text (text/comment, etc). +class TEXTEDITOR_EXPORT Format +{ +public: + Format(); + + QColor foreground() const { return m_foreground; } + void setForeground(const QColor &foreground); + + QColor background() const { return m_background; } + void setBackground(const QColor &background); + + bool bold() const { return m_bold; } + void setBold(bool bold); + + bool italic() const { return m_italic; } + void setItalic(bool italic); + + bool equals(const Format &f) const; + + QString toString() const; + bool fromString(const QString &str); + +private: + QColor m_foreground; + QColor m_background; + bool m_bold; + bool m_italic; +}; + +inline bool operator==(const Format &f1, const Format &f2) { return f1.equals(f2); } +inline bool operator!=(const Format &f1, const Format &f2) { return !f1.equals(f2); } + +/** + * Font settings (default font and enumerated list of formats). + */ +class TEXTEDITOR_EXPORT FontSettings +{ +public: + typedef QList<FormatDescription> FormatDescriptions; + + FontSettings(const FormatDescriptions &fd); + void clear(); + + void toSettings(const QString &category, + const FormatDescriptions &descriptions, + QSettings *s) const; + + bool fromSettings(const QString &category, + const FormatDescriptions &descriptions, + const QSettings *s); + + /** + * Returns the list of QTextCharFormats that corresponds to the list of + * requested format categories. + */ + QVector<QTextCharFormat> toTextCharFormats(const QVector<QString> &categories) const; + + /** + * Returns the QTextCharFormat of the given format category. + */ + QTextCharFormat toTextCharFormat(const QString &category) const; + + /** + * Returns the configured font family. + */ + QString family() const; + void setFamily(const QString &family); + + /** + * Returns the configured font size. + */ + int fontSize() const; + void setFontSize(int size); + + /** + * Returns the format for the given font category. + */ + Format &formatFor(const QString &category); + + bool equals(const FontSettings &f) const; + + static QString defaultFixedFontFamily(); + static int defaultFontSize(); + +private: + QString m_family; + int m_fontSize; + QMap<QString, Format> m_formats; +}; + +inline bool operator==(const FontSettings &f1, const FontSettings &f2) { return f1.equals(f2); } +inline bool operator!=(const FontSettings &f1, const FontSettings &f2) { return !f1.equals(f2); } + +} // namespace TextEditor + +#endif // FONTSETTINGS_H diff --git a/src/plugins/texteditor/fontsettingspage.cpp b/src/plugins/texteditor/fontsettingspage.cpp new file mode 100644 index 00000000000..1583328fc28 --- /dev/null +++ b/src/plugins/texteditor/fontsettingspage.cpp @@ -0,0 +1,465 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "fontsettingspage.h" +#include "fontsettings.h" +#include "texteditorconstants.h" +#include "ui_fontsettingspage.h" + +#include <utils/settingsutils.h> + +#include <QtCore/QSettings> +#include <QtCore/QTimer> +#include <QtGui/QListWidget> +#include <QtGui/QToolButton> +#include <QtGui/QPalette> +#include <QtGui/QCheckBox> +#include <QtGui/QColorDialog> +#include <QtGui/QTextEdit> +#include <QtGui/QTextCharFormat> +#include <QtGui/QComboBox> +#include <QtGui/QFontDatabase> +#include <QtGui/QPalette> + +static inline QString colorButtonStyleSheet(const QColor &bgColor) +{ + if (bgColor.isValid()) { + QString rc = QLatin1String("border: 2px solid black; border-radius: 2px; background:"); + rc += bgColor.name(); + return rc; + } + return QLatin1String("border: 2px dotted black; border-radius: 2px;"); +} + +namespace TextEditor { +namespace Internal { + +class FontSettingsPagePrivate +{ +public: + FontSettingsPagePrivate(const TextEditor::FormatDescriptions &fd, + const QString &name, + const QString &category, + const QString &trCategory, + Core::ICore *core); + + Core::ICore *m_core; + const QString m_name; + const QString m_settingsGroup; + const QString m_category; + const QString m_trCategory; + + TextEditor::FormatDescriptions m_descriptions; + FontSettings m_value; + FontSettings m_lastValue; + int m_curItem; + Ui::FontSettingsPage ui; +}; + +FontSettingsPagePrivate::FontSettingsPagePrivate(const TextEditor::FormatDescriptions &fd, + const QString &name, + const QString &category, + const QString &trCategory, + Core::ICore *core) : + m_core(core), + m_name(name), + m_settingsGroup(Core::Utils::settingsKey(category)), + m_category(category), + m_trCategory(trCategory), + m_descriptions(fd), + m_value(fd), + m_lastValue(fd), + m_curItem(-1) +{ + bool settingsFound = false; + if (m_core) + if (const QSettings *settings = m_core->settings()) + settingsFound = m_value.fromSettings(m_settingsGroup, m_descriptions, settings); + if (!settingsFound) { // Apply defaults + foreach (const FormatDescription &f, m_descriptions) { + const QString name = f.name(); + m_lastValue.formatFor(name).setForeground(f.foreground()); + m_lastValue.formatFor(name).setBackground(f.background()); + m_value.formatFor(name).setForeground(f.foreground()); + m_value.formatFor(name).setBackground(f.background()); + } + } + + m_lastValue = m_value; +} + +} // namespace Internal +} // namespace TextEditor + +using namespace TextEditor; +using namespace TextEditor::Internal; + +// ------- FormatDescription +FormatDescription::FormatDescription(const QString &name, const QString &trName, const QColor &color) : + m_name(name), + m_trName(trName) +{ + m_format.setForeground(color); +} + +QString FormatDescription::name() const +{ + return m_name; +} + +QString FormatDescription::trName() const +{ + return m_trName; +} + +QColor FormatDescription::foreground() const +{ + if (m_name == QLatin1String("LineNumber")) + return QApplication::palette().dark().color(); + if (m_name == QLatin1String("Parentheses")) + return QColor(Qt::red); + return m_format.foreground(); +} + +void FormatDescription::setForeground(const QColor &foreground) +{ + m_format.setForeground(foreground); +} + +QColor FormatDescription::background() const +{ + if (m_name == QLatin1String(Constants::C_TEXT)) + return Qt::white; + else if (m_name == QLatin1String(Constants::C_LINE_NUMBER)) + return QApplication::palette().background().color(); + else if (m_name == QLatin1String(Constants::C_SEARCH_RESULT)) + return QColor(0xffef0b); + else if (m_name == QLatin1String(Constants::C_PARENTHESES)) + return QColor(0xb4, 0xee, 0xb4); + else if (m_name == QLatin1String(Constants::C_CURRENT_LINE) + || m_name == QLatin1String(Constants::C_SEARCH_SCOPE)) { + const QPalette palette = QApplication::palette(); + const QColor &fg = palette.color(QPalette::Highlight); + const QColor &bg = palette.color(QPalette::Base); + + qreal smallRatio; + qreal largeRatio; + if (m_name == QLatin1String(Constants::C_CURRENT_LINE)) { + smallRatio = .15; + largeRatio = .3; + } else { + smallRatio = .05; + largeRatio = .4; + } + const qreal ratio = ((palette.color(QPalette::Text).value() < 128) + ^ (palette.color(QPalette::HighlightedText).value() < 128)) ? smallRatio : largeRatio; + + const QColor &col = QColor::fromRgbF(fg.redF() * ratio + bg.redF() * (1 - ratio), + fg.greenF() * ratio + bg.greenF() * (1 - ratio), + fg.blueF() * ratio + bg.blueF() * (1 - ratio)); + return col; + } else if (m_name == QLatin1String(Constants::C_SELECTION)) { + const QPalette palette = QApplication::palette(); + return palette.color(QPalette::Highlight); + } + return QColor(); // invalid color +} + + +// ------------ FontSettingsPage +FontSettingsPage::FontSettingsPage(const FormatDescriptions &fd, + const QString &category, + const QString &trCategory, + Core::ICore *core, + QObject *parent) : + Core::IOptionsPage(parent), + d_ptr(new FontSettingsPagePrivate(fd, tr("Font & Colors"), category, trCategory, core)) +{ +} + +FontSettingsPage::~FontSettingsPage() +{ + delete d_ptr; +} + +QString FontSettingsPage::name() const +{ + return d_ptr->m_name; +} + +QString FontSettingsPage::category() const +{ + return d_ptr->m_category; +} + +QString FontSettingsPage::trCategory() const +{ + return d_ptr->m_trCategory; +} + +QWidget *FontSettingsPage::createPage(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + d_ptr->ui.setupUi(w); + + + d_ptr->ui.itemListWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); + + foreach (const FormatDescription &d, d_ptr->m_descriptions) + d_ptr->ui.itemListWidget->addItem(d.trName()); + + QFontDatabase db; + const QStringList families = db.families(); + d_ptr->ui.familyComboBox->addItems(families); + const int idx = families.indexOf(d_ptr->m_value.family()); + d_ptr->ui.familyComboBox->setCurrentIndex(idx); + + connect(d_ptr->ui.familyComboBox, SIGNAL(activated(int)), this, SLOT(updatePointSizes())); + connect(d_ptr->ui.sizeComboBox, SIGNAL(activated(int)), this, SLOT(updatePreview())); + connect(d_ptr->ui.itemListWidget, SIGNAL(itemSelectionChanged()), + this, SLOT(itemChanged())); + connect(d_ptr->ui.foregroundToolButton, SIGNAL(clicked()), + this, SLOT(changeForeColor())); + connect(d_ptr->ui.backgroundToolButton, SIGNAL(clicked()), + this, SLOT(changeBackColor())); + connect(d_ptr->ui.eraseBackgroundToolButton, SIGNAL(clicked()), + this, SLOT(eraseBackColor())); + connect(d_ptr->ui.boldCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkCheckBoxes())); + connect(d_ptr->ui.italicCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkCheckBoxes())); + + if (!d_ptr->m_descriptions.empty()) + d_ptr->ui.itemListWidget->setCurrentRow(0); + + updatePointSizes(); + d_ptr->m_lastValue = d_ptr->m_value; + return w; +} + +void FontSettingsPage::itemChanged() +{ + QListWidgetItem *item = d_ptr->ui.itemListWidget->currentItem(); + if (!item) + return; + + const int numFormats = d_ptr->m_descriptions.size(); + for (int i = 0; i < numFormats; i++) { + if (d_ptr->m_descriptions[i].trName() == item->text()) { + d_ptr->m_curItem = i; + const Format &format = d_ptr->m_value.formatFor(d_ptr->m_descriptions[i].name()); + d_ptr->ui.foregroundToolButton->setStyleSheet(colorButtonStyleSheet(format.foreground())); + d_ptr->ui.backgroundToolButton->setStyleSheet(colorButtonStyleSheet(format.background())); + + d_ptr->ui.eraseBackgroundToolButton->setEnabled(i > 0 && format.background().isValid()); + + const bool boldBlocked = d_ptr->ui.boldCheckBox->blockSignals(true); + d_ptr->ui.boldCheckBox->setChecked(format.bold()); + d_ptr->ui.boldCheckBox->blockSignals(boldBlocked); + const bool italicBlocked = d_ptr->ui.italicCheckBox->blockSignals(true); + d_ptr->ui.italicCheckBox->setChecked(format.italic()); + d_ptr->ui.italicCheckBox->blockSignals(italicBlocked); + updatePreview(); + break; + } + } +} + +void FontSettingsPage::changeForeColor() +{ + if (d_ptr->m_curItem == -1) + return; + QColor color = d_ptr->m_value.formatFor(d_ptr->m_descriptions[d_ptr->m_curItem].name()).foreground(); + const QColor newColor = QColorDialog::getColor(color, d_ptr->ui.boldCheckBox->window()); + if (!newColor.isValid()) + return; + QPalette p = d_ptr->ui.foregroundToolButton->palette(); + p.setColor(QPalette::Active, QPalette::Button, newColor); + d_ptr->ui.foregroundToolButton->setStyleSheet(colorButtonStyleSheet(newColor)); + + const int numFormats = d_ptr->m_descriptions.size(); + for (int i = 0; i < numFormats; i++) { + QList<QListWidgetItem*> items = d_ptr->ui.itemListWidget->findItems(d_ptr->m_descriptions[i].trName(), Qt::MatchExactly); + if (!items.isEmpty() && items.first()->isSelected()) + d_ptr->m_value.formatFor(d_ptr->m_descriptions[i].name()).setForeground(newColor); + } + + updatePreview(); +} + +void FontSettingsPage::changeBackColor() +{ + if (d_ptr->m_curItem == -1) + return; + QColor color = d_ptr->m_value.formatFor(d_ptr->m_descriptions[d_ptr->m_curItem].name()).background(); + const QColor newColor = QColorDialog::getColor(color, d_ptr->ui.boldCheckBox->window()); + if (!newColor.isValid()) + return; + d_ptr->ui.backgroundToolButton->setStyleSheet(colorButtonStyleSheet(newColor)); + + const int numFormats = d_ptr->m_descriptions.size(); + for (int i = 0; i < numFormats; i++) { + QList<QListWidgetItem*> items = d_ptr->ui.itemListWidget->findItems(d_ptr->m_descriptions[i].trName(), Qt::MatchExactly); + if (!items.isEmpty() && items.first()->isSelected()) + d_ptr->m_value.formatFor(d_ptr->m_descriptions[i].name()).setBackground(newColor); + } + + updatePreview(); +} + +void FontSettingsPage::eraseBackColor() +{ + if (d_ptr->m_curItem == -1) + return; + QColor newColor; + d_ptr->ui.backgroundToolButton->setStyleSheet(colorButtonStyleSheet(newColor)); + + const int numFormats = d_ptr->m_descriptions.size(); + for (int i = 0; i < numFormats; i++) { + QList<QListWidgetItem*> items = d_ptr->ui.itemListWidget->findItems(d_ptr->m_descriptions[i].trName(), Qt::MatchExactly); + if (!items.isEmpty() && items.first()->isSelected()) + d_ptr->m_value.formatFor(d_ptr->m_descriptions[i].name()).setBackground(newColor); + } + + updatePreview(); +} + +void FontSettingsPage::checkCheckBoxes() +{ + if (d_ptr->m_curItem == -1) + return; + const int numFormats = d_ptr->m_descriptions.size(); + for (int i = 0; i < numFormats; i++) { + QList<QListWidgetItem*> items = d_ptr->ui.itemListWidget->findItems(d_ptr->m_descriptions[i].trName(), Qt::MatchExactly); + if (!items.isEmpty() && items.first()->isSelected()) { + d_ptr->m_value.formatFor(d_ptr->m_descriptions[i].name()).setBold(d_ptr->ui.boldCheckBox->isChecked()); + d_ptr->m_value.formatFor(d_ptr->m_descriptions[i].name()).setItalic(d_ptr->ui.italicCheckBox->isChecked()); + } + } + updatePreview(); +} + +void FontSettingsPage::updatePreview() +{ + if (d_ptr->m_curItem == -1) + return; + + const Format ¤tFormat = d_ptr->m_value.formatFor(d_ptr->m_descriptions[d_ptr->m_curItem].name()); + const Format &baseFormat = d_ptr->m_value.formatFor(QLatin1String("Text")); + + QPalette pal = QApplication::palette(); + if (baseFormat.foreground().isValid()) { + pal.setColor(QPalette::Text, baseFormat.foreground()); + pal.setColor(QPalette::Foreground, baseFormat.foreground()); + } + if (baseFormat.background().isValid()) + pal.setColor(QPalette::Base, baseFormat.background()); + + d_ptr->ui.previewTextEdit->setPalette(pal); + + QTextCharFormat format; + if (currentFormat.foreground().isValid()) + format.setForeground(QBrush(currentFormat.foreground())); + if (currentFormat.background().isValid()) + format.setBackground(QBrush(currentFormat.background())); + format.setFontFamily(d_ptr->ui.familyComboBox->currentText()); + bool ok; + int size = d_ptr->ui.sizeComboBox->currentText().toInt(&ok); + if (!ok) { + size = QFont().pointSize(); + } + format.setFontPointSize(size); + format.setFontItalic(currentFormat.italic()); + if (currentFormat.bold()) + format.setFontWeight(QFont::Bold); + d_ptr->ui.previewTextEdit->setCurrentCharFormat(format); + + d_ptr->ui.previewTextEdit->setPlainText(tr("\n\tThis is only an example.")); +} + +void FontSettingsPage::updatePointSizes() +{ + const int oldSize = d_ptr->m_value.fontSize(); + if (d_ptr->ui.sizeComboBox->count()) { + const QString curSize = d_ptr->ui.sizeComboBox->currentText(); + bool ok = true; + int oldSize = curSize.toInt(&ok); + if (!ok) + oldSize = d_ptr->m_value.fontSize(); + d_ptr->ui.sizeComboBox->clear(); + } + QFontDatabase db; + const QList<int> sizeLst = db.pointSizes(d_ptr->ui.familyComboBox->currentText()); + int idx = 0; + int i = 0; + for (; i<sizeLst.count(); ++i) { + if (idx == 0 && sizeLst.at(i) >= oldSize) + idx = i; + d_ptr->ui.sizeComboBox->addItem(QString::number(sizeLst.at(i))); + } + if (d_ptr->ui.sizeComboBox->count()) + d_ptr->ui.sizeComboBox->setCurrentIndex(idx); + updatePreview(); +} + +void FontSettingsPage::delayedChange() +{ + emit changed(d_ptr->m_value); +} + +void FontSettingsPage::finished(bool accepted) +{ + if (!accepted) { + d_ptr->m_value = d_ptr->m_lastValue; + return; + } + + d_ptr->m_value.setFamily(d_ptr->ui.familyComboBox->currentText()); + + bool ok = true; + const int size = d_ptr->ui.sizeComboBox->currentText().toInt(&ok); + if (ok) + d_ptr->m_value.setFontSize(size); + + + if (d_ptr->m_value != d_ptr->m_lastValue) { + d_ptr->m_lastValue = d_ptr->m_value; + if (d_ptr->m_core) + if (QSettings *settings = d_ptr->m_core->settings()) + d_ptr->m_value.toSettings(d_ptr->m_settingsGroup, d_ptr->m_descriptions, settings); + + QTimer::singleShot(0, this, SLOT(delayedChange())); + } +} + +const FontSettings &FontSettingsPage::fontSettings() const +{ + return d_ptr->m_value; +} diff --git a/src/plugins/texteditor/fontsettingspage.h b/src/plugins/texteditor/fontsettingspage.h new file mode 100644 index 00000000000..86599e70d40 --- /dev/null +++ b/src/plugins/texteditor/fontsettingspage.h @@ -0,0 +1,124 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef FONTSETTINGSPAGE_H +#define FONTSETTINGSPAGE_H + +#include "texteditor_global.h" + +#include "fontsettings.h" + +#include <coreplugin/icore.h> +#include <coreplugin/dialogs/ioptionspage.h> + +#include <QtGui/QColor> +#include <QtGui/QTextCharFormat> +#include <QtCore/QString> +#include <QtCore/QVector> + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +namespace TextEditor { + +namespace Internal { +class FontSettingsPagePrivate; +} // namespace Internal + +// GUI description of a format consisting of name (settings key) +// and trName to be displayed +class TEXTEDITOR_EXPORT FormatDescription +{ +public: + FormatDescription(const QString &name, const QString &trName, + const QColor &foreground = Qt::black); + + QString name() const; + + QString trName() const; + + QColor foreground() const; + void setForeground(const QColor &foreground); + + QColor background() const; + +private: + QString m_name; // Name of the category + QString m_trName; // Displayed name of the category + Format m_format; // Default format +}; + +typedef QList<FormatDescription> FormatDescriptions; + +class TEXTEDITOR_EXPORT FontSettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + FontSettingsPage(const FormatDescriptions &fd, + const QString &category, + const QString &trCategory, + Core::ICore *core, + QObject *parent = 0); + + ~FontSettingsPage(); + + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + + const FontSettings &fontSettings() const; + +signals: + void changed(const TextEditor::FontSettings&); + +private slots: + void delayedChange(); + void itemChanged(); + void changeForeColor(); + void changeBackColor(); + void eraseBackColor(); + void checkCheckBoxes(); + void updatePointSizes(); + void updatePreview(); + +private: + Internal::FontSettingsPagePrivate *d_ptr; +}; + +} // namespace TextEditor + +#endif // FONTSETTINGSPAGE_H diff --git a/src/plugins/texteditor/fontsettingspage.ui b/src/plugins/texteditor/fontsettingspage.ui new file mode 100644 index 00000000000..770d5e2935c --- /dev/null +++ b/src/plugins/texteditor/fontsettingspage.ui @@ -0,0 +1,268 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>TextEditor::Internal::FontSettingsPage</class> + <widget class="QWidget" name="TextEditor::Internal::FontSettingsPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>572</width> + <height>471</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>9</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>General Font Settings</string> + </property> + <layout class="QHBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>9</number> + </property> + <item> + <widget class="QLabel" name="label_5"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Family:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="familyComboBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>2</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_6"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Size:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="sizeComboBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </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> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Item Specific Settings</string> + </property> + <layout class="QHBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>9</number> + </property> + <item> + <widget class="QListWidget" name="itemListWidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <layout class="QGridLayout"> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item row="2" column="0"> + <widget class="QCheckBox" name="boldCheckBox"> + <property name="text"> + <string>Bold</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QCheckBox" name="italicCheckBox"> + <property name="text"> + <string>Italic</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QToolButton" name="foregroundToolButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Background:</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Foreground:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <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> + <item row="1" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QToolButton" name="backgroundToolButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="eraseBackgroundToolButton"> + <property name="toolTip"> + <string>Erase background</string> + </property> + <property name="text"> + <string>x</string> + </property> + <property name="arrowType"> + <enum>Qt::LeftArrow</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Preview:</string> + </property> + </widget> + </item> + <item> + <widget class="QTextEdit" name="previewTextEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/texteditor/generalsettingspage.cpp b/src/plugins/texteditor/generalsettingspage.cpp new file mode 100644 index 00000000000..7f056d8ca4f --- /dev/null +++ b/src/plugins/texteditor/generalsettingspage.cpp @@ -0,0 +1,217 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "displaysettings.h" +#include "generalsettingspage.h" +#include "storagesettings.h" +#include "tabsettings.h" +#include "ui_generalsettingspage.h" + +#include <QtCore/QSettings> +#include <QtCore/QDebug> + +using namespace TextEditor; + +struct GeneralSettingsPage::GeneralSettingsPagePrivate { + GeneralSettingsPagePrivate(Core::ICore *core, + const GeneralSettingsPageParameters &p); + + Core::ICore *m_core; + const GeneralSettingsPageParameters m_parameters; + Ui::generalSettingsPage m_page; + TabSettings m_tabSettings; + StorageSettings m_storageSettings; + DisplaySettings m_displaySettings; +}; + +GeneralSettingsPage::GeneralSettingsPagePrivate::GeneralSettingsPagePrivate(Core::ICore *core, + const GeneralSettingsPageParameters &p) : + m_core(core), + m_parameters(p) +{ + if (m_core) + if (const QSettings *s = m_core->settings()) { + m_tabSettings.fromSettings(m_parameters.settingsPrefix, s); + m_storageSettings.fromSettings(m_parameters.settingsPrefix, s); + m_displaySettings.fromSettings(m_parameters.settingsPrefix, s); + } +} + +GeneralSettingsPage::GeneralSettingsPage(Core::ICore *core, + const GeneralSettingsPageParameters &p, + QObject *parent) : + Core::IOptionsPage(parent), + m_d(new GeneralSettingsPagePrivate(core, p)) +{ +} + +GeneralSettingsPage::~GeneralSettingsPage() +{ + delete m_d; +} + +QString GeneralSettingsPage::name() const +{ + return m_d->m_parameters.name; +} + +QString GeneralSettingsPage::category() const +{ + return m_d->m_parameters.category; +} + +QString GeneralSettingsPage::trCategory() const +{ + return m_d->m_parameters.trCategory; +} + +QWidget *GeneralSettingsPage::createPage(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + m_d->m_page.setupUi(w); + + settingsToUI(); + + return w; +} + +void GeneralSettingsPage::finished(bool accepted) +{ + if (!accepted) + return; + + TabSettings newTabSettings; + StorageSettings newStorageSettings; + DisplaySettings newDisplaySettings; + settingsFromUI(newTabSettings, newStorageSettings, newDisplaySettings); + + if (newTabSettings != m_d->m_tabSettings) { + m_d->m_tabSettings = newTabSettings; + if (m_d->m_core) + if (QSettings *s = m_d->m_core->settings()) + m_d->m_tabSettings.toSettings(m_d->m_parameters.settingsPrefix, s); + + emit tabSettingsChanged(newTabSettings); + } + + if (newStorageSettings != m_d->m_storageSettings) { + m_d->m_storageSettings = newStorageSettings; + if (m_d->m_core) + if (QSettings *s = m_d->m_core->settings()) + m_d->m_storageSettings.toSettings(m_d->m_parameters.settingsPrefix, s); + + emit storageSettingsChanged(newStorageSettings); + } + + if (newDisplaySettings != m_d->m_displaySettings) { + m_d->m_displaySettings = newDisplaySettings; + if (m_d->m_core) + if (QSettings *s = m_d->m_core->settings()) + m_d->m_displaySettings.toSettings(m_d->m_parameters.settingsPrefix, s); + + emit displaySettingsChanged(newDisplaySettings); + } +} + +void GeneralSettingsPage::settingsFromUI(TabSettings &rc, + StorageSettings &storageSettings, + DisplaySettings &displaySettings) const +{ + rc.m_spacesForTabs = m_d->m_page.insertSpaces->isChecked(); + rc.m_autoIndent = m_d->m_page.autoIndent->isChecked(); + rc.m_smartBackspace = m_d->m_page.smartBackspace->isChecked(); + rc.m_tabSize = m_d->m_page.tabSize->value(); + rc.m_indentSize = m_d->m_page.indentSize->value(); + + storageSettings.m_cleanWhitespace = m_d->m_page.cleanWhitespace->isChecked(); + storageSettings.m_inEntireDocument = m_d->m_page.inEntireDocument->isChecked(); + storageSettings.m_addFinalNewLine = m_d->m_page.addFinalNewLine->isChecked(); + + displaySettings.m_displayLineNumbers = m_d->m_page.displayLineNumbers->isChecked(); + displaySettings.m_textWrapping = m_d->m_page.enableTextWrapping->isChecked(); + displaySettings.m_showWrapColumn = m_d->m_page.showWrapColumn->isChecked(); + displaySettings.m_wrapColumn = m_d->m_page.wrapColumn->value(); + displaySettings.m_visualizeWhitespace = m_d->m_page.visualizeWhitespace->isChecked(); + displaySettings.m_displayFoldingMarkers = m_d->m_page.displayFoldingMarkers->isChecked(); + displaySettings.m_highlightCurrentLine = m_d->m_page.highlightCurrentLine->isChecked(); +} + +void GeneralSettingsPage::settingsToUI() +{ + TabSettings rc = m_d->m_tabSettings; + m_d->m_page.insertSpaces->setChecked(rc.m_spacesForTabs); + m_d->m_page.autoIndent->setChecked(rc.m_autoIndent); + m_d->m_page.smartBackspace->setChecked(rc.m_smartBackspace); + m_d->m_page.tabSize->setValue(rc.m_tabSize); + m_d->m_page.indentSize->setValue(rc.m_indentSize); + + StorageSettings storageSettings = m_d->m_storageSettings; + m_d->m_page.cleanWhitespace->setChecked(storageSettings.m_cleanWhitespace); + m_d->m_page.inEntireDocument->setChecked(storageSettings.m_inEntireDocument); + m_d->m_page.addFinalNewLine->setChecked(storageSettings.m_addFinalNewLine); + + DisplaySettings displaySettings = m_d->m_displaySettings; + m_d->m_page.displayLineNumbers->setChecked(displaySettings.m_displayLineNumbers); + m_d->m_page.enableTextWrapping->setChecked(displaySettings.m_textWrapping); + m_d->m_page.showWrapColumn->setChecked(displaySettings.m_showWrapColumn); + m_d->m_page.wrapColumn->setValue(displaySettings.m_wrapColumn); + m_d->m_page.visualizeWhitespace->setChecked(displaySettings.m_visualizeWhitespace); + m_d->m_page.displayFoldingMarkers->setChecked(displaySettings.m_displayFoldingMarkers); + m_d->m_page.highlightCurrentLine->setChecked(displaySettings.m_highlightCurrentLine); +} + +TabSettings GeneralSettingsPage::tabSettings() const +{ + return m_d->m_tabSettings; +} + +StorageSettings GeneralSettingsPage::storageSettings() const +{ + return m_d->m_storageSettings; +} + +DisplaySettings GeneralSettingsPage::displaySettings() const +{ + return m_d->m_displaySettings; +} + +void GeneralSettingsPage::setDisplaySettings(const DisplaySettings &newDisplaySettings) +{ + if (newDisplaySettings != m_d->m_displaySettings) { + m_d->m_displaySettings = newDisplaySettings; + if (m_d->m_core) + if (QSettings *s = m_d->m_core->settings()) + m_d->m_displaySettings.toSettings(m_d->m_parameters.settingsPrefix, s); + + emit displaySettingsChanged(newDisplaySettings); + } +} diff --git a/src/plugins/texteditor/generalsettingspage.h b/src/plugins/texteditor/generalsettingspage.h new file mode 100644 index 00000000000..184e3bace83 --- /dev/null +++ b/src/plugins/texteditor/generalsettingspage.h @@ -0,0 +1,98 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GENERALSETTINGSPAGE_H +#define GENERALSETTINGSPAGE_H + +#include "texteditor_global.h" + +#include <coreplugin/icore.h> +#include <coreplugin/dialogs/ioptionspage.h> + +#include <QtCore/QObject> + +namespace TextEditor { + +struct TabSettings; +struct StorageSettings; +struct DisplaySettings; + +struct TEXTEDITOR_EXPORT GeneralSettingsPageParameters { + QString name; + QString category; + QString trCategory; + QString settingsPrefix; +}; + +class Ui_generalSettingsPage; + +class TEXTEDITOR_EXPORT GeneralSettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + GeneralSettingsPage(Core::ICore *core, + const GeneralSettingsPageParameters &p, + QObject *parent); + virtual ~GeneralSettingsPage(); + + //IOptionsPage + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + + TabSettings tabSettings() const; + StorageSettings storageSettings() const; + DisplaySettings displaySettings() const; + + void setDisplaySettings(const DisplaySettings &); + +signals: + void tabSettingsChanged(const TextEditor::TabSettings &); + void storageSettingsChanged(const TextEditor::StorageSettings &); + void displaySettingsChanged(const TextEditor::DisplaySettings &); + +private: + void settingsFromUI(TabSettings &rc, + StorageSettings &storageSettings, + DisplaySettings &displaySettings) const; + void settingsToUI(); + struct GeneralSettingsPagePrivate; + GeneralSettingsPagePrivate *m_d; +}; + +} // namespace TextEditor + +#endif // GENERALSETTINGSPAGE_H diff --git a/src/plugins/texteditor/generalsettingspage.ui b/src/plugins/texteditor/generalsettingspage.ui new file mode 100644 index 00000000000..11f6b8b997e --- /dev/null +++ b/src/plugins/texteditor/generalsettingspage.ui @@ -0,0 +1,336 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>TextEditor::generalSettingsPage</class> + <widget class="QWidget" name="TextEditor::generalSettingsPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>514</width> + <height>427</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Tab/Indent Settings</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QCheckBox" name="autoIndent"> + <property name="text"> + <string>Enable automatic &indentation</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="labelTabSize"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Ta&b size:</string> + </property> + <property name="buddy"> + <cstring>tabSize</cstring> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>5</width> + <height>5</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QSpinBox" name="tabSize"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximum"> + <number>20</number> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="3"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>10</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="insertSpaces"> + <property name="text"> + <string>Insert &spaces instead of tabs</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLabel" name="labelIndentSize"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Indent size:</string> + </property> + <property name="buddy"> + <cstring>indentSize</cstring> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>5</width> + <height>5</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QSpinBox" name="indentSize"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximum"> + <number>20</number> + </property> + </widget> + </item> + </layout> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="smartBackspace"> + <property name="text"> + <string>&Backspace follows indentation</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <spacer name="horizontalSpacer_5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>10</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Storage Settings</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QCheckBox" name="cleanWhitespace"> + <property name="text"> + <string>&Clean whitespace</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>30</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QCheckBox" name="inEntireDocument"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>In entire &document</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QCheckBox" name="addFinalNewLine"> + <property name="text"> + <string>&Ensure newline at end of file</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Display Settings</string> + </property> + <layout class="QGridLayout"> + <item row="0" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QCheckBox" name="showWrapColumn"> + <property name="text"> + <string>Display right &margin at column</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="wrapColumn"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maximum"> + <number>999</number> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="0"> + <widget class="QCheckBox" name="enableTextWrapping"> + <property name="text"> + <string>Enable text &wrapping</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QCheckBox" name="displayLineNumbers"> + <property name="text"> + <string>Display line &numbers</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QCheckBox" name="visualizeWhitespace"> + <property name="text"> + <string>&Visualize whitespace</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QCheckBox" name="displayFoldingMarkers"> + <property name="text"> + <string>Display &folding markers</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QCheckBox" name="highlightCurrentLine"> + <property name="text"> + <string>Highlight current &line</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>351</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>cleanWhitespace</sender> + <signal>toggled(bool)</signal> + <receiver>inEntireDocument</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>47</x> + <y>184</y> + </hint> + <hint type="destinationlabel"> + <x>91</x> + <y>212</y> + </hint> + </hints> + </connection> + <connection> + <sender>showWrapColumn</sender> + <signal>toggled(bool)</signal> + <receiver>wrapColumn</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>399</x> + <y>308</y> + </hint> + <hint type="destinationlabel"> + <x>474</x> + <y>308</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/texteditor/icompletioncollector.h b/src/plugins/texteditor/icompletioncollector.h new file mode 100644 index 00000000000..46dc3f8b3d6 --- /dev/null +++ b/src/plugins/texteditor/icompletioncollector.h @@ -0,0 +1,114 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef COMPLETIONCOLLECTORINTERFACE_H +#define COMPLETIONCOLLECTORINTERFACE_H + +#include "texteditor_global.h" + +#include <QtCore/QObject> +#include <QtCore/QVariant> +#include <QtGui/QIcon> +#include <QtGui/QKeyEvent> + +namespace TextEditor { + +class ICompletionCollector; +class ITextEditable; + +struct CompletionItem +{ + CompletionItem(ICompletionCollector *collector = 0) + : m_relevance(0), + m_collector(collector) + { } + + bool isValid() const + { return m_collector != 0; } + + operator bool() const + { return m_collector != 0; } + + QString m_text; + QString m_details; + QIcon m_icon; + QVariant m_data; + int m_relevance; + ICompletionCollector *m_collector; +}; + +/* Defines the interface to completion collectors. A completion collector tells + * the completion support code when a completion is triggered and provides the + * list of possible completions. It keeps an internal state so that it can be + * polled for the list of completions, which is reset with a call to reset. + */ +class TEXTEDITOR_EXPORT ICompletionCollector : public QObject +{ + Q_OBJECT +public: + ICompletionCollector(QObject *parent = 0) : QObject(parent) {} + virtual ~ICompletionCollector() {} + + /* This method should return whether the cursor is at a position which could + * trigger an autocomplete. It will be called each time a character is typed in + * the text editor. + */ + virtual bool triggersCompletion(ITextEditable *editor) = 0; + + // returns starting position + virtual int startCompletion(ITextEditable *editor) = 0; + + /* This method should add all the completions it wants to show into the list, + * based on the given cursor position. + */ + virtual void completions(QList<CompletionItem> *completions) = 0; + + /* This method should complete the given completion item. + */ + virtual void complete(const CompletionItem &item) = 0; + + /* This method gives the completion collector a chance to partially complete + * based on a set of items. The general use case is to complete the common + * prefix shared by all possible completion items. + * + * Returns whether the completion popup should be closed. + */ + virtual bool partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems) = 0; + + /* Called when it's safe to clean up the completion items. + */ + virtual void cleanup() = 0; +}; + +} // namespace TextEditor + +#endif // COMPLETIONCOLLECTORINTERFACE_H diff --git a/src/plugins/texteditor/images/finddirectory.png b/src/plugins/texteditor/images/finddirectory.png Binary files differnew file mode 100644 index 00000000000..f20c7d013e3 --- /dev/null +++ b/src/plugins/texteditor/images/finddirectory.png diff --git a/src/plugins/texteditor/images/finddocuments.png b/src/plugins/texteditor/images/finddocuments.png Binary files differnew file mode 100644 index 00000000000..a637456b589 --- /dev/null +++ b/src/plugins/texteditor/images/finddocuments.png diff --git a/src/plugins/texteditor/itexteditable.h b/src/plugins/texteditor/itexteditable.h new file mode 100644 index 00000000000..30ec5e14939 --- /dev/null +++ b/src/plugins/texteditor/itexteditable.h @@ -0,0 +1,64 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ITEXTEDITABLE_H +#define ITEXTEDITABLE_H + +#include "texteditor_global.h" +#include "itexteditor.h" + +namespace TextEditor { + +class TEXTEDITOR_EXPORT ITextEditable : public ITextEditor +{ + Q_OBJECT +public: + ITextEditable() {} + virtual ~ITextEditable() {} + + /* Removes 'length' characteres to the right of the cursor. */ + virtual void remove(int length) = 0; + + /* Inserts the given string to the right of the cursor. */ + virtual void insert(const QString &string) = 0; + + /* Replaces 'length' characters to the right of the cursor with the given string. */ + virtual void replace(int length, const QString &string) = 0; + + virtual void setCurPos(int pos) = 0; + + virtual void select(int toPos) = 0; +}; + +} // namespace TextEditor + +#endif // ITEXTEDITABLE_H diff --git a/src/plugins/texteditor/itexteditor.h b/src/plugins/texteditor/itexteditor.h new file mode 100644 index 00000000000..ca517577a00 --- /dev/null +++ b/src/plugins/texteditor/itexteditor.h @@ -0,0 +1,132 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ITEXTEDITOR_H +#define ITEXTEDITOR_H + +#include "texteditor_global.h" + +#include <coreplugin/editormanager/ieditor.h> + +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QtGui/QColor> +#include <QtGui/QIcon> + +QT_BEGIN_NAMESPACE +class QTextBlock; +QT_END_NAMESPACE + +namespace TextEditor { + +class ITextEditor; + +class TEXTEDITOR_EXPORT ITextMark : public QObject +{ + Q_OBJECT +public: + ITextMark(QObject *parent = 0) : QObject(parent) {} + virtual ~ITextMark() {} + + virtual QIcon icon() const = 0; + + virtual void updateLineNumber(int lineNumber) = 0; + virtual void updateBlock(const QTextBlock &block) = 0; + virtual void removedFromEditor() = 0; + virtual void documentClosing() = 0; +}; + +typedef QList<ITextMark *> TextMarks; + + +class TEXTEDITOR_EXPORT ITextMarkable : public QObject +{ + Q_OBJECT +public: + ITextMarkable(QObject *parent = 0) : QObject(parent) {} + virtual ~ITextMarkable() {} + virtual bool addMark(ITextMark *mark, int line) = 0; + + virtual TextMarks marksAt(int line) const = 0; + virtual void removeMark(ITextMark *mark) = 0; + virtual bool hasMark(ITextMark *mark) const = 0; + virtual void updateMark(ITextMark *mark) = 0; +}; + +class TEXTEDITOR_EXPORT ITextEditor : public Core::IEditor +{ + Q_OBJECT +public: + enum PositionOperation { + Current = 1, + EndOfLine = 2, + StartOfLine = 3, + Anchor = 4, + EndOfDoc = 5 + }; + + ITextEditor() {} + virtual ~ITextEditor() {} + + virtual int find(const QString &string) const = 0; + + virtual void gotoLine(int line, int column = 0) = 0; + + virtual int position(PositionOperation posOp = Current, int at = -1) const = 0; + virtual void convertPosition(int pos, int *line, int *column) const = 0; + virtual QRect cursorRect(int pos = -1) const = 0; + + virtual QString contents() const = 0; + virtual QString selectedText() const = 0; + virtual QString textAt(int pos, int length) const = 0; + virtual QChar characterAt(int pos) const = 0; + + virtual void triggerCompletions() = 0; + + virtual ITextMarkable *markableInterface() = 0; + + virtual void setContextHelpId(const QString &) = 0; + + virtual void setTextCodec(QTextCodec *) = 0; + virtual QTextCodec *textCodec() const = 0; + + +signals: + void contentsChanged(); + void markRequested(TextEditor::ITextEditor *editor, int line); + void tooltipRequested(TextEditor::ITextEditor *editor, const QPoint &globalPos, int position); + void contextHelpIdRequested(TextEditor::ITextEditor *editor, int position); +}; + +} // namespace TextEditor + +#endif // ITEXTEDITOR_H diff --git a/src/plugins/texteditor/linenumberfilter.cpp b/src/plugins/texteditor/linenumberfilter.cpp new file mode 100644 index 00000000000..ea079196f7c --- /dev/null +++ b/src/plugins/texteditor/linenumberfilter.cpp @@ -0,0 +1,80 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "linenumberfilter.h" +#include "itexteditor.h" + +#include <coreplugin/editormanager/editormanager.h> + +#include <QtCore/QVariant> + +using namespace Core; +using namespace QuickOpen; +using namespace TextEditor; +using namespace TextEditor::Internal; + +LineNumberFilter::LineNumberFilter(EditorManager *editorManager, QObject *parent): + IQuickOpenFilter(parent) +{ + m_editorManager = editorManager; + setShortcutString("l"); + setIncludedByDefault(true); +} + +QList<FilterEntry> LineNumberFilter::matchesFor(const QString &entry) +{ + bool ok; + QList<FilterEntry> value; + int line = entry.toInt(&ok); + if (line > 0 && currentTextEditor()) + value.append(FilterEntry(this, QString("Line %1").arg(line), QVariant(line))); + return value; +} + +void LineNumberFilter::accept(FilterEntry selection) const +{ + ITextEditor *editor = currentTextEditor(); + if (editor) { + m_editorManager->ensureEditorManagerVisible(); + m_editorManager->addCurrentPositionToNavigationHistory(true); + editor->gotoLine(selection.internalData.toInt()); + m_editorManager->addCurrentPositionToNavigationHistory(); + editor->widget()->setFocus(); + } +} + +ITextEditor *LineNumberFilter::currentTextEditor() const +{ + if (!m_editorManager->currentEditor()) + return 0; + return qobject_cast<TextEditor::ITextEditor*>(m_editorManager->currentEditor()); +} diff --git a/src/plugins/texteditor/linenumberfilter.h b/src/plugins/texteditor/linenumberfilter.h new file mode 100644 index 00000000000..f20de8a718e --- /dev/null +++ b/src/plugins/texteditor/linenumberfilter.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef LINENUMBERFILTER_H +#define LINENUMBERFILTER_H + +#include <quickopen/iquickopenfilter.h> + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QByteArray> +#include <QtCore/QFutureInterface> +#include <QtGui/QWidget> + +namespace Core { +class EditorManager; +} + +namespace TextEditor { + +class ITextEditor; + +namespace Internal { + +class LineNumberFilter : public QuickOpen::IQuickOpenFilter +{ + Q_OBJECT + +public: + LineNumberFilter(Core::EditorManager *editorManager, QObject *parent = 0); + QString trName() const { return tr("Line in current document"); } + QString name() const { return "Line in current document"; } + QuickOpen::IQuickOpenFilter::Priority priority() const { return QuickOpen::IQuickOpenFilter::High; } + QList<QuickOpen::FilterEntry> matchesFor(const QString &entry); + void accept(QuickOpen::FilterEntry selection) const; + void refresh(QFutureInterface<void> &) {} + +private: + ITextEditor *currentTextEditor() const; + + Core::EditorManager *m_editorManager; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // LINENUMBERFILTER_H diff --git a/src/plugins/texteditor/plaintexteditor.cpp b/src/plugins/texteditor/plaintexteditor.cpp new file mode 100644 index 00000000000..6019d73b251 --- /dev/null +++ b/src/plugins/texteditor/plaintexteditor.cpp @@ -0,0 +1,129 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "plaintexteditor.h" +#include "texteditorconstants.h" +#include "texteditorplugin.h" + +#include <coreplugin/coreconstants.h> +#include <coreplugin/icore.h> +#include <coreplugin/uniqueidmanager.h> + +using namespace TextEditor; +using namespace TextEditor::Internal; + +PlainTextEditorEditable::PlainTextEditorEditable(PlainTextEditor *editor) + :BaseTextEditorEditable(editor) +{ + Core::ICore *core = TextEditorPlugin::core(); + m_context << core->uniqueIDManager()-> + uniqueIdentifier(Core::Constants::K_DEFAULT_TEXT_EDITOR); + m_context << core->uniqueIDManager()-> + uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR); +} + +PlainTextEditor::PlainTextEditor(QWidget *parent) : + BaseTextEditor(parent) +{ + + setRevisionsVisible(true); + setMarksVisible(true); + setRequestMarkEnabled(false); + setLineSeparatorsAllowed(true); + + setMimeType(QLatin1String(TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_TEXT)); +} + +QList<int> PlainTextEditorEditable::context() const +{ + return m_context; +} + + +Core::IEditor *PlainTextEditorEditable::duplicate(QWidget *parent) +{ + PlainTextEditor *newEditor = new PlainTextEditor(parent); + newEditor->duplicateFrom(editor()); + TextEditorPlugin::instance()->initializeEditor(newEditor); + return newEditor->editableInterface(); +} + +const char *PlainTextEditorEditable::kind() const +{ + return Core::Constants::K_DEFAULT_TEXT_EDITOR; +} + +// Indent a text block based on previous line. +// Simple text paragraph layout: +// aaaa aaaa +// +// bbb bb +// bbb bb +// +// - list +// list line2 +// +// - listn +// +// ccc +// +// @todo{Add formatting to wrap paragraphs. This requires some +// hoops as the current indentation routines are not prepared +// for additional block being inserted. It might be possible +// to do in 2 steps (indenting/wrapping)} +// + +void PlainTextEditor::indentBlock(QTextDocument *doc, QTextBlock block, QChar /* typedChar */) +{ + // At beginning: Leave as is. + if (block == doc->begin()) + return; + + const QTextBlock previous = block.previous(); + const QString previousText = previous.text(); + // Empty line indicates a start of a new paragraph. Leave as is. + if (previousText.isEmpty() || previousText.trimmed().isEmpty()) + return; + + // Just use previous line. + // Skip non-alphanumerical characters when determining the indentation + // to enable writing bulleted lists whose items span several lines. + int i = 0; + while (i < previousText.size()) { + if (previousText.at(i).isLetterOrNumber()) { + const TextEditor::TabSettings &ts = tabSettings(); + ts.indentLine(block, ts.columnAt(previousText, i)); + break; + } + ++i; + } +} diff --git a/src/plugins/texteditor/plaintexteditor.h b/src/plugins/texteditor/plaintexteditor.h new file mode 100644 index 00000000000..76415807899 --- /dev/null +++ b/src/plugins/texteditor/plaintexteditor.h @@ -0,0 +1,72 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLAINTEXTEDITOR_H +#define PLAINTEXTEDITOR_H + +#include "basetexteditor.h" + +#include <QtCore/QList> + +namespace TextEditor { + +class PlainTextEditor; + +class TEXTEDITOR_EXPORT PlainTextEditorEditable : public BaseTextEditorEditable +{ +public: + PlainTextEditorEditable(PlainTextEditor *); + QList<int> context() const; + + bool duplicateSupported() const { return true; } + Core::IEditor *duplicate(QWidget *parent); + const char *kind() const; +private: + QList<int> m_context; +}; + +class TEXTEDITOR_EXPORT PlainTextEditor : public BaseTextEditor +{ + Q_OBJECT + +public: + PlainTextEditor(QWidget *parent); + +protected: + BaseTextEditorEditable *createEditableInterface() { return new PlainTextEditorEditable(this); } + // Indent a text block based on previous line. + virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar); +}; + +} // namespace TextEditor + +#endif // PLAINTEXTEDITOR_H diff --git a/src/plugins/texteditor/plaintexteditorfactory.cpp b/src/plugins/texteditor/plaintexteditorfactory.cpp new file mode 100644 index 00000000000..24e36bfff61 --- /dev/null +++ b/src/plugins/texteditor/plaintexteditorfactory.cpp @@ -0,0 +1,83 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "plaintexteditor.h" +#include "plaintexteditorfactory.h" +#include "texteditorconstants.h" +#include "texteditorplugin.h" +#include "texteditoractionhandler.h" + +#include <coreplugin/coreconstants.h> +#include <coreplugin/editormanager/editormanager.h> + +using namespace TextEditor; +using namespace TextEditor::Internal; + +PlainTextEditorFactory::PlainTextEditorFactory(QObject *parent) : + Core::IEditorFactory(parent), + m_kind(Core::Constants::K_DEFAULT_TEXT_EDITOR) +{ + m_actionHandler = new TextEditorActionHandler(TextEditorPlugin::core(), + QLatin1String(TextEditor::Constants::C_TEXTEDITOR), + TextEditorActionHandler::Format); + m_mimeTypes << QLatin1String(TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_TEXT) + << QLatin1String(TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_XML); +} + +PlainTextEditorFactory::~PlainTextEditorFactory() +{ + delete m_actionHandler; +} + +QString PlainTextEditorFactory::kind() const +{ + return m_kind; +} + +Core::IFile *PlainTextEditorFactory::open(const QString &fileName) +{ + Core::ICore *core = TextEditorPlugin::core(); + Core::IEditor *iface = core->editorManager()->openEditor(fileName, kind()); + return iface ? iface->file() : 0; +} + +Core::IEditor *PlainTextEditorFactory::createEditor(QWidget *parent) +{ + PlainTextEditor *rc = new PlainTextEditor(parent); + TextEditorPlugin::instance()->initializeEditor(rc); + return rc->editableInterface(); +} + +QStringList PlainTextEditorFactory::mimeTypes() const +{ + return m_mimeTypes; +} diff --git a/src/plugins/texteditor/plaintexteditorfactory.h b/src/plugins/texteditor/plaintexteditorfactory.h new file mode 100644 index 00000000000..0e225c942e6 --- /dev/null +++ b/src/plugins/texteditor/plaintexteditorfactory.h @@ -0,0 +1,73 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PLAINTEXTEDITORFACTORY_H +#define PLAINTEXTEDITORFACTORY_H + +#include <coreplugin/editormanager/ieditorfactory.h> +#include <QtCore/QStringList> + +namespace Core { +class IEditor; +class IFile; +} + +namespace TextEditor { +class TextEditorActionHandler; +namespace Internal { + +class PlainTextEditorFactory : public Core::IEditorFactory +{ + Q_OBJECT + +public: + PlainTextEditorFactory(QObject *parent = 0); + virtual ~PlainTextEditorFactory(); + + virtual QStringList mimeTypes() const; + //Core::IEditorFactory + QString kind() const; + Core::IFile *open(const QString &fileName); + Core::IEditor *createEditor(QWidget *parent); + + TextEditor::TextEditorActionHandler *actionHandler() const { return m_actionHandler; } + +private: + const QString m_kind; + QStringList m_mimeTypes; + TextEditor::TextEditorActionHandler *m_actionHandler; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // PLAINTEXTEDITORFACTORY_H diff --git a/src/plugins/texteditor/storagesettings.cpp b/src/plugins/texteditor/storagesettings.cpp new file mode 100644 index 00000000000..906215bf811 --- /dev/null +++ b/src/plugins/texteditor/storagesettings.cpp @@ -0,0 +1,81 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "storagesettings.h" +#include <QSettings> +#include <QString> + +namespace TextEditor { + +static const char * const cleanWhitespaceKey = "cleanWhitespace"; +static const char * const inEntireDocumentKey = "inEntireDocument"; +static const char * const addFinalNewLineKey = "addFinalNewLine"; +static const char * const groupPostfix = "StorageSettings"; + +StorageSettings::StorageSettings() + : m_cleanWhitespace(true), + m_inEntireDocument(false), + m_addFinalNewLine(true) +{ +} + +void StorageSettings::toSettings(const QString &category, QSettings *s) const +{ + QString group = QLatin1String(groupPostfix); + if (!category.isEmpty()) + group.insert(0, category); + s->beginGroup(group); + s->setValue(QLatin1String(cleanWhitespaceKey), m_cleanWhitespace); + s->setValue(QLatin1String(inEntireDocumentKey), m_inEntireDocument); + s->setValue(QLatin1String(addFinalNewLineKey), m_addFinalNewLine); + s->endGroup(); +} + +void StorageSettings::fromSettings(const QString &category, const QSettings *s) +{ + QString group = QLatin1String(groupPostfix); + if (!category.isEmpty()) + group.insert(0, category); + group += QLatin1Char('/'); + m_cleanWhitespace = s->value(group + QLatin1String(cleanWhitespaceKey), m_cleanWhitespace).toBool(); + m_inEntireDocument = s->value(group + QLatin1String(inEntireDocumentKey), m_inEntireDocument).toBool(); + m_addFinalNewLine = s->value(group + QLatin1String(addFinalNewLineKey), m_addFinalNewLine).toBool(); +} + +bool StorageSettings::equals(const StorageSettings &ts) const +{ + return m_addFinalNewLine == ts.m_addFinalNewLine + && m_cleanWhitespace == ts.m_cleanWhitespace + && m_inEntireDocument == ts.m_inEntireDocument; +} + +} // namespace TextEditor diff --git a/src/plugins/texteditor/storagesettings.h b/src/plugins/texteditor/storagesettings.h new file mode 100644 index 00000000000..dc3310094a9 --- /dev/null +++ b/src/plugins/texteditor/storagesettings.h @@ -0,0 +1,62 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef STORAGESETTINGS_H +#define STORAGESETTINGS_H + +#include "texteditor_global.h" + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace TextEditor { + +struct TEXTEDITOR_EXPORT StorageSettings { + StorageSettings(); + + void toSettings(const QString &category, QSettings *s) const; + void fromSettings(const QString &category, const QSettings *s); + + bool equals(const StorageSettings &ts) const; + + bool m_cleanWhitespace; + bool m_inEntireDocument; + bool m_addFinalNewLine; +}; + +inline bool operator==(const StorageSettings &t1, const StorageSettings &t2) { return t1.equals(t2); } +inline bool operator!=(const StorageSettings &t1, const StorageSettings &t2) { return !t1.equals(t2); } + +} // namespace TextEditor + +#endif // STORAGESETTINGS_H diff --git a/src/plugins/texteditor/tabsettings.cpp b/src/plugins/texteditor/tabsettings.cpp new file mode 100644 index 00000000000..daaee1a562f --- /dev/null +++ b/src/plugins/texteditor/tabsettings.cpp @@ -0,0 +1,241 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "tabsettings.h" + +#include <QtCore/QSettings> +#include <QtCore/QString> +#include <QtGui/QTextCursor> +#include <QtGui/QTextDocument> +#include <QDebug> + +static const char* spacesForTabsKey = "SpacesForTabs"; +static const char* smartBackspaceKey = "SmartBackspace"; +static const char* autoIndentKey = "AutoIndent"; +static const char* tabSizeKey = "TabSize"; +static const char* indentSizeKey = "IndentSize"; +static const char* groupPostfix = "TabSettings"; + +namespace TextEditor { + +TabSettings::TabSettings() : + m_spacesForTabs(true), + m_autoIndent(true), + m_smartBackspace(false), + m_tabSize(8), + m_indentSize(4) +{ +} + +void TabSettings::toSettings(const QString &category, QSettings *s) const +{ + QString group = QLatin1String(groupPostfix); + if (!category.isEmpty()) + group.insert(0, category); + s->beginGroup(group); + s->setValue(QLatin1String(spacesForTabsKey), m_spacesForTabs); + s->setValue(QLatin1String(autoIndentKey), m_autoIndent); + s->setValue(QLatin1String(smartBackspaceKey), m_smartBackspace); + s->setValue(QLatin1String(tabSizeKey), m_tabSize); + s->setValue(QLatin1String(indentSizeKey), m_indentSize); + s->endGroup(); +} + +void TabSettings::fromSettings(const QString &category, const QSettings *s) +{ + QString group = QLatin1String(groupPostfix); + if (!category.isEmpty()) + group.insert(0, category); + group += QLatin1Char('/'); + + *this = TabSettings(); // Assign defaults + + m_spacesForTabs = s->value(group + QLatin1String(spacesForTabsKey), m_spacesForTabs).toBool(); + m_autoIndent = s->value(group + QLatin1String(autoIndentKey), m_autoIndent).toBool(); + m_smartBackspace = s->value(group + QLatin1String(smartBackspaceKey), m_smartBackspace).toBool(); + m_tabSize = s->value(group + QLatin1String(tabSizeKey), m_tabSize).toInt(); + m_indentSize = s->value(group + QLatin1String(indentSizeKey), m_indentSize).toInt(); +} + + + +int TabSettings::lineIndentPosition(const QString &text) const +{ + int i = 0; + while (i < text.size()) { + if (!text.at(i).isSpace()) + break; + ++i; + } + int column = columnAt(text, i); + return i - (column % m_indentSize); +} + +int TabSettings::firstNonSpace(const QString &text) const +{ + int i = 0; + while (i < text.size()) { + if (!text.at(i).isSpace()) + return i; + ++i; + } + return i; +} + +int TabSettings::trailingWhitespaces(const QString &text) const +{ + int i = 0; + while (i < text.size()) { + if (!text.at(text.size()-1-i).isSpace()) + return i; + ++i; + } + return i; +} + +bool TabSettings::isIndentationClean(const QString &text) const +{ + int i = 0; + int spaceCount = 0; + while (i < text.size()) { + QChar c = text.at(i); + if (!c.isSpace()) + return true; + + if (c == QLatin1Char(' ')) { + ++spaceCount; + if (spaceCount == m_tabSize) + return false; + } else if (c == QLatin1Char('\t')) { + if (m_spacesForTabs || spaceCount != m_indentSize) + return false; + spaceCount = 0; + } + ++i; + } + return true; +} + + +int TabSettings::columnAt(const QString &text, int position) const +{ + int column = 0; + for (int i = 0; i < position; ++i) { + if (text.at(i) == QLatin1Char('\t')) + column = column - (column % m_tabSize) + m_tabSize; + else + ++column; + } + return column; +} + +int TabSettings::spacesLeftFromPosition(const QString &text, int position) const +{ + int i = position; + while (i > 0) { + if (!text.at(i-1).isSpace()) + break; + --i; + } + return position - i; +} + +int TabSettings::indentedColumn(int column, bool doIndent) const +{ + int aligned = (column / m_indentSize) * m_indentSize; + if (doIndent) + return aligned + m_indentSize; + if (aligned < column) + return aligned; + return qMax(0, aligned - m_indentSize); +} + +QString TabSettings::indentationString(int startColumn, int targetColumn) const +{ + targetColumn = qMax(startColumn, targetColumn); + if (m_spacesForTabs) + return QString(targetColumn - startColumn, QLatin1Char(' ')); + + QString s; + int alignedStart = startColumn - (startColumn % m_tabSize) + m_tabSize; + if (alignedStart > startColumn && alignedStart <= targetColumn) { + s += QLatin1Char('\t'); + startColumn = alignedStart; + } + if (int columns = targetColumn - startColumn) { + int tabs = columns / m_tabSize; + s += QString(tabs, QLatin1Char('\t')); + s += QString(columns - tabs * m_tabSize, QLatin1Char(' ')); + } + return s; +} + +void TabSettings::indentLine(QTextBlock block, int newIndent) const +{ + const QString text = block.text(); + const int oldBlockLength = text.size(); + + // Quickly check whether indenting is required. + if (oldBlockLength == 0 && newIndent == 0) + return; + + const QString indentString = indentationString(0, newIndent); + newIndent = indentString.length(); + + if (oldBlockLength == indentString.length() && text == indentString) + return; + + if (oldBlockLength > indentString.length() && + text.startsWith(indentString) && + !text.at(indentString.length()).isSpace()) { + return; + } + + QTextCursor cursor(block); + cursor.beginEditBlock(); + cursor.movePosition(QTextCursor::StartOfBlock); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, firstNonSpace(text)); + cursor.removeSelectedText(); + cursor.insertText(indentString); + cursor.endEditBlock(); +} + +bool TabSettings::equals(const TabSettings &ts) const +{ + return m_spacesForTabs == ts.m_spacesForTabs + && m_autoIndent == ts.m_autoIndent + && m_smartBackspace == ts.m_smartBackspace + && m_tabSize == ts.m_tabSize + && m_indentSize == ts.m_indentSize; +} + +} // namespace TextEditor diff --git a/src/plugins/texteditor/tabsettings.h b/src/plugins/texteditor/tabsettings.h new file mode 100644 index 00000000000..8b326fadc47 --- /dev/null +++ b/src/plugins/texteditor/tabsettings.h @@ -0,0 +1,83 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef TABSETTINGS_H +#define TABSETTINGS_H + +#include "texteditor_global.h" + +#include <QtGui/QTextBlock> + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace TextEditor { + +// Tab settings: Data type the GeneralSettingsPage acts on +// with some convenience functions for formatting. +struct TEXTEDITOR_EXPORT TabSettings +{ + TabSettings(); + + void toSettings(const QString &category, QSettings *s) const; + void fromSettings(const QString &category, const QSettings *s); + + + int lineIndentPosition(const QString &text) const; + int firstNonSpace(const QString &text) const; + int columnAt(const QString &text, int position) const; + int spacesLeftFromPosition(const QString &text, int position) const; + int indentedColumn(int column, bool doIndent = true) const; + QString indentationString(int startColumn, int targetColumn) const; + + void indentLine(QTextBlock block, int newIndent) const; + + int trailingWhitespaces(const QString &text) const; + bool isIndentationClean(const QString &text) const; + + + bool m_spacesForTabs; + bool m_autoIndent; + bool m_smartBackspace; + int m_tabSize; + int m_indentSize; + + bool equals(const TabSettings &ts) const; +}; + +inline bool operator==(const TabSettings &t1, const TabSettings &t2) { return t1.equals(t2); } +inline bool operator!=(const TabSettings &t1, const TabSettings &t2) { return !t1.equals(t2); } + +} // namespace TextEditor + +#endif // TABSETTINGS_H diff --git a/src/plugins/texteditor/textblockiterator.cpp b/src/plugins/texteditor/textblockiterator.cpp new file mode 100644 index 00000000000..51c894d9fc1 --- /dev/null +++ b/src/plugins/texteditor/textblockiterator.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "textblockiterator.h" + +#include <QtGui/QTextDocument> + +using namespace TextEditor; + +TextBlockIterator::TextBlockIterator() + : m_document(0), + m_initialized(false) +{ } + +TextBlockIterator::TextBlockIterator(const QTextBlock &block) + : m_document(block.document()), + m_block(block), + m_initialized(false) +{ } + +bool TextBlockIterator::equals(const TextBlockIterator &other) const +{ + if (m_document != other.m_document) + return false; + return m_block == other.m_block; +} + +QString TextBlockIterator::operator*() const +{ + if (! m_initialized) + read(); + return m_text; +} + +void TextBlockIterator::read() const +{ + m_initialized = true; + m_text = m_block.text(); +} + +TextBlockIterator &TextBlockIterator::operator++() +{ + m_initialized = false; + m_block = m_block.next(); + return *this; +} + +TextBlockIterator &TextBlockIterator::operator--() +{ + m_initialized = false; + m_block = m_block.previous(); + return *this; +} + +TextBlockIterator TextBlockIterator::operator++(int) +{ + TextBlockIterator prev; + ++*this; + return prev; +} + +TextBlockIterator TextBlockIterator::operator--(int) +{ + TextBlockIterator prev; + --*this; + return prev; +} diff --git a/src/plugins/texteditor/textblockiterator.h b/src/plugins/texteditor/textblockiterator.h new file mode 100644 index 00000000000..0f9c585a6f3 --- /dev/null +++ b/src/plugins/texteditor/textblockiterator.h @@ -0,0 +1,71 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef _TEXTBLOCKITERATOR_H +#define _TEXTBLOCKITERATOR_H + +#include "texteditor_global.h" + +#include <QtGui/QTextBlock> + +namespace TextEditor { + +/* Iterator for the text blocks of a document. */ +class TEXTEDITOR_EXPORT TextBlockIterator { +public: + TextBlockIterator(); + TextBlockIterator(const QTextBlock &block); + + bool equals(const TextBlockIterator &o) const; + + QString operator*() const; + TextBlockIterator &operator++(); + TextBlockIterator &operator--(); + TextBlockIterator operator++(int); + TextBlockIterator operator--(int); + +private: + void read() const; + +private: + const QTextDocument *m_document; + QTextBlock m_block; + mutable QString m_text; + mutable bool m_initialized; +}; + +inline bool operator==(const TextBlockIterator &i1, const TextBlockIterator &i2) { return i1.equals(i2); } +inline bool operator!=(const TextBlockIterator &i1, const TextBlockIterator &i2) { return !i1.equals(i2); } + +} // namespace TextEditor + +#endif // _TEXTBLOCKITERATOR_H diff --git a/src/plugins/texteditor/texteditor.pri b/src/plugins/texteditor/texteditor.pri new file mode 100644 index 00000000000..9d3789b8253 --- /dev/null +++ b/src/plugins/texteditor/texteditor.pri @@ -0,0 +1,3 @@ +include(texteditor_dependencies.pri) + +LIBS *= -l$$qtLibraryTarget(TextEditor) diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro new file mode 100644 index 00000000000..59c9cc4bb41 --- /dev/null +++ b/src/plugins/texteditor/texteditor.pro @@ -0,0 +1,58 @@ +TEMPLATE = lib +TARGET = TextEditor +DEFINES += TEXTEDITOR_LIBRARY +include(../../qworkbenchplugin.pri) +include(texteditor_dependencies.pri) +SOURCES += texteditorplugin.cpp \ + textfilewizard.cpp \ + plaintexteditor.cpp \ + plaintexteditorfactory.cpp \ + basetextdocument.cpp \ + basetexteditor.cpp \ + texteditoractionhandler.cpp \ + completionsupport.cpp \ + completionwidget.cpp \ + fontsettingspage.cpp \ + tabsettings.cpp \ + storagesettings.cpp \ + displaysettings.cpp \ + fontsettings.cpp \ + textblockiterator.cpp \ + linenumberfilter.cpp \ + generalsettingspage.cpp \ + basetextmark.cpp \ + findinfiles.cpp \ + basefilefind.cpp \ + texteditorsettings.cpp \ + codecselector.cpp +HEADERS += texteditorplugin.h \ + textfilewizard.h \ + plaintexteditor.h \ + plaintexteditorfactory.h \ + basetexteditor_p.h \ + basetextdocument.h \ + completionsupport.h \ + completionwidget.h \ + basetexteditor.h \ + texteditoractionhandler.h \ + fontsettingspage.h \ + icompletioncollector.h \ + texteditorconstants.h \ + tabsettings.h \ + storagesettings.h \ + displaysettings.h \ + fontsettings.h \ + textblockiterator.h \ + itexteditable.h \ + itexteditor.h \ + linenumberfilter.h \ + texteditor_global.h \ + generalsettingspage.h \ + basetextmark.h \ + findinfiles.h \ + basefilefind.h \ + texteditorsettings.h \ + codecselector.h +FORMS += fontsettingspage.ui \ + generalsettingspage.ui +RESOURCES += texteditor.qrc diff --git a/src/plugins/texteditor/texteditor.qrc b/src/plugins/texteditor/texteditor.qrc new file mode 100644 index 00000000000..191343ce0e6 --- /dev/null +++ b/src/plugins/texteditor/texteditor.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/texteditor" > + <file>images/finddocuments.png</file> + <file>images/finddirectory.png</file> + <file>TextEditor.mimetypes.xml</file> + </qresource> +</RCC> diff --git a/src/plugins/texteditor/texteditor_dependencies.pri b/src/plugins/texteditor/texteditor_dependencies.pri new file mode 100644 index 00000000000..87f23e9eede --- /dev/null +++ b/src/plugins/texteditor/texteditor_dependencies.pri @@ -0,0 +1,4 @@ +include(../../libs/utils/utils.pri) +include(../../plugins/find/find.pri) +include(../../plugins/quickopen/quickopen.pri) +include(../../plugins/coreplugin/coreplugin.pri) diff --git a/src/plugins/texteditor/texteditor_global.h b/src/plugins/texteditor/texteditor_global.h new file mode 100644 index 00000000000..ef7f01c6417 --- /dev/null +++ b/src/plugins/texteditor/texteditor_global.h @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/**************************************************************************** +** +** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef TEXTEDITOR_GLOBAL_H +#define TEXTEDITOR_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(TEXTEDITOR_LIBRARY) +# define TEXTEDITOR_EXPORT Q_DECL_EXPORT +#else +# define TEXTEDITOR_EXPORT Q_DECL_IMPORT +#endif + +#endif // TEXTEDITOR_GLOBAL_H diff --git a/src/plugins/texteditor/texteditoractionhandler.cpp b/src/plugins/texteditor/texteditoractionhandler.cpp new file mode 100644 index 00000000000..91246dcc8c3 --- /dev/null +++ b/src/plugins/texteditor/texteditoractionhandler.cpp @@ -0,0 +1,459 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "texteditoractionhandler.h" +#include "texteditorconstants.h" +#include "basetexteditor.h" +#include "texteditorplugin.h" +#include "linenumberfilter.h" + +#include <quickopen/quickopenmanager.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> + +#include <QtCore/QSet> +#include <QtCore/QtDebug> +#include <QtGui/QAction> +#include <QtGui/QTextCursor> + +using namespace TextEditor; +using namespace TextEditor::Internal; + +TextEditorActionHandler::TextEditorActionHandler(Core::ICore *core, + const QString &context, + uint optionalActions) : + QObject(core), + m_optionalActions(optionalActions), + m_currentEditor(0), + m_core(core), + m_initialized(false) +{ + m_undoAction = m_redoAction = m_copyAction = m_cutAction = m_pasteAction + = m_selectAllAction = m_gotoAction = m_printAction = m_formatAction + = m_visualizeWhitespaceAction = m_textWrappingAction + = m_unCommentSelectionAction = m_unCollapseAllAction + = m_collapseAction = m_expandAction + = m_deleteLineAction = m_selectEncodingAction + = m_increaseFontSizeAction = m_decreaseFontSizeAction + = 0; + + m_contextId << m_core->uniqueIDManager()->uniqueIdentifier(context); + + connect(m_core, SIGNAL(contextAboutToChange(Core::IContext *)), + this, SLOT(updateCurrentEditor(Core::IContext *))); +} + +void TextEditorActionHandler::setupActions(BaseTextEditor *editor) +{ + initializeActions(); + editor->setActionHack(this); + QObject::connect(editor, SIGNAL(undoAvailable(bool)), this, SLOT(updateUndoAction())); + QObject::connect(editor, SIGNAL(redoAvailable(bool)), this, SLOT(updateRedoAction())); + QObject::connect(editor, SIGNAL(copyAvailable(bool)), this, SLOT(updateCopyAction())); +} + + +void TextEditorActionHandler::initializeActions() +{ + if (!m_initialized) { + createActions(); + m_initialized = true; + } +} + +void TextEditorActionHandler::createActions() +{ + m_undoAction = registerNewAction(QLatin1String(Core::Constants::UNDO), this, SLOT(undoAction()), + tr("&Undo")); + m_redoAction = registerNewAction(QLatin1String(Core::Constants::REDO), this, SLOT(redoAction()), + tr("&Redo")); + m_copyAction = registerNewAction(QLatin1String(Core::Constants::COPY), this, SLOT(copyAction())); + m_cutAction = registerNewAction(QLatin1String(Core::Constants::CUT), this, SLOT(cutAction())); + m_pasteAction = registerNewAction(QLatin1String(Core::Constants::PASTE), this, SLOT(pasteAction())); + m_selectAllAction = registerNewAction(QLatin1String(Core::Constants::SELECTALL), this, SLOT(selectAllAction())); + m_gotoAction = registerNewAction(QLatin1String(Core::Constants::GOTO), this, SLOT(gotoAction())); + m_printAction = registerNewAction(QLatin1String(Core::Constants::PRINT), this, SLOT(printAction())); + + Core::ActionManagerInterface *am = m_core->actionManager(); + + Core::IActionContainer *medit = am->actionContainer(Core::Constants::M_EDIT); + Core::IActionContainer *advancedMenu = am->actionContainer(Core::Constants::M_EDIT_ADVANCED); + + m_selectEncodingAction = new QAction(tr("Select Encoding..."), this); + Core::ICommand *command = am->registerAction(m_selectEncodingAction, Constants::SELECT_ENCODING, m_contextId); + connect(m_selectEncodingAction, SIGNAL(triggered()), this, SLOT(selectEncoding())); + medit->addAction(command, Core::Constants::G_EDIT_OTHER); + + + m_formatAction = new QAction(tr("Auto-&indent Selection"), this); + command = am->registerAction(m_formatAction, TextEditor::Constants::AUTO_INDENT_SELECTION, m_contextId); + command->setDefaultKeySequence(QKeySequence(tr("Ctrl+I"))); + advancedMenu->addAction(command); + connect(m_formatAction, SIGNAL(triggered(bool)), this, SLOT(formatAction())); + + + m_visualizeWhitespaceAction = new QAction(tr("Visualize &Whitespace"), this); + m_visualizeWhitespaceAction->setCheckable(true); + command = am->registerAction(m_visualizeWhitespaceAction, + TextEditor::Constants::VISUALIZE_WHITESPACE, m_contextId); +#ifndef Q_OS_MAC + command->setDefaultKeySequence(QKeySequence(tr("Ctrl+E, Ctrl+V"))); +#endif + + advancedMenu->addAction(command); + connect(m_visualizeWhitespaceAction, SIGNAL(triggered(bool)), this, SLOT(setVisualizeWhitespace(bool))); + + m_textWrappingAction = new QAction(tr("Enable Text &Wrapping"), this); + m_textWrappingAction->setCheckable(true); + command = am->registerAction(m_textWrappingAction, + TextEditor::Constants::TEXT_WRAPPING, m_contextId); +#ifndef Q_OS_MAC + command->setDefaultKeySequence(QKeySequence(tr("Ctrl+E, Ctrl+W"))); +#endif + advancedMenu->addAction(command); + connect(m_textWrappingAction, SIGNAL(triggered(bool)), this, SLOT(setTextWrapping(bool))); + + + m_unCommentSelectionAction = new QAction(tr("(Un)Comment &Selection"), this); + command = am->registerAction(m_unCommentSelectionAction, Constants::UN_COMMENT_SELECTION, m_contextId); + command->setDefaultKeySequence(QKeySequence(tr("Ctrl+/"))); + connect(m_unCommentSelectionAction, SIGNAL(triggered()), this, SLOT(unCommentSelection())); + advancedMenu->addAction(command); + + m_deleteLineAction = new QAction(tr("Delete &Line"), this); + command = am->registerAction(m_deleteLineAction, Constants::DELETE_LINE, m_contextId); + command->setDefaultKeySequence(QKeySequence(tr("Shift+Del"))); + connect(m_deleteLineAction, SIGNAL(triggered()), this, SLOT(deleteLine())); + + m_collapseAction = new QAction(tr("Collapse"), this); + command = am->registerAction(m_collapseAction, Constants::COLLAPSE, m_contextId); + command->setDefaultKeySequence(QKeySequence(tr("Ctrl+<"))); + connect(m_collapseAction, SIGNAL(triggered()), this, SLOT(collapse())); + advancedMenu->addAction(command); + + m_expandAction = new QAction(tr("Expand"), this); + command = am->registerAction(m_expandAction, Constants::EXPAND, m_contextId); + command->setDefaultKeySequence(QKeySequence(tr("Ctrl+>"))); + connect(m_expandAction, SIGNAL(triggered()), this, SLOT(expand())); + advancedMenu->addAction(command); + + m_unCollapseAllAction = new QAction(tr("(Un)&Collapse All"), this); + command = am->registerAction(m_unCollapseAllAction, Constants::UN_COLLAPSE_ALL, m_contextId); + connect(m_unCollapseAllAction, SIGNAL(triggered()), this, SLOT(unCollapseAll())); + advancedMenu->addAction(command); + + m_increaseFontSizeAction = new QAction(tr("Increase Font Size"), this); + command = am->registerAction(m_increaseFontSizeAction, Constants::INCREASE_FONT_SIZE, m_contextId); + command->setDefaultKeySequence(QKeySequence(tr("Ctrl++"))); + connect(m_increaseFontSizeAction, SIGNAL(triggered()), this, SLOT(increaseFontSize())); + advancedMenu->addAction(command); + + m_decreaseFontSizeAction = new QAction(tr("Decrease Font Size"), this); + command = am->registerAction(m_decreaseFontSizeAction, Constants::DECREASE_FONT_SIZE, m_contextId); + command->setDefaultKeySequence(QKeySequence(tr("Ctrl+-"))); + connect(m_decreaseFontSizeAction, SIGNAL(triggered()), this, SLOT(decreaseFontSize())); + advancedMenu->addAction(command); +} + +bool TextEditorActionHandler::supportsAction(const QString & /*id */) const +{ + return true; +} + +QAction *TextEditorActionHandler::registerNewAction(const QString &id, const QString &title) +{ + if (!supportsAction(id)) + return 0; + + QAction *result = new QAction(title, this); + m_core->actionManager()->registerAction(result, id, m_contextId); + return result; +} + +QAction *TextEditorActionHandler::registerNewAction(const QString &id, + QObject *receiver, + const char *slot, + const QString &title) +{ + QAction *rc = registerNewAction(id, title); + if (!rc) + return 0; + + connect(rc, SIGNAL(triggered()), receiver, slot); + return rc; +} + +TextEditorActionHandler::UpdateMode TextEditorActionHandler::updateMode() const +{ + if (!m_currentEditor) + return NoEditor; + return m_currentEditor->file()->isReadOnly() ? ReadOnlyMode : WriteMode; +} + +void TextEditorActionHandler::updateActions() +{ + updateActions(updateMode()); +} + +void TextEditorActionHandler::updateActions(UpdateMode um) +{ + if (m_pasteAction) + m_pasteAction->setEnabled(um != NoEditor); + if (m_selectAllAction) + m_selectAllAction->setEnabled(um != NoEditor); + if (m_gotoAction) + m_gotoAction->setEnabled(um != NoEditor); + if (m_selectEncodingAction) + m_selectEncodingAction->setEnabled(um != NoEditor); + if (m_printAction) + m_printAction->setEnabled(um != NoEditor); + if (m_formatAction) + m_formatAction->setEnabled((m_optionalActions & Format) && um != NoEditor); + if (m_unCommentSelectionAction) + m_unCommentSelectionAction->setEnabled((m_optionalActions & UnCommentSelection) && um != NoEditor); + if (m_collapseAction) + m_collapseAction->setEnabled(um != NoEditor); + if (m_expandAction) + m_expandAction->setEnabled(um != NoEditor); + if (m_unCollapseAllAction) + m_unCollapseAllAction->setEnabled((m_optionalActions & UnCollapseAll) && um != NoEditor); + if (m_decreaseFontSizeAction) + m_decreaseFontSizeAction->setEnabled(um != NoEditor); + if (m_increaseFontSizeAction) + m_increaseFontSizeAction->setEnabled(um != NoEditor); + if (m_visualizeWhitespaceAction) { + m_visualizeWhitespaceAction->setEnabled(um != NoEditor); + if (m_currentEditor) + m_visualizeWhitespaceAction->setChecked(m_currentEditor->displaySettings().m_visualizeWhitespace); + } + if (m_textWrappingAction) { + m_textWrappingAction->setEnabled(um != NoEditor); + if (m_currentEditor) + m_textWrappingAction->setChecked(m_currentEditor->displaySettings().m_textWrapping); + } + + updateRedoAction(); + updateUndoAction(); + updateCopyAction(); +} + +void TextEditorActionHandler::updateRedoAction() +{ + if (m_redoAction) + m_redoAction->setEnabled(m_currentEditor && m_currentEditor->document()->isRedoAvailable()); +} + +void TextEditorActionHandler::updateUndoAction() +{ + if (m_undoAction) + m_undoAction->setEnabled(m_currentEditor && m_currentEditor->document()->isUndoAvailable()); +} + +void TextEditorActionHandler::updateCopyAction() +{ + const bool hasCopyableText = m_currentEditor && m_currentEditor->textCursor().hasSelection(); + if (m_cutAction) + m_cutAction->setEnabled(hasCopyableText && updateMode() == WriteMode); + if (m_copyAction) + m_copyAction->setEnabled(hasCopyableText); +} + +void TextEditorActionHandler::undoAction() +{ + if (m_currentEditor) + m_currentEditor->undo(); +} + +void TextEditorActionHandler::redoAction() +{ + if (m_currentEditor) + m_currentEditor->redo(); +} + +void TextEditorActionHandler::copyAction() +{ + if (m_currentEditor) + m_currentEditor->copy(); +} + +void TextEditorActionHandler::cutAction() +{ + if (m_currentEditor) + m_currentEditor->cut(); +} + +void TextEditorActionHandler::pasteAction() +{ + if (m_currentEditor) + m_currentEditor->paste(); +} + +void TextEditorActionHandler::selectAllAction() +{ + if (m_currentEditor) + m_currentEditor->selectAll(); +} + +void TextEditorActionHandler::gotoAction() +{ + QuickOpen::QuickOpenManager *quickopen = QuickOpen::QuickOpenManager::instance(); + Q_ASSERT(quickopen); + QString shortcut = TextEditorPlugin::instance()->lineNumberFilter()->shortcutString(); + quickopen->show(shortcut + " <line number>", 2, 13); +} + +void TextEditorActionHandler::printAction() +{ + if (m_currentEditor) + m_currentEditor->print(m_core->printer()); +} + +void TextEditorActionHandler::formatAction() +{ + if (m_currentEditor) + m_currentEditor->format(); +} + + +void TextEditorActionHandler::setVisualizeWhitespace(bool checked) +{ + if (m_currentEditor) { + DisplaySettings ds = m_currentEditor->displaySettings(); + ds.m_visualizeWhitespace = checked; + m_currentEditor->setDisplaySettings(ds); + } +} + +void TextEditorActionHandler::setTextWrapping(bool checked) +{ + if (m_currentEditor) { + DisplaySettings ds = m_currentEditor->displaySettings(); + ds.m_textWrapping = checked; + m_currentEditor->setDisplaySettings(ds); + } +} + +void TextEditorActionHandler::unCommentSelection() +{ + if (m_currentEditor) + m_currentEditor->unCommentSelection(); +} + +void TextEditorActionHandler::deleteLine() +{ + if (m_currentEditor) + m_currentEditor->deleteLine(); +} + +void TextEditorActionHandler::unCollapseAll() +{ + if (m_currentEditor) + m_currentEditor->unCollapseAll(); +} + +void TextEditorActionHandler::collapse() +{ + if (m_currentEditor) + m_currentEditor->collapse(); +} + +void TextEditorActionHandler::expand() +{ + if (m_currentEditor) + m_currentEditor->expand(); +} + +void TextEditorActionHandler::selectEncoding() +{ + if (m_currentEditor) + m_currentEditor->selectEncoding(); +} + +void TextEditorActionHandler::increaseFontSize() +{ + if (m_currentEditor) + m_currentEditor->zoomIn(); +} + +void TextEditorActionHandler::decreaseFontSize() +{ + if (m_currentEditor) + m_currentEditor->zoomOut(); +} + + +void TextEditorActionHandler::updateCurrentEditor(Core::IContext *object) +{ + do { + if (!object) { + if (!m_currentEditor) + return; + + m_currentEditor = 0; + break; + } + BaseTextEditor *editor = qobject_cast<BaseTextEditor *>(object->widget()); + if (!editor) { + if (!m_currentEditor) + return; + + m_currentEditor = 0; + break; + } + + if (editor == m_currentEditor) + return; + + if (editor->actionHack() != this) { + m_currentEditor = 0; + break; + } + + m_currentEditor = editor; + + } while (false); + updateActions(); +} + + +const QPointer<BaseTextEditor> &TextEditorActionHandler::currentEditor() const +{ + return m_currentEditor; +} + +Core::ICore *TextEditorActionHandler::core() const +{ + return m_core; +} + diff --git a/src/plugins/texteditor/texteditoractionhandler.h b/src/plugins/texteditor/texteditoractionhandler.h new file mode 100644 index 00000000000..d0c222f8484 --- /dev/null +++ b/src/plugins/texteditor/texteditoractionhandler.h @@ -0,0 +1,143 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef TEXTEDITORACTIONHANDLER_H +#define TEXTEDITORACTIONHANDLER_H + +#include "texteditor_global.h" +#include "basetexteditor.h" + +#include "coreplugin/icontext.h" +#include "coreplugin/icore.h" + +#include <QtCore/QObject> +#include <QtCore/QPointer> +#include <QtCore/QList> + +namespace TextEditor { + +class BaseTextEditor; + +// Redirects slots from global actions to the respective editor. + +class TEXTEDITOR_EXPORT TextEditorActionHandler : public QObject +{ + Q_OBJECT + +public: + enum OptionalActionsMask { + None = 0, + Format = 1, + UnCommentSelection = 2, + UnCollapseAll = 4 + }; + + TextEditorActionHandler(Core::ICore *core, + const QString &context, + uint optionalActions = None); + void setupActions(BaseTextEditor *editor); + + void initializeActions(); + +public slots: + void updateActions(); + void updateRedoAction(); + void updateUndoAction(); + void updateCopyAction(); + +protected: + const QPointer<BaseTextEditor> ¤tEditor() const; + QAction *registerNewAction(const QString &id, const QString &title = QString()); + QAction *registerNewAction(const QString &id, QObject *receiver, const char *slot, + const QString &title = QString()); + Core::ICore *core() const; + + enum UpdateMode { NoEditor , ReadOnlyMode, WriteMode }; + UpdateMode updateMode() const; + + virtual void createActions(); + virtual bool supportsAction(const QString &id) const; + virtual void updateActions(UpdateMode um); + +private slots: + void undoAction(); + void redoAction(); + void copyAction(); + void cutAction(); + void pasteAction(); + void selectAllAction(); + void gotoAction(); + void printAction(); + void formatAction(); + void setVisualizeWhitespace(bool); + void setTextWrapping(bool); + void unCommentSelection(); + void unCollapseAll(); + void collapse(); + void expand(); + void deleteLine(); + void selectEncoding(); + void increaseFontSize(); + void decreaseFontSize(); + void updateCurrentEditor(Core::IContext *object); + +private: + QAction *m_undoAction; + QAction *m_redoAction; + QAction *m_copyAction; + QAction *m_cutAction; + QAction *m_pasteAction; + QAction *m_selectAllAction; + QAction *m_gotoAction; + QAction *m_printAction; + QAction *m_formatAction; + QAction *m_visualizeWhitespaceAction; + QAction *m_textWrappingAction; + QAction *m_unCommentSelectionAction; + QAction *m_unCollapseAllAction; + QAction *m_collapseAction; + QAction *m_expandAction; + QAction *m_deleteLineAction; + QAction *m_selectEncodingAction; + QAction *m_increaseFontSizeAction; + QAction *m_decreaseFontSizeAction; + + uint m_optionalActions; + QPointer<BaseTextEditor> m_currentEditor; + Core::ICore *m_core; + QList<int> m_contextId; + bool m_initialized; +}; + +} // namespace TextEditor + +#endif // TEXTEDITORACTIONHANDLER_H diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h new file mode 100644 index 00000000000..5133a2c148f --- /dev/null +++ b/src/plugins/texteditor/texteditorconstants.h @@ -0,0 +1,88 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef TEXTEDITORCONSTANTS_H +#define TEXTEDITORCONSTANTS_H + +namespace TextEditor { +namespace Constants { + +const char * const C_TEXTEDITOR = "Text Editor"; +const char * const COMPLETE_THIS = "TextEditor.CompleteThis"; +const char * const VISUALIZE_WHITESPACE = "TextEditor.VisualizeWhitespace"; +const char * const TEXT_WRAPPING = "TextEditor.TextWrapping"; +const char * const UN_COMMENT_SELECTION = "TextEditor.UnCommentSelection"; +const char * const COLLAPSE = "TextEditor.Collapse"; +const char * const EXPAND = "TextEditor.Expand"; +const char * const UN_COLLAPSE_ALL = "TextEditor.UnCollapseAll"; +const char * const AUTO_INDENT_SELECTION = "TextEditor.AutoIndentSelection"; +const char * const INCREASE_FONT_SIZE = "TextEditor.IncreaseFontSize"; +const char * const DECREASE_FONT_SIZE = "TextEditor.DecreaseFontSize"; +const char * const DELETE_LINE = "TextEditor.DeleteLine"; +const char * const DELETE_WORD = "TextEditor.DeleteWord"; +const char * const SELECT_ENCODING = "TextEditor.SelectEncoding"; +const char * const C_TEXTEDITOR_MIMETYPE_TEXT = "text/plain"; +const char * const C_TEXTEDITOR_MIMETYPE_XML = "application/xml"; + + +// Text color and style categories +const char * const C_TEXT = "Text"; + +const char * const C_SELECTION = "Selection"; +const char * const C_LINE_NUMBER = "LineNumber"; +const char * const C_SEARCH_RESULT = "SearchResult"; +const char * const C_SEARCH_SCOPE = "SearchScope"; +const char * const C_PARENTHESES = "Parentheses"; +const char * const C_CURRENT_LINE = "CurrentLine"; + +const char * const C_NUMBER = "Number"; +const char * const C_STRING = "String"; +const char * const C_TYPE = "Type"; +const char * const C_KEYWORD = "Keyword"; +const char * const C_OPERATOR = "Operator"; +const char * const C_PREPROCESSOR = "Preprocessor"; +const char * const C_LABEL = "Label"; +const char * const C_COMMENT = "Comment"; +const char * const C_DISABLED_CODE = "DisabledCode"; + +const char * const C_ADDED_LINE = "AddedLine"; +const char * const C_REMOVED_LINE = "RemovedLine"; +const char * const C_DIFF_FILE = "DiffFile"; +const char * const C_DIFF_LOCATION = "DiffLocation"; + +const char * const C_VARIABLE = "Variable"; +const char * const C_FUNCTION = "Function"; + +} // namespace Constants +} // namespace TextEditor + +#endif // TEXTEDITORCONSTANTS_H diff --git a/src/plugins/texteditor/texteditorplugin.cpp b/src/plugins/texteditor/texteditorplugin.cpp new file mode 100644 index 00000000000..5e943bd70c8 --- /dev/null +++ b/src/plugins/texteditor/texteditorplugin.cpp @@ -0,0 +1,180 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "texteditorplugin.h" + +#include "findinfiles.h" +#include "fontsettings.h" +#include "linenumberfilter.h" +#include "texteditorconstants.h" +#include "texteditorsettings.h" +#include "textfilewizard.h" +#include "plaintexteditorfactory.h" +#include "plaintexteditor.h" +#include "storagesettings.h" + +#include <coreplugin/coreconstants.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/actionmanager/icommand.h> +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/texteditoractionhandler.h> + +#include <QtCore/qplugin.h> +#include <QtGui/QShortcut> +#include <QtGui/QMainWindow> + +using namespace TextEditor; +using namespace TextEditor::Internal; + +TextEditorPlugin *TextEditorPlugin::m_instance = 0; + +TextEditorPlugin::TextEditorPlugin() : + m_core(0), + m_settings(0), + m_wizard(0), + m_editorFactory(0), + m_lineNumberFilter(0) +{ + Q_ASSERT(!m_instance); + m_instance = this; +} + +TextEditorPlugin::~TextEditorPlugin() +{ + m_instance = 0; +} + +TextEditorPlugin *TextEditorPlugin::instance() +{ + return m_instance; +} + +Core::ICore *TextEditorPlugin::core() +{ + return m_instance->m_core; +} + +//ExtensionSystem::PluginInterface +bool TextEditorPlugin::initialize(const QStringList & /*arguments*/, QString *errorMessage) +{ + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + + if (!m_core->mimeDatabase()->addMimeTypes(QLatin1String(":/texteditor/TextEditor.mimetypes.xml"), errorMessage)) + return false; + + Core::BaseFileWizardParameters wizardParameters(Core::IWizard::FileWizard); + wizardParameters.setDescription(tr("This creates a new text file (.txt)")); + wizardParameters.setName(tr("Text File")); + wizardParameters.setCategory(QLatin1String("General")); + wizardParameters.setTrCategory(tr("General")); + m_wizard = new TextFileWizard(QLatin1String(TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_TEXT), + QLatin1String(Core::Constants::K_DEFAULT_TEXT_EDITOR), + QLatin1String("text$"), + wizardParameters, m_core); + // Add text file wizard + addAutoReleasedObject(m_wizard); + + + m_settings = new TextEditorSettings(this, this); + + // Add plain text editor factory + m_editorFactory = new PlainTextEditorFactory; + addAutoReleasedObject(m_editorFactory); + + // Goto line functionality for quick open + m_lineNumberFilter = new LineNumberFilter(m_core->editorManager()); + addAutoReleasedObject(m_lineNumberFilter); + + int contextId = m_core->uniqueIDManager()->uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR); + QList<int> context = QList<int>() << contextId; + Core::ActionManagerInterface *am = m_core->actionManager(); + + // Add shortcut for invoking automatic completion + QShortcut *completionShortcut = new QShortcut(m_core->mainWindow()); + completionShortcut->setWhatsThis(tr("Triggers a completion in this scope")); + // Make sure the shortcut still works when the completion widget is active + completionShortcut->setContext(Qt::ApplicationShortcut); + Core::ICommand *command = am->registerShortcut(completionShortcut, Constants::COMPLETE_THIS, context); +#ifndef Q_OS_MAC + command->setDefaultKeySequence(QKeySequence(tr("Ctrl+Space"))); +#else + command->setDefaultKeySequence(QKeySequence(tr("Meta+Space"))); +#endif + connect(completionShortcut, SIGNAL(activated()), this, SLOT(invokeCompletion())); + + addAutoReleasedObject(new FindInFiles(m_core, m_core->pluginManager()->getObject<Find::SearchResultWindow>())); + + return true; +} + +void TextEditorPlugin::extensionsInitialized() +{ + m_editorFactory->actionHandler()->initializeActions(); +} + +void TextEditorPlugin::initializeEditor(TextEditor::PlainTextEditor *editor) +{ + // common actions + m_editorFactory->actionHandler()->setupActions(editor); + + // settings + connect(m_settings, SIGNAL(fontSettingsChanged(TextEditor::FontSettings)), + editor, SLOT(setFontSettings(TextEditor::FontSettings))); + connect(m_settings, SIGNAL(tabSettingsChanged(TextEditor::TabSettings)), + editor, SLOT(setTabSettings(TextEditor::TabSettings))); + connect(m_settings, SIGNAL(storageSettingsChanged(TextEditor::StorageSettings)), + editor, SLOT(setStorageSettings(TextEditor::StorageSettings))); + connect(m_settings, SIGNAL(displaySettingsChanged(TextEditor::DisplaySettings)), + editor, SLOT(setDisplaySettings(TextEditor::DisplaySettings))); + + // tab settings rely on font settings + editor->setFontSettings(m_settings->fontSettings()); + editor->setTabSettings(m_settings->tabSettings()); + editor->setStorageSettings(m_settings->storageSettings()); + editor->setDisplaySettings(m_settings->displaySettings()); +} + +void TextEditorPlugin::invokeCompletion() +{ + if (!m_core) + return; + + Core::IEditor *iface = m_core->editorManager()->currentEditor(); + ITextEditor *editor = qobject_cast<ITextEditor *>(iface); + if (editor) + editor->triggerCompletions(); +} + + +Q_EXPORT_PLUGIN(TextEditorPlugin) diff --git a/src/plugins/texteditor/texteditorplugin.h b/src/plugins/texteditor/texteditorplugin.h new file mode 100644 index 00000000000..578095f6098 --- /dev/null +++ b/src/plugins/texteditor/texteditorplugin.h @@ -0,0 +1,94 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef TEXTEDITORPLUGIN_H +#define TEXTEDITORPLUGIN_H + +#include <extensionsystem/iplugin.h> + +QT_BEGIN_NAMESPACE +class QAction; +QT_END_NAMESPACE + +namespace Core { +class ICore; +class IEditor; +} + +namespace TextEditor { + +class FontSettings; +class FontSettingsPage; +class TextEditorSettings; +class TextFileWizard; +class PlainTextEditor; + +namespace Internal { + +class LineNumberFilter; +class PlainTextEditorFactory; + +class TextEditorPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + TextEditorPlugin(); + virtual ~TextEditorPlugin(); + + static TextEditorPlugin *instance(); + static Core::ICore *core(); + + // ExtensionSystem::PluginInterface + bool initialize(const QStringList &arguments, QString *); + void extensionsInitialized(); + + void initializeEditor(PlainTextEditor *editor); + + LineNumberFilter *lineNumberFilter() { return m_lineNumberFilter; } + +private slots: + void invokeCompletion(); + +private: + static TextEditorPlugin *m_instance; + Core::ICore *m_core; + TextEditorSettings *m_settings; + TextFileWizard *m_wizard; + PlainTextEditorFactory *m_editorFactory; + LineNumberFilter *m_lineNumberFilter; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // TEXTEDITORPLUGIN_H diff --git a/src/plugins/texteditor/texteditorsettings.cpp b/src/plugins/texteditor/texteditorsettings.cpp new file mode 100644 index 00000000000..0292215f99e --- /dev/null +++ b/src/plugins/texteditor/texteditorsettings.cpp @@ -0,0 +1,152 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "texteditorsettings.h" + +#include "displaysettings.h" +#include "generalsettingspage.h" +#include "fontsettingspage.h" +#include "storagesettings.h" +#include "tabsettings.h" +#include "texteditorconstants.h" +#include "texteditorplugin.h" + +#include <QApplication> + +using namespace TextEditor; +using namespace TextEditor::Constants; + +TextEditorSettings *TextEditorSettings::m_instance = 0; + +TextEditorSettings::TextEditorSettings(Internal::TextEditorPlugin *plugin, + QObject *parent) + : QObject(parent) +{ + Q_ASSERT(!m_instance); + m_instance = this; + + ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); + + // Note: default background colors are coming from FormatDescription::background() + + // Add font preference page + FormatDescriptions formatDescriptions; + formatDescriptions.push_back(FormatDescription(QLatin1String(C_TEXT), tr("Text"))); + + // Special categories + const QPalette p = QApplication::palette(); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_SELECTION), tr("Selection"), p.color(QPalette::HighlightedText))); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_LINE_NUMBER), tr("Line Number"))); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_SEARCH_RESULT), tr("Search Result"))); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_SEARCH_SCOPE), tr("Search Scope"))); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_PARENTHESES), tr("Parentheses"))); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_CURRENT_LINE), tr("Current Line"))); + + // Standard categories + formatDescriptions.push_back(FormatDescription(QLatin1String(C_NUMBER), tr("Number"), Qt::darkBlue)); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_STRING), tr("String"), Qt::darkGreen)); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_TYPE), tr("Type"), Qt::darkMagenta)); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_KEYWORD), tr("Keyword"), Qt::darkYellow)); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_OPERATOR), tr("Operator"))); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_PREPROCESSOR), tr("Preprocessor"), Qt::darkBlue)); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_LABEL), tr("Label"), Qt::darkRed)); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_COMMENT), tr("Comment"), Qt::darkGreen)); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_DISABLED_CODE), tr("Disabled Code"), Qt::lightGray)); + + // Diff categories + formatDescriptions.push_back(FormatDescription(QLatin1String(C_ADDED_LINE), tr("Added Line"), Qt::blue)); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_REMOVED_LINE), tr("Removed Line"), Qt::red)); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_DIFF_FILE), tr("Diff File"), Qt::black)); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_DIFF_LOCATION), tr("Diff Location"), Qt::green)); + + // Pro file categories + formatDescriptions.push_back(FormatDescription(QLatin1String(C_VARIABLE), tr("Variable"), Qt::blue)); + formatDescriptions.push_back(FormatDescription(QLatin1String(C_FUNCTION), tr("Function"), Qt::green)); + + m_fontSettingsPage = new FontSettingsPage(formatDescriptions, + QLatin1String("TextEditor"), + tr("Text Editor"), + plugin->core()); + pm->addObject(m_fontSettingsPage); + + // Add the GUI used to configure the tab, storage and display settings + TextEditor::GeneralSettingsPageParameters generalSettingsPageParameters; + generalSettingsPageParameters.name = tr("General"); + generalSettingsPageParameters.category = QLatin1String("TextEditor"); + generalSettingsPageParameters.trCategory = tr("Text Editor"); + generalSettingsPageParameters.settingsPrefix = QLatin1String("text"); + m_generalSettingsPage = new GeneralSettingsPage(plugin->core(), generalSettingsPageParameters, this); + pm->addObject(m_generalSettingsPage); + + connect(m_fontSettingsPage, SIGNAL(changed(TextEditor::FontSettings)), + this, SIGNAL(fontSettingsChanged(TextEditor::FontSettings))); + connect(m_generalSettingsPage, SIGNAL(tabSettingsChanged(TextEditor::TabSettings)), + this, SIGNAL(tabSettingsChanged(TextEditor::TabSettings))); + connect(m_generalSettingsPage, SIGNAL(storageSettingsChanged(TextEditor::StorageSettings)), + this, SIGNAL(storageSettingsChanged(TextEditor::StorageSettings))); + connect(m_generalSettingsPage, SIGNAL(displaySettingsChanged(TextEditor::DisplaySettings)), + this, SIGNAL(displaySettingsChanged(TextEditor::DisplaySettings))); +} + +TextEditorSettings::~TextEditorSettings() +{ + ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); + pm->removeObject(m_generalSettingsPage); + pm->removeObject(m_fontSettingsPage); + + m_instance = 0; +} + +TextEditorSettings *TextEditorSettings::instance() +{ + return m_instance; +} + +FontSettings TextEditorSettings::fontSettings() const +{ + return m_fontSettingsPage->fontSettings(); +} + +TabSettings TextEditorSettings::tabSettings() const +{ + return m_generalSettingsPage->tabSettings(); +} + +StorageSettings TextEditorSettings::storageSettings() const +{ + return m_generalSettingsPage->storageSettings(); +} + +DisplaySettings TextEditorSettings::displaySettings() const +{ + return m_generalSettingsPage->displaySettings(); +} diff --git a/src/plugins/texteditor/texteditorsettings.h b/src/plugins/texteditor/texteditorsettings.h new file mode 100644 index 00000000000..1b5a08e966f --- /dev/null +++ b/src/plugins/texteditor/texteditorsettings.h @@ -0,0 +1,88 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef TEXTEDITORSETTINGS_H +#define TEXTEDITORSETTINGS_H + +#include "texteditor_global.h" + +#include <QtCore/QObject> + +namespace TextEditor { + +class GeneralSettingsPage; +class FontSettingsPage; +class FontSettings; +struct TabSettings; +struct StorageSettings; +struct DisplaySettings; + +namespace Internal { +class TextEditorPlugin; +} + +/** + * This class provides a central place for basic text editor settings. These + * settings include font settings, tab settings, storage settings and display + * settings. + */ +class TEXTEDITOR_EXPORT TextEditorSettings : public QObject +{ + Q_OBJECT + +public: + TextEditorSettings(Internal::TextEditorPlugin *plugin, QObject *parent); + ~TextEditorSettings(); + + static TextEditorSettings *instance(); + + FontSettings fontSettings() const; + TabSettings tabSettings() const; + StorageSettings storageSettings() const; + DisplaySettings displaySettings() const; + +signals: + void fontSettingsChanged(const TextEditor::FontSettings &); + void tabSettingsChanged(const TextEditor::TabSettings &); + void storageSettingsChanged(const TextEditor::StorageSettings &); + void displaySettingsChanged(const TextEditor::DisplaySettings &); + +private: + TextEditor::FontSettingsPage *m_fontSettingsPage; + TextEditor::GeneralSettingsPage *m_generalSettingsPage; + + static TextEditorSettings *m_instance; +}; + +} // namespace TextEditor + +#endif // TEXTEDITORSETTINGS_H diff --git a/src/plugins/texteditor/textfilewizard.cpp b/src/plugins/texteditor/textfilewizard.cpp new file mode 100644 index 00000000000..b40201044c3 --- /dev/null +++ b/src/plugins/texteditor/textfilewizard.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "textfilewizard.h" +#include "basetexteditor.h" +#include "texteditorconstants.h" + +namespace TextEditor { + +TextFileWizard::TextFileWizard(const QString &mimeType, + const QString &editorKind, + const QString &suggestedFileName, + const BaseFileWizardParameters ¶meters, + Core::ICore *core, + QObject *parent) : + Core::StandardFileWizard(parameters, core, parent), + m_mimeType(mimeType), + m_editorKind(editorKind), + m_suggestedFileName(suggestedFileName) +{ +} + +Core::GeneratedFiles + TextFileWizard::generateFilesFromPath(const QString &path, const QString &name, + QString * /*errorMessage*/) const +{ + const QString suffix = preferredSuffix(m_mimeType); + const QString fileName = Core::BaseFileWizard::buildFileName(path, name, suffix); + Core::GeneratedFile file(fileName); + file.setEditorKind(m_editorKind); + return Core::GeneratedFiles() << file; +} + +} diff --git a/src/plugins/texteditor/textfilewizard.h b/src/plugins/texteditor/textfilewizard.h new file mode 100644 index 00000000000..a3cb3797338 --- /dev/null +++ b/src/plugins/texteditor/textfilewizard.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef TEXTFILEWIZARD_H +#define TEXTFILEWIZARD_H + +#include "texteditor_global.h" + +#include <coreplugin/basefilewizard.h> + +namespace TextEditor { + +class TEXTEDITOR_EXPORT TextFileWizard : public Core::StandardFileWizard +{ + Q_OBJECT + +public: + typedef Core::BaseFileWizardParameters BaseFileWizardParameters; + TextFileWizard(const QString &mimeType, + const QString &editorKind, + const QString &suggestedFileName, + const BaseFileWizardParameters ¶meters, + Core::ICore *core, + QObject *parent = 0); + +protected: + virtual Core::GeneratedFiles + generateFilesFromPath(const QString &path, const QString &name, + QString *errorMessage) const; +private: + const QString m_mimeType; + const QString m_editorKind; + const QString m_suggestedFileName; +}; + +} // namespace TextEditor + +#endif // TEXTFILEWIZARD_H diff --git a/src/plugins/vcsbase/README.txt b/src/plugins/vcsbase/README.txt new file mode 100644 index 00000000000..7b60a231109 --- /dev/null +++ b/src/plugins/vcsbase/README.txt @@ -0,0 +1,6 @@ +TODO: + +When diffing: +- Use code from current editor or +- else from project or +- System codec diff --git a/src/plugins/vcsbase/VCSBase.mimetypes.xml b/src/plugins/vcsbase/VCSBase.mimetypes.xml new file mode 100644 index 00000000000..1c970521748 --- /dev/null +++ b/src/plugins/vcsbase/VCSBase.mimetypes.xml @@ -0,0 +1,39 @@ +<?xml version="1.0"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="text/x-patch"> + <sub-class-of type="text/plain"/> + <comment>Differences between files</comment> + <glob pattern="*.diff"/> <comment xml:lang="bg">Разлики между файлове</comment> + <comment xml:lang="ca">diferències entre fitxers</comment> + <comment xml:lang="cs">Rozdíly mezi soubory</comment> + <comment xml:lang="da">forskel mellem filer</comment> + <comment xml:lang="de">Unterschied zwischen Dateien</comment> + <comment xml:lang="el">διαφορές μεταξύ αρχείων</comment> + <comment xml:lang="eo">diferencoj inter dosieroj</comment> + <comment xml:lang="es">diferencias entre ficheros</comment> + <comment xml:lang="eu">fitxategien arteko ezberdintasunak</comment> + <comment xml:lang="fi">tiedostojen väliset erot</comment> + <comment xml:lang="fr">différences entre fichiers</comment> + <comment xml:lang="hu">diff-különbségfájl</comment> + <comment xml:lang="it">Differenze tra file</comment> + <comment xml:lang="ja">ファイル間差分</comment> + <comment xml:lang="ko">파일사이의 바뀐점</comment> + <comment xml:lang="lt">skirtumai tarp rinkmenų</comment> + <comment xml:lang="ms">Perbezaan antara fail</comment> + <comment xml:lang="nb">forskjeller mellom filer</comment> + <comment xml:lang="nl">verschillen tussen bestanden</comment> + <comment xml:lang="nn">skilnader mellom filer</comment> + <comment xml:lang="pl">różnica pomiędzy plikami</comment> + <comment xml:lang="pt">diferenças entre ficheiros</comment> + <comment xml:lang="pt_BR">Diferenças entre arquivos</comment> + <comment xml:lang="ru">различия между файлами</comment> + <comment xml:lang="sq">diferenca midis files</comment> + <comment xml:lang="sr">разлике међу датотекама</comment> + <comment xml:lang="sv">skillnader mellan filer</comment> + <comment xml:lang="uk">різниця між файлами</comment> + <comment xml:lang="vi">khác biệt giữa nhiều tập tin</comment> + <comment xml:lang="zh_CN">文件的区别</comment> + <comment xml:lang="zh_TW">檔案內容差異</comment> + <glob pattern="*.patch"/> + </mime-type> +</mime-info> diff --git a/src/plugins/vcsbase/VCSBase.pluginspec b/src/plugins/vcsbase/VCSBase.pluginspec new file mode 100644 index 00000000000..da0b618d260 --- /dev/null +++ b/src/plugins/vcsbase/VCSBase.pluginspec @@ -0,0 +1,12 @@ +<plugin name="VCSBase" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Version Control System Base Plugin</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> + <dependency name="TextEditor" version="0.9.1"/> + <dependency name="ProjectExplorer" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/vcsbase/baseannotationhighlighter.cpp b/src/plugins/vcsbase/baseannotationhighlighter.cpp new file mode 100644 index 00000000000..e287718d711 --- /dev/null +++ b/src/plugins/vcsbase/baseannotationhighlighter.cpp @@ -0,0 +1,102 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "baseannotationhighlighter.h" + +#include <math.h> +#include <QtCore/QSet> +#include <QtCore/QDebug> +#include <QtGui/QColor> +#include <QtGui/QTextDocument> +#include <QtGui/QTextEdit> +#include <QtGui/QTextCharFormat> + +typedef QMap<QString, QTextCharFormat> ChangeNumberFormatMap; + +namespace VCSBase { + +struct BaseAnnotationHighlighterPrivate { + ChangeNumberFormatMap m_changeNumberMap; +}; + +BaseAnnotationHighlighter::BaseAnnotationHighlighter(const ChangeNumbers &changeNumbers, + QTextDocument *document) : + QSyntaxHighlighter(document), + m_d(new BaseAnnotationHighlighterPrivate) +{ + setChangeNumbers(changeNumbers); +} + +BaseAnnotationHighlighter::~BaseAnnotationHighlighter() +{ + delete m_d; +} + +void BaseAnnotationHighlighter::setChangeNumbers(const ChangeNumbers &changeNumbers) +{ + m_d->m_changeNumberMap.clear(); + if (!changeNumbers.isEmpty()) { + // Assign a color gradient to annotation change numbers. Give + // each change number a unique color. + const double oneThird = 1.0 / 3.0; + const int step = qRound(ceil(pow(changeNumbers.count(), oneThird))); + QList<QColor> colors; + const int factor = 255 / step; + for (int i=0; i<step; ++i) + for (int j=0; j<step; ++j) + for (int k=0; k<step; ++k) + colors.append(QColor(i*factor, j*factor, k*factor)); + + int m = 0; + const int cstep = colors.count() / changeNumbers.count(); + const ChangeNumbers::const_iterator cend = changeNumbers.constEnd(); + for (ChangeNumbers::const_iterator it = changeNumbers.constBegin(); it != cend; ++it) { + QTextCharFormat format; + format.setForeground(colors.at(m)); + m_d->m_changeNumberMap.insert(*it, format); + m += cstep; + } + } +} + +void BaseAnnotationHighlighter::highlightBlock(const QString &text) +{ + if (text.isEmpty() || m_d->m_changeNumberMap.empty()) + return; + const QString change = changeNumber(text); + const ChangeNumberFormatMap::const_iterator it = m_d->m_changeNumberMap.constFind(change); + if (it != m_d->m_changeNumberMap.constEnd()) + setFormat(0, text.length(), it.value()); +} + +} + diff --git a/src/plugins/vcsbase/baseannotationhighlighter.h b/src/plugins/vcsbase/baseannotationhighlighter.h new file mode 100644 index 00000000000..ee5808b7b03 --- /dev/null +++ b/src/plugins/vcsbase/baseannotationhighlighter.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BASEANNOTATIONHIGHLIGHTER_H +#define BASEANNOTATIONHIGHLIGHTER_H + +#include "vcsbase_global.h" + +#include <QtCore/QMap> +#include <QtCore/QSet> +#include <QtGui/QSyntaxHighlighter> +#include <QtGui/QTextCharFormat> + +namespace VCSBase { + +struct BaseAnnotationHighlighterPrivate; + +// Base for a highlighter for annotation lines of the form +// 'changenumber:XXXX'. The change numbers are assigned a color gradient. +// Example: +// 112: text1 <color 1> +// 113: text2 <color 2> +// 112: text3 <color 1> +class VCSBASE_EXPORT BaseAnnotationHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT +public: + typedef QSet<QString> ChangeNumbers; + + explicit BaseAnnotationHighlighter(const ChangeNumbers &changeNumbers, + QTextDocument *document = 0); + virtual ~BaseAnnotationHighlighter(); + + void setChangeNumbers(const ChangeNumbers &changeNumbers); + + virtual void highlightBlock(const QString &text); + +private: + // Implement this to return the change number of a line + virtual QString changeNumber(const QString &block) const = 0; + + BaseAnnotationHighlighterPrivate *m_d; +}; + +} //namespace Internal + +#endif diff --git a/src/plugins/vcsbase/basevcseditorfactory.cpp b/src/plugins/vcsbase/basevcseditorfactory.cpp new file mode 100644 index 00000000000..459a6683e67 --- /dev/null +++ b/src/plugins/vcsbase/basevcseditorfactory.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "basevcseditorfactory.h" +#include "vcsbaseplugin.h" +#include "vcsbaseeditor.h" + +#include <coreplugin/icore.h> +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/fontsettings.h> +#include <texteditor/texteditoractionhandler.h> +#include <texteditor/texteditorsettings.h> + +namespace VCSBase { + +struct BaseVCSEditorFactoryPrivate { + BaseVCSEditorFactoryPrivate(const VCSBaseEditorParameters *t, Core::ICore *core); + + const VCSBaseEditorParameters *m_type; + const QString m_kind; + const QStringList m_mimeTypes; + Core::ICore *m_core; + TextEditor::TextEditorActionHandler *m_editorHandler; +}; + +BaseVCSEditorFactoryPrivate::BaseVCSEditorFactoryPrivate(const VCSBaseEditorParameters *t, Core::ICore *core) : + m_type(t), + m_kind(QLatin1String(t->kind)), + m_mimeTypes(QStringList(QLatin1String(t->mimeType))), + m_core(core), + m_editorHandler(new TextEditor::TextEditorActionHandler(core, t->kind)) +{ +} + +BaseVCSEditorFactory::BaseVCSEditorFactory(const VCSBaseEditorParameters *t, + Core::ICore *core) : + m_d(new BaseVCSEditorFactoryPrivate(t, core)) +{ +} + +BaseVCSEditorFactory::~BaseVCSEditorFactory() +{ + delete m_d; +} + +QStringList BaseVCSEditorFactory::mimeTypes() const +{ + return m_d->m_mimeTypes; +} + +QString BaseVCSEditorFactory::kind() const +{ + return m_d->m_kind; +} + +Core::IFile *BaseVCSEditorFactory::open(const QString &fileName) +{ + Core::IEditor *iface = m_d->m_core->editorManager()->openEditor(fileName, kind()); + return iface ? iface->file() : 0; +} + +Core::IEditor *BaseVCSEditorFactory::createEditor(QWidget *parent) +{ + VCSBaseEditor *vcsEditor = createVCSBaseEditor(m_d->m_type, parent); + + vcsEditor ->setMimeType(m_d->m_mimeTypes.front()); + m_d->m_editorHandler->setupActions(vcsEditor); + + // Wire font settings and set initial values + TextEditor::TextEditorSettings *settings = TextEditor::TextEditorSettings::instance(); + connect(settings, SIGNAL(fontSettingsChanged(TextEditor::FontSettings)), + vcsEditor, SLOT(setFontSettings(TextEditor::FontSettings))); + vcsEditor->setFontSettings(settings->fontSettings()); + return vcsEditor->editableInterface(); +} + +} // namespace VCSBase diff --git a/src/plugins/vcsbase/basevcseditorfactory.h b/src/plugins/vcsbase/basevcseditorfactory.h new file mode 100644 index 00000000000..1fb9408c8c9 --- /dev/null +++ b/src/plugins/vcsbase/basevcseditorfactory.h @@ -0,0 +1,121 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef BASEVCSEDITORFACTORY_H +#define BASEVCSEDITORFACTORY_H + +#include "vcsbase_global.h" +#include "vcsbaseeditor.h" + +#include <coreplugin/editormanager/ieditorfactory.h> + +#include <QtCore/QStringList> + +namespace TextEditor { + class TextEditorActionHandler; +} + +namespace Core { + class ICore; +} + +namespace VCSBase { +struct BaseVCSEditorFactoryPrivate; + +// Base class for editor factories creating instances of VCSBaseEditor +// subclasses. +class VCSBASE_EXPORT BaseVCSEditorFactory : public Core::IEditorFactory +{ + Q_OBJECT +public: + explicit BaseVCSEditorFactory(const VCSBaseEditorParameters *type, + Core::ICore *core); + virtual ~BaseVCSEditorFactory(); + + virtual QStringList mimeTypes() const; + // IEditorFactory + + virtual QString kind() const; + virtual Core::IFile *open(const QString &fileName); + virtual Core::IEditor *createEditor(QWidget *parent); + +private: + // Implement to create and initialize (call init()) a + // VCSBaseEditor subclass + virtual VCSBaseEditor *createVCSBaseEditor(const VCSBaseEditorParameters *type, + QWidget *parent) = 0; + + BaseVCSEditorFactoryPrivate *m_d; +}; + +// Utility template to create an editor. +template <class Editor> +class VCSEditorFactory : public BaseVCSEditorFactory +{ +public: + explicit VCSEditorFactory(const VCSBaseEditorParameters *type, + Core::ICore *core, + QObject *describeReceiver = 0, + const char *describeSlot = 0); + +private: + virtual VCSBaseEditor *createVCSBaseEditor(const VCSBaseEditorParameters *type, + QWidget *parent); + QObject *m_describeReceiver; + const char *m_describeSlot; +}; + +template <class Editor> +VCSEditorFactory<Editor>::VCSEditorFactory(const VCSBaseEditorParameters *type, + Core::ICore *core, + QObject *describeReceiver, + const char *describeSlot) : + BaseVCSEditorFactory(type, core), + m_describeReceiver(describeReceiver), + m_describeSlot(describeSlot) +{ +} + +template <class Editor> +VCSBaseEditor *VCSEditorFactory<Editor>::createVCSBaseEditor(const VCSBaseEditorParameters *type, + QWidget *parent) +{ + VCSBaseEditor *rc = new Editor(type, parent); + rc->init(); + if (m_describeReceiver) + connect(rc, SIGNAL(describeRequested(QString,QString)), m_describeReceiver, m_describeSlot); + return rc; + +} +} +#endif + diff --git a/src/plugins/vcsbase/basevcssubmiteditorfactory.cpp b/src/plugins/vcsbase/basevcssubmiteditorfactory.cpp new file mode 100644 index 00000000000..4aff7347fac --- /dev/null +++ b/src/plugins/vcsbase/basevcssubmiteditorfactory.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "basevcssubmiteditorfactory.h" +#include "vcsbasesubmiteditor.h" + +#include <coreplugin/icore.h> +#include <coreplugin/editormanager/editormanager.h> + +namespace VCSBase { + +struct BaseVCSSubmitEditorFactoryPrivate { + BaseVCSSubmitEditorFactoryPrivate(const VCSBaseSubmitEditorParameters *parameters); + + const VCSBaseSubmitEditorParameters *m_parameters; + const QString m_kind; + const QStringList m_mimeTypes; +}; + +BaseVCSSubmitEditorFactoryPrivate::BaseVCSSubmitEditorFactoryPrivate(const VCSBaseSubmitEditorParameters *parameters) : + m_parameters(parameters), + m_kind(QLatin1String(parameters->kind)), + m_mimeTypes(QLatin1String(parameters->mimeType)) +{ +} + +BaseVCSSubmitEditorFactory::BaseVCSSubmitEditorFactory(const VCSBaseSubmitEditorParameters *parameters) : + m_d(new BaseVCSSubmitEditorFactoryPrivate(parameters)) +{ +} + +BaseVCSSubmitEditorFactory::~BaseVCSSubmitEditorFactory() +{ + delete m_d; +} + +Core::IEditor *BaseVCSSubmitEditorFactory::createEditor(QWidget *parent) +{ + return createBaseSubmitEditor(m_d->m_parameters, parent); +} + +QString BaseVCSSubmitEditorFactory::kind() const +{ + return m_d->m_kind; +} + +QStringList BaseVCSSubmitEditorFactory::mimeTypes() const +{ + return m_d->m_mimeTypes; +} + +Core::IFile *BaseVCSSubmitEditorFactory::open(const QString &fileName) +{ + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + if (Core::IEditor *iface = core->editorManager()->openEditor(fileName, kind())) + return iface->file(); + return 0; +} + +} diff --git a/src/plugins/vcsbase/basevcssubmiteditorfactory.h b/src/plugins/vcsbase/basevcssubmiteditorfactory.h new file mode 100644 index 00000000000..d258e305718 --- /dev/null +++ b/src/plugins/vcsbase/basevcssubmiteditorfactory.h @@ -0,0 +1,100 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef VCSBaseBASEEDITORFACTORY_H +#define VCSBaseBASEEDITORFACTORY_H + +#include "vcsbase_global.h" + +#include <coreplugin/editormanager/ieditorfactory.h> + +namespace VCSBase { + +class VCSBaseSubmitEditor; +struct VCSBaseSubmitEditorParameters; +struct BaseVCSSubmitEditorFactoryPrivate; + +// Parametrizable base class for editor factories creating instances of +// VCSBaseSubmitEditor subclasses. +class VCSBASE_EXPORT BaseVCSSubmitEditorFactory : public Core::IEditorFactory +{ + Q_OBJECT + +protected: + explicit BaseVCSSubmitEditorFactory(const VCSBaseSubmitEditorParameters *parameters); + +public: + virtual ~BaseVCSSubmitEditorFactory(); + + virtual Core::IEditor *createEditor(QWidget *parent); + virtual QString kind() const; + virtual QStringList mimeTypes() const; + Core::IFile *open(const QString &fileName); + +private: + virtual VCSBaseSubmitEditor + *createBaseSubmitEditor(const VCSBaseSubmitEditorParameters *parameters, + QWidget *parent) = 0; + + BaseVCSSubmitEditorFactoryPrivate *m_d; +}; + +// Utility template to create an editor that has a constructor taking the +// parameter struct and a parent widget. + +template <class Editor> +class VCSSubmitEditorFactory : public BaseVCSSubmitEditorFactory +{ +public: + explicit VCSSubmitEditorFactory(const VCSBaseSubmitEditorParameters *parameters); + +private: + virtual VCSBaseSubmitEditor + *createBaseSubmitEditor(const VCSBaseSubmitEditorParameters *parameters, + QWidget *parent); +}; + +template <class Editor> +VCSSubmitEditorFactory<Editor>::VCSSubmitEditorFactory(const VCSBaseSubmitEditorParameters *parameters) : + BaseVCSSubmitEditorFactory(parameters) +{ +} + +template <class Editor> +VCSBaseSubmitEditor *VCSSubmitEditorFactory<Editor>::createBaseSubmitEditor(const VCSBaseSubmitEditorParameters *parameters, + QWidget *parent) +{ + return new Editor(parameters, parent); +} +} + +#endif // VCSBaseBASEEDITOR_H diff --git a/src/plugins/vcsbase/diffhighlighter.cpp b/src/plugins/vcsbase/diffhighlighter.cpp new file mode 100644 index 00000000000..7da9f6df915 --- /dev/null +++ b/src/plugins/vcsbase/diffhighlighter.cpp @@ -0,0 +1,119 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "diffhighlighter.h" + +#include <QtCore/QDebug> +#include <QtCore/QtAlgorithms> +#include <QtCore/QRegExp> + +namespace VCSBase { + +// Formats used by DiffHighlighter +enum DiffFormats { + DiffTextFormat, + DiffInFormat, + DiffOutFormat, + DiffFileFormat, + DiffLocationFormat, + NumDiffFormats +}; + +// --- DiffHighlighterPrivate +struct DiffHighlighterPrivate { + DiffHighlighterPrivate(const QRegExp &filePattern); + inline DiffFormats analyzeLine(const QString &block) const; + + const QRegExp m_filePattern; + const QString m_locationIndicator; + const QChar m_diffInIndicator; + const QChar m_diffOutIndicator; + QTextCharFormat m_formats[NumDiffFormats]; +}; + +DiffHighlighterPrivate::DiffHighlighterPrivate(const QRegExp &filePattern) : + m_filePattern(filePattern), + m_locationIndicator(QLatin1String("@@")), + m_diffInIndicator(QLatin1Char('+')), + m_diffOutIndicator(QLatin1Char('-')) +{ + Q_ASSERT(filePattern.isValid()); +} + +DiffFormats DiffHighlighterPrivate::analyzeLine(const QString &text) const +{ + // Do not match on git "--- a/" as a deleted line, check + // file first + if (m_filePattern.exactMatch(text)) + return DiffFileFormat; + if (text.startsWith(m_diffInIndicator)) + return DiffInFormat; + if (text.startsWith(m_diffOutIndicator)) + return DiffOutFormat; + if (text.startsWith(m_locationIndicator)) + return DiffLocationFormat; + return DiffTextFormat; +} + +// --- DiffHighlighter +DiffHighlighter::DiffHighlighter(const QRegExp &filePattern, + QTextDocument *document) : + QSyntaxHighlighter(document), + m_d(new DiffHighlighterPrivate(filePattern)) +{ +} + +DiffHighlighter::~DiffHighlighter() +{ + delete m_d; +} + +void DiffHighlighter::highlightBlock(const QString &text) +{ + if (text.isEmpty()) + return; + + const DiffFormats format = m_d->analyzeLine(text); + if (format != DiffTextFormat) + setFormat(0, text.length(), m_d->m_formats[format]); +} + +void DiffHighlighter::setFormats(const QVector<QTextCharFormat> &s) +{ + if (s.size() == NumDiffFormats) { + qCopy(s.constBegin(), s.constEnd(), m_d->m_formats); + } else { + qWarning("%s: insufficient setting size: %d", Q_FUNC_INFO, s.size()); + } +} + +} // namespace VCSBase diff --git a/src/plugins/vcsbase/diffhighlighter.h b/src/plugins/vcsbase/diffhighlighter.h new file mode 100644 index 00000000000..eae25797bb2 --- /dev/null +++ b/src/plugins/vcsbase/diffhighlighter.h @@ -0,0 +1,91 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef DIFFHIGHLIGHTER_H +#define DIFFHIGHLIGHTER_H + +#include "vcsbase_global.h" + +#include <QtGui/QSyntaxHighlighter> +#include <QtGui/QTextCharFormat> +#include <QtCore/QVector> + +QT_BEGIN_NAMESPACE +class QRegExp; +QT_END_NAMESPACE + +namespace Core { + class ICore; +} +namespace TextEditor { + class FontSettingsPage; +} + +namespace VCSBase { + +struct DiffHighlighterPrivate; + +/* A highlighter for diffs. Parametrizable by the file indicator, + * which is for example '^====' in case of p4: + * \code + ==== //depot/research/main/qdynamicmainwindow3/qdynamicdockwidgetlayout_p.h#34 (text) ==== + * \endcode + * Or '--- a/|'+++ b/' in case of git: + * \code + diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro + index 9401ee7..ef35c3b 100644 + --- a/src/plugins/plugins.pro + +++ b/src/plugins/plugins.pro + @@ -10,6 +10,7 @@ SUBDIRS = plugin_coreplugin \ + * \endcode + * */ + +class VCSBASE_EXPORT DiffHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT +public: + explicit DiffHighlighter(const QRegExp &filePattern, + QTextDocument *document = 0); + virtual ~DiffHighlighter(); + + virtual void highlightBlock(const QString &text); + + // Set formats from a sequence of type QTextCharFormat + void setFormats(const QVector<QTextCharFormat> &s); + +private: + DiffHighlighterPrivate *m_d; +}; + +} //namespace VCSBase + +#endif // DIFFHIGHLIGHTER_H diff --git a/src/plugins/vcsbase/submiteditorfile.cpp b/src/plugins/vcsbase/submiteditorfile.cpp new file mode 100644 index 00000000000..adb82732079 --- /dev/null +++ b/src/plugins/vcsbase/submiteditorfile.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "submiteditorfile.h" + +namespace VCSBase { +namespace Internal { + +SubmitEditorFile::SubmitEditorFile(const QString &mimeType, + QObject *parent) : + Core::IFile(parent), + m_mimeType(mimeType), + m_modified(false) +{ +} + +void SubmitEditorFile::setFileName(const QString name) +{ + m_fileName = name; +} + +void SubmitEditorFile::setModified(bool modified) +{ + if (m_modified == modified) + return; + m_modified = modified; + emit changed(); +} + +bool SubmitEditorFile::save(const QString &fileName) +{ + emit saveMe(fileName); + return true; +} + +QString SubmitEditorFile::mimeType() const +{ + return m_mimeType; +} + +} +} diff --git a/src/plugins/vcsbase/submiteditorfile.h b/src/plugins/vcsbase/submiteditorfile.h new file mode 100644 index 00000000000..f1f2183fce5 --- /dev/null +++ b/src/plugins/vcsbase/submiteditorfile.h @@ -0,0 +1,77 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SUBMITEDITORFILE_H +#define SUBMITEDITORFILE_H + +#include <coreplugin/ifile.h> + +namespace VCSBase { +namespace Internal { + +// A non-saveable IFile for submit editor files. +class SubmitEditorFile : public Core::IFile +{ + Q_OBJECT +public: + explicit SubmitEditorFile(const QString &mimeType, + QObject *parent = 0); + + QString fileName() const { return m_fileName; } + QString defaultPath() const { return QString(); } + QString suggestedFileName() const { return QString(); } + + bool isModified() const { return m_modified; } + virtual QString mimeType() const; + bool isReadOnly() const { return false; } + bool isSaveAsAllowed() const { return false; } + bool save(const QString &fileName); + void modified(ReloadBehavior * /*behavior*/) { return; } + + void setFileName(const QString name); + void setModified(bool modified = true); + +signals: + void changed(); + void saveMe(const QString &fileName); + +private: + const QString m_mimeType; + bool m_modified; + QString m_fileName; +}; + + +} // namespace Internal +} + +#endif // SUBMITEDITORFILE_H diff --git a/src/plugins/vcsbase/vcsbase.pri b/src/plugins/vcsbase/vcsbase.pri new file mode 100644 index 00000000000..56f7418c666 --- /dev/null +++ b/src/plugins/vcsbase/vcsbase.pri @@ -0,0 +1,3 @@ +include(vcsbase_dependencies.pri) + +LIBS *= -l$$qtLibraryTarget(VCSBase) diff --git a/src/plugins/vcsbase/vcsbase.pro b/src/plugins/vcsbase/vcsbase.pro new file mode 100644 index 00000000000..329e27b0686 --- /dev/null +++ b/src/plugins/vcsbase/vcsbase.pro @@ -0,0 +1,31 @@ +TEMPLATE = lib +TARGET = VCSBase + +DEFINES += VCSBASE_LIBRARY + +include(../../qworkbenchplugin.pri) +include(vcsbase_dependencies.pri) + +HEADERS += vcsbase_global.h \ +vcsbaseconstants.h \ +vcsbaseplugin.h \ +baseannotationhighlighter.h \ +diffhighlighter.h \ +vcsbasetextdocument.h \ +vcsbaseeditor.h \ +vcsbasesubmiteditor.h \ +basevcseditorfactory.h \ +submiteditorfile.h \ +basevcssubmiteditorfactory.h + +SOURCES += vcsbaseplugin.cpp \ +baseannotationhighlighter.cpp \ +diffhighlighter.cpp \ +vcsbasetextdocument.cpp \ +vcsbaseeditor.cpp \ +vcsbasesubmiteditor.cpp \ +basevcseditorfactory.cpp \ +submiteditorfile.cpp \ +basevcssubmiteditorfactory.cpp + +RESOURCES=vcsbase.qrc diff --git a/src/plugins/vcsbase/vcsbase.qrc b/src/plugins/vcsbase/vcsbase.qrc new file mode 100644 index 00000000000..648425980aa --- /dev/null +++ b/src/plugins/vcsbase/vcsbase.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/trolltech.vcsbase" > + <file>VCSBase.mimetypes.xml</file> + </qresource> +</RCC> diff --git a/src/plugins/vcsbase/vcsbase_dependencies.pri b/src/plugins/vcsbase/vcsbase_dependencies.pri new file mode 100644 index 00000000000..3a68ac96009 --- /dev/null +++ b/src/plugins/vcsbase/vcsbase_dependencies.pri @@ -0,0 +1,4 @@ +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/texteditor/texteditor.pri) +include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/find/find.pri) diff --git a/src/plugins/vcsbase/vcsbase_global.h b/src/plugins/vcsbase/vcsbase_global.h new file mode 100644 index 00000000000..191587af6f4 --- /dev/null +++ b/src/plugins/vcsbase/vcsbase_global.h @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/**************************************************************************** +** +** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef VCSBASEGLOBAL_H +#define VCSBASEGLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(VCSBASE_LIBRARY) +# define VCSBASE_EXPORT Q_DECL_EXPORT +#else +# define VCSBASE_EXPORT Q_DECL_IMPORT +#endif + +#endif // VCSBASEGLOBAL_H diff --git a/src/plugins/vcsbase/vcsbaseconstants.h b/src/plugins/vcsbase/vcsbaseconstants.h new file mode 100644 index 00000000000..bbe6003ed1f --- /dev/null +++ b/src/plugins/vcsbase/vcsbaseconstants.h @@ -0,0 +1,46 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef VCSBaseCONSTANTS_H +#define VCSBaseCONSTANTS_H + +namespace VCSBase { + namespace Constants { + + namespace Internal { + + enum { debug = 0 }; + } // namespace Internal + } // namespace Constants +} // VCSBase + +#endif // VCSBaseCONSTANTS_H diff --git a/src/plugins/vcsbase/vcsbaseeditor.cpp b/src/plugins/vcsbase/vcsbaseeditor.cpp new file mode 100644 index 00000000000..9dfc9b12dbf --- /dev/null +++ b/src/plugins/vcsbase/vcsbaseeditor.cpp @@ -0,0 +1,486 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "vcsbaseeditor.h" +#include "diffhighlighter.h" +#include "baseannotationhighlighter.h" +#include "vcsbasetextdocument.h" +#include "vcsbaseconstants.h" + +#include <coreplugin/icore.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/fontsettings.h> +#include <texteditor/texteditorconstants.h> + +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/session.h> +#include <projectexplorer/editorconfiguration.h> + +#include <QtCore/QFileInfo> +#include <QtCore/QTextStream> +#include <QtCore/QSet> +#include <QtCore/QRegExp> +#include <QtCore/QDebug> +#include <QtCore/QTextCodec> +#include <QtGui/QKeyEvent> +#include <QtGui/QLayout> +#include <QtGui/QTextEdit> +#include <QtGui/QMenu> +#include <QtGui/QAction> +#include <QtGui/QTextCursor> +#include <QtCore/QProcess> + +namespace VCSBase { + +// VCSBaseEditorEditable: An editable with no support for duplicates +class VCSBaseEditorEditable : public TextEditor::BaseTextEditorEditable +{ +public: + VCSBaseEditorEditable(VCSBaseEditor *, + const VCSBaseEditorParameters *type, + Core::ICore *); + QList<int> context() const; + + bool duplicateSupported() const { return false; } + Core::IEditor *duplicate(QWidget * /*parent*/) { return 0; } + const char *kind() const { return m_kind; } + +private: + const char *m_kind; + QList<int> m_context; + +}; + +VCSBaseEditorEditable::VCSBaseEditorEditable(VCSBaseEditor *editor, + const VCSBaseEditorParameters *type, + Core::ICore *core) : + BaseTextEditorEditable(editor), + m_kind(type->kind) +{ + m_context << core->uniqueIDManager()->uniqueIdentifier(QLatin1String(type->context)) + << core->uniqueIDManager()->uniqueIdentifier(QLatin1String(TextEditor::Constants::C_TEXTEDITOR)); + +} + +QList<int> VCSBaseEditorEditable::context() const +{ + return m_context; +} + +// ----------- VCSBaseEditorPrivate + +struct VCSBaseEditorPrivate { + VCSBaseEditorPrivate(const VCSBaseEditorParameters *type, QObject *parent); + + const VCSBaseEditorParameters *m_parameters; + QAction *m_describeAction; + QString m_currentChange; + Core::ICore *m_core; + QString m_source; +}; + +VCSBaseEditorPrivate::VCSBaseEditorPrivate(const VCSBaseEditorParameters *type, QObject *parent) : + m_parameters(type), + m_describeAction(new QAction(parent)), + m_core(ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()) +{ +} + +// ------------ VCSBaseEditor +VCSBaseEditor::VCSBaseEditor(const VCSBaseEditorParameters *type, + QWidget *parent) : + BaseTextEditor(parent), + m_d(new VCSBaseEditorPrivate(type, this)) +{ + if (VCSBase::Constants::Internal::debug) + qDebug() << "VCSBaseEditor::VCSBaseEditor" << type->type << type->kind; + + setReadOnly(true); + + connect(m_d->m_describeAction, SIGNAL(triggered()), this, SLOT(describe())); + + viewport()->setMouseTracking(true); + + setBaseTextDocument(new Internal::VCSBaseTextDocument); + + setMimeType(QLatin1String(m_d->m_parameters->mimeType)); +} + +void VCSBaseEditor::init() +{ + switch (m_d->m_parameters->type) { + case RegularCommandOutput: + case LogOutput: + case AnnotateOutput: + // Annotation highlighting depends on contents, which is set later on + connect(this, SIGNAL(textChanged()), this, SLOT(slotActivateAnnotation())); + break; + case DiffOutput: + baseTextDocument()->setSyntaxHighlighter(createDiffHighlighter()); + break; + } +} + +VCSBaseEditor::~VCSBaseEditor() +{ + delete m_d; +} + +QString VCSBaseEditor::source() const +{ + return m_d->m_source; +} + +void VCSBaseEditor::setSource(const QString &source) +{ + m_d->m_source = source; +} + +QTextCodec *VCSBaseEditor::codec() const +{ + return baseTextDocument()->codec(); +} + +void VCSBaseEditor::setCodec(QTextCodec *c) +{ + if (c) { + baseTextDocument()->setCodec(c); + } else { + qWarning("%s: Attempt to set 0 codec.", Q_FUNC_INFO); + } +} + +EditorContentType VCSBaseEditor::contentType() const +{ + return m_d->m_parameters->type; +} + +bool VCSBaseEditor::isModified() const +{ + return false; +} + +TextEditor::BaseTextEditorEditable *VCSBaseEditor::createEditableInterface() +{ + return new VCSBaseEditorEditable(this, m_d->m_parameters, m_d->m_core); +} + +void VCSBaseEditor::contextMenuEvent(QContextMenuEvent *e) +{ + QMenu *menu = createStandardContextMenu(); + // 'click on change-interaction' + if (m_d->m_parameters->type == LogOutput || m_d->m_parameters->type == AnnotateOutput) { + m_d->m_currentChange = changeUnderCursor(cursorForPosition(e->pos())); + if (!m_d->m_currentChange.isEmpty()) { + m_d->m_describeAction->setText(tr("Describe change %1").arg(m_d->m_currentChange)); + menu->addSeparator(); + menu->addAction(m_d->m_describeAction); + } + } + menu->exec(e->globalPos()); + delete menu; +} + +void VCSBaseEditor::mouseMoveEvent(QMouseEvent *e) +{ + if (m_d->m_parameters->type == LogOutput || m_d->m_parameters->type == AnnotateOutput) { + // Link emulation behaviour for 'click on change-interaction' + QTextCursor cursor = cursorForPosition(e->pos()); + QString change = changeUnderCursor(cursor); + if (!change.isEmpty()) { + QTextEdit::ExtraSelection sel; + sel.cursor = cursor; + sel.cursor.select(QTextCursor::WordUnderCursor); + sel.format.setFontUnderline(true); + change = changeUnderCursor(cursor); + sel.format.setProperty(QTextFormat::UserProperty, change); + bool found = false; + foreach (QTextEdit::ExtraSelection es, extraSelections()) { + if (es.format.stringProperty(QTextFormat::UserProperty) == sel.format.stringProperty(QTextFormat::UserProperty)) { + found = true; + break; + } + } + if (!found) { + setExtraSelections(QList<QTextEdit::ExtraSelection>() << sel); + viewport()->setCursor(Qt::PointingHandCursor); + } + } else { + if (!extraSelections().isEmpty()) { + setExtraSelections(QList<QTextEdit::ExtraSelection>()); + viewport()->setCursor(Qt::IBeamCursor); + } + } + } + TextEditor::BaseTextEditor::mouseMoveEvent(e); +} + +void VCSBaseEditor::mouseReleaseEvent(QMouseEvent *e) +{ + if (m_d->m_parameters->type == LogOutput || m_d->m_parameters->type == AnnotateOutput) { + if (e->button() == Qt::LeftButton &&!(e->modifiers() & Qt::ShiftModifier)) { + QTextCursor cursor = cursorForPosition(e->pos()); + m_d->m_currentChange = changeUnderCursor(cursor); + if (!m_d->m_currentChange.isEmpty()) { + describe(); + e->accept(); + return; + } + } + } + TextEditor::BaseTextEditor::mouseReleaseEvent(e); +} + +void VCSBaseEditor::mouseDoubleClickEvent(QMouseEvent *e) +{ + if (m_d->m_parameters->type == DiffOutput) { + if (e->button() == Qt::LeftButton &&!(e->modifiers() & Qt::ShiftModifier)) { + QTextCursor cursor = cursorForPosition(e->pos()); + jumpToChangeFromDiff(cursor); + } + } + TextEditor::BaseTextEditor::mouseDoubleClickEvent(e); +} + +void VCSBaseEditor::keyPressEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { + jumpToChangeFromDiff(textCursor()); + return; + } + BaseTextEditor::keyPressEvent(e); +} + +void VCSBaseEditor::describe() +{ + if (VCSBase::Constants::Internal::debug) + qDebug() << "VCSBaseEditor::describe" << m_d->m_currentChange; + if (!m_d->m_currentChange.isEmpty()) + emit describeRequested(m_d->m_source, m_d->m_currentChange); +} + +void VCSBaseEditor::slotActivateAnnotation() +{ + // The annotation highlighting depends on contents (change number + // set with assigned colors) + if (m_d->m_parameters->type != AnnotateOutput) + return; + + const QSet<QString> changes = annotationChanges(); + if (changes.isEmpty()) + return; + if (VCSBase::Constants::Internal::debug) + qDebug() << "VCSBaseEditor::slotActivateAnnotation(): #" << changes.size(); + + disconnect(this, SIGNAL(textChanged()), this, SLOT(slotActivateAnnotation())); + + if (BaseAnnotationHighlighter *ah = qobject_cast<BaseAnnotationHighlighter *>(baseTextDocument()->syntaxHighlighter())) { + ah->setChangeNumbers(changes); + ah->rehighlight(); + } else { + baseTextDocument()->setSyntaxHighlighter(createAnnotationHighlighter(changes)); + } +} + +// Check for a change chunk "@@ -91,7 +95,7 @@" and return +// the modified line number (95). +// Note that git appends stuff after " @@" (function names, etc.). +static inline bool checkChunkLine(const QString &line, int *modifiedLineNumber) +{ + if (!line.startsWith(QLatin1String("@@ "))) + return false; + const int endPos = line.indexOf(QLatin1String(" @@"), 3); + if (endPos == -1) + return false; + // the first chunk range applies to the original file, the second one to + // the modified file, the one we're interested int + const int plusPos = line.indexOf(QLatin1Char('+'), 3); + if (plusPos == -1 || plusPos > endPos) + return false; + const int lineNumberPos = plusPos + 1; + const int commaPos = line.indexOf(QLatin1Char(','), lineNumberPos); + if (commaPos == -1 || commaPos > endPos) + return false; + const QString lineNumberStr = line.mid(lineNumberPos, commaPos - lineNumberPos); + bool ok; + *modifiedLineNumber = lineNumberStr.toInt(&ok); + return ok; +} + +void VCSBaseEditor::jumpToChangeFromDiff(QTextCursor cursor) +{ + int chunkStart = 0; + int lineCount = -1; + const QChar deletionIndicator = QLatin1Char('-'); + // find nearest change hunk + QTextBlock block = cursor.block(); + for ( ; block.isValid() ; block = block.previous()) { + const QString line = block.text(); + if (checkChunkLine(line, &chunkStart)) { + break; + } else { + if (!line.startsWith(deletionIndicator)) + ++lineCount; + } + } + + if (VCSBase::Constants::Internal::debug) + qDebug() << "VCSBaseEditor::jumpToChangeFromDiff()1" << chunkStart << lineCount; + + if (chunkStart == -1 || lineCount < 0 || !block.isValid()) + return; + + // find the filename in previous line, map depot name back + block = block.previous(); + if (!block.isValid()) + return; + const QString fileName = fileNameFromDiffSpecification(block); + + const bool exists = fileName.isEmpty() ? false : QFile::exists(fileName); + + if (VCSBase::Constants::Internal::debug) + qDebug() << "VCSBaseEditor::jumpToChangeFromDiff()2" << fileName << "ex=" << exists << "line" << chunkStart << lineCount; + + if (!exists) + return; + + Core::IEditor *ediface = m_d->m_core->editorManager()->openEditor(fileName); + m_d->m_core->editorManager()->ensureEditorManagerVisible(); + if (TextEditor::ITextEditor *editor = qobject_cast<TextEditor::ITextEditor *>(ediface)) + editor->gotoLine(chunkStart + lineCount); +} + +void VCSBaseEditor::setPlainTextData(const QByteArray &data) +{ + setPlainText(codec()->toUnicode(data)); +} + +void VCSBaseEditor::setFontSettings(const TextEditor::FontSettings &fs) +{ + TextEditor::BaseTextEditor::setFontSettings(fs); + if (m_d->m_parameters->type == DiffOutput) { + if (DiffHighlighter *highlighter = qobject_cast<DiffHighlighter*>(baseTextDocument()->syntaxHighlighter())) { + static QVector<QString> categories; + if (categories.isEmpty()) { + categories << QLatin1String(TextEditor::Constants::C_TEXT) + << QLatin1String(TextEditor::Constants::C_ADDED_LINE) + << QLatin1String(TextEditor::Constants::C_REMOVED_LINE) + << QLatin1String(TextEditor::Constants::C_DIFF_FILE) + << QLatin1String(TextEditor::Constants::C_DIFF_LOCATION); + } + highlighter->setFormats(fs.toTextCharFormats(categories)); + highlighter->rehighlight(); + } + } +} + +const VCSBaseEditorParameters *VCSBaseEditor::findType(const VCSBaseEditorParameters *array, + int arraySize, + EditorContentType et) +{ + for (int i = 0; i < arraySize; i++) + if (array[i].type == et) + return array + i; + return 0; +} + +// Find the codec used for a file querying the editor. +static QTextCodec *findFileCodec(const Core::ICore *core, const QString &source) +{ + typedef QList<Core::IEditor *> EditorList; + + const EditorList editors = core->editorManager()->editorsForFileName(source); + if (!editors.empty()) { + const EditorList::const_iterator ecend = editors.constEnd(); + for (EditorList::const_iterator it = editors.constBegin(); it != ecend; ++it) + if (const TextEditor::BaseTextEditorEditable *be = qobject_cast<const TextEditor::BaseTextEditorEditable *>(*it)) { + QTextCodec *codec = be->editor()->textCodec(); + if (VCSBase::Constants::Internal::debug) + qDebug() << Q_FUNC_INFO << source << codec->name(); + return codec; + } + } + if (VCSBase::Constants::Internal::debug) + qDebug() << Q_FUNC_INFO << source << "not found"; + return 0; +} + +// Find the codec by checking the projects (root dir of project file) +static QTextCodec *findProjectCodec(const QString &dir) +{ + typedef QList<ProjectExplorer::Project*> ProjectList; + // Try to find a project under which file tree the file is. + const ProjectExplorer::SessionManager *sm = ProjectExplorer::ProjectExplorerPlugin::instance()->session(); + const ProjectList projects = sm->projects(); + if (!projects.empty()) { + const ProjectList::const_iterator pcend = projects.constEnd(); + for (ProjectList::const_iterator it = projects.constBegin(); it != pcend; ++it) + if (const Core::IFile *file = (*it)->file()) + if (file->fileName().startsWith(dir)) { + QTextCodec *codec = (*it)->editorConfiguration()->defaultTextCodec(); + if (VCSBase::Constants::Internal::debug) + qDebug() << Q_FUNC_INFO << dir << (*it)->name() << codec->name(); + return codec; + } + } + if (VCSBase::Constants::Internal::debug) + qDebug() << Q_FUNC_INFO << dir << "not found"; + return 0; +} + +QTextCodec *VCSBaseEditor::getCodec(const Core::ICore *core, const QString &source) +{ + if (!source.isEmpty()) { + // Check file + const QFileInfo sourceFi(source); + if (sourceFi.isFile()) + if (QTextCodec *fc = findFileCodec(core, source)) + return fc; + // Find by project via directory + if (QTextCodec *pc = findProjectCodec(sourceFi.isFile() ? sourceFi.absolutePath() : source)) + return pc; + } + QTextCodec *sys = QTextCodec::codecForLocale(); + if (VCSBase::Constants::Internal::debug) + qDebug() << Q_FUNC_INFO << source << "defaulting to " << sys->name(); + return sys; +} + +VCSBaseEditor *VCSBaseEditor::getVcsBaseEditor(const Core::IEditor *editor) +{ + if (const TextEditor::BaseTextEditorEditable *be = qobject_cast<const TextEditor::BaseTextEditorEditable *>(editor)) + return qobject_cast<VCSBaseEditor *>(be->editor()); + return 0; +} + +} diff --git a/src/plugins/vcsbase/vcsbaseeditor.h b/src/plugins/vcsbase/vcsbaseeditor.h new file mode 100644 index 00000000000..364a9fb7e97 --- /dev/null +++ b/src/plugins/vcsbase/vcsbaseeditor.h @@ -0,0 +1,173 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef VCSBaseBASEEDITOR_H +#define VCSBaseBASEEDITOR_H + +#include "vcsbase_global.h" + +#include <texteditor/basetexteditor.h> + +#include <QtCore/QSet> + +QT_BEGIN_NAMESPACE +class QAction; +class QTextCodec; +class QTextCursor; +QT_END_NAMESPACE + +namespace Core { +class ICore; +} + +namespace VCSBase { + +struct VCSBaseEditorPrivate; +class DiffHighlighter; +class BaseAnnotationHighlighter; + +// Contents of a VCSBaseEditor +enum EditorContentType { + // No special handling. + RegularCommandOutput, + // Log of a file under revision control. Provide 'click on change' + // description. + LogOutput, + // <change description>: file line + // Color per change number and provide 'click on change' description. + AnnotateOutput, + // Diff output. Might includes describe output, which consists of a + // header and diffs. Interaction is 'double click in hunk' which + // opens the file + DiffOutput +}; + +// Helper struct used to parametrize an editor with mime type, context +// and kind. The extension is currently only a suggestion when running +// VCS commands with redirection. +struct VCSBASE_EXPORT VCSBaseEditorParameters { + EditorContentType type; + const char *kind; + const char *context; + const char *mimeType; + const char *extension; +}; + +// Base class for editors showing version control system output +// of the type enumerated by EditorContentType. +// The source property should contain the file or directory the log +// refers to and will be emitted with describeRequested(). +// This is for VCS that need a current directory. +class VCSBASE_EXPORT VCSBaseEditor : public TextEditor::BaseTextEditor +{ + Q_PROPERTY(QString source READ source WRITE setSource); + Q_PROPERTY(QTextCodec *codec READ codec WRITE setCodec); + Q_OBJECT +protected: + // Initialization requires calling init() (which in turns calls + // virtual functions). + explicit VCSBaseEditor(const VCSBaseEditorParameters *type, + QWidget *parent); +public: + void init(); + + virtual ~VCSBaseEditor(); + + QString source() const; + void setSource(const QString &source); + + QTextCodec *codec() const; + void setCodec(QTextCodec *); + + bool isModified() const; + + EditorContentType contentType() const; + + // Utility to find a parameter set by type in an array. + static const VCSBaseEditorParameters * + findType(const VCSBaseEditorParameters *array, int arraySize, EditorContentType et); + + // Utility to find the codec for a source (file or directory), querying + // the editor manager and the project managers (defaults to system codec). + // The codec should be set on editors displaying diff or annotation + // output. + static QTextCodec *getCodec(const Core::ICore *core, const QString &source); + + // Utility to return the editor from the IEditor returned by the editor + // manager which is a BaseTextEditable. + static VCSBaseEditor *getVcsBaseEditor(const Core::IEditor *editor); + +signals: + void describeRequested(const QString &source, const QString &change); + +public slots: + // Convenience slot to set data read from stdout, will use the + // documents' codec to decode + void setPlainTextData(const QByteArray &data); + +protected: + virtual TextEditor::BaseTextEditorEditable *createEditableInterface(); + + void contextMenuEvent(QContextMenuEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseDoubleClickEvent(QMouseEvent *e); + void keyPressEvent(QKeyEvent *); + +public slots: + void setFontSettings(const TextEditor::FontSettings &); + +private slots: + void describe(); + void slotActivateAnnotation(); + +private: + // Implement to return a set of change identifiers in + // annotation mode + virtual QSet<QString> annotationChanges() const = 0; + // Implement to identify a change number at the cursor position + virtual QString changeUnderCursor(const QTextCursor &) const = 0; + // Factory functions for highlighters + virtual DiffHighlighter *createDiffHighlighter() const = 0; + virtual BaseAnnotationHighlighter *createAnnotationHighlighter(const QSet<QString> &changes) const = 0; + // Implement to return a local file name from the diff file specification + // (text cursor at position above change hunk) + virtual QString fileNameFromDiffSpecification(const QTextBlock &diffFileSpec) const = 0; + + void jumpToChangeFromDiff(QTextCursor cursor); + + VCSBaseEditorPrivate *m_d; +}; + +} // namespace Internal + +#endif // VCSBaseBASEEDITOR_H diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp new file mode 100644 index 00000000000..7337ba5d314 --- /dev/null +++ b/src/plugins/vcsbase/vcsbaseplugin.cpp @@ -0,0 +1,81 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "vcsbaseplugin.h" +#include "diffhighlighter.h" + +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/mimedatabase.h> + +#include <QtCore/qplugin.h> + +namespace VCSBase { +namespace Internal { + +VCSBasePlugin *VCSBasePlugin::m_instance = 0; + +VCSBasePlugin::VCSBasePlugin() +{ + m_instance = this; +} + +VCSBasePlugin::~VCSBasePlugin() +{ + m_instance = 0; +} + +bool VCSBasePlugin::initialize(const QStringList & /*arguments*/, QString *errorMessage) +{ + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + + if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/trolltech.vcsbase/VCSBase.mimetypes.xml"), errorMessage)) + return false; + + return true; +} + +void VCSBasePlugin::extensionsInitialized() +{ +} + +VCSBasePlugin *VCSBasePlugin::instance() +{ + return m_instance; +} + +} // namespace Internal +} // namespace VCSBase + +Q_EXPORT_PLUGIN(VCSBase::Internal::VCSBasePlugin) diff --git a/src/plugins/vcsbase/vcsbaseplugin.h b/src/plugins/vcsbase/vcsbaseplugin.h new file mode 100644 index 00000000000..c10616ad937 --- /dev/null +++ b/src/plugins/vcsbase/vcsbaseplugin.h @@ -0,0 +1,64 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef VCSBASEPLUGIN_H +#define VCSBASEPLUGIN_H + +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> + +namespace VCSBase { +namespace Internal { + +class VCSBasePlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + VCSBasePlugin(); + ~VCSBasePlugin(); + + bool initialize(const QStringList &arguments, QString *error_message); + + void extensionsInitialized(); + + static VCSBasePlugin *instance(); + +private: + static VCSBasePlugin *m_instance; +}; + +} // namespace Internal +} // namespace VCSBase + +#endif // VCSBASEPLUGIN_H diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp new file mode 100644 index 00000000000..6d271910e68 --- /dev/null +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp @@ -0,0 +1,281 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "vcsbasesubmiteditor.h" +#include "submiteditorfile.h" + +#include <coreplugin/ifile.h> +#include <coreplugin/icore.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> + +#include <utils/submiteditorwidget.h> +#include <find/basetextfind.h> + +#include <QtGui/QToolBar> +#include <QtGui/QStyle> +#include <QtCore/QPointer> +#include <QtCore/QFileInfo> +#include <QtCore/QFile> +#include <QtCore/QTextStream> +#include <QtCore/QDebug> + +enum { debug = 0 }; + +static inline QAction *actionFromId(const Core::ICore *core, const char *id) +{ + QAction *rc = 0; + if (id) + if (const Core::ICommand *cmd = core->actionManager()->command(id)) + rc = cmd->action(); + if (debug) + qDebug() << Q_FUNC_INFO << id << rc; + return rc; +} + +namespace VCSBase { + +struct VCSBaseSubmitEditorPrivate { + VCSBaseSubmitEditorPrivate(const VCSBaseSubmitEditorParameters *parameters, + Core::Utils::SubmitEditorWidget *editorWidget, + QObject *q); + + Core::ICore *m_core; + Core::Utils::SubmitEditorWidget *m_widget; + QToolBar *m_toolWidget; + const VCSBaseSubmitEditorParameters *m_parameters; + QString m_displayName; + VCSBase::Internal::SubmitEditorFile *m_file; + QList<int> m_contexts; + + QPointer<QAction> m_undoAction; + QPointer<QAction> m_redoAction; + QPointer<QAction> m_submitAction; + QPointer<QAction> m_diffAction; +}; + +VCSBaseSubmitEditorPrivate::VCSBaseSubmitEditorPrivate(const VCSBaseSubmitEditorParameters *parameters, + Core::Utils::SubmitEditorWidget *editorWidget, + QObject *q) : + m_core(ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()), + m_widget(editorWidget), + m_toolWidget(0), + m_parameters(parameters), + m_file(new VCSBase::Internal::SubmitEditorFile(QLatin1String(m_parameters->mimeType), q)), + m_undoAction(actionFromId(m_core, m_parameters->undoActionId)), + m_redoAction(actionFromId(m_core, m_parameters->redoActionId)), + m_submitAction(actionFromId(m_core, m_parameters->submitActionId)), + m_diffAction(actionFromId(m_core, m_parameters->diffActionId)) +{ + m_contexts << m_core->uniqueIDManager()->uniqueIdentifier(m_parameters->context); +} + +VCSBaseSubmitEditor::VCSBaseSubmitEditor(const VCSBaseSubmitEditorParameters *parameters, + Core::Utils::SubmitEditorWidget *editorWidget) : + m_d(new VCSBaseSubmitEditorPrivate(parameters, editorWidget, this)) +{ + m_d->m_file->setModified(false); + // We are always clean to prevent the editor manager from asking to save. + connect(m_d->m_file, SIGNAL(saveMe(QString)), this, SLOT(save(QString))); + + m_d->m_widget->registerActions(m_d->m_undoAction, m_d->m_redoAction, m_d->m_submitAction, m_d->m_diffAction); + connect(m_d->m_widget, SIGNAL(diffSelected(QStringList)), this, SLOT(slotDiffSelectedVCSFiles(QStringList))); + connect(m_d->m_widget->descriptionEdit(), SIGNAL(textChanged()), this, SLOT(slotDescriptionChanged())); + + Aggregation::Aggregate *aggregate = new Aggregation::Aggregate; + aggregate->add(new Find::BaseTextFind(m_d->m_widget->descriptionEdit())); + aggregate->add(this); +} + +VCSBaseSubmitEditor::~VCSBaseSubmitEditor() +{ + delete m_d->m_toolWidget; + delete m_d->m_widget; + delete m_d; +} + +void VCSBaseSubmitEditor::slotDescriptionChanged() +{ +} + +bool VCSBaseSubmitEditor::createNew(const QString &contents) +{ + setFileContents(contents); + return true; +} + +bool VCSBaseSubmitEditor::open(const QString &fileName) +{ + if (fileName.isEmpty()) + return false; + + const QFileInfo fi(fileName); + if (!fi.isFile() || !fi.isReadable()) + return false; + + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) { + qWarning("Unable to open %s: %s", qPrintable(fileName), qPrintable(file.errorString())); + return false; + } + + const QString text = QString::fromLocal8Bit(file.readAll()); + if (!createNew(text)) + return false; + + m_d->m_file->setFileName(fi.absoluteFilePath()); + return true; +} + +Core::IFile *VCSBaseSubmitEditor::file() +{ + return m_d->m_file; +} + +QString VCSBaseSubmitEditor::displayName() const +{ + return m_d->m_displayName; +} + +void VCSBaseSubmitEditor::setDisplayName(const QString &title) +{ + m_d->m_displayName = title; +} + +bool VCSBaseSubmitEditor::duplicateSupported() const +{ + return false; +} + +Core::IEditor *VCSBaseSubmitEditor::duplicate(QWidget * /*parent*/) +{ + return 0; +} + +const char *VCSBaseSubmitEditor::kind() const +{ + return m_d->m_parameters->kind; +} + +QToolBar *VCSBaseSubmitEditor::toolBar() +{ + if (m_d->m_toolWidget) + return m_d->m_toolWidget; + + if (!m_d->m_diffAction && !m_d->m_submitAction) + return 0; + + // Create + QToolBar *toolBar = new QToolBar; + toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + const int size = m_d->m_widget->style()->pixelMetric(QStyle::PM_SmallIconSize); + toolBar->setIconSize(QSize(size, size)); + toolBar->addSeparator(); + + if (m_d->m_submitAction) + toolBar->addAction(m_d->m_submitAction); + if (m_d->m_diffAction) + toolBar->addAction(m_d->m_diffAction); + m_d->m_toolWidget = toolBar; + return toolBar; +} + +QList<int> VCSBaseSubmitEditor::context() const +{ + return m_d->m_contexts; +} + +QWidget *VCSBaseSubmitEditor::widget() +{ + return m_d->m_widget; +} + +QByteArray VCSBaseSubmitEditor::saveState() const +{ + return QByteArray(); +} + +bool VCSBaseSubmitEditor::restoreState(const QByteArray &/*state*/) +{ + return true; +} + +QStringList VCSBaseSubmitEditor::checkedFiles() const +{ + return vcsFileListToFileList(m_d->m_widget->checkedFiles()); +} + +void VCSBaseSubmitEditor::setFileList(const QStringList &l) +{ + m_d->m_widget->setFileList(l); +} + +void VCSBaseSubmitEditor::addFiles(const QStringList& list, bool checked, bool userCheckable) +{ + m_d->m_widget->addFiles(list, checked, userCheckable); +} + +void VCSBaseSubmitEditor::slotDiffSelectedVCSFiles(const QStringList &rawList) +{ + emit diffSelectedFiles(vcsFileListToFileList(rawList)); +} + +bool VCSBaseSubmitEditor::save(const QString &fileName) +{ + const QString fName = fileName.isEmpty() ? m_d->m_file->fileName() : fileName; + QFile file(fName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { + qWarning("Unable to open %s: %s", qPrintable(fName), qPrintable(file.errorString())); + return false; + } + file.write(fileContents().toLocal8Bit()); + if (!file.flush()) + return false; + file.close(); + const QFileInfo fi(fName); + m_d->m_file->setFileName(fi.absoluteFilePath()); + m_d->m_file->setModified(false); + return true; +} + +QString VCSBaseSubmitEditor::fileContents() const +{ + return m_d->m_widget->trimmedDescriptionText(); +} + +bool VCSBaseSubmitEditor::setFileContents(const QString &contents) +{ + m_d->m_widget->setDescriptionText(contents); + return true; +} + +} diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.h b/src/plugins/vcsbase/vcsbasesubmiteditor.h new file mode 100644 index 00000000000..f2ba36b0c90 --- /dev/null +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.h @@ -0,0 +1,146 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef VCSBaseSUBMITEDITOR_H +#define VCSBaseSUBMITEDITOR_H + +#include "vcsbase_global.h" + +#include <coreplugin/editormanager/ieditor.h> + +#include <QtCore/QList> + +namespace Core { + namespace Utils { + class SubmitEditorWidget; + } +} + +namespace VCSBase { + +struct VCSBaseSubmitEditorPrivate; + +/* Utility struct to parametrize a VCSBaseSubmitEditor. */ +struct VCSBASE_EXPORT VCSBaseSubmitEditorParameters { + const char *mimeType; + const char *kind; + const char *context; + const char *undoActionId; + const char *redoActionId; + const char *submitActionId; + const char *diffActionId; +}; + +/* Base class for a submit editor based on the Core::Utils::SubmitEditorWidget + * that presents the commit message in a text editor and an + * checkable list of modified files in a list window. The user can delete + * files from the list by pressing unchecking them or diff the selection + * by doubleclicking. + * + * The action matching the the ids (unless 0) of the parameter struct will be + * registered with the EditorWidget and submit/diff actions will be added to + * a toolbar. + * + * For the given context, there must be only one instance of the editor + * active. + * To start a submit, set the submit template on the editor and the output + * of the VCS status command listing the modified files as fileList and open + * it. + * The submit process is started by listening on the editor close + * signal and then asking the IFile interface of the editor to save the file + * within a IFileManager::blockFileChange() section + * and to launch the submit process. In addition, the action registered + * for submit should be connected to a slot triggering the close of the + * current editor in the editor manager. */ + +class VCSBASE_EXPORT VCSBaseSubmitEditor : public Core::IEditor +{ + Q_OBJECT +public: + typedef QList<int> Context; + +protected: + explicit VCSBaseSubmitEditor(const VCSBaseSubmitEditorParameters *parameters, + Core::Utils::SubmitEditorWidget *editorWidget); + +public: + virtual ~VCSBaseSubmitEditor(); + + // Core::IEditor + virtual bool createNew(const QString &contents); + virtual bool open(const QString &fileName); + virtual Core::IFile *file(); + virtual QString displayName() const; + virtual void setDisplayName(const QString &title); + virtual bool duplicateSupported() const; + virtual Core::IEditor *duplicate(QWidget * parent); + virtual const char *kind() const; + + virtual QToolBar *toolBar(); + virtual QList<int> context() const; + virtual QWidget *widget(); + + virtual QByteArray saveState() const; + virtual bool restoreState(const QByteArray &state); + + QStringList checkedFiles() const; + + void setFileList(const QStringList&); + void addFiles(const QStringList&, bool checked = true, bool userCheckable = true); + +signals: + void diffSelectedFiles(const QStringList &files); + +private slots: + void slotDiffSelectedVCSFiles(const QStringList &rawList); + bool save(const QString &fileName); + void slotDescriptionChanged(); + +protected: + /* Implemented this to extract the real file list from the status + * output of the versioning system as displayed in the file list + * for example "M foo.cpp" -> "foo.cpp". */ + virtual QStringList vcsFileListToFileList(const QStringList &) const = 0; + + /* These hooks allow for modifying the contents that goes to + * the file. The default implementation uses the text + * of the description editor. */ + virtual QString fileContents() const; + virtual bool setFileContents(const QString &contents); + +private: + VCSBaseSubmitEditorPrivate *m_d; +}; + +} + +#endif // VCSBaseSUBMITEDITOR_H diff --git a/src/plugins/vcsbase/vcsbasetextdocument.cpp b/src/plugins/vcsbase/vcsbasetextdocument.cpp new file mode 100644 index 00000000000..2e52e56ea0f --- /dev/null +++ b/src/plugins/vcsbase/vcsbasetextdocument.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "vcsbasetextdocument.h" + +namespace VCSBase { +namespace Internal { + +VCSBaseTextDocument::VCSBaseTextDocument() +{ +} + +bool VCSBaseTextDocument::isReadOnly() const +{ + return true; +} + +bool VCSBaseTextDocument::isModified() const +{ + return false; +} + +} +} diff --git a/src/plugins/vcsbase/vcsbasetextdocument.h b/src/plugins/vcsbase/vcsbasetextdocument.h new file mode 100644 index 00000000000..9c2b2c66c60 --- /dev/null +++ b/src/plugins/vcsbase/vcsbasetextdocument.h @@ -0,0 +1,55 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef VCSBASETEXTDOCUMENT_H +#define VCSBASETEXTDOCUMENT_H + +#include <texteditor/basetextdocument.h> + +namespace VCSBase { +namespace Internal { + +// A read-only text document. +class VCSBaseTextDocument : public TextEditor::BaseTextDocument +{ + Q_OBJECT +public: + VCSBaseTextDocument(); + + bool isReadOnly() const; + bool isModified() const; +}; + +} // namespace Internal +} // namespace VCSBase + +#endif // VCSBASETEXTDOCUMENT_H diff --git a/src/qworkbench.pri b/src/qworkbench.pri new file mode 100644 index 00000000000..d7ac4f34c5a --- /dev/null +++ b/src/qworkbench.pri @@ -0,0 +1,41 @@ +IDE_SOURCE_TREE = $$PWD/../ + +isEmpty(TEST) { + CONFIG(debug, debug|release) { + TEST = 1 + } +} + +!isEmpty(TEST) { + equals(TEST, 1) { + QT +=testlib + DEFINES+=WITH_TESTS + } +} + +isEmpty(IDE_BUILD_TREE) { + error("qworkbench.pri: including file must define IDE_BUILD_TREE (probably a relative path)") +} +macx { + IDE_APP_TARGET = QtCreator + IDE_LIBRARY_PATH = $$IDE_BUILD_TREE/bin/$${IDE_APP_TARGET}.app/Contents/PlugIns + contains(QT_CONFIG, ppc):CONFIG += ppc x86 +} else { + IDE_APP_TARGET = qtcreator + IDE_LIBRARY_PATH = $$IDE_BUILD_TREE/lib +} +IDE_APP_PATH = $$IDE_BUILD_TREE/bin +win32 { + IDE_LIBRARY_PATH ~= s|/+|\| + IDE_APP_PATH ~= s|/+|\| +} + +INCLUDEPATH += \ + $$IDE_SOURCE_TREE/src/libs \ + $$IDE_SOURCE_TREE/tools \ + +DEPENDPATH += \ + $$IDE_SOURCE_TREE/src/libs \ + $$IDE_SOURCE_TREE/tools \ + +LIBS += -L$$IDE_LIBRARY_PATH diff --git a/src/qworkbenchlibrary.pri b/src/qworkbenchlibrary.pri new file mode 100644 index 00000000000..1ef9b3fbec4 --- /dev/null +++ b/src/qworkbenchlibrary.pri @@ -0,0 +1,22 @@ +IDE_BUILD_TREE = $$OUT_PWD/../../../ +include(qworkbench.pri) + +win32 { + DLLDESTDIR = $$IDE_APP_PATH +} + +DESTDIR = $$IDE_LIBRARY_PATH + +macx { + QMAKE_LFLAGS_SONAME = -Wl,-install_name,@executable_path/../PlugIns/ +} else:linux-* { + #do the rpath by hand since it's not possible to use ORIGIN in QMAKE_RPATHDIR + QMAKE_RPATHDIR += \$\$ORIGIN + IDE_PLUGIN_RPATH = $$join(QMAKE_RPATHDIR, ":") + QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,$${IDE_PLUGIN_RPATH}\' + QMAKE_RPATHDIR = +} + +TARGET = $$qtLibraryTarget($$TARGET) + +contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols diff --git a/src/qworkbenchplugin.pri b/src/qworkbenchplugin.pri new file mode 100644 index 00000000000..50b667fc716 --- /dev/null +++ b/src/qworkbenchplugin.pri @@ -0,0 +1,57 @@ +isEmpty(IDE_BUILD_TREE) { + IDE_BUILD_TREE = $$OUT_PWD/../../../ +} +include(qworkbench.pri) + +isEmpty(PROVIDER) { + PROVIDER = Nokia +} + +DESTDIR = $$IDE_LIBRARY_PATH/$$PROVIDER/ +LIBS += -L$$DESTDIR +INCLUDEPATH += $$IDE_SOURCE_TREE/src/plugins +DEPENDPATH += $$IDE_SOURCE_TREE/src/plugins + +# copy the plugin spec +isEmpty(TARGET) { + error("qworkbenchplugin.pri: You must provide a TARGET") +} + +# Copy the pluginspec file to the liberary directyory. +# Note: On Windows/MinGW with some sh.exe in the path, +# QMAKE_COPY is some cp command that does not understand +# "\". Force the standard windows copy. +COPYDEST = $${DESTDIR} +COPYSRC = $${_PRO_FILE_PWD_}/$${TARGET}.pluginspec + +TARGET = $$qtLibraryTarget($$TARGET) + +win32 { + COPYDEST ~= s|/+|\| + COPYSRC ~= s|/+|\| + COPY_CMD=xcopy /y +} else { + COPY_CMD=$${QMAKE_COPY} +} + +QMAKE_POST_LINK += $${COPY_CMD} $${COPYSRC} $${COPYDEST} + +macx { + QMAKE_LFLAGS_SONAME = -Wl,-install_name,@executable_path/../PlugIns/$${PROVIDER}/ +} else:linux-* { + #do the rpath by hand since it's not possible to use ORIGIN in QMAKE_RPATHDIR + QMAKE_RPATHDIR += \$\$ORIGIN/.. + IDE_PLUGIN_RPATH = $$join(QMAKE_RPATHDIR, ":") + QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,$${IDE_PLUGIN_RPATH}\' + QMAKE_RPATHDIR = +} + + +unix { + OBJECTS_DIR = $${OUT_PWD}/.obj/ + MOC_DIR = $${OUT_PWD}/.moc/ + RCC_DIR = $${OUT_PWD}/.rcc/ + UI_DIR = $${OUT_PWD}/.uic/ +} + +contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols diff --git a/src/src.pro b/src/src.pro new file mode 100644 index 00000000000..0db231df31b --- /dev/null +++ b/src/src.pro @@ -0,0 +1,9 @@ +TEMPLATE = subdirs +CONFIG += ordered + +SUBDIRS = \ + libs \ + app \ + plugins + + diff --git a/src/tools/makespy/main.cpp b/src/tools/makespy/main.cpp new file mode 100644 index 00000000000..bee1a8f0d57 --- /dev/null +++ b/src/tools/makespy/main.cpp @@ -0,0 +1,211 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include <QApplication> +#include <QDebug> +#include <QDir> +#include <QProcess> +#include <QStringList> +#include <QTextStream> +#include <QTextEdit> + +class Process : public QProcess +{ + Q_OBJECT +public: + Process(); + + QString output() const { return m_output; } + QString error() const { return m_error; } + +private slots: + void standardErrorReady(); + void standardOutputReady(); + +private: + QString m_output; + QString m_error; +}; + +Process::Process() +{ + connect(this, SIGNAL(readyReadStandardError()), + this, SLOT(standardErrorReady())); + connect(this, SIGNAL(readyReadStandardOutput()), + this, SLOT(standardOutputReady())); +} + +void Process::standardErrorReady() +{ + m_error += readAllStandardError(); +} + +void Process::standardOutputReady() +{ + m_output += readAllStandardOutput(); +} + + +class MakeProcess : public Process +{ +public: + void handleOutput(); + +private: + void handleMakeLine(const QString &line); + void handleGccLine(const QString &line); + + QStringList m_dirStack; + + QVector<QString> m_options; + QStringList m_sourceFiles; + QStringList m_headerFiles; +}; + +void MakeProcess::handleOutput() +{ + QStringList lines = output().split('\n'); + m_dirStack.clear(); + m_dirStack.append(workingDirectory()); + foreach (const QString &line, lines) { + qDebug() << "LINE : " << line; + if (line.startsWith("make[")) + handleMakeLine(line); + else if (line.startsWith("gcc") || line.startsWith("g++")) + handleGccLine(line); + else + qDebug() << "IGNORE: " << line; + } +} + + +void MakeProcess::handleMakeLine(const QString &line) +{ + int pos1 = line.indexOf('`'); + int pos2 = line.indexOf('\''); + if (pos1 >= 0 && pos2 >= 0) { + QString dir = line.mid(pos1 + 1, pos2 - pos1 - 1); + qDebug() << "MAKE" << pos1 << pos2 << dir; + if (line.contains(": Entering directory")) { + qDebug() << "ENTER: " << dir; + m_dirStack.append(dir); + } else if (line.contains(": Leaving directory")) { + qDebug() << "LEAVE: " << dir; + Q_ASSERT(m_dirStack.last() == dir); + (void) m_dirStack.takeLast(); + } + } +} + +QStringList parseLine(const QString &line) +{ + QStringList result; + QString word; + bool quoted = false; + bool escaped = false; + for (int i = 0; i != line.size(); ++i) { + char c = line.at(i).unicode(); + if (c == '\'') { + escaped = true; + continue; + } + if (c == '\"' && !escaped) { + quoted = !quoted; + } else if (c == ' ' && !quoted) { + if (!word.isEmpty()) + result.append(word); + word.clear(); + } else { + word += c; + } + } + if (!word.isEmpty()) + result.append(word); + return result; +} + +void MakeProcess::handleGccLine(const QString &line) +{ + QStringList args = parseLine(line); + qDebug() << "GCC: " << args; +} + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QStringList args = app.arguments(); + QString buildName = ".makespybuild/gdb"; + //args << " + + QDir oldDir = QDir::current(); + QDir buildDir("/home/apoenitz/gdb/archer"); + buildDir.mkdir(buildName); + buildDir.cd(buildName); + QDir::setCurrent(buildDir.absolutePath()); + +/* + Process configure; + configure.setWorkingDirectory(buildDir.absolutePath()); + configure.start("../configure", QStringList()); + configure.waitForFinished(); + qDebug() << configure.errorString(); + qDebug() << configure.error(); + qDebug() << configure.output(); +*/ + MakeProcess make; + make.setWorkingDirectory(buildDir.absolutePath()); + make.start("make", QStringList()); + make.waitForFinished(); + make.handleOutput(); + qDebug() << make.errorString(); + qDebug() << make.error(); + qDebug() << make.output(); + + Process clean; + clean.setWorkingDirectory(buildDir.absolutePath()); + clean.start("make", QStringList() << "clean"); + clean.waitForFinished(); + qDebug() << clean.errorString(); + qDebug() << clean.error(); + qDebug() << clean.output(); + + QTextEdit edit; + edit.setText(make.error() + make.output()); + edit.resize(800, 600); + edit.show(); + + return app.exec(); + return 0; +} + +#include "main.moc" diff --git a/src/tools/makespy/makespy.pro b/src/tools/makespy/makespy.pro new file mode 100644 index 00000000000..604f309a261 --- /dev/null +++ b/src/tools/makespy/makespy.pro @@ -0,0 +1,6 @@ +TARGET = MakeSpy +CONFIG += console +CONFIG -= app_bundle +TEMPLATE = app +SOURCES += main.cpp +HEADERS += diff --git a/src/tools/qdebugger/lean.h b/src/tools/qdebugger/lean.h new file mode 100644 index 00000000000..69935c83ee6 --- /dev/null +++ b/src/tools/qdebugger/lean.h @@ -0,0 +1,153 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QDBIMPORTS_H +#define QDBIMPORTS_H + +#include <QtCore/QFile> +#include <QtCore/QList> +#include <QtGui/QIcon> +#include <QtGui/QPlainTextEdit> + +namespace TextEditor { + +class BaseTextMark : public QObject +{ +// Q_OBJECT +public: + BaseTextMark(const QString &/*filename*/, int /*line*/) {} + ~BaseTextMark() {} + + // return your icon here + virtual QIcon icon() const { return QIcon(); } + + // called if the linenumber changes + virtual void updateLineNumber(int /*lineNumber*/) {} + + // called whenever the text of the block for the marker changed + virtual void updateBlock(const QTextBlock &/*block*/) {} + + // called if the block containing this mark has been removed + // if this also removes your mark call this->deleteLater(); + virtual void removedFromEditor() {} + // call this if the icon has changed. + void updateMarker() {} + +public: + QString m_fileName; + int m_line; +}; + +} // namespace TextEditor + + + +#ifdef USE_BASETEXTEDITOR + +#define BASETEXTMARK_H +#include "../../plugins/texteditor/basetexteditor.h" + + +class Editable : public TextEditor::BaseTextEditorEditable +{ + Q_OBJECT + +public: + Editable(TextEditor::BaseTextEditor *editor) + : TextEditor::BaseTextEditorEditable(editor) + {} + + // Core::IContext + QList<int> context() const { return m_context; } + + // Core::IEditor + const char* kind() const { return "MYEDITOR"; } + bool duplicateSupported() const { return false; } + Core::IEditor* duplicate(QWidget*) { return 0; } + + QList<int> m_context; +}; + + +class TextViewer : public TextEditor::BaseTextEditor +{ + Q_OBJECT + +public: + TextViewer(QWidget *parent) + : TextEditor::BaseTextEditor(parent) + { + setCodeFoldingVisible(true); + setMarksVisible(true); + setHighlightCurrentLine(true); + } + + TextEditor::BaseTextEditorEditable* createEditableInterface() + { + return new Editable(this); + } + + QString fileName() { return file()->fileName(); } + + int currentLine() const { return textCursor().blockNumber() + 1; } +}; + + +#else // defined(USE_BASETEXTEDITOR) + + +class TextViewer : public QPlainTextEdit +{ + Q_OBJECT + +public: + TextViewer(QWidget *parent) : QPlainTextEdit(parent) {} + QString fileName() const { return m_fileName; } + int currentLine() const { return textCursor().blockNumber() + 1; } + + bool open(const QString &fileName) + { + m_fileName = fileName; + QFile file(fileName); + bool result = file.open(QIODevice::ReadOnly); + QString contents = file.readAll(); + setPlainText(contents); + return result; + } + +public: + QString m_fileName; +}; + +#endif // defined(USE_BASETEXTEDITOR) + +#endif // QDBIMPORTS diff --git a/src/tools/qdebugger/main.cpp b/src/tools/qdebugger/main.cpp new file mode 100644 index 00000000000..101a046cf2c --- /dev/null +++ b/src/tools/qdebugger/main.cpp @@ -0,0 +1,46 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + MainWindow mw; + mw.show(); + QStringList args = app.arguments(); + args.removeFirst(); // program name.. + mw.loadFiles(args); + return app.exec(); +} + diff --git a/src/tools/qdebugger/mainwindow.cpp b/src/tools/qdebugger/mainwindow.cpp new file mode 100644 index 00000000000..e1203b6fac7 --- /dev/null +++ b/src/tools/qdebugger/mainwindow.cpp @@ -0,0 +1,543 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "mainwindow.h" + +#include "gdbdebugger.h" +#include "gdboutputwindow.h" +#include "lean.h" + +#include <QtCore/QDebug> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QPointer> +#include <QtCore/QSettings> + +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QButtonGroup> +#include <QtGui/QComboBox> +#include <QtGui/QDockWidget> +#include <QtGui/QFileDialog> +#include <QtGui/QGridLayout> +#include <QtGui/QHeaderView> +#include <QtGui/QMainWindow> +#include <QtGui/QMenu> +#include <QtGui/QMenuBar> +#include <QtGui/QMessageBox> +#include <QtGui/QStandardItemModel> +#include <QtGui/QStatusBar> +#include <QtGui/QTabWidget> +#include <QtGui/QTextBlock> +#include <QtGui/QToolBar> +#include <QtGui/QTreeView> +#include <QtGui/QWidget> + +using namespace GdbDebugger; +using namespace GdbDebugger::Internal; + + +/////////////////////////////////////////////////////////////////////// +// +// LocationMark +// +/////////////////////////////////////////////////////////////////////// + +#ifdef USE_BASETEXTEDITOR + +class LocationMark : public TextEditor::ITextMark +{ +public: + LocationMark() { m_editor = 0; } + ~LocationMark() { qDebug() << "LOCATIONMARK DESTRUCTOR" << m_editor; } + + QIcon icon() const; + QColor color() const; + + void contextMenu(const QPoint &/*position*/) {} + void toggle() {} + + void updateLineNumber(int /*line*/) {} + void removedFromEditor() { m_editor = 0; } + + TextViewer *editor() const { return m_editor.data(); } + void setEditor(TextViewer *editor); + + void updateBlock(const QTextBlock &) {} + void documentClosing() {} + +private: + QPointer<TextViewer> m_editor; +}; + +void LocationMark::setEditor(TextViewer *editor) +{ + if (m_editor) + m_editor->markableInterface()->removeMark(this); + if (editor) + m_editor = editor; + else + m_editor = 0; +} + +QIcon LocationMark::icon() const +{ + static const QIcon icon(":/gdbdebugger/images/location.svg"); + return icon; +} + +QColor LocationMark::color() const +{ + return QColor(255, 255, 0, 20); +} + +LocationMark *theLocationMark() +{ + static LocationMark *mark = new LocationMark; + return mark; +} + +#endif + +/////////////////////////////////////////////////////////////////////////// +// +// MainWindow +// +/////////////////////////////////////////////////////////////////////////// + +MainWindow::MainWindow() +{ + m_manager = new DebuggerManager; + m_manager->initialize(); + m_manager->createDockWidgets(); + m_manager->setSimpleDockWidgetArrangement(); + setCentralWidget(m_manager->mainWindow()); + + // + // Source code view + // + m_textViewers = new QTabWidget(m_manager->mainWindow()); + m_textViewers->setObjectName("Editors"); + m_textViewers->setElideMode(Qt::ElideLeft); + //setCentralWidget(m_textViewers); + m_manager->mainWindow()->setCentralWidget(m_textViewers); + setGeometry(QRect(0, 0, 800, 600)); + + // + // Actions + // + m_fileOpenAction = new QAction(tr("Open file"), this); + m_fileOpenAction->setShortcut(QKeySequence(tr("Ctrl+O"))); + + m_quitAction = new QAction(tr("Quit"), this); + m_quitAction->setShortcut(QKeySequence(tr("Ctrl+Q"))); + +#if 0 + m_startDebuggerAction = new QAction(tr("Run to main()"), this); + m_startDebuggerAction->setShortcut(QKeySequence(tr("Ctrl+F5"))); + connect(m_startDebuggerAction, SIGNAL(triggered()), + this, SLOT(startDebuggerRequest())); + connect(m_resetAction, SIGNAL(triggered()), + m_manager, SLOT(resetDebugger())); + connect(m_watchAction, SIGNAL(triggered()), + m_manager, SLOT(addToWatchWindow())); + connect(m_breakAction, SIGNAL(triggered()), + this, SLOT(toggleBreakpoint())); +#endif + + m_manager->m_continueAction->setShortcut(QKeySequence(tr("F5"))); + m_manager->m_stopAction->setShortcut(QKeySequence(tr("Shift+F5"))); + + //m_resetAction = new QAction(tr("Reset Debugger"), this); + //m_resetAction->setShortcut(QKeySequence(tr("Ctrl+Shift+F5"))); + + m_manager->m_nextAction->setShortcut(QKeySequence(tr("F6"))); + m_manager->m_stepAction->setShortcut(QKeySequence(tr("F7"))); + m_manager->m_nextIAction->setShortcut(QKeySequence(tr("Shift+F6"))); + m_manager->m_stepIAction->setShortcut(QKeySequence(tr("Shift+F9"))); + m_manager->m_stepOutAction->setShortcut(QKeySequence(tr("Shift+F7"))); + m_manager->m_runToLineAction->setShortcut(QKeySequence(tr("Shift+F8"))); + //m_manager->m_jumpToLineAction->setShortcut(QKeySequence(tr("Shift+F8"))); + m_manager->m_breakAction->setShortcut(QKeySequence(tr("F8"))); + m_manager->m_watchAction->setShortcut(QKeySequence(tr("ALT+D,ALT+W"))); + + // + // Files + // + QDockWidget *filesDock = new QDockWidget(this); + filesDock->setObjectName("FilesDock"); + filesDock->setGeometry(QRect(0, 0, 200, 200)); + filesDock->setWindowTitle(tr("Files")); + filesDock->setAllowedAreas(Qt::AllDockWidgetAreas); + addDockWidget(Qt::LeftDockWidgetArea, filesDock); + + m_filesModel = new QStandardItemModel(this); + m_filesWindow = new QTreeView(this); + m_filesWindow->setModel(m_filesModel); + m_filesWindow->header()->hide(); + m_filesWindow->setRootIsDecorated(false); + filesDock->setWidget(m_filesWindow); + connect(m_filesWindow, SIGNAL(activated(QModelIndex)), + this, SLOT(changeCurrentFile(QModelIndex))); + connect(m_filesWindow, SIGNAL(clicked(QModelIndex)), + this, SLOT(changeCurrentFile(QModelIndex))); + + // + // Menubar + // + QMenu *fileMenu = new QMenu(menuBar()); + fileMenu->setTitle(tr("File")); + fileMenu->addAction(m_fileOpenAction); + fileMenu->addSeparator(); + fileMenu->addAction(m_quitAction); + menuBar()->addMenu(fileMenu); + + QMenu *debugMenu = new QMenu(menuBar()); + debugMenu->setTitle(tr("Debug")); + debugMenu->addAction(m_manager->m_continueAction); + debugMenu->addAction(m_manager->m_stopAction); + debugMenu->addSeparator(); + debugMenu->addAction(m_manager->m_nextAction); + debugMenu->addAction(m_manager->m_stepAction); + debugMenu->addAction(m_manager->m_nextIAction); + debugMenu->addAction(m_manager->m_stepIAction); + debugMenu->addAction(m_manager->m_stepOutAction); + debugMenu->addAction(m_manager->m_runToLineAction); + debugMenu->addAction(m_manager->m_runToFunctionAction); + debugMenu->addAction(m_manager->m_jumpToLineAction); + debugMenu->addSeparator(); + debugMenu->addAction(m_manager->m_breakAction); + //debugMenu->addAction(m_startDebuggerAction); + menuBar()->addMenu(debugMenu); + + // + // Toolbar + // + QToolBar *toolbar = m_manager->createToolBar(); + toolbar->setObjectName("ToolBar"); + addToolBar(Qt::TopToolBarArea, toolbar); + + // + // Statusbar + // + QStatusBar *statusbar = new QStatusBar(this); + statusbar->setObjectName("StatusBar"); + statusbar->setGeometry(QRect(0, 578, 800, 22)); + setStatusBar(statusbar); + + show(); + restoreState(settings().value("MainWindow/State").toByteArray()); + setGeometry(settings().value("MainWindow/Geometry").toRect()); + + connect(m_fileOpenAction, SIGNAL(triggered()), + this, SLOT(fileOpen())); + connect(m_quitAction, SIGNAL(triggered()), + this, SLOT(quit())); + + connect(m_manager, SIGNAL(resetLocationRequested()), + this, SLOT(resetLocation())); + connect(m_manager, SIGNAL(gotoLocationRequested(QString,int,bool)), + this, SLOT(gotoLocation(QString,int,bool))); + connect(m_manager, SIGNAL(dataDumpersUnavailable()), + this, SLOT(handleDataDumpersUnavailable())); + + // Application interaction + connect(m_manager, SIGNAL(currentTextEditorRequested(QString*,int*,QObject**)), + this, SLOT(queryCurrentTextEditor(QString*,int*,QObject**))); +} + +MainWindow::~MainWindow() +{ + settings().setValue("MainWindow/State", saveState()); + settings().setValue("MainWindow/Geometry", geometry()); + settings().sync(); +} + +QSettings &MainWindow::settings() +{ + static QSettings s("Nokia", "Qdb"); + return s; +} + +void MainWindow::loadFile(const QString &fileName) +{ + QFileInfo fi(fileName); + if (fi.isExecutable() && !fileName.endsWith(".cpp") && !fileName.endsWith(".h")) { + m_executable = fileName; + QStandardItem *item = new QStandardItem(fileName); + QStandardItem *childItem = new QStandardItem("<gdb not running>"); + item->appendRow(childItem); // force [+] on item + item->setToolTip(fi.absoluteFilePath()); + //m_fileModel->appendRow(item); + } else { + (void)findOrCreateTextViewer(fileName); + } +} + +void MainWindow::loadFiles(const QStringList &fileNames) +{ + if (fileNames.isEmpty()) + return; + foreach (const QString &fileName, fileNames) + loadFile(fileName); + startDebuggingRequest(); +} + +void MainWindow::startDebuggingRequest() +{ + if (m_manager->m_startIsContinue) { + m_manager->continueInferior(); + return; + } + + m_manager->settings()->m_autoRun = true; + m_manager->settings()->m_autoQuit = true; + m_manager->settings()->m_gdbCmd = "gdb"; + m_manager->settings()->m_breakOnMain = "gdb"; + qDebug() << "START REQUEST"; + + if (m_executable.isEmpty()) { + QMessageBox::warning(0, + tr("Not a runnable project"), + tr("The current startup project can not be run.")); + return; + } + StartData sd; + sd.outputFile = m_executable; + //Project *pro = project(); + //sd.outputFile = pro->executable(pro->activeConfiguration()); + // NBS TODO pid + // pid = pe->configLanguage( + // ProjectExplorer::Constants::P_RUNNINGPID, langID).toString(); + // sd.env = pro->environment(pro->activeConfiguration()).toStringList(); + // sd.workingDir = pro->workingDirectory(pro->activeConfiguration()); + // sd.processArgs = pro->executableArguments(pro->activeConfiguration()); + // const QFileInfo editorFile(textViewer->file()->fileName()); + // sd.currentEditorDir = editorFile.dir().absolutePath(); + + m_manager->startDebuggerAndRunInferior(sd); +} + +void MainWindow::startDebuggerRequest() +{ + // FIXME: Ignored + StartData sd; + sd.outputFile = m_executable; + //Project *pro = project(); + //sd.outputFile = pro->executable(pro->activeConfiguration()); + // NBS TODO pid + // pid = pe->configLanguage( + // ProjectExplorer::Constants::P_RUNNINGPID, langID).toString(); + // sd.env = pro->environment(pro->activeConfiguration()).toStringList(); + // sd.workingDir = pro->workingDirectory(pro->activeConfiguration()); + // sd.processArgs = pro->executableArguments(pro->activeConfiguration()); + // const QFileInfo editorFile(textViewer->file()->fileName()); + // sd.currentEditorDir = editorFile.dir().absolutePath(); + + m_manager->m_settings->m_autoRun = false; + m_manager->m_settings->m_autoQuit = true; + m_manager->startDebuggerAndRunInferior(sd); +} + +void MainWindow::jumpToExec() +{ +#if 0 + TextViewer *editor = currentTextViewer(); + if (!editor) + return; + + int lineNumber = editor->currentLine(); + QString fileName = editor->file()->fileName(); + + if (fileName.isEmpty()) + return; + + m_manager->jumpToLineExec(fileName, lineNumber); +#endif +} + +void MainWindow::runToExec() +{ +#if 0 + TextViewer *editor = currentTextViewer(); + if (!editor) + return; + + int lineNumber = editor->currentLine(); + QString fileName = editor->file()->fileName(); + + if (fileName.isEmpty()) + return; + + m_manager->runToLineExec(fileName, lineNumber); +#endif +} + +void MainWindow::showStatusMessage(const QString &msg, int timeout) +{ + statusBar()->showMessage(msg, timeout); +} + +void MainWindow::resetLocation() +{ + TextViewer *textViewer = currentTextViewer(); + if (!textViewer) + return; + const int blockNumber = m_textBlockFromName.value(textViewer, -1); + if (blockNumber == 1) + return; + const QTextBlock &block = textViewer->document()->findBlockByNumber(blockNumber); + if (block.isValid()) { + QTextCursor cursor(block); + //QTextBlockFormat format = block.blockFormat(); + //format.setBackground(QColor(200, 200, 250)); + cursor.setBlockFormat(QTextBlockFormat()); + textViewer->setTextCursor(cursor); + textViewer->centerCursor(); + } +#ifdef USE_BASETEXTEDITOR + if (TextViewer *editor = theLocationMark()->editor()) + editor->markableInterface()->removeMark(theLocationMark()); + theLocationMark()->setEditor(0); +#endif +} + +void MainWindow::gotoLocation(const QString &fileName, int line, bool setMarker) +{ + //qDebug() << "GOTO " << fileName << line << setMarker; + Q_UNUSED(setMarker); + TextViewer *textViewer = findOrCreateTextViewer(fileName); + m_textViewers->setCurrentWidget(textViewer); + + const int blockNumber = line - 1; + const QTextBlock &block = textViewer->document()->findBlockByNumber(blockNumber); + if (block.isValid()) { + QTextCursor cursor(block); + QTextBlockFormat format = block.blockFormat(); + format.setBackground(QColor(230, 200, 200)); + cursor.setBlockFormat(format); + textViewer->setTextCursor(cursor); + textViewer->centerCursor(); + m_textBlockFromName[textViewer] = blockNumber; + +#ifdef USE_BASETEXTEDITOR + if (line >= 1) { + if (setMarker) { + theLocationMark()->setEditor(textViewer); + textViewer->markableInterface()->addMark(theLocationMark(), line); + } + textViewer->gotoLine(line + 8); + textViewer->gotoLine(line); + // FIXME: editor->centerCursor(); + } +#endif + } else { + qDebug() << "INVALID BLOCK FOR LINE " << line << " IN " << fileName; + } +} + +void MainWindow::quit() +{ + m_manager->exitDebugger(); + close(); +} + +void MainWindow::fileOpen() +{ + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open File"), + settings().value("FileOpen/LastDir").toString() + // filterQString & filter = QString(), + //QString * selectedFilter = 0, Options options = 0 + ); + settings().setValue("FileOpen/LastDir", QFileInfo(fileName).dir().dirName()); + if (fileName.isEmpty()) + return; + loadFile(fileName); +} + +void MainWindow::queryCurrentTextEditor(QString *fileName, int *line, QObject **object) +{ + //Core::IEditor *editor = core->editorManager()->currentEditor(); + //*textEditor = qobject_cast<ITextEditor*>(current); + TextViewer *textEditor = currentTextViewer(); + if (fileName) + *fileName = textEditor->fileName(); + if (line) + *line = textEditor->currentLine(); + if (object) + *object = textEditor; +} + + +TextViewer *MainWindow::findOrCreateTextViewer(const QString &fileName) +{ + TextViewer *textViewer = m_textViewerFromName.value(fileName); + if (textViewer) + return textViewer; + + textViewer = new TextViewer(this); + textViewer->open(fileName); + textViewer->setReadOnly(true); + m_textViewerFromName[fileName] = textViewer; + m_textViewers->addTab(textViewer, fileName); + m_textViewers->setCurrentWidget(textViewer); + + if (m_filesModel->findItems(fileName).isEmpty()) + m_filesModel->appendRow(new QStandardItem(fileName)); + //m_manager->textEditorOpened(textViewer->editableInterface()); + + return textViewer; +} + +TextViewer *MainWindow::currentTextViewer() +{ + return qobject_cast<TextViewer *>(m_textViewers->currentWidget()); +} + +void MainWindow::changeCurrentFile(const QModelIndex &idx) +{ + QString fileName = m_filesModel->data(idx).toString(); + m_textViewers->setCurrentWidget(findOrCreateTextViewer(fileName)); +} + +void MainWindow::handleDataDumpersUnavailable() +{ + QMessageBox::warning(this, tr("Cannot find special data dumpers"), + tr("The debugged binary does not contain information needed for " + "nice display of Qt data types.\n\n" + "Make sure you use something like\n\n" + "SOURCES *= .../ide/main/bin/gdbmacros/gdbmacros.cpp\n\n" + "in your .pro file.") + ); +} diff --git a/src/tools/qdebugger/mainwindow.h b/src/tools/qdebugger/mainwindow.h new file mode 100644 index 00000000000..6a313b6ed12 --- /dev/null +++ b/src/tools/qdebugger/mainwindow.h @@ -0,0 +1,104 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef QDB_MAINWINDOW_H +#define QDB_MAINWINDOW_H + +#include <QtCore/QHash> + +#include <QtGui/QApplication> +#include <QtGui/QMainWindow> + +namespace GdbDebugger { +namespace Internal { +class DebuggerManager; +} // namespace Internal +} // namespace GdbDebugger + +class TextViewer; + +QT_BEGIN_NAMESPACE +class QModelIndex; +class QPlainTextEdit; +class QSettings; +class QTabWidget; +class QTreeView; +class QStandardItemModel; +QT_END_NAMESPACE + + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + MainWindow(); + ~MainWindow(); + + void loadFile(const QString &fileName); + void loadFiles(const QStringList &fileNames); + +private slots: + void startDebuggerRequest(); + void startDebuggingRequest(); + void jumpToExec(); + void runToExec(); + void showStatusMessage(const QString &msg, int timeout); + void resetLocation(); + void gotoLocation(const QString &fileName, int line, bool setMarker); + void fileOpen(); + void quit(); + void changeCurrentFile(const QModelIndex &idx); + void handleDataDumpersUnavailable(); + void queryCurrentTextEditor(QString *fileName, int *lineNumber, + QObject **widget); + +private: + QSettings &settings(); + + QAction *m_fileOpenAction; + QAction *m_quitAction; + //QAction *m_resetAction; // FIXME: Should not be needed in a stable release + + QTreeView *m_filesWindow; + QStandardItemModel *m_filesModel; + GdbDebugger::Internal::DebuggerManager *m_manager; + + QTabWidget *m_textViewers; + QHash<QString, TextViewer *> m_textViewerFromName; + QHash<TextViewer *, int> m_textBlockFromName; + TextViewer *findOrCreateTextViewer(const QString &fileName); + TextViewer *currentTextViewer(); + + QString m_executable; +}; + +#endif // QDB_MAINWINDOW_H diff --git a/src/tools/qdebugger/qdebugger.pro b/src/tools/qdebugger/qdebugger.pro new file mode 100644 index 00000000000..a4ff3e3bf93 --- /dev/null +++ b/src/tools/qdebugger/qdebugger.pro @@ -0,0 +1,123 @@ + +QTCREATOR = ../../plugins/ + +TARGET = ../../../bin/qdebugger + +QT += gui \ + network + +CONFIG -= release +CONFIG += debug + +DEFINES += GDBDEBUGGERLEAN + +INCLUDEPATH += $${QTCREATOR} +INCLUDEPATH += $${QTCREATOR}/gdbdebugger + +HEADERS += \ + lean.h \ + mainwindow.h \ + $${QTCREATOR}/gdbdebugger/attachexternaldialog.h \ + $${QTCREATOR}/gdbdebugger/breakhandler.h \ + $${QTCREATOR}/gdbdebugger/breakwindow.h \ + $${QTCREATOR}/gdbdebugger/disassemblerhandler.h \ + $${QTCREATOR}/gdbdebugger/disassemblerwindow.h \ + $${QTCREATOR}/gdbdebugger/gdbdebuggerconstants.h \ + $${QTCREATOR}/gdbdebugger/gdbdebugger.h \ + $${QTCREATOR}/gdbdebugger/gdbmi.h \ + $${QTCREATOR}/gdbdebugger/imports.h \ + $${QTCREATOR}/gdbdebugger/moduleshandler.h \ + $${QTCREATOR}/gdbdebugger/moduleswindow.h \ + $${QTCREATOR}/gdbdebugger/registerhandler.h \ + $${QTCREATOR}/gdbdebugger/registerwindow.h \ + $${QTCREATOR}/gdbdebugger/gdboutputwindow.h \ + $${QTCREATOR}/gdbdebugger/procinterrupt.h \ + $${QTCREATOR}/gdbdebugger/stackhandler.h \ + $${QTCREATOR}/gdbdebugger/stackwindow.h \ + $${QTCREATOR}/gdbdebugger/startexternaldialog.h \ + $${QTCREATOR}/gdbdebugger/threadswindow.h \ + $${QTCREATOR}/gdbdebugger/watchhandler.h \ + $${QTCREATOR}/gdbdebugger/watchwindow.h \ + +SOURCES += \ + main.cpp \ + mainwindow.cpp \ + $${QTCREATOR}/gdbdebugger/attachexternaldialog.cpp \ + $${QTCREATOR}/gdbdebugger/breakhandler.cpp \ + $${QTCREATOR}/gdbdebugger/breakwindow.cpp \ + $${QTCREATOR}/gdbdebugger/disassemblerhandler.cpp \ + $${QTCREATOR}/gdbdebugger/disassemblerwindow.cpp \ + $${QTCREATOR}/gdbdebugger/gdbdebugger.cpp \ + $${QTCREATOR}/gdbdebugger/gdbmi.cpp \ + $${QTCREATOR}/gdbdebugger/moduleshandler.cpp \ + $${QTCREATOR}/gdbdebugger/moduleswindow.cpp \ + $${QTCREATOR}/gdbdebugger/registerhandler.cpp \ + $${QTCREATOR}/gdbdebugger/registerwindow.cpp \ + $${QTCREATOR}/gdbdebugger/gdboutputwindow.cpp \ + $${QTCREATOR}/gdbdebugger/procinterrupt.cpp \ + $${QTCREATOR}/gdbdebugger/stackhandler.cpp \ + $${QTCREATOR}/gdbdebugger/stackwindow.cpp \ + $${QTCREATOR}/gdbdebugger/startexternaldialog.cpp \ + $${QTCREATOR}/gdbdebugger/threadswindow.cpp \ + $${QTCREATOR}/gdbdebugger/watchhandler.cpp \ + $${QTCREATOR}/gdbdebugger/watchwindow.cpp + +SOURCES *= $${QTCREATOR}/../../bin/gdbmacros/gdbmacros.cpp + +FORMS += \ + $${QTCREATOR}/gdbdebugger/attachexternaldialog.ui \ + $${QTCREATOR}/gdbdebugger/gdboptionpage.ui \ + $${QTCREATOR}/gdbdebugger/generaloptionpage.ui \ + $${QTCREATOR}/gdbdebugger/debugmode.ui \ + $${QTCREATOR}/gdbdebugger/startexternaldialog.ui + +RESOURCES += \ + $${QTCREATOR}/gdbdebugger/gdbdebugger.qrc + +FORMS += \ + $${QTCREATOR}/gdbdebugger/breakbyfunction.ui + +# The following chooses between a "really lean" build and a version +# using the BaseTextEditor + +true { + +DEFINES += USE_BASETEXTEDITOR +DEFINES += TEXTEDITOR_STANDALONE + +INCLUDEPATH += $${QTCREATOR} +INCLUDEPATH += $${QTCREATOR}/texteditor +INCLUDEPATH += $${QTCREATOR}/../libs + +HEADERS += \ + $${QTCREATOR}/texteditor/basetextdocument.h \ + $${QTCREATOR}/texteditor/basetexteditor.h \ + $${QTCREATOR}/texteditor/storagesettings.h \ + $${QTCREATOR}/texteditor/itexteditable.h \ + $${QTCREATOR}/texteditor/itexteditor.h \ + $${QTCREATOR}/texteditor/tabsettings.h \ + $${QTCREATOR}/texteditor/displaysettings.h \ + $${QTCREATOR}/coreplugin/icontext.h \ + $${QTCREATOR}/coreplugin/ifile.h \ + $${QTCREATOR}/coreplugin/editormanager/ieditor.h \ + $${QTCREATOR}/../libs/utils/linecolumnlabel.h \ + +SOURCES += \ + #$${QTCREATOR}/texteditor/basetextmark.cpp \ + $${QTCREATOR}/texteditor/basetextdocument.cpp \ + $${QTCREATOR}/texteditor/basetexteditor.cpp \ + $${QTCREATOR}/texteditor/storagesettings.cpp \ + $${QTCREATOR}/texteditor/tabsettings.cpp \ + $${QTCREATOR}/texteditor/displaysettings.cpp \ + $${QTCREATOR}/../libs/utils/linecolumnlabel.cpp \ +} + + + +unix { + OBJECTS_DIR = .tmp/ + MOC_DIR = .tmp/ + RCC_DIR = .tmp/ + UI_DIR = .tmp/ +} + diff --git a/src/tools/qtcreatorwidgets/README.txt b/src/tools/qtcreatorwidgets/README.txt new file mode 100644 index 00000000000..6ccf8f56998 --- /dev/null +++ b/src/tools/qtcreatorwidgets/README.txt @@ -0,0 +1,8 @@ +A Designer custom widget collection containing the Qt Creator +widgets from the utils library. + +Installs to Designer and has rpath pointing to the QtCreator library +directory, so, it should work on Linux. + +On Windows, it might require copying the utils library around or setup +some path pointing to it. diff --git a/src/tools/qtcreatorwidgets/customwidget.h b/src/tools/qtcreatorwidgets/customwidget.h new file mode 100644 index 00000000000..23f22cf5a25 --- /dev/null +++ b/src/tools/qtcreatorwidgets/customwidget.h @@ -0,0 +1,163 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CUSTOMWIDGET +#define CUSTOMWIDGET + +#include <QDesignerCustomWidgetInterface> + +#include <QtCore/QString> +#include <QtCore/QSize> +#include <QtCore/QTextStream> +#include <QtGui/QIcon> + +// Parametrizable Template for custom widgets. + +template <class Widget> +class CustomWidget : public QDesignerCustomWidgetInterface +{ + +public: + explicit CustomWidget(const QString &includeFile, + bool isContainer, + const QString &group, + const QIcon &icon, + const QString &toolTip, + const QSize &size = QSize()); + + bool isContainer() const { return m_isContainer; } + bool isInitialized() const { return m_initialized; } + QIcon icon() const { return m_icon; } + QString domXml() const; + QString group() const { return m_group; } + QString includeFile() const { return m_includeFile; } + QString name() const; + QString toolTip() const { return m_toolTip; } + QString whatsThis() const { return m_toolTip; } + QWidget *createWidget(QWidget *parent); + void initialize(QDesignerFormEditorInterface *core); + +private: + QString displayName() const; + QString geometryProperty() const; + + const QString m_includeFile; + const bool m_isContainer; + const QString m_group; + const QIcon m_icon; + const QString m_toolTip; + const QSize m_size; + + bool m_initialized; +}; + + +template <class Widget> + CustomWidget<Widget>::CustomWidget(const QString &includeFile, + bool isContainer, + const QString &group, + const QIcon &icon, + const QString &toolTip, + const QSize &size) : + m_includeFile(includeFile), + m_isContainer(isContainer), + m_group(group), + m_icon(icon), + m_toolTip(toolTip), + m_size(size), + m_initialized(false) +{ +} + +// Determine the name to be displayed in the widget box: Strip +// namespaces. +template <class Widget> + QString CustomWidget<Widget>::displayName() const +{ + QString rc = name(); + const int index = rc.lastIndexOf(QLatin1Char(':')); + if (index != -1) + rc.remove(0, index + 1); + return rc; +} + +template <class Widget> + QString CustomWidget<Widget>::geometryProperty() const +{ + QString rc; + if (m_size.isEmpty()) + return rc; + QTextStream(&rc) << "<property name=\"geometry\"><rect><x>0</x><y>0</y><width>" + << m_size.width() << "</width><height>" << m_size.height() + << "</height></rect></property>"; + return rc; +} + +template <class Widget> + QString CustomWidget<Widget>::domXml() const +{ + const QString className = name(); + QString rc; + // Name: 'QClass' -> 'class' + QString name = className; + if (name.startsWith(QLatin1Char('Q'))) + name.remove(0, 1); + name[0] = name.at(0).toLower(); + // Format + QTextStream str(&rc); + str << "<ui displayname=\"" << displayName() + << "\" language=\"c++\"><widget class=\"" << className << "\" name=\"" + << name << "\">" << geometryProperty() << "</widget></ui>"; + return rc; +} + +template <class Widget> + QString CustomWidget<Widget>::name() const +{ + return QLatin1String(Widget::staticMetaObject.className()); +} + +template <class Widget> + QWidget *CustomWidget<Widget>::createWidget(QWidget *parent) +{ + return new Widget(parent); +} + +template <class Widget> + void CustomWidget<Widget>::initialize(QDesignerFormEditorInterface *) +{ + if (!m_initialized) { + m_initialized = true; + } +} + +#endif // CUSTOMWIDGET diff --git a/src/tools/qtcreatorwidgets/customwidgets.cpp b/src/tools/qtcreatorwidgets/customwidgets.cpp new file mode 100644 index 00000000000..80a6d31e5d4 --- /dev/null +++ b/src/tools/qtcreatorwidgets/customwidgets.cpp @@ -0,0 +1,171 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "customwidgets.h" + +#include <QtGui/QMenu> +#include <QtGui/QAction> + +static const char *groupC = "QtCreator"; + +NewClassCustomWidget::NewClassCustomWidget(QObject *parent) : + QObject(parent), + CustomWidget<Core::Utils::NewClassWidget> + (QLatin1String("<utils/newclasswidget.h>"), + false, + QLatin1String(groupC), + QIcon(), + tr("Widget to enter classes and source files")) +{ +} + +ClassNameValidatingLineEdit_CW::ClassNameValidatingLineEdit_CW(QObject *parent) : + QObject(parent), + CustomWidget<Core::Utils::ClassNameValidatingLineEdit> + (QLatin1String("<utils/classnamevalidatinglineedit.h>"), + false, + QLatin1String(groupC), + QIcon(), + tr("Line Edit that validates a class name")) +{ +} + +FileNameValidatingLineEdit_CW::FileNameValidatingLineEdit_CW(QObject *parent) : + QObject(parent), + CustomWidget<Core::Utils::FileNameValidatingLineEdit> + (QLatin1String("<utils/filenamevalidatinglineedit.h>"), + false, + QLatin1String(groupC), + QIcon(), + tr("Line Edit that validates a file name")) +{ +} + +ProjectNameValidatingLineEdit_CW::ProjectNameValidatingLineEdit_CW(QObject *parent) : + QObject(parent), + CustomWidget<Core::Utils::ProjectNameValidatingLineEdit> + (QLatin1String("<utils/projectnamevalidatinglineedit.h>"), + false, + QLatin1String(groupC), + QIcon(), + tr("Line Edit that validates a project name")) +{ +} + +LineColumnLabel_CW::LineColumnLabel_CW(QObject *parent) : + QObject(parent), + CustomWidget<Core::Utils::LineColumnLabel> + (QLatin1String("<utils/linecolumnlabel.h>"), + false, + QLatin1String(groupC), + QIcon(), + tr("Label suited for displaying line numbers with a fixed with depending on the font size"), + QSize(100, 20)) +{ +} + +PathChooser_CW::PathChooser_CW(QObject *parent) : + QObject(parent), + CustomWidget<Core::Utils::PathChooser> + (QLatin1String("<utils/pathchooser.h>"), + false, + QLatin1String(groupC), + QIcon(), + tr("Input widget for paths with a browse button")) +{ +} + +FancyLineEdit_CW::FancyLineEdit_CW(QObject *parent) : + QObject(parent), + CustomWidget<Core::Utils::FancyLineEdit> + (QLatin1String("<utils/fancylineedit.h>"), + false, + QLatin1String(groupC), + QIcon(), + tr("A Line edit with a clickable menu pixmap")) +{ +} + +QtColorButton_CW::QtColorButton_CW(QObject *parent) : + QObject(parent), + CustomWidget<Core::Utils::QtColorButton> + (QLatin1String("<utils/qtcolorbutton.h>"), + false, + QLatin1String(groupC), + QIcon(), + tr("A color button that spawns a QColorDialog on click")) +{ +} + +QWidget *FancyLineEdit_CW::createWidget(QWidget *parent) +{ + Core::Utils::FancyLineEdit *fle = new Core::Utils::FancyLineEdit(parent); + QMenu *menu = new QMenu(fle); + menu->addAction("Test"); + fle->setMenu(menu); + return fle; +} + +SubmitEditorWidget_CW::SubmitEditorWidget_CW(QObject *parent) : + QObject(parent), + CustomWidget<Core::Utils::SubmitEditorWidget> + (QLatin1String("<utils/submiteditorwidget.h>"), + false, + QLatin1String(groupC), + QIcon(), + tr("Submit editor showing message and file list")) +{ +} + +// -------------- WidgetCollection +WidgetCollection::WidgetCollection(QObject *parent) : + QObject(parent) +{ + + m_plugins.push_back(new NewClassCustomWidget(this)); + m_plugins.push_back(new ClassNameValidatingLineEdit_CW(this)); + m_plugins.push_back(new FileNameValidatingLineEdit_CW(this)); + m_plugins.push_back(new ProjectNameValidatingLineEdit_CW(this)); + m_plugins.push_back(new LineColumnLabel_CW(this)); + m_plugins.push_back(new PathChooser_CW(this)); + m_plugins.push_back(new FancyLineEdit_CW(this)); + m_plugins.push_back(new QtColorButton_CW(this)); + m_plugins.push_back(new SubmitEditorWidget_CW(this)); +} + +QList<QDesignerCustomWidgetInterface*> WidgetCollection::customWidgets() const +{ + return m_plugins; +} + +Q_EXPORT_PLUGIN(WidgetCollection) diff --git a/src/tools/qtcreatorwidgets/customwidgets.h b/src/tools/qtcreatorwidgets/customwidgets.h new file mode 100644 index 00000000000..fbf638bdeda --- /dev/null +++ b/src/tools/qtcreatorwidgets/customwidgets.h @@ -0,0 +1,162 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef _CUSTOMWIDGETS_ +#define _CUSTOMWIDGETS_ + +#include "customwidget.h" + +#include <utils/newclasswidget.h> +#include <utils/classnamevalidatinglineedit.h> +#include <utils/filenamevalidatinglineedit.h> +#include <utils/linecolumnlabel.h> +#include <utils/pathchooser.h> +#include <utils/projectnamevalidatinglineedit.h> +#include <utils/fancylineedit.h> +#include <utils/qtcolorbutton.h> +#include <utils/submiteditorwidget.h> + +#include <QtDesigner/QDesignerCustomWidgetCollectionInterface> + +#include <QtCore/qplugin.h> +#include <QtCore/QList> + +// Custom Widgets + +class NewClassCustomWidget: + public QObject, + public CustomWidget<Core::Utils::NewClassWidget> +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) +public: + explicit NewClassCustomWidget(QObject *parent = 0); +}; + +class ClassNameValidatingLineEdit_CW: + public QObject, + public CustomWidget<Core::Utils::ClassNameValidatingLineEdit> +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) +public: + explicit ClassNameValidatingLineEdit_CW(QObject *parent = 0); +}; + +class FileNameValidatingLineEdit_CW: + public QObject, + public CustomWidget<Core::Utils::FileNameValidatingLineEdit> +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) +public: + explicit FileNameValidatingLineEdit_CW(QObject *parent = 0); +}; + +class ProjectNameValidatingLineEdit_CW: + public QObject, + public CustomWidget<Core::Utils::ProjectNameValidatingLineEdit> +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) +public: + explicit ProjectNameValidatingLineEdit_CW(QObject *parent = 0); +}; + +class LineColumnLabel_CW: + public QObject, + public CustomWidget<Core::Utils::LineColumnLabel> +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) +public: + explicit LineColumnLabel_CW(QObject *parent = 0); +}; + +class PathChooser_CW: + public QObject, + public CustomWidget<Core::Utils::PathChooser> +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) +public: + explicit PathChooser_CW(QObject *parent = 0); +}; + +class FancyLineEdit_CW: + public QObject, + public CustomWidget<Core::Utils::FancyLineEdit> +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) +public: + explicit FancyLineEdit_CW(QObject *parent = 0); + + virtual QWidget *createWidget(QWidget *parent); +}; + +class QtColorButton_CW: + public QObject, + public CustomWidget<Core::Utils::QtColorButton> +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) +public: + explicit QtColorButton_CW(QObject *parent = 0); +}; + +class SubmitEditorWidget_CW: + public QObject, + public CustomWidget<Core::Utils::SubmitEditorWidget> +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) +public: + explicit SubmitEditorWidget_CW(QObject *parent = 0); +}; + +// Collection + +class WidgetCollection: public QObject, public QDesignerCustomWidgetCollectionInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) +public: + explicit WidgetCollection(QObject *parent = 0); + + virtual QList<QDesignerCustomWidgetInterface*> customWidgets() const; + +private: + QList<QDesignerCustomWidgetInterface*> m_plugins; +}; + +#endif // _CUSTOMWIDGETS_ diff --git a/src/tools/qtcreatorwidgets/qtcreatorwidgets.pro b/src/tools/qtcreatorwidgets/qtcreatorwidgets.pro new file mode 100644 index 00000000000..6923e09837d --- /dev/null +++ b/src/tools/qtcreatorwidgets/qtcreatorwidgets.pro @@ -0,0 +1,25 @@ +CONFIG += designer plugin debug_and_release +TARGET = $$qtLibraryTarget(qtcreatorwidgets) +TEMPLATE = lib + +HEADERS = customwidgets.h \ + customwidget.h + +SOURCES = customwidgets.cpp + +# Link against the qtcreator utils lib + +unix { + # form abs path to qtcreator lib dir + GH_LIB=$$dirname(PWD) + GH_LIB=$$dirname(GH_LIB) + GH_LIB=$$dirname(GH_LIB) + GH_LIB=$$GH_LIB/lib + QMAKE_RPATHDIR *= $$GH_LIB +} + +INCLUDEPATH += ../../../src/libs +LIBS += -L../../../lib -lUtils + +DESTDIR= $$[QT_INSTALL_PLUGINS]/designer + diff --git a/src/tools/texteditor/main.cpp b/src/tools/texteditor/main.cpp new file mode 100644 index 00000000000..101a046cf2c --- /dev/null +++ b/src/tools/texteditor/main.cpp @@ -0,0 +1,46 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + MainWindow mw; + mw.show(); + QStringList args = app.arguments(); + args.removeFirst(); // program name.. + mw.loadFiles(args); + return app.exec(); +} + diff --git a/src/tools/texteditor/mainwindow.cpp b/src/tools/texteditor/mainwindow.cpp new file mode 100644 index 00000000000..087bdc92cea --- /dev/null +++ b/src/tools/texteditor/mainwindow.cpp @@ -0,0 +1,215 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "mainwindow.h" + +#include "texteditor/basetexteditor.h" + +#include <QtCore/QDebug> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QSettings> + +#include <QtGui/QAction> +#include <QtGui/QFileDialog> +#include <QtGui/QMenu> +#include <QtGui/QMenuBar> +#include <QtGui/QMessageBox> +#include <QtGui/QStatusBar> +#include <QtGui/QTabWidget> +#include <QtGui/QTextBlock> +#include <QtGui/QToolBar> + + +class MyEditor; + +class MyEditable : public TextEditor::BaseTextEditorEditable +{ +public: + MyEditable(MyEditor *editor); + ~MyEditable() {} + + // Core::IContext + const QList<int>& context() const { return m_context; } + // Core::IEditor + const char* kind() const { return "MYEDITOR"; } + bool duplicateSupported() const { return false; } + Core::IEditor* duplicate(QWidget*) { return 0; } + + MyEditor *m_editor; + QList<int> m_context; +}; + + +class MyEditor : public TextEditor::BaseTextEditor +{ +public: + MyEditor(QWidget *parent) + : TextEditor::BaseTextEditor(parent, 0) + {} + + TextEditor::BaseTextEditorEditable* createEditableInterface() + { + return new MyEditable(this); + } + + void setPlainText(const QString &contents) + { + qDebug() << "Setting contents to:\n" << contents; + QPlainTextEdit::setPlainText(contents); + } +}; + + +MyEditable::MyEditable(MyEditor *editor) + : TextEditor::BaseTextEditorEditable(editor), m_editor(editor) +{} + + + +/////////////////////////////////////////////////////////////////////////// +// +// MainWindow +// +/////////////////////////////////////////////////////////////////////////// + +MainWindow::MainWindow() +{ + // + // Source code view + // + m_textViewers = new QTabWidget(this); + m_textViewers->setObjectName("Editors"); + m_textViewers->setElideMode(Qt::ElideLeft); + setCentralWidget(m_textViewers); + setGeometry(QRect(0, 0, 800, 600)); + + + m_fileOpenAction = new QAction("Open", this); + m_quitAction = new QAction("Quit", this); + + // + // Menubar + // + QMenu *fileMenu = new QMenu(menuBar()); + fileMenu->setTitle(tr("File")); + fileMenu->addAction(m_fileOpenAction); + fileMenu->addSeparator(); + fileMenu->addAction(m_quitAction); + menuBar()->addMenu(fileMenu); + + // + // Toolbar + // + QToolBar *toolbar = new QToolBar(this); + toolbar->setObjectName("ToolBar"); + toolbar->addAction(m_fileOpenAction); + toolbar->addAction(m_quitAction); + addToolBar(Qt::TopToolBarArea, toolbar); + + // + // Statusbar + // + QStatusBar *statusbar = new QStatusBar(this); + statusbar->setObjectName("StatusBar"); + setStatusBar(statusbar); + + show(); + restoreState(settings().value("MainWindow/State").toByteArray()); + setGeometry(settings().value("MainWindow/Geometry").toRect()); + + // + // Connections + // + connect(m_fileOpenAction, SIGNAL(triggered()), + this, SLOT(fileOpen())); + connect(m_quitAction, SIGNAL(triggered()), + this, SLOT(close())); +} + +MainWindow::~MainWindow() +{ + settings().setValue("MainWindow/State", saveState()); + settings().setValue("MainWindow/Geometry", geometry()); + settings().sync(); +} + +QSettings &MainWindow::settings() +{ + static QSettings s("Nokia", "TextEditor"); + return s; +} + +void MainWindow::loadFile(const QString &fileName) +{ + findOrCreateTextViewer(fileName); +} + +void MainWindow::loadFiles(const QStringList &fileNames) +{ + foreach (const QString &fileName, fileNames) + loadFile(fileName); +} + +void MainWindow::fileOpen() +{ + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open Executable File"), + settings().value("FileOpen/LastDir").toString() + // filterQString & filter = QString(), + //QString * selectedFilter = 0, Options options = 0 + ); + settings().setValue("FileOpen/LastDir", QFileInfo(fileName).dir().dirName()); + if (fileName.isEmpty()) + return; + loadFile(fileName); +} + + +Editor *MainWindow::findOrCreateTextViewer(const QString &fileName) +{ + Editor *textViewer = m_textViewerFromName.value(fileName); + if (textViewer) + return textViewer; + + QFile file(fileName); + file.open(QIODevice::ReadOnly); + QString contents = file.readAll(); + + textViewer = new MyEditor(this); + textViewer->setPlainText(contents); + m_textViewerFromName[fileName] = textViewer; + m_textViewers->addTab(textViewer, fileName); + return textViewer; +} + diff --git a/src/tools/texteditor/mainwindow.h b/src/tools/texteditor/mainwindow.h new file mode 100644 index 00000000000..cf9983d5b46 --- /dev/null +++ b/src/tools/texteditor/mainwindow.h @@ -0,0 +1,75 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef TEXTEDITOR_MAINWINDOW_H +#define TEXTEDITOR_MAINWINDOW_H + +#include "itexteditor.h" + +#include <QtCore/QHash> + +#include <QtGui/QApplication> +#include <QtGui/QMainWindow> + +QT_BEGIN_NAMESPACE +class QTabWidget; +class QSettings; +QT_END_NAMESPACE + +namespace TextEditor { class BaseTextEditor; } +typedef TextEditor::BaseTextEditor Editor; + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + MainWindow(); + ~MainWindow(); + + void loadFile(const QString &fileName); + void loadFiles(const QStringList &fileNames); + +private slots: + void fileOpen(); + +private: + QTabWidget *m_textViewers; + QHash<QString, Editor *> m_textViewerFromName; + Editor *findOrCreateTextViewer(const QString &fileName); + Editor *currentTextViewer(); + + QSettings &settings(); + QAction * m_fileOpenAction; + QAction * m_quitAction; +}; + +#endif // TEXTEDITOR_MAINWINDOW_H diff --git a/src/tools/texteditor/texteditor.pro b/src/tools/texteditor/texteditor.pro new file mode 100644 index 00000000000..fd4db878264 --- /dev/null +++ b/src/tools/texteditor/texteditor.pro @@ -0,0 +1,46 @@ + +QTCREATOR = ../../plugins/ + +TARGET = ../../../bin/texteditor + +QT += gui + +CONFIG -= release +CONFIG += debug + +DEFINES += TEXTEDITOR_STANDALONE + +INCLUDEPATH += $${QTCREATOR} +INCLUDEPATH += $${QTCREATOR}/texteditor + +HEADERS += \ + mainwindow.h \ + $${QTCREATOR}/texteditor/basetextdocument.h \ + $${QTCREATOR}/texteditor/basetexteditor.h \ + $${QTCREATOR}/texteditor/storagesettings.h \ + $${QTCREATOR}/texteditor/itexteditable.h \ + $${QTCREATOR}/texteditor/itexteditor.h \ + $${QTCREATOR}/texteditor/tabsettings.h \ + $${QTCREATOR}/texteditor/displaysettings.h \ + $${QTCREATOR}/coreplugin/icontext.h \ + $${QTCREATOR}/coreplugin/ifile.h \ + $${QTCREATOR}/coreplugin/editormanager/ieditor.h \ + +SOURCES += \ + main.cpp \ + mainwindow.cpp \ + $${QTCREATOR}/texteditor/basetextdocument.cpp \ + $${QTCREATOR}/texteditor/basetexteditor.cpp \ + $${QTCREATOR}/texteditor/storagesettings.cpp \ + $${QTCREATOR}/texteditor/tabsettings.cpp \ + $${QTCREATOR}/texteditor/displaysettings.cpp \ + +#RESOURCES += \ +# $${QTCREATOR}/gdbdebugger/gdbdebugger.qrc + +unix { + OBJECTS_DIR = .tmp/ + MOC_DIR = .tmp/ + RCC_DIR = .tmp/ + UI_DIR = .tmp/ +} |