diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index 16548f9146e916b8040b78e47ec350c5e732f954..2c4db8eabc00f049d6ac60857787f7a6d6b2b613 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -39,6 +39,7 @@ #include "cdboptionspage.h" #include "cdboptions.h" #include "cdbexceptionutils.h" +#include "cdbsymbolpathlisteditor.h" #include "debuggeragents.h" #include "debuggeruiswitcher.h" #include "debuggermainwindow.h" @@ -59,6 +60,7 @@ #include <utils/fancymainwindow.h> #include <texteditor/itexteditor.h> #include <utils/savedaction.h> +#include <utils/checkablemessagebox.h> #include <QtCore/QDebug> #include <QtCore/QTimer> @@ -347,11 +349,48 @@ void CdbDebugEnginePrivate::checkVersion() } } +void CdbDebugEngine::startupChecks() +{ + // Check symbol server unless the user has an external/internal setup + if (!qgetenv("_NT_SYMBOL_PATH").isEmpty() + || CdbOptions::indexOfSymbolServerPath(m_d->m_options->symbolPaths) != -1) + return; + // Prompt to use Symbol server unless the user checked "No nagging". + Core::ICore *core = Core::ICore::instance(); + const QString nagSymbolServerKey = CdbOptions::settingsGroup() + QLatin1String("/NoPromptSymbolServer"); + bool noFurtherNagging = core->settings()->value(nagSymbolServerKey, false).toBool(); + if (noFurtherNagging) + return; + + const QString symServUrl = QLatin1String("http://support.microsoft.com/kb/311503"); + const QString msg = tr("<html><head/><body><p>The debugger is not configured to use the public " + "<a href=\"%1\">Microsoft Symbol Server</a>. This is recommended " + "for retrieval of the symbols of the operating system libraries.</p>" + "<p><i>Note:</i> A fast internet connection is required for this to work smoothly. Also, a delay " + "might occur when connecting for the first time.</p>" + "<p>Would you like to set it up?</p></br>" + "</body></html>").arg(symServUrl); + const QDialogButtonBox::StandardButton answer = + Utils::CheckableMessageBox::question(core->mainWindow(), tr("Symbol Server"), msg, + tr("Do not ask again"), &noFurtherNagging); + core->settings()->setValue(nagSymbolServerKey, noFurtherNagging); + if (answer == QDialogButtonBox::No) + return; + // Prompt for path and add it. Synchronize QSetting and debugger. + const QString cacheDir = CdbSymbolPathListEditor::promptCacheDirectory(core->mainWindow()); + if (cacheDir.isEmpty()) + return; + m_d->m_options->symbolPaths.push_back(CdbOptions::symbolServerPath(cacheDir)); + m_d->m_options->toSettings(core->settings()); + syncDebuggerPaths(); +} + void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> &sp) { if (debugCDBExecution) qDebug() << "startDebugger" << *sp; CdbCore::BreakPoint::clearNormalizeFileNameCache(); + startupChecks(); setState(AdapterStarting, Q_FUNC_INFO, __LINE__); m_d->checkVersion(); if (m_d->m_hDebuggeeProcess) { diff --git a/src/plugins/debugger/cdb/cdbdebugengine.h b/src/plugins/debugger/cdb/cdbdebugengine.h index 452f8df87ff000ed52260aa87319598f98fd9548..16d928a16c083116cd00c07d2ad376973e351bdf 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.h +++ b/src/plugins/debugger/cdb/cdbdebugengine.h @@ -112,6 +112,7 @@ private slots: void warning(const QString &w); private: + void startupChecks(); void setState(DebuggerState state, const char *func, int line); inline bool startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage); void processTerminated(unsigned long exitCode); diff --git a/src/plugins/debugger/cdb/cdboptions.cpp b/src/plugins/debugger/cdb/cdboptions.cpp index fda335be03c7ab6f3e3f53cc1194d922f7d4e23f..0c3e00b383bb685253081ce4700ca38eb11b26d8 100644 --- a/src/plugins/debugger/cdb/cdboptions.cpp +++ b/src/plugins/debugger/cdb/cdboptions.cpp @@ -50,6 +50,11 @@ CdbOptions::CdbOptions() : { } +QString CdbOptions::settingsGroup() +{ + return QLatin1String(settingsGroupC); +} + void CdbOptions::clear() { enabled = false; @@ -99,5 +104,37 @@ unsigned CdbOptions::compare(const CdbOptions &rhs) const return rc; } +static const char symbolServerPrefixC[] = "symsrv*symsrv.dll*"; +static const char symbolServerPostfixC[] = "*http://msdl.microsoft.com/download/symbols"; + +QString CdbOptions::symbolServerPath(const QString &cacheDir) +{ + QString s = QLatin1String(symbolServerPrefixC); + s += QDir::toNativeSeparators(cacheDir); + s += QLatin1String(symbolServerPostfixC); + return s; +} + +bool CdbOptions::isSymbolServerPath(const QString &path, QString *cacheDir /* = 0 */) +{ + // Split apart symbol server post/prefixes + if (!path.startsWith(QLatin1String(symbolServerPrefixC)) || !path.endsWith(QLatin1String(symbolServerPostfixC))) + return false; + if (cacheDir) { + const unsigned prefixLength = qstrlen(symbolServerPrefixC); + *cacheDir = path.mid(prefixLength, path.size() - prefixLength - qstrlen(symbolServerPostfixC)); + } + return true; +} + +int CdbOptions::indexOfSymbolServerPath(const QStringList &paths, QString *cacheDir /* = 0 */) +{ + const int count = paths.size(); + for (int i = 0; i < count; i++) + if (CdbOptions::isSymbolServerPath(paths.at(i), cacheDir)) + return i; + return -1; +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdboptions.h b/src/plugins/debugger/cdb/cdboptions.h index cb7bd07ef2ce782aa6373116f1528c2602465aeb..97f08d108a218136a96a4e61993cabfbdfc72069 100644 --- a/src/plugins/debugger/cdb/cdboptions.h +++ b/src/plugins/debugger/cdb/cdboptions.h @@ -54,6 +54,14 @@ public: SymbolOptionsChanged = 0x4 }; unsigned compare(const CdbOptions &s) const; + // Format a symbol server specification with a cache directory + static QString symbolServerPath(const QString &cacheDir); + // Check whether the path is a symbol server specification and return the cache directory + static bool isSymbolServerPath(const QString &symbolPath, QString *cacheDir = 0); + static int indexOfSymbolServerPath(const QStringList &symbolPaths, QString *cacheDir = 0); + + static QString settingsGroup(); + bool enabled; QString path; QStringList symbolPaths; diff --git a/src/plugins/debugger/cdb/cdbsymbolpathlisteditor.cpp b/src/plugins/debugger/cdb/cdbsymbolpathlisteditor.cpp index 4bd5e2c953a25eb2dc5bdec6be9ed3c198de41cd..a3fc68922aaf3a3e723d10cdbeb827d1f3155006 100644 --- a/src/plugins/debugger/cdb/cdbsymbolpathlisteditor.cpp +++ b/src/plugins/debugger/cdb/cdbsymbolpathlisteditor.cpp @@ -28,13 +28,83 @@ **************************************************************************/ #include "cdbsymbolpathlisteditor.h" +#include "cdboptions.h" +#include <utils/pathchooser.h> + +#include <QtCore/QDir> +#include <QtCore/QDebug> #include <QtGui/QFileDialog> #include <QtGui/QAction> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QVBoxLayout> +#include <QtGui/QFormLayout> +#include <QtGui/QMessageBox> namespace Debugger { namespace Internal { +CacheDirectoryDialog::CacheDirectoryDialog(QWidget *parent) : + QDialog(parent), m_chooser(new Utils::PathChooser), + m_buttonBox(new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel)) +{ + setWindowTitle(tr("Select Local Cache Folder")); + setModal(true); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + QFormLayout *formLayout = new QFormLayout; + m_chooser->setExpectedKind(Utils::PathChooser::Directory); + m_chooser->setMinimumWidth(400); + formLayout->addRow(tr("Path:"), m_chooser); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(formLayout); + mainLayout->addWidget(m_buttonBox); + + setLayout(mainLayout); + + connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(reject())); +} + +void CacheDirectoryDialog::setPath(const QString &p) +{ + m_chooser->setPath(p); +} + +QString CacheDirectoryDialog::path() const +{ + return m_chooser->path(); +} + +void CacheDirectoryDialog::accept() +{ + // Ensure path exists + QString cache = path(); + if (cache.isEmpty()) + return; + QFileInfo fi(cache); + // Folder exists - all happy. + if (fi.isDir()) { + QDialog::accept(); + return; + } + // Does a file of the same name exist? + if (fi.exists()) { + QMessageBox::warning(this, tr("Already Exists"), + tr("A file named '%1' already exists.").arg(cache)); + return; + } + // Create + QDir root(QDir::root()); + if (!root.mkpath(cache)) { + QMessageBox::warning(this, tr("Cannot Create"), + tr("The folder '%1' could not be created.").arg(cache)); + return; + } + QDialog::accept(); +} + CdbSymbolPathListEditor::CdbSymbolPathListEditor(QWidget *parent) : Utils::PathListEditor(parent) { @@ -44,15 +114,20 @@ CdbSymbolPathListEditor::CdbSymbolPathListEditor(QWidget *parent) : "Requires specifying a local cache directory.")); } +QString CdbSymbolPathListEditor::promptCacheDirectory(QWidget *parent) +{ + CacheDirectoryDialog dialog(parent); + dialog.setPath(QDir::tempPath() + QDir::separator() + QLatin1String("symbolcache")); + if (dialog.exec() != QDialog::Accepted) + return QString(); + return dialog.path(); +} + void CdbSymbolPathListEditor::addSymbolServer() { - const QString title = tr("Pick a local cache directory"); - const QString cacheDir = QFileDialog::getExistingDirectory(this, title); - if (!cacheDir.isEmpty()) { - const QString path = QString::fromLatin1("symsrv*symsrv.dll*%1*http://msdl.microsoft.com/download/symbols"). - arg(QDir::toNativeSeparators(cacheDir)); - insertPathAtCursor(path); - } + const QString cacheDir = promptCacheDirectory(this); + if (!cacheDir.isEmpty()) + insertPathAtCursor(CdbOptions::symbolServerPath(cacheDir)); } } // namespace Internal diff --git a/src/plugins/debugger/cdb/cdbsymbolpathlisteditor.h b/src/plugins/debugger/cdb/cdbsymbolpathlisteditor.h index 9959c001d0b49fa5a8d19ed0ee0c4f42234cbaa7..9152762fcb031a40fa4de720f4331f581f540c36 100644 --- a/src/plugins/debugger/cdb/cdbsymbolpathlisteditor.h +++ b/src/plugins/debugger/cdb/cdbsymbolpathlisteditor.h @@ -32,15 +32,49 @@ #include <utils/pathlisteditor.h> +#include <QtGui/QDialog> + +namespace Utils { + class PathChooser; +} + +QT_BEGIN_NAMESPACE +class QDialogButtonBox; +QT_END_NAMESPACE + namespace Debugger { namespace Internal { +// Internal helper dialog prompting for a cache directory +// using a PathChooser. +// Note that QFileDialog does not offer a way of suggesting +// a non-existent folder, which is in turn automatically +// created. This is done here (suggest $TEMP\symbolcache +// regardless of its existence). + +class CacheDirectoryDialog : public QDialog { + Q_OBJECT +public: + explicit CacheDirectoryDialog(QWidget *parent = 0); + + void setPath(const QString &p); + QString path() const; + + virtual void accept(); + +private: + Utils::PathChooser *m_chooser; + QDialogButtonBox *m_buttonBox; +}; + class CdbSymbolPathListEditor : public Utils::PathListEditor { Q_OBJECT public: explicit CdbSymbolPathListEditor(QWidget *parent = 0); + static QString promptCacheDirectory(QWidget *parent); + private slots: void addSymbolServer(); };