From 1729c2c0c22e605058e119a38c0238f48be8788d Mon Sep 17 00:00:00 2001 From: Friedemann Kleint <Friedemann.Kleint@nokia.com> Date: Fri, 7 Aug 2009 15:20:05 +0200 Subject: [PATCH] Debugger: Improve the helper build mechanism on the settings page. - Give the controls a tooltip listing file details (date) - Make building a QtConcurrent task - Make log window scroll to bottom and pop up on error - Make the build code pass on error messages about copying the source files to the log file - Clean up the building code string-wise, use QLatin1String and translate messages, cache the icons. Use a QSharedPointer for the QtVersions in the settings page since they are passed to the BuildTask, which may outlive the dialog. --- .../projectexplorer/debugginghelper.cpp | 160 +++++++------ src/plugins/projectexplorer/debugginghelper.h | 4 +- .../qt4projectmanager/qtoptionspage.cpp | 224 ++++++++++++------ src/plugins/qt4projectmanager/qtoptionspage.h | 45 +++- .../qt4projectmanager/qtversionmanager.cpp | 6 +- .../qt4projectmanager/qtversionmanager.ui | 162 +++++++------ 6 files changed, 374 insertions(+), 227 deletions(-) diff --git a/src/plugins/projectexplorer/debugginghelper.cpp b/src/plugins/projectexplorer/debugginghelper.cpp index 8af5ba30456..63d499b6b1d 100644 --- a/src/plugins/projectexplorer/debugginghelper.cpp +++ b/src/plugins/projectexplorer/debugginghelper.cpp @@ -28,13 +28,15 @@ **************************************************************************/ #include "debugginghelper.h" + #include <coreplugin/icore.h> #include <QtCore/QFileInfo> +#include <QtCore/QCoreApplication> #include <QtCore/QHash> #include <QtCore/QProcess> #include <QtCore/QDir> #include <QtCore/QDateTime> -#include <QtGui/QApplication> + #include <QtGui/QDesktopServices> using namespace ProjectExplorer; @@ -44,7 +46,7 @@ QString DebuggingHelperLibrary::findSystemQt(const Environment &env) QStringList paths = env.path(); foreach (const QString &path, paths) { foreach (const QString &possibleCommand, possibleQMakeCommands()) { - QFileInfo qmake(path + "/" + possibleCommand); + const QFileInfo qmake(path + QLatin1Char('/') + possibleCommand); if (qmake.exists()) { if (!qtVersionForQMake(qmake.absoluteFilePath()).isNull()) { return qmake.absoluteFilePath(); @@ -62,12 +64,13 @@ bool DebuggingHelperLibrary::hasDebuggingHelperLibrary(const QString &qmakePath) QStringList DebuggingHelperLibrary::debuggingHelperLibraryDirectories(const QString &qtInstallData, const QString &qtpath) { - uint hash = qHash(qtpath); + const QChar slash = QLatin1Char('/'); + const uint hash = qHash(qtpath); QStringList directories; directories - << (qtInstallData + "/qtc-debugging-helper/") - << QDir::cleanPath((QApplication::applicationDirPath() + "/../qtc-debugging-helper/" + QString::number(hash))) + "/" - << (QDesktopServices::storageLocation(QDesktopServices::DataLocation) + "/qtc-debugging-helper/" + QString::number(hash)) + "/"; + << (qtInstallData + QLatin1String("/qtc-debugging-helper/")) + << QDir::cleanPath((QCoreApplication::applicationDirPath() + QLatin1String("/../qtc-debugging-helper/") + QString::number(hash))) + slash + << (QDesktopServices::storageLocation(QDesktopServices::DataLocation) + QLatin1String("/qtc-debugging-helper/") + QString::number(hash)) + slash; return directories; } @@ -84,7 +87,7 @@ QString DebuggingHelperLibrary::debuggingHelperLibrary(const QString &qmakePath) QString DebuggingHelperLibrary::qtInstallDataDir(const QString &qmakePath) { QProcess proc; - proc.start(qmakePath, QStringList() << "-query"<< "QT_INSTALL_DATA"); + proc.start(qmakePath, QStringList() << QLatin1String("-query") << QLatin1String("QT_INSTALL_DATA")); if (proc.waitForFinished()) return QString(proc.readAll().trimmed()); return QString::null; @@ -99,119 +102,142 @@ QString DebuggingHelperLibrary::qtDir(const QString &qmakePath) // Debugging Helper Library -QStringList DebuggingHelperLibrary::debuggingHelperLibraryLocations(const QString &qtInstallData, const QString &qtpath) +static inline QString helperFilePath(const QString &directory) { - QStringList result; - foreach(const QString &directory, debuggingHelperLibraryDirectories(qtInstallData, qtpath)) { #if defined(Q_OS_WIN) - QFileInfo fi(directory + "debug/gdbmacros.dll"); + return directory + QLatin1String("debug/gdbmacros.dll"); #elif defined(Q_OS_MAC) - QFileInfo fi(directory + "libgdbmacros.dylib"); + return directory + QLatin1String("libgdbmacros.dylib"); #else // generic UNIX - QFileInfo fi(directory + "libgdbmacros.so"); + return directory + QLatin1String("libgdbmacros.so"); #endif - result << fi.filePath(); - } +} + +QStringList DebuggingHelperLibrary::debuggingHelperLibraryLocations(const QString &qtInstallData, const QString &qtpath) +{ + QStringList result; + foreach(const QString &directory, debuggingHelperLibraryDirectories(qtInstallData, qtpath)) + result << QFileInfo(helperFilePath(directory)).filePath(); return result; } QString DebuggingHelperLibrary::debuggingHelperLibrary(const QString &qtInstallData, const QString &qtpath) { foreach(const QString &directory, debuggingHelperLibraryDirectories(qtInstallData, qtpath)) { -#if defined(Q_OS_WIN) - QFileInfo fi(directory + "debug/gdbmacros.dll"); -#elif defined(Q_OS_MAC) - QFileInfo fi(directory + "libgdbmacros.dylib"); -#else // generic UNIX - QFileInfo fi(directory + "libgdbmacros.so"); -#endif + const QFileInfo fi(helperFilePath(directory)); if (fi.exists()) return fi.filePath(); } return QString(); } - QString DebuggingHelperLibrary::buildDebuggingHelperLibrary(const QString &qmakePath, const QString &make, const Environment &env) { - QString directory = copyDebuggingHelperLibrary(qtInstallDataDir(qmakePath), qtDir(qmakePath)); + QString errorMessage; + const QString directory = copyDebuggingHelperLibrary(qtInstallDataDir(qmakePath), qtDir(qmakePath), &errorMessage); if (directory.isEmpty()) - return QString::null; + return errorMessage; return buildDebuggingHelperLibrary(directory, make, qmakePath, QString::null, env); } -QString DebuggingHelperLibrary::copyDebuggingHelperLibrary(const QString &qtInstallData, const QString &qtdir) +// Copy helper source files to a target directory, replacing older files. +static bool copyDebuggingHelperFiles(const QStringList &files, + const QString &targetDirectory, + QString *errorMessage) +{ + const QString dumperSourcePath = Core::ICore::instance()->resourcePath() + QLatin1String("/gdbmacros/"); + if (!QDir().mkpath(targetDirectory)) { + *errorMessage = QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "The target directory %1 could not be created.").arg(targetDirectory); + return false; + } + foreach (const QString &file, files) { + const QString source = dumperSourcePath + file; + const QString dest = targetDirectory + file; + const QFileInfo destInfo(dest); + if (destInfo.exists()) { + if (destInfo.lastModified() >= QFileInfo(source).lastModified()) + continue; + if (!QFile::remove(dest)) { + *errorMessage = QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "The existing file %1 could not be removed.").arg(destInfo.absoluteFilePath()); + return false; + } + } + if (!QFile::copy(source, dest)) { + *errorMessage = QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "The file %1 could not be copied to %2.").arg(source, dest); + return false; + } + } + return true; +} + +QString DebuggingHelperLibrary::copyDebuggingHelperLibrary(const QString &qtInstallData, + const QString &qtdir, + QString *errorMessage) { // Locations to try: // $QTDIR/qtc-debugging-helper // $APPLICATION-DIR/qtc-debugging-helper/$hash // $USERDIR/qtc-debugging-helper/$hash - QStringList directories = DebuggingHelperLibrary::debuggingHelperLibraryDirectories(qtInstallData, qtdir); + const QStringList directories = DebuggingHelperLibrary::debuggingHelperLibraryDirectories(qtInstallData, qtdir); QStringList files; - files << "gdbmacros.cpp" << "gdbmacros.pro" - << "LICENSE.LGPL" << "LGPL_EXCEPTION.TXT"; - foreach(const QString &directory, directories) { - QString dumperPath = Core::ICore::instance()->resourcePath() + "/gdbmacros/"; - bool success = true; - QDir().mkpath(directory); - foreach (const QString &file, files) { - QString source = dumperPath + file; - QString dest = directory + file; - QFileInfo destInfo(dest); - if (destInfo.exists()) { - if (destInfo.lastModified() >= QFileInfo(source).lastModified()) - continue; - success &= QFile::remove(dest); - } - success &= QFile::copy(source, dest); - } - if (success) + files << QLatin1String("gdbmacros.cpp") << QLatin1String("gdbmacros.h") << QLatin1String("gdbmacros.pro") + << QLatin1String("LICENSE.LGPL") << QLatin1String("LGPL_EXCEPTION.TXT"); + // Try to find a writeable directory. + foreach(const QString &directory, directories) + if (copyDebuggingHelperFiles(files, directory, errorMessage)) { + errorMessage->clear(); return directory; - } - return QString::null; + } + *errorMessage = QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "The debugger helpers could not be built in any of the directories:\n- %1\n\nReason: %2") + .arg(directories.join(QLatin1String("\n- ")), *errorMessage); + return QString(); } QString DebuggingHelperLibrary::buildDebuggingHelperLibrary(const QString &directory, const QString &makeCommand, const QString &qmakeCommand, const QString &mkspec, const Environment &env) { QString output; + const QChar newline = QLatin1Char('\n'); // Setup process QProcess proc; proc.setEnvironment(env.toStringList()); proc.setWorkingDirectory(directory); proc.setProcessChannelMode(QProcess::MergedChannels); - output += QString("Building debugging helper library in %1\n").arg(directory); - output += "\n"; + output += QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "Building debugging helper library in %1\n").arg(directory); + output += newline; - QString makeFullPath = env.searchInPath(makeCommand); - if (QFileInfo(directory + "/Makefile").exists()) { + const QString makeFullPath = env.searchInPath(makeCommand); + if (QFileInfo(directory + QLatin1String("/Makefile")).exists()) { if (!makeFullPath.isEmpty()) { - output += QString("Running %1 clean...\n").arg(makeFullPath); - proc.start(makeFullPath, QStringList() << "clean"); + const QString cleanTarget = QLatin1String("distclean"); + output += QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "Running %1 %2...\n").arg(makeFullPath, cleanTarget); + proc.start(makeFullPath, QStringList(cleanTarget)); proc.waitForFinished(); - output += proc.readAll(); + output += QString::fromLocal8Bit(proc.readAll()); } else { - output += QString("%1 not found in PATH\n").arg(makeCommand); + output += QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "%1 not found in PATH\n").arg(makeCommand); return output; } } + output += newline; + output += QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "Running %1 ...\n").arg(qmakeCommand); - output += QString("\nRunning %1 ...\n").arg(qmakeCommand); - - proc.start(qmakeCommand, QStringList()<<"-spec"<< (mkspec.isEmpty() ? "default" : mkspec) <<"gdbmacros.pro"); + QStringList makeArgs; + makeArgs << QLatin1String("-spec")<< (mkspec.isEmpty() ? QString(QLatin1String("default")) : mkspec) << QLatin1String("gdbmacros.pro"); + proc.start(qmakeCommand, makeArgs); proc.waitForFinished(); output += proc.readAll(); - output += "\n"; + output += newline;; if (!makeFullPath.isEmpty()) { - output += QString("Running %1 ...\n").arg(makeFullPath); + output += QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "Running %1 ...\n").arg(makeFullPath); proc.start(makeFullPath, QStringList()); proc.waitForFinished(); output += proc.readAll(); } else { - output += QString("%1 not found in PATH\n").arg(makeCommand); + output += QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "%1 not found in PATH\n").arg(makeCommand); } return output; } @@ -219,14 +245,14 @@ QString DebuggingHelperLibrary::buildDebuggingHelperLibrary(const QString &direc QString DebuggingHelperLibrary::qtVersionForQMake(const QString &qmakePath) { QProcess qmake; - qmake.start(qmakePath, QStringList()<<"--version"); + qmake.start(qmakePath, QStringList(QLatin1String("--version"))); if (!qmake.waitForFinished()) return false; QString output = qmake.readAllStandardOutput(); - QRegExp regexp("(QMake version|QMake version:)[\\s]*([\\d.]*)", Qt::CaseInsensitive); + QRegExp regexp(QLatin1String("(QMake version|QMake version:)[\\s]*([\\d.]*)"), Qt::CaseInsensitive); regexp.indexIn(output); - if (regexp.cap(2).startsWith("2.")) { - QRegExp regexp2("Using Qt version[\\s]*([\\d\\.]*)", Qt::CaseInsensitive); + if (regexp.cap(2).startsWith(QLatin1String("2."))) { + QRegExp regexp2(QLatin1String("Using Qt version[\\s]*([\\d\\.]*)"), Qt::CaseInsensitive); regexp2.indexIn(output); return regexp2.cap(1); } @@ -237,11 +263,11 @@ QStringList DebuggingHelperLibrary::possibleQMakeCommands() { // On windows noone has renamed qmake, right? #ifdef Q_OS_WIN - return QStringList() << "qmake.exe"; + return QStringList(QLatin1String("qmake.exe")); #else // On unix some distributions renamed qmake to avoid clashes QStringList result; - result << "qmake-qt4" << "qmake4" << "qmake"; + result << QLatin1String("qmake-qt4") << QLatin1String("qmake4") << QLatin1String("qmake"); return result; #endif } diff --git a/src/plugins/projectexplorer/debugginghelper.h b/src/plugins/projectexplorer/debugginghelper.h index a98ab2a6166..47727d1c466 100644 --- a/src/plugins/projectexplorer/debugginghelper.h +++ b/src/plugins/projectexplorer/debugginghelper.h @@ -56,10 +56,12 @@ public: static QString buildDebuggingHelperLibrary(const QString &qmakePath, const QString &make, const Environment &env); static QString buildDebuggingHelperLibrary(const QString &directory, const QString &makeCommand, const QString &qmakeCommand, const QString &mkspec, const Environment &env); + // Build the helpers and return the output log/errormessage. static QStringList debuggingHelperLibraryLocations(const QString &qmakePath); static QStringList debuggingHelperLibraryLocations(const QString &qtInstallData, const QString &qtpath); - static QString copyDebuggingHelperLibrary(const QString &qtInstallData, const QString &qtdir); + // Copy the source files to a target location and return the chosen target location. + static QString copyDebuggingHelperLibrary(const QString &qtInstallData, const QString &qtdir, QString *errorMessage); private: static QStringList debuggingHelperLibraryDirectories(const QString &qtInstallData, const QString &qtpath); diff --git a/src/plugins/qt4projectmanager/qtoptionspage.cpp b/src/plugins/qt4projectmanager/qtoptionspage.cpp index 6c5c4c4a9cc..b695c6cde9f 100644 --- a/src/plugins/qt4projectmanager/qtoptionspage.cpp +++ b/src/plugins/qt4projectmanager/qtoptionspage.cpp @@ -3,14 +3,42 @@ #include "ui_qtversionmanager.h" #include "qt4projectmanagerconstants.h" #include "qtversionmanager.h" + #include <coreplugin/coreconstants.h> +#include <coreplugin/icore.h> +#include <coreplugin/progressmanager/progressmanager.h> #include <utils/treewidgetcolumnstretcher.h> +#include <utils/qtcassert.h> +#include <QtCore/QFuture> +#include <QtCore/QtConcurrentRun> #include <QtCore/QDebug> #include <QtCore/QDir> +#include <QtCore/QDateTime> using namespace Qt4ProjectManager; using namespace Qt4ProjectManager::Internal; + +/// +// DebuggingHelperBuildTask +/// + +DebuggingHelperBuildTask::DebuggingHelperBuildTask(const QSharedPointerQtVersion &version) : + m_version(version) +{ +} + +DebuggingHelperBuildTask::~DebuggingHelperBuildTask() +{ +} + +void DebuggingHelperBuildTask::run() +{ + const QString output = m_version->buildDebuggingHelperLibrary(); + emit finished(m_version->name(), output); + deleteLater(); +} + /// // QtOptionsPage /// @@ -57,7 +85,11 @@ void QtOptionsPage::apply() m_widget->finish(); QtVersionManager *vm = QtVersionManager::instance(); - vm->setNewQtVersions(m_widget->versions(), m_widget->defaultVersion()); + // Turn into flat list + QList<QtVersion *> versions; + foreach(const QSharedPointerQtVersion &spv, m_widget->versions()) + versions.push_back(new QtVersion(*spv)); + vm->setNewQtVersions(versions, m_widget->defaultVersion()); } //----------------------------------------------------- @@ -65,17 +97,19 @@ void QtOptionsPage::apply() QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent, QList<QtVersion *> versions, QtVersion *defaultVersion) : QWidget(parent) - , m_defaultVersion(versions.indexOf(defaultVersion)) + , m_debuggingHelperOkPixmap(QLatin1String(":/extensionsystem/images/ok.png")) + , m_debuggingHelperErrorPixmap(QLatin1String(":/extensionsystem/images/error.png")) + , m_debuggingHelperOkIcon(m_debuggingHelperOkPixmap) + , m_debuggingHelperErrorIcon(m_debuggingHelperErrorPixmap) , m_specifyNameString(tr("<specify a name>")) , m_specifyPathString(tr("<specify a path>")) + , m_ui(new Internal::Ui::QtVersionManager()) + , m_defaultVersion(versions.indexOf(defaultVersion)) { // Initialize m_versions - foreach(QtVersion *version, versions) { - m_versions.append(new QtVersion(*version)); - } + foreach(QtVersion *version, versions) + m_versions.push_back(QSharedPointerQtVersion(new QtVersion(*version))); - - m_ui = new Internal::Ui::QtVersionManager(); m_ui->setupUi(this); m_ui->qtPath->setExpectedKind(Core::Utils::PathChooser::Directory); m_ui->qtPath->setPromptDialogTitle(tr("Select QTDIR")); @@ -99,17 +133,14 @@ QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent, QList<QtVersion *> ver manualItem->setFirstColumnSpanned(true); for (int i = 0; i < m_versions.count(); ++i) { - const QtVersion * const version = m_versions.at(i); + const QtVersion * const version = m_versions.at(i).data(); QTreeWidgetItem *item = new QTreeWidgetItem(version->isAutodetected()? autoItem : manualItem); item->setText(0, version->name()); item->setText(1, QDir::toNativeSeparators(version->path())); item->setData(0, Qt::UserRole, version->uniqueId()); if (version->isValid()) { - if (version->hasDebuggingHelper()) - item->setData(2, Qt::DecorationRole, QIcon(":/extensionsystem/images/ok.png")); - else - item->setData(2, Qt::DecorationRole, QIcon(":/extensionsystem/images/error.png")); + item->setData(2, Qt::DecorationRole, version->hasDebuggingHelper() ? m_debuggingHelperOkIcon : m_debuggingHelperErrorIcon); } else { item->setData(2, Qt::DecorationRole, QIcon()); } @@ -160,27 +191,68 @@ QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent, QList<QtVersion *> ver updateState(); } -void QtOptionsPageWidget::buildDebuggingHelper() +int QtOptionsPageWidget::currentIndex() const { - // Find the qt version for this button.. - QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem(); - int currentItemIndex = indexForTreeItem(currentItem); - if (currentItemIndex < 0) - return; + if (QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem()) + return indexForTreeItem(currentItem); + return -1; +} - QtVersion *version = m_versions[currentItemIndex]; +QtVersion *QtOptionsPageWidget::currentVersion() const +{ + const int currentItemIndex = currentIndex(); + if (currentItemIndex >= 0 && currentItemIndex < m_versions.size()) + return m_versions.at(currentItemIndex).data(); + return 0; +} - QString result = m_versions.at(currentItemIndex)->buildDebuggingHelperLibrary(); - currentItem->setData(2, Qt::UserRole, result); +static inline int findVersionByName(const QList<QSharedPointerQtVersion> &l, const QString &name) +{ + const int size = l.size(); + for (int i = 0; i < size; i++) + if (l.at(i)->name() == name) + return i; + return -1; +} - if (version->hasDebuggingHelper()) { - m_ui->debuggingHelperStateLabel->setPixmap(QPixmap(":/extensionsystem/images/ok.png")); - currentItem->setData(2, Qt::DecorationRole, QIcon(":/extensionsystem/images/ok.png")); - } else { - m_ui->debuggingHelperStateLabel->setPixmap(QPixmap(":/extensionsystem/images/error.png")); - currentItem->setData(2, Qt::DecorationRole, QIcon(":/extensionsystem/images/error.png")); +// Update with results of terminated helper build +void QtOptionsPageWidget::debuggingHelperBuildFinished(const QString &name, const QString &output) +{ + const int index = findVersionByName(m_versions, name); + if (index == -1) + return; // Oops, somebody managed to delete the version + // Update item view + QTreeWidgetItem *item = treeItemForIndex(index); + QTC_ASSERT(item, return) + item->setData(2, Qt::UserRole, output); + const bool success = m_versions.at(index)->hasDebuggingHelper(); + item->setData(2, Qt::DecorationRole, success ? m_debuggingHelperOkIcon : m_debuggingHelperErrorIcon); + + // Update bottom control if the selection is still the same + if (index == currentIndex()) { + m_ui->showLogButton->setEnabled(true); + updateDebuggingHelperStateLabel(m_versions.at(index).data()); + if (!success) + showDebuggingBuildLog(); } - m_ui->showLogButton->setEnabled(true); +} + +void QtOptionsPageWidget::buildDebuggingHelper() +{ + const int index = currentIndex(); + if (index < 0) + return; + + m_ui->showLogButton->setEnabled(false); + // Run a debugging helper build task in the background. + DebuggingHelperBuildTask *buildTask = new DebuggingHelperBuildTask(m_versions.at(index)); + connect(buildTask, SIGNAL(finished(QString,QString)), this, SLOT(debuggingHelperBuildFinished(QString,QString)), + Qt::QueuedConnection); + QFuture<void> task = QtConcurrent::run(buildTask, &DebuggingHelperBuildTask::run); + const QString taskName = tr("Building helpers"); + Core::ICore::instance()->progressManager()->addTask(task, taskName, + QLatin1String("Qt4ProjectManager::BuildHelpers"), + Core::ProgressManager::CloseOnSuccess); } void QtOptionsPageWidget::showDebuggingBuildLog() @@ -190,22 +262,24 @@ void QtOptionsPageWidget::showDebuggingBuildLog() int currentItemIndex = indexForTreeItem(currentItem); if (currentItemIndex < 0) return; + // Show and scroll to bottom QDialog dlg; Ui_ShowBuildLog ui; ui.setupUi(&dlg); ui.log->setPlainText(currentItem->data(2, Qt::UserRole).toString()); + ui.log->moveCursor(QTextCursor::End); + ui.log->ensureCursorVisible(); dlg.exec(); } QtOptionsPageWidget::~QtOptionsPageWidget() { - qDeleteAll(m_versions); delete m_ui; } void QtOptionsPageWidget::addQtDir() { - QtVersion *newVersion = new QtVersion(m_specifyNameString, m_specifyPathString); + QSharedPointerQtVersion newVersion(new QtVersion(m_specifyNameString, m_specifyPathString)); m_versions.append(newVersion); QTreeWidgetItem *item = new QTreeWidgetItem(m_ui->qtdirList->topLevelItem(1)); @@ -239,38 +313,57 @@ void QtOptionsPageWidget::removeQtDir() delete item; - delete m_versions.takeAt(index); + m_versions.removeAt(index); updateState(); } +// Format html table tooltip about helpers +static inline QString msgHtmlHelperToolTip(const QFileInfo &fi) +{ + return QtOptionsPageWidget::tr("<html><body><table><tr><td>File:</td><td><pre>%1</pre></td></tr>" + "<tr><td>Last modified:</td><td>%2</td></tr>" + "<tr><td>Size:</td><td>%3 Bytes</td></tr></table></body></html>"). + arg(fi.absoluteFilePath()). + arg(fi.lastModified().toString(Qt::SystemLocaleLongDate)). + arg(fi.size()); +} + +// Update the state label with a pixmap and set a tooltip describing +// the file on neighbouring controls. +void QtOptionsPageWidget::updateDebuggingHelperStateLabel(const QtVersion *version) +{ + QString tooltip; + if (version && version->isValid()) { + const bool hasHelper = version->hasDebuggingHelper(); + m_ui->debuggingHelperStateLabel->setPixmap(hasHelper ? m_debuggingHelperOkPixmap : m_debuggingHelperErrorPixmap); + if (hasHelper) + tooltip = msgHtmlHelperToolTip(QFileInfo(version->debuggingHelperLibrary())); + } else { + m_ui->debuggingHelperStateLabel->setPixmap(QPixmap()); + } + m_ui->debuggingHelperStateLabel->setToolTip(tooltip); + m_ui->debuggingHelperLabel->setToolTip(tooltip); + m_ui->showLogButton->setToolTip(tooltip); + m_ui->rebuildButton->setToolTip(tooltip); +} + void QtOptionsPageWidget::updateState() { - int currentIndex = indexForTreeItem(m_ui->qtdirList->currentItem()); - bool enabled = (currentIndex >= 0); - bool isAutodetected = (enabled - && m_versions.at(currentIndex)->isAutodetected()); + const QtVersion *version = currentVersion(); + const bool enabled = version != 0; + const bool isAutodetected = enabled && version->isAutodetected(); m_ui->delButton->setEnabled(enabled && !isAutodetected); m_ui->nameEdit->setEnabled(enabled && !isAutodetected); m_ui->qtPath->setEnabled(enabled && !isAutodetected); m_ui->mingwPath->setEnabled(enabled); - bool hasLog = enabled && !m_ui->qtdirList->currentItem()->data(2, Qt::UserRole).toString().isEmpty(); + const bool hasLog = enabled && !m_ui->qtdirList->currentItem()->data(2, Qt::UserRole).toString().isEmpty(); m_ui->showLogButton->setEnabled(hasLog); - QtVersion *version = 0; - if (enabled) - version = m_versions.at(currentIndex); - if (version) { - m_ui->rebuildButton->setEnabled(version->isValid()); - if (version->hasDebuggingHelper()) - m_ui->debuggingHelperStateLabel->setPixmap(QPixmap(":/extensionsystem/images/ok.png")); - else - m_ui->debuggingHelperStateLabel->setPixmap(QPixmap(":/extensionsystem/images/error.png")); - } else { - m_ui->rebuildButton->setEnabled(false); - m_ui->debuggingHelperStateLabel->setPixmap(QPixmap()); - } + m_ui->rebuildButton->setEnabled(version && version->isValid()); + updateDebuggingHelperStateLabel(version); } + void QtOptionsPageWidget::makeMingwVisible(bool visible) { m_ui->mingwLabel->setVisible(visible); @@ -358,11 +451,11 @@ void QtOptionsPageWidget::showEnvironmentPage(QTreeWidgetItem *item) } } -int QtOptionsPageWidget::indexForTreeItem(QTreeWidgetItem *item) const +int QtOptionsPageWidget::indexForTreeItem(const QTreeWidgetItem *item) const { if (!item || !item->parent()) return -1; - int uniqueId = item->data(0, Qt::UserRole).toInt(); + const int uniqueId = item->data(0, Qt::UserRole).toInt(); for (int index = 0; index < m_versions.size(); ++index) { if (m_versions.at(index)->uniqueId() == uniqueId) return index; @@ -372,7 +465,7 @@ int QtOptionsPageWidget::indexForTreeItem(QTreeWidgetItem *item) const QTreeWidgetItem *QtOptionsPageWidget::treeItemForIndex(int index) const { - int uniqueId = m_versions.at(index)->uniqueId(); + const int uniqueId = m_versions.at(index)->uniqueId(); for (int i = 0; i < m_ui->qtdirList->topLevelItemCount(); ++i) { QTreeWidgetItem *toplevelItem = m_ui->qtdirList->topLevelItem(i); for (int j = 0; j < toplevelItem->childCount(); ++j) { @@ -396,7 +489,7 @@ void QtOptionsPageWidget::versionChanged(QTreeWidgetItem *item, QTreeWidgetItem m_ui->qtPath->setPath(item->text(1)); } else { m_ui->nameEdit->clear(); - m_ui->qtPath->setPath(""); // clear() + m_ui->qtPath->setPath(QString()); // clear() } showEnvironmentPage(item); updateState(); @@ -500,30 +593,23 @@ void QtOptionsPageWidget::updateCurrentQtPath() int currentItemIndex = indexForTreeItem(currentItem); if (currentItemIndex < 0) return; - if (m_versions[currentItemIndex]->path() == m_ui->qtPath->path()) + QtVersion *version = m_versions.at(currentItemIndex).data(); + if (version->path() == m_ui->qtPath->path()) return; - m_versions[currentItemIndex]->setPath(m_ui->qtPath->path()); - currentItem->setText(1, QDir::toNativeSeparators(m_versions[currentItemIndex]->path())); - + version->setPath(m_ui->qtPath->path()); + currentItem->setText(1, QDir::toNativeSeparators(version->path())); showEnvironmentPage(currentItem); - if (m_versions[currentItemIndex]->isValid()) { - bool hasLog = !currentItem->data(2, Qt::UserRole).toString().isEmpty(); - bool hasHelper = m_versions[currentItemIndex]->hasDebuggingHelper(); - if (hasHelper) { - currentItem->setData(2, Qt::DecorationRole, QIcon(":/extensionsystem/images/ok.png")); - m_ui->debuggingHelperStateLabel->setPixmap(QPixmap(":/extensionsystem/images/ok.png")); - } else { - currentItem->setData(2, Qt::DecorationRole, QIcon(":/extensionsystem/images/error.png")); - m_ui->debuggingHelperStateLabel->setPixmap(QPixmap(":/extensionsystem/images/error.png")); - } + if (version->isValid()) { + const bool hasLog = !currentItem->data(2, Qt::UserRole).toString().isEmpty(); + currentItem->setData(2, Qt::DecorationRole, version->hasDebuggingHelper() ? m_debuggingHelperOkIcon : m_debuggingHelperErrorIcon); m_ui->showLogButton->setEnabled(hasLog); m_ui->rebuildButton->setEnabled(true); } else { currentItem->setData(2, Qt::DecorationRole, QIcon()); - m_ui->debuggingHelperStateLabel->setPixmap(QPixmap()); m_ui->rebuildButton->setEnabled(true); } + updateDebuggingHelperStateLabel(version); } void QtOptionsPageWidget::updateCurrentMingwDirectory() @@ -559,7 +645,7 @@ void QtOptionsPageWidget::updateCurrentMwcDirectory() } #endif -QList<QtVersion *> QtOptionsPageWidget::versions() const +QList<QSharedPointerQtVersion> QtOptionsPageWidget::versions() const { return m_versions; } diff --git a/src/plugins/qt4projectmanager/qtoptionspage.h b/src/plugins/qt4projectmanager/qtoptionspage.h index 57275795fe6..32cc9472bcb 100644 --- a/src/plugins/qt4projectmanager/qtoptionspage.h +++ b/src/plugins/qt4projectmanager/qtoptionspage.h @@ -31,7 +31,11 @@ #include <coreplugin/dialogs/ioptionspage.h> +#include <QtCore/QSharedPointer> + #include <QtGui/QWidget> +#include <QtGui/QPixmap> +#include <QtGui/QIcon> QT_BEGIN_NAMESPACE class QTreeWidgetItem; @@ -40,34 +44,62 @@ QT_END_NAMESPACE namespace Qt4ProjectManager { class QtVersion; +typedef QSharedPointer<QtVersion> QSharedPointerQtVersion; namespace Internal { namespace Ui { class QtVersionManager; } +// A task suitable to be run by QtConcurrent to build the helpers. +// Note that it may outlive the settings page if someone quickly cancels it, +// so, the versions are passed around by QSharedPointer. +class DebuggingHelperBuildTask : public QObject { + Q_DISABLE_COPY(DebuggingHelperBuildTask) + Q_OBJECT +public: + explicit DebuggingHelperBuildTask(const QSharedPointerQtVersion &version); + virtual ~DebuggingHelperBuildTask(); + + void run(); + +signals: + void finished(const QString &versionName, const QString &output); + +private: + QSharedPointerQtVersion m_version; +}; + class QtOptionsPageWidget : public QWidget { Q_OBJECT + Q_DISABLE_COPY(QtOptionsPageWidget) public: QtOptionsPageWidget(QWidget *parent, QList<QtVersion *> versions, QtVersion *defaultVersion); ~QtOptionsPageWidget(); - QList<QtVersion *> versions() const; + QList<QSharedPointerQtVersion> versions() const; int defaultVersion() const; void finish(); private: void showEnvironmentPage(QTreeWidgetItem * item); void fixQtVersionName(int index); - int indexForWidget(QWidget *debuggingHelperWidget) const; - int indexForTreeItem(QTreeWidgetItem *item) const; + int indexForTreeItem(const QTreeWidgetItem *item) const; QTreeWidgetItem *treeItemForIndex(int index) const; + QtVersion *currentVersion() const; + int currentIndex() const; + void updateDebuggingHelperStateLabel(const QtVersion *version = 0); + + const QPixmap m_debuggingHelperOkPixmap; + const QPixmap m_debuggingHelperErrorPixmap; + const QIcon m_debuggingHelperOkIcon; + const QIcon m_debuggingHelperErrorIcon; + const QString m_specifyNameString; + const QString m_specifyPathString; Internal::Ui::QtVersionManager *m_ui; - QList<QtVersion *> m_versions; + QList<QSharedPointerQtVersion> m_versions; // Passed on to the helper build task, so, use QSharedPointerQtVersion int m_defaultVersion; - QString m_specifyNameString; - QString m_specifyPathString; private slots: void versionChanged(QTreeWidgetItem *item, QTreeWidgetItem *old); @@ -89,6 +121,7 @@ private slots: void msvcVersionChanged(); void buildDebuggingHelper(); void showDebuggingBuildLog(); + void debuggingHelperBuildFinished(const QString &versionName, const QString &output); }; class QtOptionsPage : public Core::IOptionsPage diff --git a/src/plugins/qt4projectmanager/qtversionmanager.cpp b/src/plugins/qt4projectmanager/qtversionmanager.cpp index a0680c44589..c5ce9097337 100644 --- a/src/plugins/qt4projectmanager/qtversionmanager.cpp +++ b/src/plugins/qt4projectmanager/qtversionmanager.cpp @@ -1266,8 +1266,10 @@ QString QtVersion::buildDebuggingHelperLibrary() // TODO: the debugging helper doesn't comply to actual tool chain yet ProjectExplorer::ToolChain *tc = createToolChain(defaultToolchainType()); tc->addToEnvironment(env); - QString directory = DebuggingHelperLibrary::copyDebuggingHelperLibrary(qtInstallData, path()); - QString output = DebuggingHelperLibrary::buildDebuggingHelperLibrary(directory, tc->makeCommand(), qmakeCommand(), mkspec(), env); + QString output; + QString directory = DebuggingHelperLibrary::copyDebuggingHelperLibrary(qtInstallData, path(), &output); + if (!directory.isEmpty()) + output += DebuggingHelperLibrary::buildDebuggingHelperLibrary(directory, tc->makeCommand(), qmakeCommand(), mkspec(), env); m_hasDebuggingHelper = !debuggingHelperLibrary().isEmpty(); delete tc; return output; diff --git a/src/plugins/qt4projectmanager/qtversionmanager.ui b/src/plugins/qt4projectmanager/qtversionmanager.ui index b92932ece62..bc716ab3cd8 100644 --- a/src/plugins/qt4projectmanager/qtversionmanager.ui +++ b/src/plugins/qt4projectmanager/qtversionmanager.ui @@ -17,31 +17,6 @@ <string>Qt versions</string> </property> <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0" colspan="2"> - <widget class="QTreeWidget" name="qtdirList"> - <property name="uniformRowHeights"> - <bool>true</bool> - </property> - <property name="columnCount"> - <number>3</number> - </property> - <column> - <property name="text"> - <string>Name</string> - </property> - </column> - <column> - <property name="text"> - <string>Path</string> - </property> - </column> - <column> - <property name="text"> - <string>Debugging Helper</string> - </property> - </column> - </widget> - </item> <item row="0" column="2"> <layout class="QVBoxLayout"> <property name="spacing"> @@ -91,43 +66,6 @@ </item> </layout> </item> - <item row="1" column="0"> - <widget class="QLabel" name="versionNameLabel"> - <property name="text"> - <string>Version Name:</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="nameEdit"/> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="pathLabel"> - <property name="text"> - <string>Path:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="Core::Utils::PathChooser" name="qtPath" native="true"/> - </item> - <item row="3" column="0"> - <widget class="QLabel" name="mingwLabel"> - <property name="text"> - <string>MinGw Directory:</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="Core::Utils::PathChooser" name="mingwPath" native="true"/> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="msvcLabel"> - <property name="text"> - <string>MSVC Version:</string> - </property> - </widget> - </item> <item row="4" column="1"> <layout class="QHBoxLayout" name="horizontalLayout"> <property name="spacing"> @@ -162,23 +100,6 @@ p, li { white-space: pre-wrap; } </item> </layout> </item> - <item row="5" column="0"> - <widget class="QLabel" name="mwcLabel"> - <property name="text"> - <string>MWC Directory:</string> - </property> - </widget> - </item> - <item row="5" column="1"> - <widget class="Core::Utils::PathChooser" name="mwcPath" native="true"/> - </item> - <item row="6" column="0"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Debugging Helper:</string> - </property> - </widget> - </item> <item row="6" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> @@ -210,6 +131,75 @@ p, li { white-space: pre-wrap; } </item> </layout> </item> + <item row="0" column="0" colspan="2"> + <widget class="QTreeWidget" name="qtdirList"> + <property name="uniformRowHeights"> + <bool>true</bool> + </property> + <property name="columnCount"> + <number>3</number> + </property> + <column> + <property name="text"> + <string>Name</string> + </property> + </column> + <column> + <property name="text"> + <string>Path</string> + </property> + </column> + <column> + <property name="text"> + <string>Debugging Helper</string> + </property> + </column> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="versionNameLabel"> + <property name="text"> + <string>Version Name:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="nameEdit"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="pathLabel"> + <property name="text"> + <string>Path:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="Core::Utils::PathChooser" name="qtPath"/> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="mingwLabel"> + <property name="text"> + <string>MinGw Directory:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="Core::Utils::PathChooser" name="mingwPath"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="msvcLabel"> + <property name="text"> + <string>MSVC Version:</string> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="debuggingHelperLabel"> + <property name="text"> + <string>Debugging Helper:</string> + </property> + </widget> + </item> <item row="7" column="1"> <widget class="QLabel" name="errorLabel"> <property name="text"> @@ -217,6 +207,16 @@ p, li { white-space: pre-wrap; } </property> </widget> </item> + <item row="5" column="0"> + <widget class="QLabel" name="mwcLabel"> + <property name="text"> + <string>MWC Directory:</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="Core::Utils::PathChooser" name="mwcPath"/> + </item> </layout> <zorder>qtdirList</zorder> <zorder>versionNameLabel</zorder> @@ -226,12 +226,10 @@ p, li { white-space: pre-wrap; } <zorder>mingwLabel</zorder> <zorder>mingwPath</zorder> <zorder>msvcLabel</zorder> - <zorder>label</zorder> + <zorder>debuggingHelperLabel</zorder> <zorder>errorLabel</zorder> <zorder>mwcLabel</zorder> <zorder>mwcPath</zorder> - <zorder>msvcComboBox</zorder> - <zorder>msvcNotFoundLabel</zorder> </widget> </item> <item> -- GitLab