diff options
Diffstat (limited to 'src/libs')
217 files changed, 18317 insertions, 0 deletions
diff --git a/src/libs/aggregation/aggregate.cpp b/src/libs/aggregation/aggregate.cpp new file mode 100644 index 0000000000..bfd9e0f4dc --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..5040aec8b2 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..a6c48c59cb --- /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 0000000000..c970be0ed1 --- /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 0000000000..cae0917d65 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..7389255366 --- /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 0000000000..1a0ecac816 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..3a458923b4 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..32d39511ee --- /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 0000000000..62dad429e2 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..3893c5e95f --- /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 0000000000..6143ca9805 --- /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 0000000000..098cc723af --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..b2e0ca4be1 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..47f2366ddd --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..92d2cd8893 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..e3fa442326 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..70c4214259 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..c549c1c429 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..027cfb577d --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..d2fe8b7bef --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..d54e7f5521 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..f68795cea0 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..5d6fddfb63 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..3868432650 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..ec40f75b51 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..6b84a4b53f --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..028811e6ae --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..9ff920487b --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..6071f235c4 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..75963bb2fe --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..f047d540c9 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..9bbba41950 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..84a1992244 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..cd08833d21 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..f6ad3bdc53 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..87df28f1ba --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..3301a8c8d1 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..fd00338184 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..e2d0f6f033 --- /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 0000000000..1a2b532662 --- /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 0000000000..73d4c6395e --- /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 0000000000..88432d2cb1 --- /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 0000000000..42a9e83bc7 --- /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 0000000000..25fc49c659 --- /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 0000000000..e515e76e61 --- /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 0000000000..49dda7dfea --- /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 0000000000..f8add65e07 --- /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 0000000000..e5a51858d9 --- /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 0000000000..53e42af63d --- /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 0000000000..18d2941572 --- /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 0000000000..a4de5dddfe --- /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 0000000000..5534bbfe08 --- /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 0000000000..8f585e875d --- /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 0000000000..469e9c18d0 --- /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 0000000000..089cfb45e5 --- /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 0000000000..8c6cf64fe7 --- /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 0000000000..a7496aada0 --- /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 0000000000..527148af8e --- /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 0000000000..43855eb168 --- /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 0000000000..fb26b9c59c --- /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 0000000000..63b2e339a3 --- /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 0000000000..ebd1f34da2 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..e2f85d98eb --- /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 0000000000..15cd35d27b --- /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 0000000000..47cb702e16 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..b263e7b0d2 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..461a903a39 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..77b6ed869e --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..65ea70c9ba --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..349cc48a12 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..99386f29d0 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..e73c9de39b --- /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 0000000000..c1572d8829 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..87b4097d03 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..69f49e53c6 --- /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 0000000000..daf4169a80 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..7f003b4f89 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..6b8df8d2be --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..dfa7bdbe7c --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..06a219fe2d --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..a11167e85e --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..d75911ea37 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..eacf38edaa --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..7b78566568 --- /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 0000000000..8d9123dfbf --- /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 0000000000..48d63ba359 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..2dd6400212 --- /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 0000000000..21f257cf08 --- /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 0000000000..db201f34c3 --- /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 0000000000..69b9821b3b --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..d3c493aeba --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..4181188287 --- /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 0000000000..5436967a80 --- /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 0000000000..c5af5f39a3 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..b1cd95c762 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..58798b54f1 --- /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 0000000000..f7e90978ba --- /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 0000000000..9b38565c35 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..7514d63bd7 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..f601f06162 --- /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 0000000000..f0d76950e8 --- /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 0000000000..db201f34c3 --- /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 0000000000..8149d046d2 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..4be9f9bbf7 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..9101770f9a --- /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 0000000000..5436967a80 --- /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 0000000000..99f192766a --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..08d77a38c7 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..a80f4a5c76 --- /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 0000000000..234bf56ea2 --- /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 0000000000..4410c34511 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..0991b7a152 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..c5ff581b1b --- /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 0000000000..57b026f5fb --- /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 0000000000..c79b29780d --- /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 0000000000..6f0483f0f8 --- /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 0000000000..6bd573957b --- /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 0000000000..c5934b1331 --- /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 0000000000..426901ea74 --- /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 0000000000..f54b978e9a --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..d4b941b232 --- /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 0000000000..d8fda88917 --- /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 0000000000..426901ea74 --- /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 0000000000..137e1b494c --- /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 0000000000..451f854185 --- /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 0000000000..9fa01a442f --- /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 0000000000..b06bab2fa0 --- /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 0000000000..aab8f424c3 --- /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 0000000000..6bd573957b --- /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 0000000000..8c3db6c189 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..10fc0acf50 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..8b4d81a82a --- /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 0000000000..f8ab3f7a39 --- /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 0000000000..ae40ac8ea8 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..6bd573957b --- /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 0000000000..31cebf4414 --- /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 0000000000..454f58cb75 --- /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 0000000000..e6fe956c98 --- /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 0000000000..200a1fd1ac --- /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 0000000000..13bbdb09a8 --- /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 0000000000..b904f90138 --- /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 0000000000..1c110d1eea --- /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 0000000000..f3471edcaa --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..6ad874add3 --- /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 0000000000..829f2fce57 --- /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 0000000000..3282d9862e --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..8987230681 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..63faecdf52 --- /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 0000000000..32d3bdf070 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..f993b25c9f --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..9101770f9a --- /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 0000000000..1a230ad38d --- /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 0000000000..3ce6f258fa --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..5120038754 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..a80f4a5c76 --- /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 0000000000..fd2279824e --- /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 0000000000..ff37d06096 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..00bd642912 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..c5ff581b1b --- /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 0000000000..61f37bd706 --- /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 0000000000..f0d76950e8 --- /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 0000000000..8527b82c47 --- /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 0000000000..4b9cd21d8a --- /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 0000000000..426901ea74 --- /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 0000000000..471ea892f1 --- /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 0000000000..0bb942edad --- /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 0000000000..ffd0cb10a7 --- /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 0000000000..fc66de1715 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..57929a4cf1 --- /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 0000000000..46302f11cd --- /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 0000000000..15d47af5f3 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..43707c14d5 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..cb936e3c2d --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..3482687011 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..b52758eda5 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..c0a209d0d7 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..3af484935a --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..1a20c76d08 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..e7e930c544 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..24a109eada --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..1beed717ef --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..7535fc196d --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..9756984b4b --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..3b747fb548 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..2843a304e5 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..6a4a7d9ba6 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..a448ebe739 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..b2ae28a9d0 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..2e614c6f55 --- /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 0000000000..c6028ab13f --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..d5dea084cc --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..3b688f336d --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..67cd1f8691 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..e534189d77 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..14e0a6574b --- /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 0000000000..188aa3b126 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..e09040c4c0 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..bc17333fb9 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..56dcc25327 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..f530b78044 --- /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 0000000000..fb979d3934 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..c677cea141 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..8b54bdda1d --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..07355c883e --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..ca1d9e23de --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..deaf4b920a --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..ca8de01828 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..734a2f02f9 --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..aeafcd828d --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..3c40ccecba --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..1a30e8b791 --- /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 0000000000..71bcdffb6c --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..9458655d6b --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. 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 0000000000..4e173f2cad --- /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 0000000000..d98ca1d889 --- /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 0000000000..3a91f77a2b --- /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 (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** 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 |