OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / cdb / cdboptionspage.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "cdboptionspage.h"
35 #include "cdboptions.h"
36 #include "debuggerconstants.h"
37 #include "cdbengine.h"
38
39 #ifdef Q_OS_WIN
40 #    include <utils/winutils.h>
41 #endif
42 #include <utils/synchronousprocess.h>
43
44 #include <coreplugin/icore.h>
45
46 #include <QtCore/QCoreApplication>
47 #include <QtCore/QUrl>
48 #include <QtCore/QFileInfo>
49 #include <QtCore/QDir>
50 #include <QtCore/QDateTime>
51 #include <QtCore/QTextStream>
52 #include <QtCore/QTimer>
53 #include <QtCore/QProcess>
54 #include <QtGui/QMessageBox>
55 #include <QtGui/QLineEdit>
56 #include <QtGui/QDesktopServices>
57
58 static const char *dgbToolsDownloadLink32C = "http://www.microsoft.com/whdc/devtools/debugging/installx86.Mspx";
59 static const char *dgbToolsDownloadLink64C = "http://www.microsoft.com/whdc/devtools/debugging/install64bit.Mspx";
60
61 namespace Debugger {
62 namespace Internal {
63
64 struct EventsDescription {
65     const char *abbreviation;
66     bool hasParameter;
67     const char *description;
68 };
69
70 // Parameters of the "sxe" command
71 const EventsDescription eventDescriptions[] =
72 {
73     {"eh", false, QT_TRANSLATE_NOOP("Debugger::Cdb::CdbBreakEventWidget",
74                                     "C++ exception")},
75     {"ct", false, QT_TRANSLATE_NOOP("Debugger::Cdb::CdbBreakEventWidget",
76                                     "Thread creation")},
77     {"et", false, QT_TRANSLATE_NOOP("Debugger::Cdb::CdbBreakEventWidget",
78                                     "Thread exit")},
79     {"ld", true,  QT_TRANSLATE_NOOP("Debugger::Cdb::CdbBreakEventWidget",
80                                     "Load Module:")},
81     {"ud", true,  QT_TRANSLATE_NOOP("Debugger::Cdb::CdbBreakEventWidget",
82                                     "Unload Module:")},
83     {"out", true, QT_TRANSLATE_NOOP("Debugger::Cdb::CdbBreakEventWidget",
84                                     "Output:")}
85 };
86
87 static inline int indexOfEvent(const QString &abbrev)
88 {
89     const size_t eventCount = sizeof(eventDescriptions) / sizeof(EventsDescription);
90     for (size_t e = 0; e < eventCount; e++)
91         if (abbrev == QLatin1String(eventDescriptions[e].abbreviation))
92                 return int(e);
93     return -1;
94 }
95
96 CdbBreakEventWidget::CdbBreakEventWidget(QWidget *parent) : QWidget(parent)
97 {
98     // 1 column with checkboxes only,
99     // further columns with checkbox + parameter
100     QHBoxLayout *mainLayout = new QHBoxLayout;
101     QVBoxLayout *leftLayout = new QVBoxLayout;
102     QFormLayout *parameterLayout = 0;
103     mainLayout->addLayout(leftLayout);
104     const size_t eventCount = sizeof(eventDescriptions) / sizeof(EventsDescription);
105     for (size_t e = 0; e < eventCount; e++) {
106         QCheckBox *cb = new QCheckBox(tr(eventDescriptions[e].description));
107         QLineEdit *le = 0;
108         if (eventDescriptions[e].hasParameter) {
109             if (!parameterLayout) {
110                 parameterLayout = new QFormLayout;
111                 mainLayout->addSpacerItem(new QSpacerItem(20, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Ignored));
112                 mainLayout->addLayout(parameterLayout);
113             }
114             le = new QLineEdit;
115             parameterLayout->addRow(cb, le);
116             if (parameterLayout->count() >= 4) // New column
117                 parameterLayout = 0;
118         } else {
119             leftLayout->addWidget(cb);
120         }
121         m_checkBoxes.push_back(cb);
122         m_lineEdits.push_back(le);
123     }
124     setLayout(mainLayout);
125 }
126
127 void CdbBreakEventWidget::clear()
128 {
129     foreach (QLineEdit *l, m_lineEdits) {
130         if (l)
131             l->clear();
132     }
133     foreach (QCheckBox *c, m_checkBoxes)
134         c->setChecked(false);
135 }
136
137 void CdbBreakEventWidget::setBreakEvents(const QStringList &l)
138 {
139     clear();
140     // Split the list of ("eh", "out:MyOutput")
141     foreach (const QString &evt, l) {
142         const int colonPos = evt.indexOf(QLatin1Char(':'));
143         const QString abbrev = colonPos != -1 ? evt.mid(0, colonPos) : evt;
144         const int index = indexOfEvent(abbrev);
145         if (index != -1)
146             m_checkBoxes.at(index)->setChecked(true);
147         if (colonPos != -1 && m_lineEdits.at(index))
148             m_lineEdits.at(index)->setText(evt.mid(colonPos + 1));
149     }
150 }
151
152 QString CdbBreakEventWidget::filterText(int i) const
153 {
154     return m_lineEdits.at(i) ? m_lineEdits.at(i)->text() : QString();
155 }
156
157 QStringList CdbBreakEventWidget::breakEvents() const
158 {
159     // Compile a list of ("eh", "out:MyOutput")
160     QStringList rc;
161     const int eventCount = sizeof(eventDescriptions) / sizeof(EventsDescription);
162     for (int e = 0; e < eventCount; e++) {
163         if (m_checkBoxes.at(e)->isChecked()) {
164             const QString filter = filterText(e);
165             QString s = QLatin1String(eventDescriptions[e].abbreviation);
166             if (!filter.isEmpty()) {
167                 s += QLatin1Char(':');
168                 s += filter;
169             }
170             rc.push_back(s);
171         }
172     }
173     return rc;
174 }
175
176 static inline QString msgPathConfigNote()
177 {
178 #ifdef Q_OS_WIN
179     const bool is64bit = Utils::winIs64BitSystem();
180 #else
181     const bool is64bit = false;
182 #endif
183     const QString link = is64bit ? QLatin1String(dgbToolsDownloadLink64C) : QLatin1String(dgbToolsDownloadLink32C);
184     //: Label text for path configuration. %2 is "x-bit version".
185     return CdbOptionsPageWidget::tr(
186     "<html><body><p>Specify the path to the "
187     "<a href=\"%1\">Windows Console Debugger executable</a>"
188     " (%2) here.</p>"
189     "</body></html>").arg(link, (is64bit ? CdbOptionsPageWidget::tr("64-bit version")
190                                          : CdbOptionsPageWidget::tr("32-bit version")));
191 }
192
193 CdbOptionsPageWidget::CdbOptionsPageWidget(QWidget *parent) :
194     QWidget(parent), m_breakEventWidget(new CdbBreakEventWidget),
195     m_reportTimer(0)
196 {
197     m_ui.setupUi(this);
198     m_ui.noteLabel->setText(msgPathConfigNote());
199     m_ui.noteLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
200     connect(m_ui.noteLabel, SIGNAL(linkActivated(QString)), this, SLOT(downLoadLinkActivated(QString)));
201
202     m_ui.pathChooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
203     m_ui.pathChooser->addButton(tr("Autodetect"), this, SLOT(autoDetect()));
204     m_ui.cdbPathGroupBox->installEventFilter(this);
205     QVBoxLayout *eventLayout = new QVBoxLayout;
206     eventLayout->addWidget(m_breakEventWidget);
207     m_ui.eventGroupBox->setLayout(eventLayout);
208 }
209
210 void CdbOptionsPageWidget::setOptions(CdbOptions &o)
211 {
212     m_ui.pathChooser->setPath(o.executable);
213     m_ui.is64BitCheckBox->setChecked(o.is64bit);
214     m_ui.cdbPathGroupBox->setChecked(o.enabled);
215     setSymbolPaths(o.symbolPaths);
216     m_ui.sourcePathListEditor->setPathList(o.sourcePaths);
217     m_breakEventWidget->setBreakEvents(o.breakEvents);
218 }
219
220 bool CdbOptionsPageWidget::is64Bit() const
221 {
222     return m_ui.is64BitCheckBox->isChecked();
223 }
224
225 QString CdbOptionsPageWidget::path() const
226 {
227     return m_ui.pathChooser->path();
228 }
229
230 CdbOptions CdbOptionsPageWidget::options() const
231 {
232     CdbOptions  rc;
233     rc.executable = path();
234     rc.enabled = m_ui.cdbPathGroupBox->isChecked();
235     rc.is64bit = is64Bit();
236     rc.symbolPaths = symbolPaths();
237     rc.sourcePaths = m_ui.sourcePathListEditor->pathList();
238     rc.breakEvents = m_breakEventWidget->breakEvents();
239     return rc;
240 }
241
242 QStringList CdbOptionsPageWidget::symbolPaths() const
243 {
244     return m_ui.symbolPathListEditor->pathList();
245 }
246
247 void CdbOptionsPageWidget::setSymbolPaths(const QStringList &s)
248 {
249     m_ui.symbolPathListEditor->setPathList(s);
250 }
251
252 void CdbOptionsPageWidget::hideReportLabel()
253 {
254     m_ui.reportLabel->clear();
255     m_ui.reportLabel->setVisible(false);
256 }
257
258 void CdbOptionsPageWidget::autoDetect()
259 {
260     QString executable;
261     QStringList checkedDirectories;
262     bool is64bit;
263     const bool ok = CdbOptions::autoDetectExecutable(&executable, &is64bit, &checkedDirectories);
264     m_ui.cdbPathGroupBox->setChecked(ok);
265     if (ok) {
266         m_ui.is64BitCheckBox->setChecked(is64bit);
267         m_ui.pathChooser->setPath(executable);
268         QString report;
269         // Now check for the extension library as well.
270         const bool allOk = checkInstallation(executable, is64Bit(), &report);
271         setReport(report, allOk);
272         // On this occasion, if no symbol paths are specified, check for an
273         // old CDB installation
274         if (symbolPaths().isEmpty())
275             setSymbolPaths(CdbOptions::oldEngineSymbolPaths(Core::ICore::instance()->settings()));
276     } else {
277         const QString msg = tr("\"Debugging Tools for Windows\" could not be found.");
278         const QString details = tr("Checked:\n%1").arg(checkedDirectories.join(QString(QLatin1Char('\n'))));
279         QMessageBox msbBox(QMessageBox::Information, tr("Autodetection"), msg, QMessageBox::Ok, this);
280         msbBox.setDetailedText(details);
281         msbBox.exec();
282     }
283 }
284
285 void CdbOptionsPageWidget::setReport(const QString &msg, bool success)
286 {
287     // Hide label after some interval
288     if (!m_reportTimer) {
289         m_reportTimer = new QTimer(this);
290         m_reportTimer->setSingleShot(true);
291         connect(m_reportTimer, SIGNAL(timeout()), this, SLOT(hideReportLabel()));
292     } else {
293         if (m_reportTimer->isActive())
294             m_reportTimer->stop();
295     }
296     m_reportTimer->setInterval(success ? 10000 : 20000);
297     m_reportTimer->start();
298
299     m_ui.reportLabel->setText(msg);
300     m_ui.reportLabel->setStyleSheet(success ? QString() : QString::fromAscii("background-color : 'red'"));
301     m_ui.reportLabel->setVisible(true);
302 }
303
304 void CdbOptionsPageWidget::downLoadLinkActivated(const QString &link)
305 {
306     QDesktopServices::openUrl(QUrl(link));
307 }
308
309 QString CdbOptionsPageWidget::searchKeywords() const
310 {
311     QString rc;
312     QTextStream(&rc) << m_ui.pathLabel->text() << ' ' << m_ui.symbolPathLabel->text()
313             << ' ' << m_ui.sourcePathLabel->text();
314     rc.remove(QLatin1Char('&'));
315     return rc;
316 }
317
318 static QString cdbVersion(const QString &executable)
319 {
320     QProcess cdb;
321     cdb.start(executable, QStringList(QLatin1String("-version")));
322     cdb.closeWriteChannel();
323     if (!cdb.waitForStarted())
324         return QString();
325     if (!cdb.waitForFinished()) {
326         Utils::SynchronousProcess::stopProcess(cdb);
327         return QString();
328     }
329     return QString::fromLocal8Bit(cdb.readAllStandardOutput());
330 }
331
332 bool CdbOptionsPageWidget::checkInstallation(const QString &executable,
333                                              bool is64Bit, QString *message)
334 {
335     // 1) Check on executable
336     unsigned checkedItems = 0;
337     QString rc;
338     if (executable.isEmpty()) {
339         message->append(tr("No cdb executable specified.\n"));
340     } else {
341         const QString version = cdbVersion(executable);
342         if (version.isEmpty()) {
343             message->append(tr("Unable to determine version of %1.\n").
344                             arg(executable));
345         } else {
346             message->append(tr("Version: %1").arg(version));
347             checkedItems++;
348         }
349     }
350
351     // 2) Check on extension library
352     const QFileInfo extensionFi(CdbEngine::extensionLibraryName(is64Bit));
353     if (extensionFi.isFile()) {
354         message->append(tr("Extension library: %1, built: %3.\n").
355                         arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath())).
356                         arg(extensionFi.lastModified().toString(Qt::SystemLocaleShortDate)));
357         checkedItems++;
358     } else {
359         message->append("Extension library not found.\n");
360     }
361     return checkedItems == 2u;
362 }
363
364 bool CdbOptionsPageWidget::eventFilter(QObject *o, QEvent *e)
365 {
366     if (o != m_ui.cdbPathGroupBox || e->type() != QEvent::ToolTip)
367         return QWidget::eventFilter(o, e);
368     QString message;
369     checkInstallation(path(), is64Bit(), &message);
370     m_ui.cdbPathGroupBox->setToolTip(message);
371     return false;
372 }
373
374 // ---------- CdbOptionsPage
375
376 CdbOptionsPage *CdbOptionsPage::m_instance = 0;
377
378 CdbOptionsPage::CdbOptionsPage() :
379         m_options(new CdbOptions)
380 {
381     CdbOptionsPage::m_instance = this;
382     m_options->fromSettings(Core::ICore::instance()->settings());
383 }
384
385 CdbOptionsPage::~CdbOptionsPage()
386 {
387     CdbOptionsPage::m_instance = 0;
388 }
389
390 QString CdbOptionsPage::settingsId()
391 {
392     return QLatin1String("F.Cda"); // before old CDB
393 }
394
395 QString CdbOptionsPage::displayName() const
396 {
397     return tr("CDB");
398 }
399
400 QString CdbOptionsPage::category() const
401 {
402     return QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY);
403 }
404
405 QString CdbOptionsPage::displayCategory() const
406 {
407     return QCoreApplication::translate("Debugger", Debugger::Constants::DEBUGGER_SETTINGS_TR_CATEGORY);
408 }
409
410 QIcon CdbOptionsPage::categoryIcon() const
411 {
412     return QIcon(QLatin1String(Debugger::Constants::DEBUGGER_COMMON_SETTINGS_CATEGORY_ICON));
413 }
414
415 QWidget *CdbOptionsPage::createPage(QWidget *parent)
416 {
417     m_widget = new CdbOptionsPageWidget(parent);
418     m_widget->setOptions(*m_options);
419     if (m_searchKeywords.isEmpty())
420         m_searchKeywords = m_widget->searchKeywords();
421     return m_widget;
422 }
423
424 void CdbOptionsPage::apply()
425 {
426     if (!m_widget)
427         return;
428     const CdbOptions newOptions = m_widget->options();
429     if (*m_options != newOptions) {
430         *m_options = newOptions;
431         m_options->toSettings(Core::ICore::instance()->settings());
432     }
433 }
434
435 void CdbOptionsPage::finish()
436 {
437 }
438
439 bool CdbOptionsPage::matches(const QString &s) const
440 {
441     return m_searchKeywords.contains(s, Qt::CaseInsensitive);
442 }
443
444 CdbOptionsPage *CdbOptionsPage::instance()
445 {
446     return m_instance;
447 }
448
449 } // namespace Internal
450 } // namespace Debugger