From 709cb2946b606a9889e4be66e8f3491ad515baa8 Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Date: Fri, 14 May 2010 11:06:33 +0200
Subject: [PATCH] Debugger[CDB]: Prompt to set up the Symbol server.

Prompt to set up the public symbol server unless
it is already configured or the environment
variable exists. Change the dialog to be
based on the PathChooser to be able to suggest
a non-existent directory.
Acked-by: Alessandro Portale <alessandro.portale@nokia.com>
---
 src/plugins/debugger/cdb/cdbdebugengine.cpp   | 39 ++++++++
 src/plugins/debugger/cdb/cdbdebugengine.h     |  1 +
 src/plugins/debugger/cdb/cdboptions.cpp       | 37 ++++++++
 src/plugins/debugger/cdb/cdboptions.h         |  8 ++
 .../debugger/cdb/cdbsymbolpathlisteditor.cpp  | 89 +++++++++++++++++--
 .../debugger/cdb/cdbsymbolpathlisteditor.h    | 34 +++++++
 6 files changed, 201 insertions(+), 7 deletions(-)

diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp
index 16548f9146e..2c4db8eabc0 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 452f8df87ff..16d928a16c0 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 fda335be03c..0c3e00b383b 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 cb7bd07ef2c..97f08d108a2 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 4bd5e2c953a..a3fc68922aa 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 9959c001d0b..9152762fcb0 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();
 };
-- 
GitLab