diff options
Diffstat (limited to 'src/libs/utils')
47 files changed, 2003 insertions, 734 deletions
diff --git a/src/libs/utils/algorithm.h b/src/libs/utils/algorithm.h index 5d79c6f2d47..0fb7a35a418 100644 --- a/src/libs/utils/algorithm.h +++ b/src/libs/utils/algorithm.h @@ -324,7 +324,7 @@ template<template<typename> class C, // result container type typename R, typename S> Q_REQUIRED_RESULT -auto transform(const SC &container, R (S::*p)()) +auto transform(const SC &container, R (S::*p)() const) -> C<typename RemoveCvAndReference<R>::type> { return TransformImpl< diff --git a/src/libs/utils/basetreeview.cpp b/src/libs/utils/basetreeview.cpp index a6af74a9e4e..f87a7ba7f02 100644 --- a/src/libs/utils/basetreeview.cpp +++ b/src/libs/utils/basetreeview.cpp @@ -30,6 +30,8 @@ #include "basetreeview.h" +#include "progressindicator.h" + #include <utils/qtcassert.h> #include <QDebug> @@ -54,7 +56,7 @@ class BaseTreeViewPrivate : public QObject public: explicit BaseTreeViewPrivate(BaseTreeView *parent) - : q(parent), m_settings(0), m_expectUserChanges(false) + : q(parent), m_settings(0), m_expectUserChanges(false), m_progressIndicator(0) {} bool eventFilter(QObject *, QEvent *event) @@ -122,7 +124,7 @@ public: } } - Q_SLOT void handleSectionResized(int logicalIndex, int /*oldSize*/, int newSize) + void handleSectionResized(int logicalIndex, int /*oldSize*/, int newSize) { if (m_expectUserChanges) { m_userHandled[logicalIndex] = newSize; @@ -157,7 +159,7 @@ public: return minimum; } - Q_SLOT void resizeColumns() + Q_SLOT void resizeColumns() // Needs moc, see BaseTreeView::setModel { QHeaderView *h = q->header(); QTC_ASSERT(h, return); @@ -176,17 +178,7 @@ public: } } - Q_SLOT void rowActivatedHelper(const QModelIndex &index) - { - q->rowActivated(index); - } - - Q_SLOT void rowClickedHelper(const QModelIndex &index) - { - q->rowClicked(index); - } - - Q_SLOT void toggleColumnWidth(int logicalIndex) + void toggleColumnWidth(int logicalIndex) { QHeaderView *h = q->header(); const int currentSize = h->sectionSize(logicalIndex); @@ -211,6 +203,7 @@ public: QSettings *m_settings; QString m_settingsKey; bool m_expectUserChanges; + ProgressIndicator *m_progressIndicator; }; class BaseTreeViewDelegate : public QItemDelegate @@ -251,14 +244,15 @@ BaseTreeView::BaseTreeView(QWidget *parent) h->setSectionsClickable(true); h->viewport()->installEventFilter(d); - connect(this, SIGNAL(activated(QModelIndex)), - d, SLOT(rowActivatedHelper(QModelIndex))); - connect(this, SIGNAL(clicked(QModelIndex)), - d, SLOT(rowClickedHelper(QModelIndex))); - connect(h, SIGNAL(sectionClicked(int)), - d, SLOT(toggleColumnWidth(int))); - connect(h, SIGNAL(sectionResized(int,int,int)), - d, SLOT(handleSectionResized(int,int,int))); + connect(this, &QAbstractItemView::activated, + this, &BaseTreeView::rowActivated); + connect(this, &QAbstractItemView::clicked, + this, &BaseTreeView::rowClicked); + + connect(h, &QHeaderView::sectionClicked, + d, &BaseTreeViewPrivate::toggleColumnWidth); + connect(h, &QHeaderView::sectionResized, + d, &BaseTreeViewPrivate::handleSectionResized); } BaseTreeView::~BaseTreeView() @@ -268,18 +262,36 @@ BaseTreeView::~BaseTreeView() void BaseTreeView::setModel(QAbstractItemModel *m) { + struct ExtraConnection { + const char *signature; + const char *qsignal; + QObject *receiver; + const char *qslot; + }; +#define DESC(sign, receiver, slot) { #sign, SIGNAL(sign), receiver, SLOT(slot) } + const ExtraConnection c[] = { + DESC(columnAdjustmentRequested(), d, resizeColumns()), + DESC(requestExpansion(QModelIndex), this, expand(QModelIndex)) + }; +#undef DESC + QAbstractItemModel *oldModel = model(); - const char *sig = "columnAdjustmentRequested()"; if (oldModel) { - int index = model()->metaObject()->indexOfSignal(sig); - if (index != -1) - disconnect(model(), SIGNAL(columnAdjustmentRequested()), d, SLOT(resizeColumns())); + for (unsigned i = 0; i < sizeof(c) / sizeof(c[0]); ++i) { + int index = model()->metaObject()->indexOfSignal(c[i].signature); + if (index != -1) + disconnect(model(), c[i].qsignal, c[i].receiver, c[i].qslot); + } } + TreeView::setModel(m); + if (m) { - int index = m->metaObject()->indexOfSignal(sig); - if (index != -1) - connect(m, SIGNAL(columnAdjustmentRequested()), d, SLOT(resizeColumns())); + for (unsigned i = 0; i < sizeof(c) / sizeof(c[0]); ++i) { + int index = m->metaObject()->indexOfSignal(c[i].signature); + if (index != -1) + connect(model(), c[i].qsignal, c[i].receiver, c[i].qslot); + } d->restoreState(); } } @@ -292,6 +304,32 @@ void BaseTreeView::mousePressEvent(QMouseEvent *ev) d->toggleColumnWidth(columnAt(ev->x())); } +/*! + Shows a round spinning progress indicator on top of the tree view. + Creates a progress indicator widget if necessary. + \sa hideProgressIndicator() + */ +void BaseTreeView::showProgressIndicator() +{ + if (!d->m_progressIndicator) { + d->m_progressIndicator = new ProgressIndicator(ProgressIndicator::Large); + d->m_progressIndicator->attachToWidget(this); + } + d->m_progressIndicator->show(); +} + +/*! + Hides the round spinning progress indicator that was shown with + BaseTreeView::showProgressIndicator(). Note that the progress indicator widget is not + destroyed. + \sa showProgressIndicator() + */ +void BaseTreeView::hideProgressIndicator() +{ + QTC_ASSERT(d->m_progressIndicator, return); + d->m_progressIndicator->hide(); +} + void BaseTreeView::setSettings(QSettings *settings, const QByteArray &key) { QTC_ASSERT(!d->m_settings, qDebug() << "DUPLICATED setSettings" << key); diff --git a/src/libs/utils/basetreeview.h b/src/libs/utils/basetreeview.h index cde60b92889..777f1f51291 100644 --- a/src/libs/utils/basetreeview.h +++ b/src/libs/utils/basetreeview.h @@ -59,6 +59,9 @@ public: virtual void rowClicked(const QModelIndex &) {} void mousePressEvent(QMouseEvent *ev); + void showProgressIndicator(); + void hideProgressIndicator(); + public slots: void setAlternatingRowColorsHelper(bool on) { setAlternatingRowColors(on); } diff --git a/src/libs/utils/checkablemessagebox.cpp b/src/libs/utils/checkablemessagebox.cpp index 28e88183941..2a5126c030c 100644 --- a/src/libs/utils/checkablemessagebox.cpp +++ b/src/libs/utils/checkablemessagebox.cpp @@ -288,6 +288,49 @@ QMessageBox::StandardButton CheckableMessageBox::dialogButtonBoxToMessageBoxButt return static_cast<QMessageBox::StandardButton>(int(db)); } +bool askAgain(QSettings *settings, const QString &settingsSubKey) +{ + QTC_CHECK(settings); + if (settings) { + settings->beginGroup(QLatin1String(kDoNotAskAgainKey)); + bool shouldNotAsk = settings->value(settingsSubKey, false).toBool(); + settings->endGroup(); + if (shouldNotAsk) + return false; + } + return true; +} + +enum DoNotAskAgainType{Question, Information}; + +void initDoNotAskAgainMessageBox(CheckableMessageBox &messageBox, const QString &title, + const QString &text, QDialogButtonBox::StandardButtons buttons, + QDialogButtonBox::StandardButton defaultButton, + DoNotAskAgainType type) +{ + messageBox.setWindowTitle(title); + messageBox.setIconPixmap(QMessageBox::standardIcon(type == Information + ? QMessageBox::Information + : QMessageBox::Question)); + messageBox.setText(text); + messageBox.setCheckBoxVisible(true); + messageBox.setCheckBoxText(type == Information ? CheckableMessageBox::msgDoNotShowAgain() + : CheckableMessageBox::msgDoNotAskAgain()); + messageBox.setChecked(false); + messageBox.setStandardButtons(buttons); + messageBox.setDefaultButton(defaultButton); +} + +void doNotAskAgain(QSettings *settings, const QString &settingsSubKey) +{ + if (!settings) + return; + + settings->beginGroup(QLatin1String(kDoNotAskAgainKey)); + settings->setValue(settingsSubKey, true); + settings->endGroup(); +} + /*! Shows a message box with given \a title and \a text, and a \gui {Do not ask again} check box. If the user checks the check box and accepts the dialog with the \a acceptButton, @@ -306,35 +349,44 @@ CheckableMessageBox::doNotAskAgainQuestion(QWidget *parent, const QString &title QDialogButtonBox::StandardButton acceptButton) { - QTC_CHECK(settings); - if (settings) { - settings->beginGroup(QLatin1String(kDoNotAskAgainKey)); - bool shouldNotAsk = settings->value(settingsSubKey, false).toBool(); - settings->endGroup(); - if (shouldNotAsk) - return acceptButton; - } + if (!askAgain(settings, settingsSubKey)) + return acceptButton; - CheckableMessageBox mb(parent); - mb.setWindowTitle(title); - mb.setIconPixmap(QMessageBox::standardIcon(QMessageBox::Question)); - mb.setText(text); - mb.setCheckBoxVisible(true); - mb.setCheckBoxText(CheckableMessageBox::msgDoNotAskAgain()); - mb.setChecked(false); - mb.setStandardButtons(buttons); - mb.setDefaultButton(defaultButton); - mb.exec(); + CheckableMessageBox messageBox(parent); + initDoNotAskAgainMessageBox(messageBox, title, text, buttons, defaultButton, Question); + messageBox.exec(); + if (messageBox.isChecked() && (messageBox.clickedStandardButton() == acceptButton)) + doNotAskAgain(settings, settingsSubKey); - if (settings) { - settings->beginGroup(QLatin1String(kDoNotAskAgainKey)); - if (mb.isChecked() && (mb.clickedStandardButton() == acceptButton)) - settings->setValue(settingsSubKey, true); - else // clean up doesn't hurt - settings->remove(settingsSubKey); - settings->endGroup(); - } - return mb.clickedStandardButton(); + return messageBox.clickedStandardButton(); +} + +/*! + Shows a message box with given \a title and \a text, and a \gui {Do not show again} check box. + If the user checks the check box and quits the dialog, further invocations of this + function with the same \a settings and \a settingsSubKey will not show the dialog, but instantly return. + + Returns the clicked button, or QDialogButtonBox::NoButton if the user rejects the dialog + with the escape key, or \a defaultButton if the dialog is suppressed. +*/ +QDialogButtonBox::StandardButton +CheckableMessageBox::doNotShowAgainInformation(QWidget *parent, const QString &title, + const QString &text, QSettings *settings, + const QString &settingsSubKey, + QDialogButtonBox::StandardButtons buttons, + QDialogButtonBox::StandardButton defaultButton) + +{ + if (!askAgain(settings, settingsSubKey)) + return defaultButton; + + CheckableMessageBox messageBox(parent); + initDoNotAskAgainMessageBox(messageBox, title, text, buttons, defaultButton, Information); + messageBox.exec(); + if (messageBox.isChecked()) + doNotAskAgain(settings, settingsSubKey); + + return messageBox.clickedStandardButton(); } /*! @@ -377,4 +429,13 @@ QString CheckableMessageBox::msgDoNotAskAgain() return QApplication::translate("Utils::CheckableMessageBox", "Do not &ask again"); } +/*! + Returns the standard \gui {Do not show again} check box text. + \sa doNotShowAgainInformation() +*/ +QString CheckableMessageBox::msgDoNotShowAgain() +{ + return QApplication::translate("Utils::CheckableMessageBox", "Do not &show again"); +} + } // namespace Utils diff --git a/src/libs/utils/checkablemessagebox.h b/src/libs/utils/checkablemessagebox.h index 3c0e241659b..894d955a64f 100644 --- a/src/libs/utils/checkablemessagebox.h +++ b/src/libs/utils/checkablemessagebox.h @@ -86,6 +86,15 @@ public: QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No, QDialogButtonBox::StandardButton acceptButton = QDialogButtonBox::Yes); + static QDialogButtonBox::StandardButton + doNotShowAgainInformation(QWidget *parent, + const QString &title, + const QString &text, + QSettings *settings, + const QString &settingsSubKey, + QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Ok, + QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::NoButton); + QString text() const; void setText(const QString &); @@ -119,6 +128,7 @@ public: static void resetAllDoNotAskAgainQuestions(QSettings *settings); static bool hasSuppressedQuestions(QSettings *settings); static QString msgDoNotAskAgain(); + static QString msgDoNotShowAgain(); private slots: void slotClicked(QAbstractButton *b); diff --git a/src/libs/utils/elfreader.cpp b/src/libs/utils/elfreader.cpp index 29f33d19cfe..bc34ca3622a 100644 --- a/src/libs/utils/elfreader.cpp +++ b/src/libs/utils/elfreader.cpp @@ -117,7 +117,7 @@ bool ElfMapper::map() // Try reading the data into memory instead. try { raw = file.readAll(); - } catch (std::bad_alloc &) { + } catch (const std::bad_alloc &) { return false; } start = raw.constData(); diff --git a/src/libs/utils/fadingindicator.cpp b/src/libs/utils/fadingindicator.cpp new file mode 100644 index 00000000000..ee8c2c70b3a --- /dev/null +++ b/src/libs/utils/fadingindicator.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "fadingindicator.h" + +#include "stylehelper.h" + +#include <QGraphicsOpacityEffect> +#include <QHBoxLayout> +#include <QLabel> +#include <QPainter> +#include <QPixmap> +#include <QPropertyAnimation> +#include <QTimer> + +namespace Utils { +namespace Internal { + +class FadingIndicatorPrivate : public QWidget +{ + Q_OBJECT + +public: + FadingIndicatorPrivate(QWidget *parent = 0) + : QWidget(parent) + { + m_effect = new QGraphicsOpacityEffect(this); + setGraphicsEffect(m_effect); + m_effect->setOpacity(1.); + + m_label = new QLabel; + QFont font = m_label->font(); + font.setPixelSize(45); + m_label->setFont(font); + QPalette pal = palette(); + pal.setColor(QPalette::Foreground, pal.color(QPalette::Background)); + m_label->setPalette(pal); + auto layout = new QHBoxLayout; + setLayout(layout); + layout->addWidget(m_label); + } + + void setText(const QString &text) + { + m_pixmap = QPixmap(); + m_label->setText(text); + adjustSize(); + if (QWidget *parent = parentWidget()) + move(parent->rect().center() - rect().center()); + } + + void setPixmap(const QString &uri) + { + m_label->hide(); + m_pixmap.load(Utils::StyleHelper::dpiSpecificImageFile(uri)); + resize(m_pixmap.size() / m_pixmap.devicePixelRatio()); + if (QWidget *parent = parentWidget()) + move(parent->rect().center() - rect().center()); + } + + void run(int ms) + { + show(); + raise(); + QTimer::singleShot(ms, this, SLOT(runInternal())); + } + +protected: + void paintEvent(QPaintEvent *) + { + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + if (!m_pixmap.isNull()) { + p.drawPixmap(rect(), m_pixmap); + } else { + p.setBrush(palette().color(QPalette::Foreground)); + p.setPen(Qt::NoPen); + p.drawRoundedRect(rect(), 15, 15); + } + } + +private slots: + void runInternal() + { + QPropertyAnimation *anim = new QPropertyAnimation(m_effect, "opacity", this); + anim->setDuration(200); + anim->setEndValue(0.); + connect(anim, SIGNAL(finished()), this, SLOT(deleteLater())); + anim->start(QAbstractAnimation::DeleteWhenStopped); + } + +private: + QGraphicsOpacityEffect *m_effect; + QLabel *m_label; + QPixmap m_pixmap; +}; + +} // Internal + +namespace FadingIndicator { + +void showText(QWidget *parent, const QString &text) +{ + auto indicator = new Internal::FadingIndicatorPrivate(parent); + indicator->setText(text); + indicator->run(1000); // deletes itself +} + +void showPixmap(QWidget *parent, const QString &pixmap) +{ + auto indicator = new Internal::FadingIndicatorPrivate(parent); + indicator->setPixmap(pixmap); + indicator->run(300); // deletes itself +} + +} // FadingIndicator +} // Utils + +#include "fadingindicator.moc" diff --git a/src/libs/utils/iwelcomepage.cpp b/src/libs/utils/fadingindicator.h index c5b781fe918..7738e751a64 100644 --- a/src/libs/utils/iwelcomepage.cpp +++ b/src/libs/utils/fadingindicator.h @@ -28,20 +28,23 @@ ** ****************************************************************************/ -#include "iwelcomepage.h" -#include <QUrl> +#ifndef FADINGINDICATOR_H +#define FADINGINDICATOR_H + +#include "utils_global.h" + +#include <QString> +#include <QWidget> namespace Utils { +namespace FadingIndicator { -IWelcomePage::IWelcomePage() -{ -} +QTCREATOR_UTILS_EXPORT void showText(QWidget *parent, const QString &text); +QTCREATOR_UTILS_EXPORT void showPixmap(QWidget *parent, const QString &pixmap); -IWelcomePage::~IWelcomePage() -{ -} +} // FadingIndicator +} // Utils -} // namespace Utils +#endif // FADINGINDICATOR_H -#include "moc_iwelcomepage.cpp" diff --git a/src/libs/utils/fancymainwindow.cpp b/src/libs/utils/fancymainwindow.cpp index 3699e3af55f..097771a2c22 100644 --- a/src/libs/utils/fancymainwindow.cpp +++ b/src/libs/utils/fancymainwindow.cpp @@ -199,13 +199,6 @@ public: QWidget::enterEvent(event); } - void leaveEvent(QEvent *event) - { - if (!q->isFloating()) - setActive(false); - QWidget::leaveEvent(event); - } - void setActive(bool on) { m_active = on; @@ -311,6 +304,10 @@ void DockWidget::enterEvent(QEvent *event) void DockWidget::leaveEvent(QEvent *event) { + if (!isFloating()) { + m_timer.stop(); + m_titleBar->setActive(false); + } QApplication::instance()->removeEventFilter(this); QDockWidget::leaveEvent(event); } diff --git a/src/libs/utils/fileinprojectfinder.cpp b/src/libs/utils/fileinprojectfinder.cpp index 41110fceb2d..e40b33136b7 100644 --- a/src/libs/utils/fileinprojectfinder.cpp +++ b/src/libs/utils/fileinprojectfinder.cpp @@ -29,12 +29,15 @@ ****************************************************************************/ #include "fileinprojectfinder.h" -#include <utils/qtcassert.h> +#include "fileutils.h" +#include "qtcassert.h" #include <QDebug> #include <QFileInfo> #include <QUrl> +#include <algorithm> + enum { debug = false }; namespace Utils { @@ -211,20 +214,19 @@ QString FileInProjectFinder::findFile(const QUrl &fileUrl, bool *success) const } } - // find (solely by filename) in project files + // find best matching file path in project files if (debug) qDebug() << "FileInProjectFinder: checking project files ..."; - const QString fileName = QFileInfo(originalPath).fileName(); - foreach (const QString &f, m_projectFiles) { - if (QFileInfo(f).fileName() == fileName) { - m_cache.insert(originalPath, f); - if (success) - *success = true; - if (debug) - qDebug() << "FileInProjectFinder: found" << f << "in project files"; - return f; - } + const QString matchedFilePath + = bestMatch( + filesWithSameFileName(FileName::fromString(originalPath).fileName()), + originalPath); + if (!matchedFilePath.isEmpty()) { + m_cache.insert(originalPath, matchedFilePath); + if (success) + *success = true; + return matchedFilePath; } if (debug) @@ -251,4 +253,44 @@ QString FileInProjectFinder::findFile(const QUrl &fileUrl, bool *success) const return originalPath; } +QStringList FileInProjectFinder::filesWithSameFileName(const QString &fileName) const +{ + QStringList result; + foreach (const QString &f, m_projectFiles) { + if (FileName::fromString(f).fileName() == fileName) + result << f; + } + return result; +} + +int FileInProjectFinder::rankFilePath(const QString &candidatePath, const QString &filePathToFind) +{ + int rank = 0; + for (int a = candidatePath.length(), b = filePathToFind.length(); + --a >= 0 && --b >= 0 && candidatePath.at(a) == filePathToFind.at(b);) + rank++; + return rank; +} + +QString FileInProjectFinder::bestMatch(const QStringList &filePaths, const QString &filePathToFind) +{ + if (filePaths.isEmpty()) + return QString(); + if (filePaths.length() == 1) { + if (debug) + qDebug() << "FileInProjectFinder: found" << filePaths.first() << "in project files"; + return filePaths.first(); + } + auto it = std::max_element(filePaths.constBegin(), filePaths.constEnd(), + [&filePathToFind] (const QString &a, const QString &b) -> bool { + return rankFilePath(a, filePathToFind) < rankFilePath(b, filePathToFind); + }); + if (it != filePaths.cend()) { + if (debug) + qDebug() << "FileInProjectFinder: found best match" << *it << "in project files"; + return *it; + } + return QString(); +} + } // namespace Utils diff --git a/src/libs/utils/fileinprojectfinder.h b/src/libs/utils/fileinprojectfinder.h index 253c747da58..c208ba2ecfd 100644 --- a/src/libs/utils/fileinprojectfinder.h +++ b/src/libs/utils/fileinprojectfinder.h @@ -54,6 +54,10 @@ public: QString findFile(const QUrl &fileUrl, bool *success = 0) const; private: + QStringList filesWithSameFileName(const QString &fileName) const; + static int rankFilePath(const QString &candidatePath, const QString &filePathToFind); + static QString bestMatch(const QStringList &filePaths, const QString &filePathToFind); + QString m_projectDir; QString m_sysroot; QStringList m_projectFiles; diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 78c8809a282..2c8baf56249 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -156,7 +156,7 @@ bool FileUtils::copyRecursively(const FileName &srcFilePath, const FileName &tgt if (!tgtFilePath.exists()) { QDir targetDir(tgtFilePath.toString()); targetDir.cdUp(); - if (!targetDir.mkdir(tgtFilePath.toFileInfo().fileName())) { + if (!targetDir.mkdir(tgtFilePath.fileName())) { if (error) { *error = QCoreApplication::translate("Utils::FileUtils", "Failed to create directory \"%1\".") .arg(tgtFilePath.toUserOutput()); @@ -560,6 +560,30 @@ QString FileName::toUserOutput() const return QDir::toNativeSeparators(toString()); } +QString FileName::fileName(int pathComponents) const +{ + if (pathComponents < 0) + return *this; + const QChar slash = QLatin1Char('/'); + QTC_CHECK(!endsWith(slash)); + int i = lastIndexOf(slash); + if (pathComponents == 0 || i == -1) + return mid(i + 1); + int component = i + 1; + // skip adjacent slashes + while (i > 0 && at(--i) == slash); + while (i >= 0 && --pathComponents >= 0) { + i = lastIndexOf(slash, i); + component = i + 1; + while (i > 0 && at(--i) == slash); + } + + // If there are no more slashes before the found one, return the entire string + if (i > 0 && lastIndexOf(slash, i) != -1) + return mid(component); + return *this; +} + /// \returns a bool indicating whether a file with this /// FileName exists. bool FileName::exists() const @@ -633,6 +657,13 @@ FileName FileName::fromUserInput(const QString &filename) return FileName(clean); } +/// Constructs a FileName from \a fileName, which is encoded as UTF-8. +/// \a fileName is not checked for validity. +FileName FileName::fromUtf8(const char *filename, int filenameSize) +{ + return FileName(QString::fromUtf8(filename, filenameSize)); +} + FileName::FileName(const QString &string) : QString(string) { @@ -730,6 +761,30 @@ FileName &FileName::appendString(QChar str) return *this; } +QTextStream &operator<<(QTextStream &s, const FileName &fn) +{ + return s << fn.toString(); +} + +int FileNameList::removeDuplicates() +{ + QSet<FileName> seen; + int removed = 0; + + for (int i = 0; i < size(); ) { + const FileName &fn = at(i); + if (seen.contains(fn)) { + removeAt(i); + ++removed; + } else { + seen.insert(fn); + ++i; + } + } + + return removed; +} + static bool isFileDrop(const QMimeData *d, QList<FileDropSupport::FileSpec> *files = 0) { // internal drop diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 3ddad1697e6..1ffea746a70 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -70,8 +70,10 @@ public: static FileName fromString(const QString &filename, const QString &defaultExtension); static FileName fromLatin1(const QByteArray &filename); static FileName fromUserInput(const QString &filename); + static FileName fromUtf8(const char *filename, int filenameSize = -1); QString toString() const; QString toUserOutput() const; + QString fileName(int pathComponents = 0) const; bool exists() const; FileName parentDir() const; @@ -102,6 +104,18 @@ private: FileName(const QString &string); }; +QTCREATOR_UTILS_EXPORT QTextStream &operator<<(QTextStream &s, const FileName &fn); + +class QTCREATOR_UTILS_EXPORT FileNameList : public QList<FileName> +{ +public: + inline FileNameList() { } + inline explicit FileNameList(const FileName &i) { append(i); } + inline FileNameList(const FileNameList &l) : QList<FileName>(l) { } + inline FileNameList(const QList<FileName> &l) : QList<FileName>(l) { } + + int removeDuplicates(); +}; class QTCREATOR_UTILS_EXPORT FileUtils { public: diff --git a/src/libs/utils/images/progressindicator_big.png b/src/libs/utils/images/progressindicator_big.png Binary files differnew file mode 100644 index 00000000000..ac854f1e25e --- /dev/null +++ b/src/libs/utils/images/progressindicator_big.png diff --git a/src/libs/utils/images/progressindicator_big@2x.png b/src/libs/utils/images/progressindicator_big@2x.png Binary files differnew file mode 100644 index 00000000000..23b488f23ab --- /dev/null +++ b/src/libs/utils/images/progressindicator_big@2x.png diff --git a/src/libs/utils/images/progressindicator_medium.png b/src/libs/utils/images/progressindicator_medium.png Binary files differnew file mode 100644 index 00000000000..496f6880f2e --- /dev/null +++ b/src/libs/utils/images/progressindicator_medium.png diff --git a/src/libs/utils/images/progressindicator_medium@2x.png b/src/libs/utils/images/progressindicator_medium@2x.png Binary files differnew file mode 100644 index 00000000000..c396d400128 --- /dev/null +++ b/src/libs/utils/images/progressindicator_medium@2x.png diff --git a/src/libs/utils/images/progressindicator_small.png b/src/libs/utils/images/progressindicator_small.png Binary files differnew file mode 100644 index 00000000000..8ba536403d8 --- /dev/null +++ b/src/libs/utils/images/progressindicator_small.png diff --git a/src/libs/utils/images/progressindicator_small@2x.png b/src/libs/utils/images/progressindicator_small@2x.png Binary files differnew file mode 100644 index 00000000000..e6dc7cc62d1 --- /dev/null +++ b/src/libs/utils/images/progressindicator_small@2x.png diff --git a/src/libs/utils/iwelcomepage.h b/src/libs/utils/iwelcomepage.h deleted file mode 100644 index e98afadbc43..00000000000 --- a/src/libs/utils/iwelcomepage.h +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms and -** conditions see http://www.qt.io/terms-conditions. For further information -** use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#ifndef IWELCOMEPAGE_H -#define IWELCOMEPAGE_H - -#include "utils_global.h" - -#include <QObject> - -QT_FORWARD_DECLARE_CLASS(QUrl) -QT_FORWARD_DECLARE_CLASS(QQmlEngine) - -namespace Utils { - -class QTCREATOR_UTILS_EXPORT IWelcomePage : public QObject -{ - Q_OBJECT - - Q_PROPERTY(QString title READ title CONSTANT) - Q_PROPERTY(QUrl pageLocation READ pageLocation CONSTANT) - Q_PROPERTY(int priority READ priority CONSTANT) - Q_PROPERTY(bool hasSearchBar READ hasSearchBar CONSTANT) - -public: - enum Id { - GettingStarted = 0, - Develop = 1, - Examples = 2, - Tutorials = 3, - UserDefined = 32 - }; - - IWelcomePage(); - virtual ~IWelcomePage(); - - virtual QUrl pageLocation() const = 0; - virtual QString title() const = 0; - virtual int priority() const { return 0; } - virtual void facilitateQml(QQmlEngine *) {} - virtual bool hasSearchBar() const { return false; } - virtual Id id() const = 0; -}; - -} - -#endif // IWELCOMEPAGE_H diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp index ee3294f9ec3..4047929f101 100644 --- a/src/libs/utils/macroexpander.cpp +++ b/src/libs/utils/macroexpander.cpp @@ -372,7 +372,7 @@ void MacroExpander::registerFileVariables(const QByteArray &prefix, registerVariable(prefix + kFileNamePostfix, tr("%1: File name without path.").arg(heading), - [base]() -> QString { QString tmp = base(); return tmp.isEmpty() ? QString() : QFileInfo(tmp).fileName(); }, + [base]() -> QString { QString tmp = base(); return tmp.isEmpty() ? QString() : Utils::FileName::fromString(tmp).fileName(); }, visibleInChooser); registerVariable(prefix + kFileBaseNamePostfix, diff --git a/src/libs/utils/outputformatter.cpp b/src/libs/utils/outputformatter.cpp index ef27e3b3803..2235802bab3 100644 --- a/src/libs/utils/outputformatter.cpp +++ b/src/libs/utils/outputformatter.cpp @@ -73,8 +73,7 @@ void OutputFormatter::appendMessage(const QString &text, const QTextCharFormat & QTextCursor cursor(m_plainTextEdit->document()); cursor.movePosition(QTextCursor::End); - foreach (const FormattedText &output, - m_escapeCodeHandler->parseText(FormattedText(text, format))) { + foreach (const FormattedText &output, parseAnsi(text, format)) { int startPos = 0; int crPos = -1; while ((crPos = output.text.indexOf(QLatin1Char('\r'), startPos)) >= 0) { @@ -92,6 +91,11 @@ QTextCharFormat OutputFormatter::charFormat(OutputFormat format) const return m_formats[format]; } +QList<FormattedText> OutputFormatter::parseAnsi(const QString &text, const QTextCharFormat &format) +{ + return m_escapeCodeHandler->parseText(FormattedText(text, format)); +} + void OutputFormatter::append(QTextCursor &cursor, const QString &text, const QTextCharFormat &format) { @@ -163,6 +167,5 @@ void OutputFormatter::setFont(const QFont &font) void OutputFormatter::flush() { - if (m_escapeCodeHandler) - m_escapeCodeHandler->endFormatScope(); + m_escapeCodeHandler->endFormatScope(); } diff --git a/src/libs/utils/outputformatter.h b/src/libs/utils/outputformatter.h index 7280d3e09ef..503e73ab73a 100644 --- a/src/libs/utils/outputformatter.h +++ b/src/libs/utils/outputformatter.h @@ -47,6 +47,7 @@ QT_END_NAMESPACE namespace Utils { class AnsiEscapeCodeHandler; +class FormattedText; class QTCREATOR_UTILS_EXPORT OutputFormatter : public QObject { @@ -71,6 +72,7 @@ protected: void initFormats(); virtual void clearLastLine(); QTextCharFormat charFormat(OutputFormat format) const; + QList<Utils::FormattedText> parseAnsi(const QString &text, const QTextCharFormat &format); void append(QTextCursor &cursor, const QString &text, const QTextCharFormat &format); private: diff --git a/src/libs/utils/persistentsettings.cpp b/src/libs/utils/persistentsettings.cpp index b5493ee5d81..1a69acfc3e2 100644 --- a/src/libs/utils/persistentsettings.cpp +++ b/src/libs/utils/persistentsettings.cpp @@ -339,11 +339,11 @@ PersistentSettingsReader::PersistentSettingsReader() { } -QVariant PersistentSettingsReader::restoreValue(const QString &variable) const +QVariant PersistentSettingsReader::restoreValue(const QString &variable, const QVariant &defaultValue) const { if (m_valueMap.contains(variable)) return m_valueMap.value(variable); - return QVariant(); + return defaultValue; } QVariantMap PersistentSettingsReader::restoreValues() const diff --git a/src/libs/utils/persistentsettings.h b/src/libs/utils/persistentsettings.h index c092da9eee1..f8187412d75 100644 --- a/src/libs/utils/persistentsettings.h +++ b/src/libs/utils/persistentsettings.h @@ -45,7 +45,7 @@ class QTCREATOR_UTILS_EXPORT PersistentSettingsReader { public: PersistentSettingsReader(); - QVariant restoreValue(const QString &variable) const; + QVariant restoreValue(const QString &variable, const QVariant &defaultValue = QVariant()) const; QVariantMap restoreValues() const; bool load(const FileName &fileName); diff --git a/src/libs/utils/portlist.cpp b/src/libs/utils/portlist.cpp index cdad1958815..5bf0e098fc2 100644 --- a/src/libs/utils/portlist.cpp +++ b/src/libs/utils/portlist.cpp @@ -63,7 +63,7 @@ public: try { if (!atEnd()) parseElemList(); - } catch (ParseException &e) { + } catch (const ParseException &e) { qWarning("Malformed ports specification: %s", e.error); } return m_portList; diff --git a/src/libs/utils/progressindicator.cpp b/src/libs/utils/progressindicator.cpp new file mode 100644 index 00000000000..85ce2f1d88a --- /dev/null +++ b/src/libs/utils/progressindicator.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://www.qt.io/licensing. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "progressindicator.h" + +#include "qtcassert.h" +#include "stylehelper.h" + +#include <QEvent> +#include <QPainter> +#include <QPixmap> + +using namespace Utils; + +ProgressIndicator::ProgressIndicator(IndicatorSize size, QWidget *parent) + : QWidget(parent), + m_rotation(0) +{ + setAttribute(Qt::WA_TransparentForMouseEvents); + m_timer.setSingleShot(false); + connect(&m_timer, &QTimer::timeout, this, &ProgressIndicator::step); + setIndicatorSize(size); +} + +static QString imageFileNameForIndicatorSize(ProgressIndicator::IndicatorSize size) +{ + switch (size) { + case ProgressIndicator::Large: + return QLatin1String(":/utils/images/progressindicator_big.png"); + case ProgressIndicator::Medium: + return QLatin1String(":/utils/images/progressindicator_medium.png"); + case ProgressIndicator::Small: + default: + return QLatin1String(":/utils/images/progressindicator_small.png"); + } +} + +void ProgressIndicator::setIndicatorSize(ProgressIndicator::IndicatorSize size) +{ + m_size = size; + m_rotationStep = size == Small ? 45 : 30; + m_timer.setInterval(size == Small ? 100 : 80); + m_pixmap.load(StyleHelper::dpiSpecificImageFile( + imageFileNameForIndicatorSize(size))); + updateGeometry(); +} + +ProgressIndicator::IndicatorSize ProgressIndicator::indicatorSize() const +{ + return m_size; +} + +QSize ProgressIndicator::sizeHint() const +{ + return m_pixmap.size() / m_pixmap.devicePixelRatio(); +} + +void ProgressIndicator::attachToWidget(QWidget *parent) +{ + if (parentWidget()) + parentWidget()->removeEventFilter(this); + setParent(parent); + parent->installEventFilter(this); + resizeToParent(); + raise(); +} + +void ProgressIndicator::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.setRenderHint(QPainter::SmoothPixmapTransform); + QPoint translate(rect().width() / 2, rect().height() / 2); + QTransform t; + t.translate(translate.x(), translate.y()); + t.rotate(m_rotation); + t.translate(-translate.x(), -translate.y()); + p.setTransform(t); + QSize pixmapUserSize(m_pixmap.size() / m_pixmap.devicePixelRatio()); + p.drawPixmap(QPoint((rect().width() - pixmapUserSize.width()) / 2, + (rect().height() - pixmapUserSize.height()) / 2), + m_pixmap); +} + +void ProgressIndicator::showEvent(QShowEvent *) +{ + m_timer.start(); +} + +void ProgressIndicator::hideEvent(QHideEvent *) +{ + m_timer.stop(); +} + +bool ProgressIndicator::eventFilter(QObject *obj, QEvent *ev) +{ + if (obj == parent() && ev->type() == QEvent::Resize) { + resizeToParent(); + } + return QWidget::eventFilter(obj, ev); +} + +void ProgressIndicator::step() +{ + m_rotation = (m_rotation + m_rotationStep + 360) % 360; + update(); +} + +void ProgressIndicator::resizeToParent() +{ + QTC_ASSERT(parentWidget(), return); + setGeometry(QRect(QPoint(0, 0), parentWidget()->size())); +} + diff --git a/src/libs/utils/progressindicator.h b/src/libs/utils/progressindicator.h new file mode 100644 index 00000000000..30561e2afc8 --- /dev/null +++ b/src/libs/utils/progressindicator.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://www.qt.io/licensing. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + + +#ifndef PROGRESSINDICATOR_H +#define PROGRESSINDICATOR_H + +#include "utils_global.h" + +#include <QTimer> +#include <QWidget> + +namespace Utils { + +namespace Internal { class ProgressIndicatorPrivate; } + +class QTCREATOR_UTILS_EXPORT ProgressIndicator : public QWidget +{ + Q_OBJECT +public: + enum IndicatorSize { + Small, + Medium, + Large + }; + + explicit ProgressIndicator(IndicatorSize size, QWidget *parent = 0); + + void setIndicatorSize(IndicatorSize size); + IndicatorSize indicatorSize() const; + + QSize sizeHint() const; + + void attachToWidget(QWidget *parent); + +protected: + void paintEvent(QPaintEvent *); + void showEvent(QShowEvent *); + void hideEvent(QHideEvent *); + bool eventFilter(QObject *obj, QEvent *ev); + +private: + void step(); + void resizeToParent(); + + ProgressIndicator::IndicatorSize m_size; + int m_rotationStep; + int m_rotation; + QTimer m_timer; + QPixmap m_pixmap; +}; + +} // Utils + +#endif // PROGRESSINDICATOR_H diff --git a/src/libs/utils/projectintropage.cpp b/src/libs/utils/projectintropage.cpp index 6e5dc7777ca..5769afe7636 100644 --- a/src/libs/utils/projectintropage.cpp +++ b/src/libs/utils/projectintropage.cpp @@ -179,10 +179,8 @@ bool ProjectIntroPage::validate() } // Check existence of the directory - QString projectDir = path(); - projectDir += QDir::separator(); - projectDir += d->m_ui.nameLineEdit->text(); - const QFileInfo projectDirFile(projectDir); + const QFileInfo projectDirFile(path() + QLatin1Char('/') + + QDir::fromNativeSeparators(d->m_ui.nameLineEdit->text())); if (!projectDirFile.exists()) { // All happy hideStatusLabel(); return nameValid; diff --git a/src/libs/utils/reloadpromptutils.cpp b/src/libs/utils/reloadpromptutils.cpp index 73ae4dabdaf..d20aa0e1aae 100644 --- a/src/libs/utils/reloadpromptutils.cpp +++ b/src/libs/utils/reloadpromptutils.cpp @@ -28,6 +28,7 @@ ** ****************************************************************************/ +#include "fileutils.h" #include "reloadpromptutils.h" #include <QCoreApplication> @@ -37,7 +38,7 @@ namespace Utils { -QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const QString &fileName, +QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const FileName &fileName, bool modified, QWidget *parent) { @@ -53,8 +54,8 @@ QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const QString &fileName, msg = QCoreApplication::translate("Utils::reloadPrompt", "The file <i>%1</i> has changed outside Qt Creator. Do you want to reload it?"); } - msg = msg.arg(QFileInfo(fileName).fileName()); - return reloadPrompt(title, msg, QDir::toNativeSeparators(fileName), parent); + msg = msg.arg(fileName.fileName()); + return reloadPrompt(title, msg, fileName.toUserOutput(), parent); } QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const QString &title, diff --git a/src/libs/utils/reloadpromptutils.h b/src/libs/utils/reloadpromptutils.h index c3a53c9021c..65e564b438a 100644 --- a/src/libs/utils/reloadpromptutils.h +++ b/src/libs/utils/reloadpromptutils.h @@ -39,6 +39,7 @@ class QWidget; QT_END_NAMESPACE namespace Utils { +class FileName; enum ReloadPromptAnswer { ReloadCurrent, @@ -48,7 +49,7 @@ enum ReloadPromptAnswer { CloseCurrent }; -QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const QString &fileName, +QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const FileName &fileName, bool modified, QWidget *parent); QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const QString &title, diff --git a/src/libs/utils/synchronousprocess.cpp b/src/libs/utils/synchronousprocess.cpp index 0666740641f..6bcc01a8887 100644 --- a/src/libs/utils/synchronousprocess.cpp +++ b/src/libs/utils/synchronousprocess.cpp @@ -385,7 +385,7 @@ static bool isGuiThread() } SynchronousProcessResponse SynchronousProcess::run(const QString &binary, - const QStringList &args) + const QStringList &args) { if (debug) qDebug() << '>' << Q_FUNC_INFO << binary << args; diff --git a/src/libs/utils/theme/theme.cpp b/src/libs/utils/theme/theme.cpp index 3fd18c77c69..97a433e9554 100644 --- a/src/libs/utils/theme/theme.cpp +++ b/src/libs/utils/theme/theme.cpp @@ -116,7 +116,7 @@ QPair<QColor, QString> Theme::readNamedColor(const QString &color) const bool ok = true; const QRgb rgba = color.toLongLong(&ok, 16); if (!ok) { - qWarning("Color '%s' is neither a named color nor a valid color", qPrintable(color)); + qWarning("Color \"%s\" is neither a named color nor a valid color", qPrintable(color)); return qMakePair(Qt::black, QString()); } return qMakePair(QColor::fromRgba(rgba), QString()); diff --git a/src/libs/utils/tooltip/tipcontents.cpp b/src/libs/utils/tooltip/tipcontents.cpp deleted file mode 100644 index e28c3713199..00000000000 --- a/src/libs/utils/tooltip/tipcontents.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms and -** conditions see http://www.qt.io/terms-conditions. For further information -** use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#include "tipcontents.h" -#include "tooltip.h" -#include "tips.h" - -#include <utils/qtcassert.h> - -#include <QtGlobal> - -namespace Utils { - -TipContent::TipContent() -{} - -TipContent::~TipContent() -{} - -ColorContent::ColorContent(const QColor &color) : m_color(color) -{} - -ColorContent::~ColorContent() -{} - -TipContent *ColorContent::clone() const -{ - return new ColorContent(*this); -} - -int ColorContent::typeId() const -{ - return COLOR_CONTENT_ID; -} - -bool ColorContent::isValid() const -{ - return m_color.isValid(); -} - -bool ColorContent::isInteractive() const -{ - return false; -} - -int ColorContent::showTime() const -{ - return 4000; -} - -bool ColorContent::equals(const TipContent &tipContent) const -{ - if (typeId() == tipContent.typeId()) { - if (m_color == static_cast<const ColorContent &>(tipContent).m_color) - return true; - } - return false; -} - -const QColor &ColorContent::color() const -{ - return m_color; -} - -TextContent::TextContent(const QString &text) : m_text(text) -{} - -TextContent::~TextContent() -{} - -TipContent *TextContent::clone() const -{ - return new TextContent(*this); -} - -int TextContent::typeId() const -{ - return TEXT_CONTENT_ID; -} - -bool TextContent::isValid() const -{ - return !m_text.isEmpty(); -} - -bool TextContent::isInteractive() const -{ - return false; -} - -int TextContent::showTime() const -{ - return 10000 + 40 * qMax(0, m_text.length() - 100); -} - -bool TextContent::equals(const TipContent &tipContent) const -{ - if (typeId() == tipContent.typeId()) { - if (m_text == static_cast<const TextContent &>(tipContent).m_text) - return true; - } - return false; -} - -const QString &TextContent::text() const -{ - return m_text; -} - -WidgetContent::WidgetContent(QWidget *w, bool interactive) : - m_widget(w), m_interactive(interactive) -{ -} - -TipContent *WidgetContent::clone() const -{ - return new WidgetContent(m_widget, m_interactive); -} - -int WidgetContent::typeId() const -{ - return WIDGET_CONTENT_ID; -} - -bool WidgetContent::isValid() const -{ - return m_widget; -} - -bool WidgetContent::isInteractive() const -{ - return m_interactive; -} - -void WidgetContent::setInteractive(bool i) -{ - m_interactive = i; -} - -int WidgetContent::showTime() const -{ - return 30000; -} - -bool WidgetContent::equals(const TipContent &tipContent) const -{ - if (typeId() == tipContent.typeId()) { - if (m_widget == static_cast<const WidgetContent &>(tipContent).m_widget) - return true; - } - return false; -} - -bool WidgetContent::pinToolTip(QWidget *w, QWidget *parent) -{ - QTC_ASSERT(w, return false); - // Find the parent WidgetTip, tell it to pin/release the - // widget and close. - for (QWidget *p = w->parentWidget(); p ; p = p->parentWidget()) { - if (Internal::WidgetTip *wt = qobject_cast<Internal::WidgetTip *>(p)) { - wt->pinToolTipWidget(parent); - ToolTip::hide(); - return true; - } - } - return false; -} - -} // namespace Utils diff --git a/src/libs/utils/tooltip/tipcontents.h b/src/libs/utils/tooltip/tipcontents.h deleted file mode 100644 index 7d2c91c1b0c..00000000000 --- a/src/libs/utils/tooltip/tipcontents.h +++ /dev/null @@ -1,130 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms and -** conditions see http://www.qt.io/terms-conditions. For further information -** use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#ifndef TIPCONTENTS_H -#define TIPCONTENTS_H - -#include "../utils_global.h" - -#include <QString> -#include <QColor> -#include <QWidget> - -namespace Utils { - -class QTCREATOR_UTILS_EXPORT TipContent -{ -protected: - TipContent(); - -public: - virtual ~TipContent(); - - virtual TipContent *clone() const = 0; - virtual int typeId() const = 0; - virtual bool isValid() const = 0; - virtual bool isInteractive() const = 0; - virtual int showTime() const = 0; - virtual bool equals(const TipContent &tipContent) const = 0; -}; - -class QTCREATOR_UTILS_EXPORT ColorContent : public TipContent -{ -public: - ColorContent(const QColor &color); - virtual ~ColorContent(); - - virtual TipContent *clone() const; - virtual int typeId() const; - virtual bool isValid() const; - virtual bool isInteractive() const; - virtual int showTime() const; - virtual bool equals(const TipContent &tipContent) const; - - const QColor &color() const; - - static const int COLOR_CONTENT_ID = 0; - -private: - QColor m_color; -}; - -class QTCREATOR_UTILS_EXPORT TextContent : public TipContent -{ -public: - TextContent(const QString &text); - virtual ~TextContent(); - - virtual TipContent *clone() const; - virtual int typeId() const; - virtual bool isValid() const; - virtual bool isInteractive() const; - virtual int showTime() const; - virtual bool equals(const TipContent &tipContent) const; - - const QString &text() const; - - static const int TEXT_CONTENT_ID = 1; - -private: - QString m_text; -}; - -// A content for displaying any widget (with a layout). -class QTCREATOR_UTILS_EXPORT WidgetContent : public TipContent -{ -public: - explicit WidgetContent(QWidget *w, bool interactive = false); - - virtual TipContent *clone() const; - virtual int typeId() const; - virtual bool isValid() const; - virtual int showTime() const; - virtual bool isInteractive() const; - void setInteractive(bool i); - - virtual bool equals(const TipContent &tipContent) const; - - // Helper to 'pin' (show as real window) a tooltip shown - // using WidgetContent - static bool pinToolTip(QWidget *w, QWidget *parent); - - static const int WIDGET_CONTENT_ID = 42; - - QWidget *widget() const { return m_widget; } - -private: - QWidget *m_widget; - bool m_interactive; -}; - -} // namespace Utils - -#endif // TIPCONTENTS_H diff --git a/src/libs/utils/tooltip/tips.cpp b/src/libs/utils/tooltip/tips.cpp index 159eeb9abf8..9fd0121a682 100644 --- a/src/libs/utils/tooltip/tips.cpp +++ b/src/libs/utils/tooltip/tips.cpp @@ -29,7 +29,7 @@ ****************************************************************************/ #include "tips.h" -#include "tipcontents.h" +#include "tooltip.h" #include "reuse.h" #include <utils/qtcassert.h> @@ -49,59 +49,32 @@ #include <QVBoxLayout> namespace Utils { - namespace Internal { - -namespace { - // @todo: Reuse... - QPixmap tilePixMap(int size) - { - const int checkerbordSize= size; - QPixmap tilePixmap(checkerbordSize * 2, checkerbordSize * 2); - tilePixmap.fill(Qt::white); - QPainter tilePainter(&tilePixmap); - QColor color(220, 220, 220); - tilePainter.fillRect(0, 0, checkerbordSize, checkerbordSize, color); - tilePainter.fillRect(checkerbordSize, checkerbordSize, checkerbordSize, checkerbordSize, color); - return tilePixmap; - } -} +namespace Internal { QTipLabel::QTipLabel(QWidget *parent) : - QLabel(parent, Qt::ToolTip | Qt::BypassGraphicsProxyWidget), - m_tipContent(0) + QLabel(parent, Qt::ToolTip | Qt::BypassGraphicsProxyWidget) {} -QTipLabel::~QTipLabel() -{ - TipContent *tmpTipContent = m_tipContent; - m_tipContent = 0; - delete tmpTipContent; -} - -bool QTipLabel::isInteractive() const -{ - return m_tipContent && m_tipContent->isInteractive(); -} -void QTipLabel::setContent(const TipContent &content) +ColorTip::ColorTip(QWidget *parent) + : QTipLabel(parent) { - TipContent *tmpTipContent = m_tipContent; - m_tipContent = content.clone(); - delete tmpTipContent; + resize(40, 40); } -const TipContent &QTipLabel::content() const -{ return *m_tipContent; } - -ColorTip::ColorTip(QWidget *parent) : QTipLabel(parent) +void ColorTip::setContent(const QVariant &content) { - resize(QSize(40, 40)); - m_tilePixMap = tilePixMap(10); + m_color = content.value<QColor>(); + + const int size = 10; + m_tilePixmap = QPixmap(size * 2, size * 2); + m_tilePixmap.fill(Qt::white); + QPainter tilePainter(&m_tilePixmap); + QColor col(220, 220, 220); + tilePainter.fillRect(0, 0, size, size, col); + tilePainter.fillRect(size, size, size, size, col); } -ColorTip::~ColorTip() -{} - void ColorTip::configure(const QPoint &pos, QWidget *w) { Q_UNUSED(pos) @@ -110,31 +83,32 @@ void ColorTip::configure(const QPoint &pos, QWidget *w) update(); } -bool ColorTip::canHandleContentReplacement(const TipContent &content) const +bool ColorTip::canHandleContentReplacement(int typeId) const { - if (content.typeId() == ColorContent::COLOR_CONTENT_ID) - return true; - return false; + return typeId == ToolTip::ColorContent; +} + +bool ColorTip::equals(int typeId, const QVariant &other) const +{ + return typeId == ToolTip::ColorContent && other == m_color; } void ColorTip::paintEvent(QPaintEvent *event) { QTipLabel::paintEvent(event); - const QColor &color = static_cast<const ColorContent &>(content()).color(); - QPen pen; pen.setWidth(1); - if (color.value() > 100) - pen.setColor(color.darker()); + if (m_color.value() > 100) + pen.setColor(m_color.darker()); else - pen.setColor(color.lighter()); + pen.setColor(m_color.lighter()); QPainter painter(this); painter.setPen(pen); - painter.setBrush(color); + painter.setBrush(m_color); QRect r(0, 0, rect().width() - 1, rect().height() - 1); - painter.drawTiledPixmap(r, m_tilePixMap); + painter.drawTiledPixmap(r, m_tilePixmap); painter.drawRect(r); } @@ -150,13 +124,14 @@ TextTip::TextTip(QWidget *parent) : QTipLabel(parent) setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, 0, this) / 255.0); } -TextTip::~TextTip() -{} +void TextTip::setContent(const QVariant &content) +{ + m_text = content.toString(); +} void TextTip::configure(const QPoint &pos, QWidget *w) { - const QString &text = static_cast<const TextContent &>(content()).text(); - setText(text); + setText(m_text); // Make it look good with the default ToolTip font on Mac, which has a small descent. QFontMetrics fm(font()); @@ -181,11 +156,19 @@ void TextTip::configure(const QPoint &pos, QWidget *w) resize(tipWidth, heightForWidth(tipWidth) + extraHeight); } -bool TextTip::canHandleContentReplacement(const TipContent &content) const +bool TextTip::canHandleContentReplacement(int typeId) const { - if (content.typeId() == TextContent::TEXT_CONTENT_ID) - return true; - return false; + return typeId == ToolTip::TextContent; +} + +int TextTip::showTime() const +{ + return 10000 + 40 * qMax(0, m_text.size() - 100); +} + +bool TextTip::equals(int typeId, const QVariant &other) const +{ + return typeId == ToolTip::TextContent && other.toString() == m_text; } void TextTip::paintEvent(QPaintEvent *event) @@ -217,15 +200,17 @@ WidgetTip::WidgetTip(QWidget *parent) : setLayout(m_layout); } -void WidgetTip::configure(const QPoint &pos, QWidget *) +void WidgetTip::setContent(const QVariant &content) { - const WidgetContent &anyContent = static_cast<const WidgetContent &>(content()); - QWidget *widget = anyContent.widget(); + m_widget = content.value<QWidget *>(); +} - QTC_ASSERT(widget && m_layout->count() == 0, return); +void WidgetTip::configure(const QPoint &pos, QWidget *) +{ + QTC_ASSERT(m_widget && m_layout->count() == 0, return); move(pos); - m_layout->addWidget(widget); + m_layout->addWidget(m_widget); m_layout->setSizeConstraint(QLayout::SetFixedSize); adjustSize(); } @@ -253,12 +238,18 @@ void WidgetTip::pinToolTipWidget(QWidget *parent) widget->setAttribute(Qt::WA_DeleteOnClose); } -bool WidgetTip::canHandleContentReplacement(const TipContent & ) const +bool WidgetTip::canHandleContentReplacement(int typeId) const { // Always create a new widget. + Q_UNUSED(typeId); return false; } +bool WidgetTip::equals(int typeId, const QVariant &other) const +{ + return typeId == ToolTip::WidgetContent && other.value<QWidget *>() == m_widget; +} + // need to include it here to force it to be inside the namespaces #include "moc_tips.cpp" diff --git a/src/libs/utils/tooltip/tips.h b/src/libs/utils/tooltip/tips.h index 0b43a469096..86a9633e5da 100644 --- a/src/libs/utils/tooltip/tips.h +++ b/src/libs/utils/tooltip/tips.h @@ -31,13 +31,13 @@ #ifndef TIPS_H #define TIPS_H -#include <QSharedPointer> +#include "../utils_global.h" + #include <QLabel> #include <QPixmap> - -QT_FORWARD_DECLARE_CLASS(QVBoxLayout) - -namespace Utils { class TipContent; } +#include <QSharedPointer> +#include <QVariant> +#include <QVBoxLayout> #ifndef Q_MOC_RUN namespace Utils { @@ -48,68 +48,68 @@ namespace Internal { class QTipLabel : public QLabel { Q_OBJECT -protected: - QTipLabel(QWidget *parent); - public: - virtual ~QTipLabel(); - - void setContent(const TipContent &content); - const TipContent &content() const; + QTipLabel(QWidget *parent); + virtual void setContent(const QVariant &content) = 0; + virtual bool isInteractive() const { return false; } + virtual int showTime() const = 0; virtual void configure(const QPoint &pos, QWidget *w) = 0; - virtual bool canHandleContentReplacement(const TipContent &content) const = 0; - - bool isInteractive() const; - -private: - TipContent *m_tipContent; + virtual bool canHandleContentReplacement(int typeId) const = 0; + virtual bool equals(int typeId, const QVariant &other) const = 0; }; -class ColorTip : public QTipLabel +class TextTip : public QTipLabel { - Q_OBJECT public: - ColorTip(QWidget *parent); - virtual ~ColorTip(); + TextTip(QWidget *parent); + virtual void setContent(const QVariant &content); virtual void configure(const QPoint &pos, QWidget *w); - virtual bool canHandleContentReplacement(const TipContent &content) const; - -private: + virtual bool canHandleContentReplacement(int typeId) const; + virtual int showTime() const; + virtual bool equals(int typeId, const QVariant &other) const; virtual void paintEvent(QPaintEvent *event); + virtual void resizeEvent(QResizeEvent *event); - QPixmap m_tilePixMap; +private: + QString m_text; }; -class TextTip : public QTipLabel +class ColorTip : public QTipLabel { - Q_OBJECT public: - TextTip(QWidget *parent); - virtual ~TextTip(); + ColorTip(QWidget *parent); + virtual void setContent(const QVariant &content); virtual void configure(const QPoint &pos, QWidget *w); - virtual bool canHandleContentReplacement(const TipContent &content) const; + virtual bool canHandleContentReplacement(int typeId) const; + virtual int showTime() const { return 4000; } + virtual bool equals(int typeId, const QVariant &other) const; + virtual void paintEvent(QPaintEvent *event); private: - virtual void paintEvent(QPaintEvent *event); - virtual void resizeEvent(QResizeEvent *event); + QColor m_color; + QPixmap m_tilePixmap; }; class WidgetTip : public QTipLabel { Q_OBJECT + public: explicit WidgetTip(QWidget *parent = 0); + void pinToolTipWidget(QWidget *parent); + virtual void setContent(const QVariant &content); virtual void configure(const QPoint &pos, QWidget *w); - virtual bool canHandleContentReplacement(const TipContent &content) const; - -public slots: - void pinToolTipWidget(QWidget *parent); + virtual bool canHandleContentReplacement(int typeId) const; + virtual int showTime() const { return 30000; } + virtual bool equals(int typeId, const QVariant &other) const; + virtual bool isInteractive() const { return true; } private: + QWidget *m_widget; QVBoxLayout *m_layout; }; diff --git a/src/libs/utils/tooltip/tooltip.cpp b/src/libs/utils/tooltip/tooltip.cpp index 5859dd1f2de..08e05ea4a1d 100644 --- a/src/libs/utils/tooltip/tooltip.cpp +++ b/src/libs/utils/tooltip/tooltip.cpp @@ -30,11 +30,11 @@ #include "tooltip.h" #include "tips.h" -#include "tipcontents.h" #include "effects.h" #include "reuse.h" #include <utils/hostosinfo.h> +#include <utils/qtcassert.h> #include <QString> #include <QColor> @@ -51,8 +51,8 @@ using namespace Internal; ToolTip::ToolTip() : m_tip(0), m_widget(0) { - connect(&m_showTimer, SIGNAL(timeout()), this, SLOT(hideTipImmediately())); - connect(&m_hideDelayTimer, SIGNAL(timeout()), this, SLOT(hideTipImmediately())); + connect(&m_showTimer, &QTimer::timeout, this, &ToolTip::hideTipImmediately); + connect(&m_hideDelayTimer, &QTimer::timeout, this, &ToolTip::hideTipImmediately); } ToolTip::~ToolTip() @@ -66,9 +66,28 @@ ToolTip *ToolTip::instance() return &tooltip; } -void ToolTip::show(const QPoint &pos, const TipContent &content, QWidget *w, const QRect &rect) +void ToolTip::show(const QPoint &pos, const QString &content, QWidget *w, const QRect &rect) { - instance()->showInternal(pos, content, w, rect); + if (content.isEmpty()) + instance()->hideTipWithDelay(); + else + instance()->showInternal(pos, QVariant(content), TextContent, w, rect); +} + +void ToolTip::show(const QPoint &pos, const QColor &color, QWidget *w, const QRect &rect) +{ + if (!color.isValid()) + instance()->hideTipWithDelay(); + else + instance()->showInternal(pos, QVariant(color), ColorContent, w, rect); +} + +void ToolTip::show(const QPoint &pos, QWidget *content, QWidget *w, const QRect &rect) +{ + if (!content) + instance()->hideTipWithDelay(); + else + instance()->showInternal(pos, QVariant::fromValue(content), WidgetContent, w, rect); } void ToolTip::move(const QPoint &pos, QWidget *w) @@ -77,27 +96,37 @@ void ToolTip::move(const QPoint &pos, QWidget *w) instance()->placeTip(pos, w); } -void ToolTip::show(const QPoint &pos, const TipContent &content, QWidget *w) +bool ToolTip::pinToolTip(QWidget *w, QWidget *parent) { - show(pos, content, w, QRect()); + QTC_ASSERT(w, return false); + // Find the parent WidgetTip, tell it to pin/release the + // widget and close. + for (QWidget *p = w->parentWidget(); p ; p = p->parentWidget()) { + if (WidgetTip *wt = qobject_cast<WidgetTip *>(p)) { + wt->pinToolTipWidget(parent); + ToolTip::hide(); + return true; + } + } + return false; } -bool ToolTip::acceptShow(const TipContent &content, +bool ToolTip::acceptShow(const QVariant &content, + int typeId, const QPoint &pos, QWidget *w, const QRect &rect) { - if (!validateContent(content)) - return false; - if (isVisible()) { - if (m_tip->canHandleContentReplacement(content)) { + if (m_tip->canHandleContentReplacement(typeId)) { // Reuse current tip. QPoint localPos = pos; if (w) localPos = w->mapFromGlobal(pos); - if (tipChanged(localPos, content, w)) - setUp(pos, content, w, rect); + if (tipChanged(localPos, content, typeId, w)) { + m_tip->setContent(content); + setUp(pos, w, rect); + } return false; } hideTipImmediately(); @@ -114,19 +143,8 @@ bool ToolTip::acceptShow(const TipContent &content, return true; } -bool ToolTip::validateContent(const TipContent &content) +void ToolTip::setUp(const QPoint &pos, QWidget *w, const QRect &rect) { - if (!content.isValid()) { - if (isVisible()) - hideTipWithDelay(); - return false; - } - return true; -} - -void ToolTip::setUp(const QPoint &pos, const TipContent &content, QWidget *w, const QRect &rect) -{ - m_tip->setContent(content); m_tip->configure(pos, w); placeTip(pos, w); @@ -134,12 +152,12 @@ void ToolTip::setUp(const QPoint &pos, const TipContent &content, QWidget *w, co if (m_hideDelayTimer.isActive()) m_hideDelayTimer.stop(); - m_showTimer.start(content.showTime()); + m_showTimer.start(m_tip->showTime()); } -bool ToolTip::tipChanged(const QPoint &pos, const TipContent &content, QWidget *w) const +bool ToolTip::tipChanged(const QPoint &pos, const QVariant &content, int typeId, QWidget *w) const { - if (!m_tip->content().equals(content) || m_widget != w) + if (!m_tip->equals(typeId, content) || m_widget != w) return true; if (!m_rect.isNull()) return !m_rect.contains(pos); @@ -199,27 +217,29 @@ void ToolTip::hideTipImmediately() qApp->removeEventFilter(this); } -void ToolTip::showInternal(const QPoint &pos, const TipContent &content, QWidget *w, const QRect &rect) +void ToolTip::showInternal(const QPoint &pos, const QVariant &content, + int typeId, QWidget *w, const QRect &rect) { - if (acceptShow(content, pos, w, rect)) { + if (acceptShow(content, typeId, pos, w, rect)) { QWidget *target = 0; if (HostOsInfo::isWindowsHost()) target = QApplication::desktop()->screen(Internal::screenNumber(pos, w)); else target = w; - switch (content.typeId()) { - case TextContent::TEXT_CONTENT_ID: - m_tip = new TextTip(target); - break; - case ColorContent::COLOR_CONTENT_ID: + switch (typeId) { + case ColorContent: m_tip = new ColorTip(target); break; - case WidgetContent::WIDGET_CONTENT_ID: + case TextContent: + m_tip = new TextTip(target); + break; + case WidgetContent: m_tip = new WidgetTip(target); break; } - setUp(pos, content, w, rect); + m_tip->setContent(content); + setUp(pos, w, rect); qApp->installEventFilter(this); showTip(); } diff --git a/src/libs/utils/tooltip/tooltip.h b/src/libs/utils/tooltip/tooltip.h index dd3e17cb656..b95d3a32060 100644 --- a/src/libs/utils/tooltip/tooltip.h +++ b/src/libs/utils/tooltip/tooltip.h @@ -50,14 +50,13 @@ QT_BEGIN_NAMESPACE class QPoint; +class QVariant; class QWidget; QT_END_NAMESPACE namespace Utils { namespace Internal { class QTipLabel; } -class TipContent; - class QTCREATOR_UTILS_EXPORT ToolTip : public QObject { Q_OBJECT @@ -67,27 +66,33 @@ protected: public: ~ToolTip(); + enum { + ColorContent = 0, + TextContent = 1, + WidgetContent = 42 + }; + bool eventFilter(QObject *o, QEvent *event); static ToolTip *instance(); - static void show(const QPoint &pos, const TipContent &content, QWidget *w = 0); - static void show(const QPoint &pos, const TipContent &content, QWidget *w, const QRect &rect); + static void show(const QPoint &pos, const QString &content, QWidget *w = 0, const QRect &rect = QRect()); + static void show(const QPoint &pos, const QColor &color, QWidget *w = 0, const QRect &rect = QRect()); + static void show(const QPoint &pos, QWidget *content, QWidget *w = 0, const QRect &rect = QRect()); static void move(const QPoint &pos, QWidget *w); static void hide(); static bool isVisible(); -protected slots: - void hideTipImmediately(); - -protected: - void showInternal(const QPoint &pos, const TipContent &content, QWidget *w, const QRect &rect); + // Helper to 'pin' (show as real window) a tooltip shown + // using WidgetContent + static bool pinToolTip(QWidget *w, QWidget *parent); private: - bool acceptShow(const TipContent &content, const QPoint &pos, QWidget *w, const QRect &rect); - bool validateContent(const TipContent &content); - void setUp(const QPoint &pos, const TipContent &content, QWidget *w, const QRect &rect); - bool tipChanged(const QPoint &pos, const TipContent &content, QWidget *w) const; + void showInternal(const QPoint &pos, const QVariant &content, int typeId, QWidget *w, const QRect &rect); + void hideTipImmediately(); + bool acceptShow(const QVariant &content, int typeId, const QPoint &pos, QWidget *w, const QRect &rect); + void setUp(const QPoint &pos, QWidget *w, const QRect &rect); + bool tipChanged(const QPoint &pos, const QVariant &content, int typeId, QWidget *w) const; void setTipRect(QWidget *w, const QRect &rect); void placeTip(const QPoint &pos, QWidget *w); void showTip(); diff --git a/src/libs/utils/treemodel.cpp b/src/libs/utils/treemodel.cpp index 2b93d233e41..c582000bcb0 100644 --- a/src/libs/utils/treemodel.cpp +++ b/src/libs/utils/treemodel.cpp @@ -31,12 +31,579 @@ #include "treemodel.h" #include "qtcassert.h" +#include <QStack> +#include <QSize> + +#define USE_MODEL_TEST 0 +#define USE_INDEX_CHECKS 0 + +#if USE_INDEX_CHECKS +#define CHECK_INDEX(index) \ + do { \ + if (index.isValid()) { \ + QTC_CHECK(index.model() == this); \ + } else { \ + QTC_CHECK(index.model() == 0); \ + } \ + } while (false) +#else +#define CHECK_INDEX(index) +#endif + +#if USE_MODEL_TEST + +namespace { + +class ModelTest : public QObject +{ +public: + ModelTest(QAbstractItemModel *model, QObject *parent = 0); + +private: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void rowsInserted(const QModelIndex & parent, int start, int end); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void rowsRemoved(const QModelIndex & parent, int start, int end); + +private: + void checkChildren(const QModelIndex &parent, int currentDepth = 0); + + QAbstractItemModel *model; + + struct Changing + { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack<Changing> insert; + QStack<Changing> remove; + + bool fetchingMore; + + QList<QPersistentModelIndex> changing; +}; + /*! - \class Utils::TreeModel + Connect to all of the models signals. Whenever anything happens + recheck everything. +*/ +ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) +{ + Q_ASSERT(model); - \brief The TreeModel class is a convienience base class for models - to use in a QTreeView. + connect(model, &QAbstractItemModel::columnsAboutToBeInserted, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::columnsAboutToBeRemoved, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::columnsInserted, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::columnsRemoved, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::dataChanged, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::headerDataChanged, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::layoutAboutToBeChanged, this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::layoutChanged, this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::modelReset, this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::rowsAboutToBeInserted, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::rowsAboutToBeRemoved, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::rowsInserted, + this, &ModelTest::runAllTests); + connect(model, &QAbstractItemModel::rowsRemoved, + this, &ModelTest::runAllTests); + + // Special checks for inserting/removing + connect(model, &QAbstractItemModel::layoutAboutToBeChanged, + this, &ModelTest::layoutAboutToBeChanged); + connect(model, &QAbstractItemModel::layoutChanged, + this, &ModelTest::layoutChanged); + + connect(model, &QAbstractItemModel::rowsAboutToBeInserted, + this, &ModelTest::rowsAboutToBeInserted); + connect(model, &QAbstractItemModel::rowsAboutToBeRemoved, + this, &ModelTest::rowsAboutToBeRemoved); + connect(model, &QAbstractItemModel::rowsInserted, + this, &ModelTest::rowsInserted); + connect(model, &QAbstractItemModel::rowsRemoved, + this, &ModelTest::rowsRemoved); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if (fetchingMore) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. */ +void ModelTest::nonDestructiveBasicTest() +{ + Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); + model->canFetchMore(QModelIndex()); + Q_ASSERT(model->columnCount(QModelIndex()) >= 0); + Q_ASSERT(model->data(QModelIndex()) == QVariant()); + fetchingMore = true; + model->fetchMore(QModelIndex()); + fetchingMore = false; + Qt::ItemFlags flags = model->flags(QModelIndex()); + Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); + model->hasChildren(QModelIndex()); + model->hasIndex(0, 0); + model->headerData(0, Qt::Horizontal); + model->index(0, 0); + model->itemData(QModelIndex()); + QVariant cache; + model->match(QModelIndex(), -1, cache); + model->mimeTypes(); + QModelIndex m1 = model->parent(QModelIndex()); + QModelIndex m2 = QModelIndex(); + Q_ASSERT(m1 == m2); + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + Q_ASSERT(model->rowCount() >= 0); + QVariant variant; + model->setData(QModelIndex(), variant, -1); + model->setHeaderData(-1, Qt::Horizontal, QVariant()); + model->setHeaderData(999999, Qt::Horizontal, QVariant()); + QMap<int, QVariant> roles; + model->sibling(0, 0, QModelIndex()); + model->span(QModelIndex()); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + int rows = model->rowCount(topIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(topIndex) == true); + + QModelIndex secondLevelIndex = model->index(0, 0, topIndex); + if (secondLevelIndex.isValid()) { // not the top level + // check a row count where parent is valid + rows = model->rowCount(secondLevelIndex); + Q_ASSERT(rows >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(secondLevelIndex) == true); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + Q_ASSERT(model->columnCount(topIndex) >= 0); + + // check a column count where parent is valid + QModelIndex childIndex = model->index(0, 0, topIndex); + if (childIndex.isValid()) + Q_ASSERT(model->columnCount(childIndex) >= 0); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->hasIndex(-2, -2) == false); + Q_ASSERT(model->hasIndex(-2, 0) == false); + Q_ASSERT(model->hasIndex(0, -2) == false); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + Q_ASSERT(model->hasIndex(rows, columns) == false); + Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); + + if (rows > 0) + Q_ASSERT(model->hasIndex(0, 0) == true); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ + // Make sure that invalid values returns an invalid index + Q_ASSERT(model->index(-2, -2) == QModelIndex()); + Q_ASSERT(model->index(-2, 0) == QModelIndex()); + Q_ASSERT(model->index(0, -2) == QModelIndex()); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if (rows == 0) + return; + + // Catch off by one errors + QModelIndex tmp; + tmp = model->index(rows, columns); + Q_ASSERT(tmp == QModelIndex()); + tmp = model->index(0, 0); + Q_ASSERT(tmp.isValid() == true); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index(0, 0); + QModelIndex b = model->index(0, 0); + Q_ASSERT(a == b); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ + // Make sure the model wont crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); + + if (model->rowCount() == 0) + return; + + QModelIndex tmp; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index(0, 0, QModelIndex()); + tmp = model->parent(topIndex); + Q_ASSERT(tmp == QModelIndex()); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if (model->rowCount(topIndex) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + tmp = model->parent(childIndex); + Q_ASSERT(tmp == topIndex); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); + if (model->rowCount(topIndex1) > 0) { + QModelIndex childIndex = model->index(0, 0, topIndex); + QModelIndex childIndex1 = model->index(0, 0, topIndex1); + Q_ASSERT(childIndex != childIndex1); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren(QModelIndex()); +} + +/*! + Called from the parent() test. + + A model that returns an index of parent X should also return X when asking + for the parent of the index. + + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) +{ + QModelIndex tmp; + + // First just try walking back up the tree. + QModelIndex p = parent; + while (p.isValid()) + p = p.parent(); + + // For models that are dynamically populated + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + + int rows = model->rowCount(parent); + int columns = model->columnCount(parent); + + if (rows > 0) + Q_ASSERT(model->hasChildren(parent)); + + // Some further testing against rows(), columns(), and hasChildren() + Q_ASSERT(rows >= 0); + Q_ASSERT(columns >= 0); + if (rows > 0) + Q_ASSERT(model->hasChildren(parent) == true); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); + for (int r = 0; r < rows; ++r) { + if (model->canFetchMore(parent)) { + fetchingMore = true; + model->fetchMore(parent); + fetchingMore = false; + } + Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); + for (int c = 0; c < columns; ++c) { + Q_ASSERT(model->hasIndex(r, c, parent) == true); + QModelIndex index = model->index(r, c, parent); + // rowCount() and columnCount() said that it existed... + Q_ASSERT(index.isValid() == true); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index(r, c, parent); + Q_ASSERT(index == modifiedIndex); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index(r, c, parent); + QModelIndex b = model->index(r, c, parent); + Q_ASSERT(a == b); + + // Some basic checking on the index that is returned + Q_ASSERT(index.model() == model); + Q_ASSERT(index.row() == r); + Q_ASSERT(index.column() == c); + // While you can technically return a QVariant usually this is a sign + // of an bug in data() Disable if this really is ok in your model. + //Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); + + // If the next test fails here is some somewhat useful debug you play with. + /* + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); + // And a view that you can even use to show the model. + //QTreeView view; + //view.setModel(model); + //view.show(); + }*/ + + // Check that we can get back our real parent. + //qDebug() << "TTT 1: " << model->parent(index); + //qDebug() << "TTT 2: " << parent; + //qDebug() << "TTT 3: " << index; + tmp = model->parent(index); + Q_ASSERT(tmp == parent); + + // recursively go down the children + if (model->hasChildren(index) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren(index, ++currentDepth); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index(r, c, parent); + Q_ASSERT(index == newerIndex); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + Q_ASSERT(!model->data(QModelIndex()).isValid()); + + if (model->rowCount() == 0) + return; + + // A valid index should have a valid QVariant data + Q_ASSERT(model->index(0, 0).isValid()); + + // shouldn't be able to set data on an invalid index + Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); + + // General Purpose roles that should return a QString + QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); + if (variant.isValid()) + Q_ASSERT(variant.canConvert(QVariant::String)); + variant = model->data(model->index(0, 0), Qt::StatusTipRole); + if (variant.isValid()) + Q_ASSERT(variant.canConvert(QVariant::String)); + variant = model->data(model->index(0, 0), Qt::WhatsThisRole); + if (variant.isValid()) + Q_ASSERT(variant.canConvert(QVariant::String)); + + // General Purpose roles that should return a QSize + variant = model->data(model->index(0, 0), Qt::SizeHintRole); + if (variant.isValid()) + Q_ASSERT(variant.canConvert(QVariant::Size)); + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); + if (fontVariant.isValid()) + Q_ASSERT(fontVariant.canConvert(QVariant::Font)); + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); + if (textAlignmentVariant.isValid()) { + int alignment = textAlignmentVariant.toInt(); + Q_ASSERT(alignment == (alignment & (Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask))); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); + if (colorVariant.isValid()) + Q_ASSERT(colorVariant.canConvert(QVariant::Color)); + + colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); + if (colorVariant.isValid()) + Q_ASSERT(colorVariant.canConvert(QVariant::Color)); + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); + if (checkStateVariant.isValid()) { + int state = checkStateVariant.toInt(); + Q_ASSERT(state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(end); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(start, 0, parent)); + insert.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) +{ + Changing c = insert.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + /* + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + */ + Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) + changing.append(QPersistentModelIndex(model->index(i, 0))); +} + +void ModelTest::layoutChanged() +{ + for (int i = 0; i < changing.count(); ++i) { + QPersistentModelIndex p = changing[i]; + Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Changing c; + c.parent = parent; + c.oldSize = model->rowCount(parent); + c.last = model->data(model->index(start - 1, 0, parent)); + c.next = model->data(model->index(end + 1, 0, parent)); + remove.push(c); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) +{ + Changing c = remove.pop(); + Q_ASSERT(c.parent == parent); + Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); + Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); + Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); +} + +} // anon namespace + +#endif // ModelTest namespace Utils { @@ -44,22 +611,28 @@ namespace Utils { // TreeItem // TreeItem::TreeItem() - : m_parent(0), m_lazy(false), m_populated(false), + : m_parent(0), m_model(0), m_displays(0), m_lazy(false), m_populated(false), m_flags(Qt::ItemIsEnabled|Qt::ItemIsSelectable) { } +TreeItem::TreeItem(const QStringList &displays, int flags) + : m_parent(0), m_model(0), m_displays(new QStringList(displays)), m_lazy(false), m_populated(false), + m_flags(flags) +{ +} + TreeItem::~TreeItem() { clear(); + delete m_displays; } TreeItem *TreeItem::child(int pos) const { ensurePopulated(); QTC_ASSERT(pos >= 0, return 0); - QTC_ASSERT(pos < m_children.size(), return 0); - return m_children.at(pos); + return pos < m_children.size() ? m_children.at(pos) : 0; } bool TreeItem::isLazy() const @@ -67,11 +640,6 @@ bool TreeItem::isLazy() const return m_lazy; } -int TreeItem::columnCount() const -{ - return 1; -} - int TreeItem::rowCount() const { ensurePopulated(); @@ -84,9 +652,17 @@ void TreeItem::populate() QVariant TreeItem::data(int column, int role) const { + if (role == Qt::DisplayRole && m_displays && column >= 0 && column < m_displays->size()) + return m_displays->at(column); + return QVariant(); +} + +bool TreeItem::setData(int column, const QVariant &data, int role) +{ Q_UNUSED(column); + Q_UNUSED(data); Q_UNUSED(role); - return QVariant(); + return false; } Qt::ItemFlags TreeItem::flags(int column) const @@ -95,18 +671,82 @@ Qt::ItemFlags TreeItem::flags(int column) const return m_flags; } +bool TreeItem::hasChildren() const +{ + return canFetchMore() || rowCount() > 0; +} + +bool TreeItem::canFetchMore() const +{ + return false; +} + void TreeItem::prependChild(TreeItem *item) { - QTC_CHECK(!item->parent()); - item->m_parent = this; - m_children.prepend(item); + insertChild(0, item); } void TreeItem::appendChild(TreeItem *item) { + insertChild(m_children.size(), item); +} + +void TreeItem::insertChild(int pos, TreeItem *item) +{ QTC_CHECK(!item->parent()); - item->m_parent = this; - m_children.append(item); + QTC_ASSERT(0 <= pos && pos <= m_children.size(), return); // '<= size' is intentional. + + if (m_model && !m_lazy) { + QModelIndex idx = index(); + m_model->beginInsertRows(idx, pos, pos); + item->m_parent = this; + item->propagateModel(m_model); + m_children.insert(m_children.begin() + pos, item); + m_model->endInsertRows(); + } else { + item->m_parent = this; + m_children.insert(m_children.begin() + pos, item); + } +} + +void TreeItem::removeChildren() +{ + if (rowCount() == 0) + return; + if (m_model) { + QModelIndex idx = index(); + m_model->beginRemoveRows(idx, 0, rowCount() - 1); + clear(); + m_model->endRemoveRows(); + } else { + clear(); + } +} + +void TreeItem::update() +{ + if (m_model) { + QModelIndex idx = index(); + m_model->dataChanged(idx.sibling(idx.row(), 0), idx.sibling(idx.row(), m_model->m_columnCount - 1)); + } +} + +TreeItem *TreeItem::firstChild() const +{ + return m_children.isEmpty() ? 0 : m_children.first(); +} + +TreeItem *TreeItem::lastChild() const +{ + return m_children.isEmpty() ? 0 : m_children.last(); +} + +int TreeItem::level() const +{ + int l = 0; + for (TreeItem *item = this->parent(); item; item = item->parent()) + ++l; + return l; } void TreeItem::setLazy(bool on) @@ -119,6 +759,40 @@ void TreeItem::setFlags(Qt::ItemFlags flags) m_flags = flags; } +QModelIndex TreeItem::index() const +{ + QTC_ASSERT(m_model, return QModelIndex()); + return m_model->indexFromItem(this); +} + +void TreeItem::setModel(TreeModel *model) +{ + if (m_model == model) + return; + m_model = model; + foreach (TreeItem *item, m_children) + item->setModel(model); +} + +void TreeItem::walkTree(TreeItemVisitor *visitor) +{ + if (visitor->preVisit(this)) { + ++visitor->m_level; + visitor->visit(this); + foreach (TreeItem *item, m_children) + item->walkTree(visitor); + --visitor->m_level; + } + visitor->postVisit(this); +} + +void TreeItem::walkTree(std::function<void (TreeItem *)> f) +{ + f(this); + foreach (TreeItem *item, m_children) + item->walkTree(f); +} + void TreeItem::clear() { while (m_children.size()) { @@ -128,6 +802,12 @@ void TreeItem::clear() } } +void TreeItem::expand() +{ + QTC_ASSERT(m_model, return); + m_model->requestExpansion(index()); +} + void TreeItem::ensurePopulated() const { if (!m_populated) { @@ -137,12 +817,41 @@ void TreeItem::ensurePopulated() const } } -// -// TreeModel -// +void TreeItem::propagateModel(TreeModel *m) +{ + QTC_ASSERT(m, return); + QTC_ASSERT(m_model == 0 || m_model == m, return); + if (m && !m_model) { + m_model = m; + foreach (TreeItem *item, m_children) + item->propagateModel(m); + } +} + +/*! + \class Utils::TreeModel + + \brief The TreeModel class is a convienience base class for models + to use in a QTreeView. +*/ + TreeModel::TreeModel(QObject *parent) - : QAbstractItemModel(parent), m_root(new TreeItem) + : QAbstractItemModel(parent), + m_root(new TreeItem) { + m_columnCount = 1; + m_root->m_model = this; +#if USE_MODEL_TEST + new ModelTest(this, this); +#endif +} + +TreeModel::TreeModel(TreeItem *root, QObject *parent) + : QAbstractItemModel(parent), + m_root(root) +{ + m_columnCount = 1; + m_root->propagateModel(this); } TreeModel::~TreeModel() @@ -152,11 +861,12 @@ TreeModel::~TreeModel() QModelIndex TreeModel::parent(const QModelIndex &idx) const { - checkIndex(idx); + CHECK_INDEX(idx); if (!idx.isValid()) return QModelIndex(); const TreeItem *item = itemFromIndex(idx); + QTC_ASSERT(item, return QModelIndex()); const TreeItem *parent = item->parent(); if (!parent || parent == m_root) return QModelIndex(); @@ -174,22 +884,31 @@ QModelIndex TreeModel::parent(const QModelIndex &idx) const int TreeModel::rowCount(const QModelIndex &idx) const { - checkIndex(idx); + CHECK_INDEX(idx); if (!idx.isValid()) return m_root->rowCount(); if (idx.column() > 0) return 0; - return itemFromIndex(idx)->rowCount(); + const TreeItem *item = itemFromIndex(idx); + QTC_ASSERT(item, return 0); + return item->rowCount(); } int TreeModel::columnCount(const QModelIndex &idx) const { - checkIndex(idx); - if (!idx.isValid()) - return m_root->columnCount(); + CHECK_INDEX(idx); if (idx.column() > 0) return 0; - return itemFromIndex(idx)->columnCount(); + return m_columnCount; +} + +bool TreeModel::setData(const QModelIndex &idx, const QVariant &data, int role) +{ + TreeItem *item = itemFromIndex(idx); + bool res = item ? item->setData(idx.column(), data, role) : false; + if (res) + emit dataChanged(idx, idx); + return res; } QVariant TreeModel::data(const QModelIndex &idx, int role) const @@ -198,21 +917,72 @@ QVariant TreeModel::data(const QModelIndex &idx, int role) const return item ? item->data(idx.column(), role) : QVariant(); } +QVariant TreeModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section < m_header.size()) + return m_header.at(section); + return QVariant(); +} + +bool TreeModel::hasChildren(const QModelIndex &idx) const +{ + TreeItem *item = itemFromIndex(idx); + return !item || item->hasChildren(); +} + Qt::ItemFlags TreeModel::flags(const QModelIndex &idx) const { + if (!idx.isValid()) + return 0; TreeItem *item = itemFromIndex(idx); return item ? item->flags(idx.column()) : (Qt::ItemIsEnabled|Qt::ItemIsSelectable); } +bool TreeModel::canFetchMore(const QModelIndex &idx) const +{ + if (!idx.isValid()) + return false; + TreeItem *item = itemFromIndex(idx); + return item ? item->canFetchMore() : false; +} + +void TreeModel::fetchMore(const QModelIndex &idx) +{ + if (!idx.isValid()) + return; + TreeItem *item = itemFromIndex(idx); + if (item) + item->fetchMore(); +} + TreeItem *TreeModel::rootItem() const { return m_root; } +void TreeModel::setRootItem(TreeItem *item) +{ + delete m_root; + m_root = item; + item->setModel(this); +} + +void TreeModel::setHeader(const QStringList &displays) +{ + m_header = displays; + m_columnCount = displays.size(); +} + +void TreeModel::setColumnCount(int columnCount) +{ + m_columnCount = columnCount; +} + QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const { - checkIndex(parent); + CHECK_INDEX(parent); if (!hasIndex(row, column, parent)) return QModelIndex(); @@ -225,42 +995,96 @@ QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) con TreeItem *TreeModel::itemFromIndex(const QModelIndex &idx) const { - checkIndex(idx); + CHECK_INDEX(idx); TreeItem *item = idx.isValid() ? static_cast<TreeItem*>(idx.internalPointer()) : m_root; -// CHECK(checkItem(item)); + QTC_ASSERT(item, return 0); + QTC_ASSERT(item->m_model == this, return 0); return item; } QModelIndex TreeModel::indexFromItem(const TreeItem *item) const { -// CHECK(checkItem(item)); - return indexFromItemHelper(item, m_root, QModelIndex()); + QTC_ASSERT(item, return QModelIndex()); + if (item == m_root) + return QModelIndex(); + + TreeItem *mitem = const_cast<TreeItem *>(item); + int row = item->parent()->m_children.indexOf(mitem); + return createIndex(row, 0, mitem); } -QModelIndex TreeModel::indexFromItemHelper(const TreeItem *needle, - TreeItem *parentItem, const QModelIndex &parentIndex) const +void TreeModel::removeItems() { - checkIndex(parentIndex); - if (needle == parentItem) - return parentIndex; - for (int i = parentItem->rowCount(); --i >= 0; ) { - TreeItem *childItem = parentItem->child(i); - QModelIndex childIndex = index(i, 0, parentIndex); - QModelIndex idx = indexFromItemHelper(needle, childItem, childIndex); - checkIndex(idx); - if (idx.isValid()) - return idx; - } - return QModelIndex(); + if (m_root) + m_root->removeChildren(); } -void TreeModel::checkIndex(const QModelIndex &index) const +UntypedTreeLevelItems TreeModel::untypedLevelItems(int level, TreeItem *start) const { - if (index.isValid()) { - QTC_CHECK(index.model() == this); + if (start == 0) + start = m_root; + return UntypedTreeLevelItems(start, level); +} + +UntypedTreeLevelItems TreeModel::untypedLevelItems(TreeItem *start) const +{ + return UntypedTreeLevelItems(start, 1); +} + +void TreeModel::removeItem(TreeItem *item) +{ +#if USE_MODEL_TEST + (void) new ModelTest(this, this); +#endif + + QTC_ASSERT(item, return); + TreeItem *parent = item->parent(); + QTC_ASSERT(parent, return); + int pos = parent->m_children.indexOf(item); + QTC_ASSERT(pos != -1, return); + + QModelIndex idx = indexFromItem(parent); + beginRemoveRows(idx, pos, pos); + item->m_parent = 0; + parent->m_children.removeAt(pos); + endRemoveRows(); +} + +// +// TreeLevelItems +// + +UntypedTreeLevelItems::UntypedTreeLevelItems(TreeItem *item, int level) + : m_item(item), m_level(level) +{} + +UntypedTreeLevelItems::const_iterator::const_iterator(TreeItem *base, int level) + : m_level(level) +{ + QTC_ASSERT(level > 0, return); + if (base) { + // "begin()" + m_depth = 0; + // Level x: The item m_item[x] is the m_pos[x]'th child of its + // parent, out of m_size[x]. + m_pos[0] = 0; + m_size[0] = 1; + m_item[0] = base; + goDown(); } else { - QTC_CHECK(index.model() == 0); + // "end()" + m_depth = -1; } } +UntypedTreeLevelItems::const_iterator UntypedTreeLevelItems::begin() const +{ + return const_iterator(m_item, m_level); +} + +UntypedTreeLevelItems::const_iterator UntypedTreeLevelItems::end() const +{ + return const_iterator(0, m_level); +} + } // namespace Utils diff --git a/src/libs/utils/treemodel.h b/src/libs/utils/treemodel.h index ffcb0b381df..6e654fdfaab 100644 --- a/src/libs/utils/treemodel.h +++ b/src/libs/utils/treemodel.h @@ -33,32 +33,78 @@ #include "utils_global.h" +#include "algorithm.h" +#include "qtcassert.h" + #include <QAbstractItemModel> +#include <functional> +#include <iterator> + namespace Utils { +class TreeItem; +class TreeModel; + +class QTCREATOR_UTILS_EXPORT TreeItemVisitor +{ +public: + TreeItemVisitor() : m_level(0) {} + virtual ~TreeItemVisitor() {} + + virtual bool preVisit(TreeItem *) { return true; } + virtual void visit(TreeItem *) {} + virtual void postVisit(TreeItem *) {} + + int level() const { return m_level; } + +private: + friend class TreeItem; + int m_level; +}; + class QTCREATOR_UTILS_EXPORT TreeItem { public: TreeItem(); + explicit TreeItem(const QStringList &displays, int flags = Qt::ItemIsEnabled); virtual ~TreeItem(); virtual TreeItem *parent() const { return m_parent; } virtual TreeItem *child(int pos) const; virtual bool isLazy() const; - virtual int columnCount() const; virtual int rowCount() const; virtual void populate(); virtual QVariant data(int column, int role) const; + virtual bool setData(int column, const QVariant &data, int role); virtual Qt::ItemFlags flags(int column) const; + virtual bool hasChildren() const; + virtual bool canFetchMore() const; + virtual void fetchMore() {} + void prependChild(TreeItem *item); void appendChild(TreeItem *item); + void insertChild(int pos, TreeItem *item); + void removeChildren(); + void update(); + void expand(); + TreeItem *firstChild() const; + TreeItem *lastChild() const; + int level() const; void setLazy(bool on); void setPopulated(bool on); void setFlags(Qt::ItemFlags flags); + QVector<TreeItem *> children() const { return m_children; } + QModelIndex index() const; + + TreeModel *model() const { return m_model; } + void setModel(TreeModel *model); + + void walkTree(TreeItemVisitor *visitor); + void walkTree(std::function<void(TreeItem *)> f); private: TreeItem(const TreeItem &) Q_DECL_EQ_DELETE; @@ -66,39 +112,207 @@ private: void clear(); void ensurePopulated() const; + void propagateModel(TreeModel *m); TreeItem *m_parent; // Not owned. + TreeModel *m_model; // Not owned. QVector<TreeItem *> m_children; // Owned. + QStringList *m_displays; bool m_lazy; mutable bool m_populated; Qt::ItemFlags m_flags; + + friend class TreeModel; +}; + +class QTCREATOR_UTILS_EXPORT UntypedTreeLevelItems +{ +public: + enum { MaxSearchDepth = 12 }; // FIXME. + explicit UntypedTreeLevelItems(TreeItem *item, int level = 1); + + typedef TreeItem *value_type; + + class const_iterator + { + public: + typedef std::forward_iterator_tag iterator_category; + typedef TreeItem *value_type; + typedef std::ptrdiff_t difference_type; + typedef const value_type *pointer; + typedef const value_type &reference; + + const_iterator(TreeItem *base, int level); + + TreeItem *operator*() { return m_item[m_depth]; } + + void operator++() + { + QTC_ASSERT(m_depth == m_level, return); + + int pos = ++m_pos[m_depth]; + if (pos < m_size[m_depth]) + m_item[m_depth] = m_item[m_depth - 1]->child(pos); + else + goUpNextDown(); + } + + bool operator==(const const_iterator &other) const + { + if (m_depth != other.m_depth) + return false; + for (int i = 0; i <= m_depth; ++i) + if (m_item[i] != other.m_item[i]) + return false; + return true; + } + + bool operator!=(const const_iterator &other) const + { + return !operator==(other); + } + + private: + // Result is either an item of the target level, or 'end'. + void goDown() + { + QTC_ASSERT(m_depth != -1, return); + QTC_ASSERT(m_depth < m_level, return); + do { + TreeItem *curr = m_item[m_depth]; + ++m_depth; + int size = curr->rowCount(); + if (size == 0) { + // This is a dead end not reaching to the desired level. + goUpNextDown(); + return; + } + m_size[m_depth] = size; + m_pos[m_depth] = 0; + m_item[m_depth] = curr->child(0); + } while (m_depth < m_level); + // Did not reach the required level? Set to end(). + if (m_depth != m_level) + m_depth = -1; + } + void goUpNextDown() + { + // Go up until we can move sidewards. + do { + --m_depth; + if (m_depth < 0) + return; // Solid end. + } while (++m_pos[m_depth] >= m_size[m_depth]); + m_item[m_depth] = m_item[m_depth - 1]->child(m_pos[m_depth]); + goDown(); + } + + int m_level; + int m_depth; + TreeItem *m_item[MaxSearchDepth]; + int m_pos[MaxSearchDepth]; + int m_size[MaxSearchDepth]; + }; + + const_iterator begin() const; + const_iterator end() const; + +private: + TreeItem *m_item; + int m_level; +}; + +template <class T> +class TreeLevelItems +{ +public: + typedef T value_type; + + explicit TreeLevelItems(const UntypedTreeLevelItems &items) : m_items(items) {} + + struct const_iterator : public UntypedTreeLevelItems::const_iterator + { + typedef std::forward_iterator_tag iterator_category; + typedef T value_type; + typedef std::ptrdiff_t difference_type; + typedef const value_type *pointer; + typedef const value_type &reference; + + const_iterator(UntypedTreeLevelItems::const_iterator it) : UntypedTreeLevelItems::const_iterator(it) {} + T operator*() { return static_cast<T>(UntypedTreeLevelItems::const_iterator::operator*()); } + }; + + const_iterator begin() const { return const_iterator(m_items.begin()); } + const_iterator end() const { return const_iterator(m_items.end()); } + +private: + UntypedTreeLevelItems m_items; }; class QTCREATOR_UTILS_EXPORT TreeModel : public QAbstractItemModel { + Q_OBJECT + public: explicit TreeModel(QObject *parent = 0); + explicit TreeModel(TreeItem *root, QObject *parent = 0); virtual ~TreeModel(); int rowCount(const QModelIndex &idx = QModelIndex()) const; int columnCount(const QModelIndex &idx) const; + bool setData(const QModelIndex &idx, const QVariant &data, int role); QVariant data(const QModelIndex &idx, int role) const; QModelIndex index(int, int, const QModelIndex &idx) const; QModelIndex parent(const QModelIndex &idx) const; Qt::ItemFlags flags(const QModelIndex &idx) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + bool hasChildren(const QModelIndex &idx) const; + + bool canFetchMore(const QModelIndex &idx) const; + void fetchMore(const QModelIndex &idx); TreeItem *rootItem() const; + void setRootItem(TreeItem *item); TreeItem *itemFromIndex(const QModelIndex &) const; QModelIndex indexFromItem(const TreeItem *needle) const; + void removeItems(); -private: - QModelIndex indexFromItemHelper(const TreeItem *needle, - TreeItem *parentItem, const QModelIndex &parentIndex) const; + void setHeader(const QStringList &displays); + void setColumnCount(int columnCount); + + UntypedTreeLevelItems untypedLevelItems(int level = 0, TreeItem *start = 0) const; + UntypedTreeLevelItems untypedLevelItems(TreeItem *start) const; - void checkIndex(const QModelIndex &index) const; + template <class T> + TreeLevelItems<T> treeLevelItems(int level, TreeItem *start = 0) const + { + return TreeLevelItems<T>(untypedLevelItems(level, start)); + } + + template <class T> + TreeLevelItems<T> treeLevelItems(TreeItem *start) const + { + return TreeLevelItems<T>(untypedLevelItems(start)); + } + + template <class T> + T findItemAtLevel(int level, std::function<bool(T)> f, TreeItem *start = 0) const + { + return Utils::findOrDefault(treeLevelItems<T>(level, start), f); + } + + void removeItem(TreeItem *item); // item is not destroyed. + +signals: + void requestExpansion(QModelIndex); + +private: + friend class TreeItem; TreeItem *m_root; // Owned. + QStringList m_header; + int m_columnCount; }; } // namespace Utils diff --git a/src/libs/utils/unixutils.cpp b/src/libs/utils/unixutils.cpp index d6d98973817..a93209d463e 100644 --- a/src/libs/utils/unixutils.cpp +++ b/src/libs/utils/unixutils.cpp @@ -28,8 +28,9 @@ ** ****************************************************************************/ - #include "unixutils.h" +#include "fileutils.h" + #include <QSettings> #include <QFileInfo> #include <QCoreApplication> @@ -81,7 +82,7 @@ QString UnixUtils::substituteFileBrowserParameters(const QString &pre, const QSt } else if (c == QLatin1Char('f')) { s = QLatin1Char('"') + file + QLatin1Char('"'); } else if (c == QLatin1Char('n')) { - s = QLatin1Char('"') + QFileInfo(file).fileName() + QLatin1Char('"'); + s = QLatin1Char('"') + FileName::fromString(file).fileName() + QLatin1Char('"'); } else if (c == QLatin1Char('%')) { s = c; } else { diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri index f339efdb8a2..34b3549dc8e 100644 --- a/src/libs/utils/utils-lib.pri +++ b/src/libs/utils/utils-lib.pri @@ -49,7 +49,6 @@ SOURCES += $$PWD/environment.cpp \ $$PWD/checkablemessagebox.cpp \ $$PWD/styledbar.cpp \ $$PWD/stylehelper.cpp \ - $$PWD/iwelcomepage.cpp \ $$PWD/fancymainwindow.cpp \ $$PWD/detailsbutton.cpp \ $$PWD/detailswidget.cpp \ @@ -82,7 +81,6 @@ SOURCES += $$PWD/environment.cpp \ $$PWD/hostosinfo.cpp \ $$PWD/tooltip/tooltip.cpp \ $$PWD/tooltip/tips.cpp \ - $$PWD/tooltip/tipcontents.cpp \ $$PWD/unixutils.cpp \ $$PWD/ansiescapecodehandler.cpp \ $$PWD/execmenu.cpp \ @@ -93,7 +91,9 @@ SOURCES += $$PWD/environment.cpp \ $$PWD/treeviewcombobox.cpp \ $$PWD/proxycredentialsdialog.cpp \ $$PWD/macroexpander.cpp \ - $$PWD/theme/theme.cpp + $$PWD/theme/theme.cpp \ + $$PWD/progressindicator.cpp \ + $$PWD/fadingindicator.cpp win32:SOURCES += $$PWD/consoleprocess_win.cpp else:SOURCES += $$PWD/consoleprocess_unix.cpp @@ -139,7 +139,6 @@ HEADERS += \ $$PWD/qtcassert.h \ $$PWD/styledbar.h \ $$PWD/stylehelper.h \ - $$PWD/iwelcomepage.h \ $$PWD/fancymainwindow.h \ $$PWD/detailsbutton.h \ $$PWD/detailswidget.h \ @@ -175,7 +174,6 @@ HEADERS += \ $$PWD/elidinglabel.h \ $$PWD/tooltip/tooltip.h \ $$PWD/tooltip/tips.h \ - $$PWD/tooltip/tipcontents.h \ $$PWD/tooltip/reuse.h \ $$PWD/tooltip/effects.h \ $$PWD/unixutils.h \ @@ -192,7 +190,9 @@ HEADERS += \ $$PWD/proxycredentialsdialog.h \ $$PWD/macroexpander.h \ $$PWD/theme/theme.h \ - $$PWD/theme/theme_p.h + $$PWD/theme/theme_p.h \ + $$PWD/progressindicator.h \ + $$PWD/fadingindicator.h FORMS += $$PWD/filewizardpage.ui \ $$PWD/projectintropage.ui \ diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index 456dbd7dbc4..603a0e6e49f 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -72,6 +72,8 @@ QtcLibrary { "environmentmodel.h", "execmenu.cpp", "execmenu.h", + "fadingindicator.cpp", + "fadingindicator.h", "faketooltip.cpp", "faketooltip.h", "fancylineedit.cpp", @@ -101,8 +103,6 @@ QtcLibrary { "htmldocextractor.h", "itemviews.cpp", "itemviews.h", - "iwelcomepage.cpp", - "iwelcomepage.h", "json.cpp", "json.h", "linecolumnlabel.cpp", @@ -132,6 +132,8 @@ QtcLibrary { "persistentsettings.h", "portlist.cpp", "portlist.h", + "progressindicator.cpp", + "progressindicator.h", "projectintropage.cpp", "projectintropage.h", "projectintropage.ui", @@ -204,6 +206,10 @@ QtcLibrary { "images/crumblepath-segment-selected-end.png", "images/crumblepath-segment-selected.png", "images/crumblepath-segment.png", + "images/progressindicator_big.png", + "images/progressindicator_big@2x.png", + "images/progressindicator_small.png", + "images/progressindicator_small@2x.png", "images/triangle_vert.png", ] @@ -223,8 +229,6 @@ QtcLibrary { files: [ "effects.h", "reuse.h", - "tipcontents.cpp", - "tipcontents.h", "tips.cpp", "tips.h", "tooltip.cpp", diff --git a/src/libs/utils/utils.qrc b/src/libs/utils/utils.qrc index 2748bccb6c8..c7be7e8de8e 100644 --- a/src/libs/utils/utils.qrc +++ b/src/libs/utils/utils.qrc @@ -7,6 +7,12 @@ <file>images/crumblepath-segment-hover.png</file> <file>images/crumblepath-segment-selected-end.png</file> <file>images/crumblepath-segment-selected.png</file> + <file>images/progressindicator_big.png</file> + <file>images/progressindicator_big@2x.png</file> + <file>images/progressindicator_medium.png</file> + <file>images/progressindicator_medium@2x.png</file> + <file>images/progressindicator_small.png</file> + <file>images/progressindicator_small@2x.png</file> <file>images/triangle_vert.png</file> </qresource> </RCC> diff --git a/src/libs/utils/wizard.cpp b/src/libs/utils/wizard.cpp index a551825e37d..022ce467826 100644 --- a/src/libs/utils/wizard.cpp +++ b/src/libs/utils/wizard.cpp @@ -322,9 +322,9 @@ Wizard::Wizard(QWidget *parent, Qt::WindowFlags flags) : { d_ptr->q_ptr = this; d_ptr->m_wizardProgress = new WizardProgress(this); - connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(_q_currentPageChanged(int))); - connect(this, SIGNAL(pageAdded(int)), this, SLOT(_q_pageAdded(int))); - connect(this, SIGNAL(pageRemoved(int)), this, SLOT(_q_pageRemoved(int))); + connect(this, &QWizard::currentIdChanged, this, &Wizard::_q_currentPageChanged); + connect(this, &QWizard::pageAdded, this, &Wizard::_q_pageAdded); + connect(this, &QWizard::pageRemoved, this, &Wizard::_q_pageRemoved); setSideWidget(new LinearProgressWidget(d_ptr->m_wizardProgress, this)); setOption(QWizard::NoCancelButton, false); setOption(QWizard::NoDefaultButton, false); diff --git a/src/libs/utils/wizardpage.h b/src/libs/utils/wizardpage.h index 13a9871d302..5ff240339b4 100644 --- a/src/libs/utils/wizardpage.h +++ b/src/libs/utils/wizardpage.h @@ -52,6 +52,10 @@ public: void registerFieldWithName(const QString &name, QWidget *widget, const char *property = 0, const char *changedSignal = 0); +signals: + // Emitted when there is something that the developer using this page should be aware of. + void reportError(const QString &errorMessage); + private: QSet<QString> m_toRegister; }; |