summaryrefslogtreecommitdiff
path: root/src/plugins/debugger
diff options
context:
space:
mode:
authorcon <qtc-commiter@nokia.com>2008-12-02 12:01:29 +0100
committercon <qtc-commiter@nokia.com>2008-12-02 12:01:29 +0100
commit05c35356abc31549c5db6eba31fb608c0365c2a0 (patch)
treebe044530104267afaff13f8943889cb97f8c8bad /src/plugins/debugger
downloadqt-creator-05c35356abc31549c5db6eba31fb608c0365c2a0.tar.gz
Initial import
Diffstat (limited to 'src/plugins/debugger')
-rw-r--r--src/plugins/debugger/Debugger.pluginspec13
-rw-r--r--src/plugins/debugger/assert.h45
-rw-r--r--src/plugins/debugger/attachexternaldialog.cpp344
-rw-r--r--src/plugins/debugger/attachexternaldialog.h65
-rw-r--r--src/plugins/debugger/attachexternaldialog.ui64
-rw-r--r--src/plugins/debugger/attachremotedialog.cpp344
-rw-r--r--src/plugins/debugger/attachremotedialog.h65
-rw-r--r--src/plugins/debugger/attachremotedialog.ui64
-rw-r--r--src/plugins/debugger/breakbyfunction.ui58
-rw-r--r--src/plugins/debugger/breakcondition.ui95
-rw-r--r--src/plugins/debugger/breakhandler.cpp559
-rw-r--r--src/plugins/debugger/breakhandler.h174
-rw-r--r--src/plugins/debugger/breakwindow.cpp164
-rw-r--r--src/plugins/debugger/breakwindow.h76
-rw-r--r--src/plugins/debugger/debugger.pro92
-rw-r--r--src/plugins/debugger/debugger.qrc24
-rw-r--r--src/plugins/debugger/debuggerconstants.h65
-rw-r--r--src/plugins/debugger/debuggermanager.cpp1298
-rw-r--r--src/plugins/debugger/debuggermanager.h451
-rw-r--r--src/plugins/debugger/debuggeroutputwindow.cpp318
-rw-r--r--src/plugins/debugger/debuggeroutputwindow.h87
-rw-r--r--src/plugins/debugger/debuggerplugin.cpp610
-rw-r--r--src/plugins/debugger/debuggerplugin.h111
-rw-r--r--src/plugins/debugger/debuggerrunner.cpp161
-rw-r--r--src/plugins/debugger/debuggerrunner.h96
-rw-r--r--src/plugins/debugger/disassemblerhandler.cpp187
-rw-r--r--src/plugins/debugger/disassemblerhandler.h78
-rw-r--r--src/plugins/debugger/disassemblerwindow.cpp140
-rw-r--r--src/plugins/debugger/disassemblerwindow.h71
-rw-r--r--src/plugins/debugger/gdbengine.cpp4035
-rw-r--r--src/plugins/debugger/gdbengine.h351
-rw-r--r--src/plugins/debugger/gdbmi.cpp473
-rw-r--r--src/plugins/debugger/gdbmi.h183
-rw-r--r--src/plugins/debugger/gdboptionpage.cpp128
-rw-r--r--src/plugins/debugger/gdboptionpage.h106
-rw-r--r--src/plugins/debugger/gdboptionpage.ui111
-rw-r--r--src/plugins/debugger/gdbtypemacros.cpp214
-rw-r--r--src/plugins/debugger/gdbtypemacros.ui186
-rw-r--r--src/plugins/debugger/idebuggerengine.h88
-rw-r--r--src/plugins/debugger/images/breakpoint.svg154
-rw-r--r--src/plugins/debugger/images/breakpoint_pending.svg534
-rw-r--r--src/plugins/debugger/images/debugger_breakpoints.pngbin0 -> 650 bytes
-rw-r--r--src/plugins/debugger/images/debugger_continue_small.pngbin0 -> 739 bytes
-rw-r--r--src/plugins/debugger/images/debugger_interrupt_small.pngbin0 -> 417 bytes
-rw-r--r--src/plugins/debugger/images/debugger_start.pngbin0 -> 1235 bytes
-rw-r--r--src/plugins/debugger/images/debugger_start_small.pngbin0 -> 739 bytes
-rw-r--r--src/plugins/debugger/images/debugger_stepinto_small.pngbin0 -> 420 bytes
-rw-r--r--src/plugins/debugger/images/debugger_steponeproc_small.pngbin0 -> 297 bytes
-rw-r--r--src/plugins/debugger/images/debugger_stepout_small.pngbin0 -> 382 bytes
-rw-r--r--src/plugins/debugger/images/debugger_stepover_small.pngbin0 -> 617 bytes
-rw-r--r--src/plugins/debugger/images/debugger_stepoverproc_small.pngbin0 -> 517 bytes
-rw-r--r--src/plugins/debugger/images/debugger_stop_small.pngbin0 -> 314 bytes
-rw-r--r--src/plugins/debugger/images/delete.pngbin0 -> 578 bytes
-rw-r--r--src/plugins/debugger/images/done.pngbin0 -> 235 bytes
-rw-r--r--src/plugins/debugger/images/empty.svg67
-rw-r--r--src/plugins/debugger/images/error.pngbin0 -> 326 bytes
-rw-r--r--src/plugins/debugger/images/location.svg121
-rw-r--r--src/plugins/debugger/images/newitem.pngbin0 -> 239 bytes
-rw-r--r--src/plugins/debugger/images/running.pngbin0 -> 532 bytes
-rw-r--r--src/plugins/debugger/imports.h49
-rw-r--r--src/plugins/debugger/mode.cpp233
-rw-r--r--src/plugins/debugger/mode.h84
-rw-r--r--src/plugins/debugger/mode.ui76
-rw-r--r--src/plugins/debugger/moduleshandler.cpp172
-rw-r--r--src/plugins/debugger/moduleshandler.h104
-rw-r--r--src/plugins/debugger/moduleswindow.cpp136
-rw-r--r--src/plugins/debugger/moduleswindow.h71
-rw-r--r--src/plugins/debugger/procinterrupt.cpp182
-rw-r--r--src/plugins/debugger/procinterrupt.h47
-rw-r--r--src/plugins/debugger/registerhandler.cpp128
-rw-r--r--src/plugins/debugger/registerhandler.h81
-rw-r--r--src/plugins/debugger/registerwindow.cpp181
-rw-r--r--src/plugins/debugger/registerwindow.h71
-rw-r--r--src/plugins/debugger/scriptengine.cpp677
-rw-r--r--src/plugins/debugger/scriptengine.h134
-rw-r--r--src/plugins/debugger/stackhandler.cpp271
-rw-r--r--src/plugins/debugger/stackhandler.h136
-rw-r--r--src/plugins/debugger/stackwindow.cpp117
-rw-r--r--src/plugins/debugger/stackwindow.h75
-rw-r--r--src/plugins/debugger/startexternaldialog.cpp124
-rw-r--r--src/plugins/debugger/startexternaldialog.h63
-rw-r--r--src/plugins/debugger/startexternaldialog.ui93
-rw-r--r--src/plugins/debugger/threadswindow.cpp112
-rw-r--r--src/plugins/debugger/threadswindow.h68
-rw-r--r--src/plugins/debugger/watchhandler.cpp1137
-rw-r--r--src/plugins/debugger/watchhandler.h219
-rw-r--r--src/plugins/debugger/watchwindow.cpp253
-rw-r--r--src/plugins/debugger/watchwindow.h95
88 files changed, 17859 insertions, 0 deletions
diff --git a/src/plugins/debugger/Debugger.pluginspec b/src/plugins/debugger/Debugger.pluginspec
new file mode 100644
index 0000000000..d68c17517e
--- /dev/null
+++ b/src/plugins/debugger/Debugger.pluginspec
@@ -0,0 +1,13 @@
+<plugin name="Debugger" version="0.9.1" compatVersion="0.9.1">
+ <vendor>Nokia Corporation</vendor>
+ <copyright>(C) 2008 Nokia Corporation</copyright>
+ <license>Nokia Technology Preview License Agreement</license>
+ <description>Debugger integration.</description>
+ <url>http://www.trolltech.com/</url>
+ <dependencyList>
+ <dependency name="CppEditor" version="0.9.1"/><!-- Debugger plugin adds items to the editor's context menu -->
+ <dependency name="ProjectExplorer" version="0.9.1"/>
+ <dependency name="Core" version="0.9.1"/>
+ <dependency name="Find" version="0.9.1"/>
+ </dependencyList>
+</plugin>
diff --git a/src/plugins/debugger/assert.h b/src/plugins/debugger/assert.h
new file mode 100644
index 0000000000..a4310040fe
--- /dev/null
+++ b/src/plugins/debugger/assert.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_QWB_ASSERT_H
+#define DEBUGGER_QWB_ASSERT_H
+
+#ifdef Q_OS_UNIX
+#define QWB_ASSERT(cond, action) \
+ if(cond){}else{qDebug()<<"ASSERTION"<<#cond<<"FAILED"<<__FILE__<<__LINE__;action;}
+#else
+#define QWB_ASSERT(cond, action) \
+ if(cond){}else{qDebug()<<"ASSERTION"<<#cond<<"FAILED";action;}
+#endif
+
+#endif
+
diff --git a/src/plugins/debugger/attachexternaldialog.cpp b/src/plugins/debugger/attachexternaldialog.cpp
new file mode 100644
index 0000000000..5aeec2ec62
--- /dev/null
+++ b/src/plugins/debugger/attachexternaldialog.cpp
@@ -0,0 +1,344 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "attachexternaldialog.h"
+
+#include <QDebug>
+#include <QDir>
+#include <QFile>
+#include <QPushButton>
+#include <QStandardItemModel>
+#include <QHeaderView>
+
+using namespace Debugger::Internal;
+
+AttachExternalDialog::AttachExternalDialog(QWidget *parent, const QString &pid)
+ : QDialog(parent)
+{
+ setupUi(this);
+ buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
+ m_defaultPID = pid;
+ m_model = new QStandardItemModel(this);
+
+ procView->setSortingEnabled(true);
+
+ connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
+ connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+
+ connect(procView, SIGNAL(activated(const QModelIndex &)),
+ this, SLOT(procSelected(const QModelIndex &)));
+
+
+ pidLineEdit->setText(m_defaultPID);
+ rebuildProcessList();
+}
+
+static bool isProcessName(const QString &procname)
+{
+ for (int i = 0; i != procname.size(); ++i)
+ if (!procname.at(i).isDigit())
+ return false;
+ return true;
+}
+
+struct ProcData {
+ QString ppid;
+ QString name;
+ QString state;
+};
+
+static void insertItem(QStandardItem *root, const QString &pid,
+ const QMap<QString, ProcData> &procs, QMap<QString, QStandardItem *> &known)
+{
+ //qDebug() << "HANDLING " << pid;
+ QStandardItem *parent = 0;
+ const ProcData &proc = procs[pid];
+ if (1 || pid == "0") {
+ parent = root;
+ } else {
+ if (!known.contains(proc.ppid))
+ insertItem(root, proc.ppid, procs, known);
+ parent = known[proc.ppid];
+ }
+ QList<QStandardItem *> row;
+ row.append(new QStandardItem(pid));
+ row.append(new QStandardItem(proc.name));
+ //row.append(new QStandardItem(proc.ppid));
+ row.append(new QStandardItem(proc.state));
+ parent->appendRow(row);
+ known[pid] = row[0];
+}
+
+void AttachExternalDialog::rebuildProcessList()
+{
+ QStringList procnames = QDir("/proc/").entryList();
+ if (procnames.isEmpty()) {
+ procView->hide();
+ return;
+ }
+
+ typedef QMap<QString, ProcData> Procs;
+ Procs procs;
+
+ foreach (const QString &procname, procnames) {
+ if (!isProcessName(procname))
+ continue;
+ QString filename = "/proc/" + procname + "/stat";
+ QFile file(filename);
+ file.open(QIODevice::ReadOnly);
+ QStringList data = QString::fromLocal8Bit(file.readAll()).split(' ');
+ //qDebug() << filename << data;
+ ProcData proc;
+ proc.name = data.at(1);
+ if (proc.name.startsWith('(') && proc.name.endsWith(')'))
+ proc.name = proc.name.mid(1, proc.name.size() - 2);
+ proc.state = data.at(2);
+ proc.ppid = data.at(3);
+ procs[procname] = proc;
+ }
+
+ m_model->clear();
+ QMap<QString, QStandardItem *> known;
+ for (Procs::const_iterator it = procs.begin(); it != procs.end(); ++it)
+ insertItem(m_model->invisibleRootItem(), it.key(), procs, known);
+ m_model->setHeaderData(0, Qt::Horizontal, "Process ID", Qt::DisplayRole);
+ m_model->setHeaderData(1, Qt::Horizontal, "Name", Qt::DisplayRole);
+ //model->setHeaderData(2, Qt::Horizontal, "Parent", Qt::DisplayRole);
+ m_model->setHeaderData(2, Qt::Horizontal, "State", Qt::DisplayRole);
+
+ procView->setModel(m_model);
+ procView->expandAll();
+ procView->resizeColumnToContents(0);
+ procView->resizeColumnToContents(1);
+}
+
+#ifdef Q_OS_WINDOWS
+
+#include <windows.h>
+#include <tlhelp32.h>
+#include <tchar.h>
+#include <stdio.h>
+
+// Forward declarations:
+BOOL GetProcessList( );
+BOOL ListProcessModules( DWORD dwPID );
+BOOL ListProcessThreads( DWORD dwOwnerPID );
+void printError( TCHAR* msg );
+
+BOOL GetProcessList( )
+{
+ HANDLE hProcessSnap;
+ HANDLE hProcess;
+ PROCESSENTRY32 pe32;
+ DWORD dwPriorityClass;
+
+ // Take a snapshot of all processes in the system.
+ hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
+ if( hProcessSnap == INVALID_HANDLE_VALUE )
+ {
+ printError( TEXT("CreateToolhelp32Snapshot (of processes)") );
+ return( FALSE );
+ }
+
+ // Set the size of the structure before using it.
+ pe32.dwSize = sizeof( PROCESSENTRY32 );
+
+ // Retrieve information about the first process,
+ // and exit if unsuccessful
+ if( !Process32First( hProcessSnap, &pe32 ) )
+ {
+ printError( TEXT("Process32First") ); // show cause of failure
+ CloseHandle( hProcessSnap ); // clean the snapshot object
+ return( FALSE );
+ }
+
+ // Now walk the snapshot of processes, and
+ // display information about each process in turn
+ do
+ {
+ printf( "\n\n=====================================================" );
+ _tprintf( TEXT("\nPROCESS NAME: %s"), pe32.szExeFile );
+ printf( "\n-----------------------------------------------------" );
+
+ // Retrieve the priority class.
+ dwPriorityClass = 0;
+ hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID );
+ if( hProcess == NULL )
+ printError( TEXT("OpenProcess") );
+ else
+ {
+ dwPriorityClass = GetPriorityClass( hProcess );
+ if( !dwPriorityClass )
+ printError( TEXT("GetPriorityClass") );
+ CloseHandle( hProcess );
+ }
+
+ printf( "\n Process ID = 0x%08X", pe32.th32ProcessID );
+ printf( "\n Thread count = %d", pe32.cntThreads );
+ printf( "\n Parent process ID = 0x%08X", pe32.th32ParentProcessID );
+ printf( "\n Priority base = %d", pe32.pcPriClassBase );
+ if( dwPriorityClass )
+ printf( "\n Priority class = %d", dwPriorityClass );
+
+ // List the modules and threads associated with this process
+ ListProcessModules( pe32.th32ProcessID );
+ ListProcessThreads( pe32.th32ProcessID );
+
+ } while( Process32Next( hProcessSnap, &pe32 ) );
+
+ CloseHandle( hProcessSnap );
+ return( TRUE );
+}
+
+
+BOOL ListProcessModules( DWORD dwPID )
+{
+ HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
+ MODULEENTRY32 me32;
+
+ // Take a snapshot of all modules in the specified process.
+ hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID );
+ if( hModuleSnap == INVALID_HANDLE_VALUE )
+ {
+ printError( TEXT("CreateToolhelp32Snapshot (of modules)") );
+ return( FALSE );
+ }
+
+ // Set the size of the structure before using it.
+ me32.dwSize = sizeof( MODULEENTRY32 );
+
+ // Retrieve information about the first module,
+ // and exit if unsuccessful
+ if( !Module32First( hModuleSnap, &me32 ) )
+ {
+ printError( TEXT("Module32First") ); // show cause of failure
+ CloseHandle( hModuleSnap ); // clean the snapshot object
+ return( FALSE );
+ }
+
+ // Now walk the module list of the process,
+ // and display information about each module
+ do
+ {
+ _tprintf( TEXT("\n\n MODULE NAME: %s"), me32.szModule );
+ _tprintf( TEXT("\n Executable = %s"), me32.szExePath );
+ printf( "\n Process ID = 0x%08X", me32.th32ProcessID );
+ printf( "\n Ref count (g) = 0x%04X", me32.GlblcntUsage );
+ printf( "\n Ref count (p) = 0x%04X", me32.ProccntUsage );
+ printf( "\n Base address = 0x%08X", (DWORD) me32.modBaseAddr );
+ printf( "\n Base size = %d", me32.modBaseSize );
+
+ } while( Module32Next( hModuleSnap, &me32 ) );
+
+ CloseHandle( hModuleSnap );
+ return( TRUE );
+}
+
+BOOL ListProcessThreads( DWORD dwOwnerPID )
+{
+ HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
+ THREADENTRY32 te32;
+
+ // Take a snapshot of all running threads
+ hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
+ if( hThreadSnap == INVALID_HANDLE_VALUE )
+ return( FALSE );
+
+ // Fill in the size of the structure before using it.
+ te32.dwSize = sizeof(THREADENTRY32 );
+
+ // Retrieve information about the first thread,
+ // and exit if unsuccessful
+ if( !Thread32First( hThreadSnap, &te32 ) )
+ {
+ printError( TEXT("Thread32First") ); // show cause of failure
+ CloseHandle( hThreadSnap ); // clean the snapshot object
+ return( FALSE );
+ }
+
+ // Now walk the thread list of the system,
+ // and display information about each thread
+ // associated with the specified process
+ do
+ {
+ if( te32.th32OwnerProcessID == dwOwnerPID )
+ {
+ printf( "\n\n THREAD ID = 0x%08X", te32.th32ThreadID );
+ printf( "\n Base priority = %d", te32.tpBasePri );
+ printf( "\n Delta priority = %d", te32.tpDeltaPri );
+ }
+ } while( Thread32Next(hThreadSnap, &te32 ) );
+
+ CloseHandle( hThreadSnap );
+ return( TRUE );
+}
+
+void printError( TCHAR* msg )
+{
+ DWORD eNum;
+ TCHAR sysMsg[256];
+ TCHAR* p;
+
+ eNum = GetLastError( );
+ FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, eNum,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ sysMsg, 256, NULL );
+
+ // Trim the end of the line and terminate it with a null
+ p = sysMsg;
+ while( ( *p > 31 ) || ( *p == 9 ) )
+ ++p;
+ do { *p-- = 0; } while( ( p >= sysMsg ) &&
+ ( ( *p == '.' ) || ( *p < 33 ) ) );
+
+ // Display the message
+ _tprintf( TEXT("\n WARNING: %s failed with error %d (%s)"), msg, eNum, sysMsg );
+}
+
+#endif
+
+
+void AttachExternalDialog::procSelected(const QModelIndex &index0)
+{
+ QModelIndex index = index0.sibling(index0.row(), 0);
+ QStandardItem *item = m_model->itemFromIndex(index);
+ if (!item)
+ return;
+ pidLineEdit->setText(item->text());
+ accept();
+}
+
+int AttachExternalDialog::attachPID() const
+{
+ return pidLineEdit->text().toInt();
+}
diff --git a/src/plugins/debugger/attachexternaldialog.h b/src/plugins/debugger/attachexternaldialog.h
new file mode 100644
index 0000000000..6c77d206b6
--- /dev/null
+++ b/src/plugins/debugger/attachexternaldialog.h
@@ -0,0 +1,65 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef ATTACHEXTERNALDIALOG_H
+#define ATTACHEXTERNALDIALOG_H
+
+#include "ui_attachexternaldialog.h"
+
+QT_BEGIN_NAMESPACE
+class QStandardItemModel;
+QT_END_NAMESPACE
+
+namespace Debugger {
+namespace Internal {
+
+class AttachExternalDialog : public QDialog, Ui::AttachExternalDialog
+{
+ Q_OBJECT
+
+public:
+ explicit AttachExternalDialog(QWidget *parent, const QString &pid);
+ int attachPID() const;
+
+private slots:
+ void rebuildProcessList();
+ void procSelected(const QModelIndex &);
+
+private:
+ QString m_defaultPID;
+ QStandardItemModel *m_model;
+};
+
+} // namespace Debugger
+} // namespace Internal
+
+#endif // ATTACHEEXTERNALDIALOG_H
diff --git a/src/plugins/debugger/attachexternaldialog.ui b/src/plugins/debugger/attachexternaldialog.ui
new file mode 100644
index 0000000000..63f214c51e
--- /dev/null
+++ b/src/plugins/debugger/attachexternaldialog.ui
@@ -0,0 +1,64 @@
+<ui version="4.0" >
+ <class>AttachExternalDialog</class>
+ <widget class="QDialog" name="AttachExternalDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>561</width>
+ <height>866</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Start Debugger</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout" >
+ <item>
+ <widget class="QLabel" name="pidLabel" >
+ <property name="text" >
+ <string>Attach to Process ID:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="pidLineEdit" />
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QTreeView" name="procView" >
+ <property name="editTriggers" >
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="Line" name="line" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/debugger/attachremotedialog.cpp b/src/plugins/debugger/attachremotedialog.cpp
new file mode 100644
index 0000000000..ffd09e7b78
--- /dev/null
+++ b/src/plugins/debugger/attachremotedialog.cpp
@@ -0,0 +1,344 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "attachremotedialog.h"
+
+#include <QDebug>
+#include <QDir>
+#include <QFile>
+#include <QPushButton>
+#include <QStandardItemModel>
+#include <QHeaderView>
+
+using namespace Debugger::Internal;
+
+AttachRemoteDialog::AttachRemoteDialog(QWidget *parent, const QString &pid)
+ : QDialog(parent)
+{
+ setupUi(this);
+ buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
+ m_defaultPID = pid;
+ m_model = new QStandardItemModel(this);
+
+ procView->setSortingEnabled(true);
+
+ connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
+ connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+
+ connect(procView, SIGNAL(activated(const QModelIndex &)),
+ this, SLOT(procSelected(const QModelIndex &)));
+
+
+ pidLineEdit->setText(m_defaultPID);
+ rebuildProcessList();
+}
+
+static bool isProcessName(const QString &procname)
+{
+ for (int i = 0; i != procname.size(); ++i)
+ if (!procname.at(i).isDigit())
+ return false;
+ return true;
+}
+
+struct ProcData {
+ QString ppid;
+ QString name;
+ QString state;
+};
+
+static void insertItem(QStandardItem *root, const QString &pid,
+ const QMap<QString, ProcData> &procs, QMap<QString, QStandardItem *> &known)
+{
+ //qDebug() << "HANDLING " << pid;
+ QStandardItem *parent = 0;
+ const ProcData &proc = procs[pid];
+ if (1 || pid == "0") {
+ parent = root;
+ } else {
+ if (!known.contains(proc.ppid))
+ insertItem(root, proc.ppid, procs, known);
+ parent = known[proc.ppid];
+ }
+ QList<QStandardItem *> row;
+ row.append(new QStandardItem(pid));
+ row.append(new QStandardItem(proc.name));
+ //row.append(new QStandardItem(proc.ppid));
+ row.append(new QStandardItem(proc.state));
+ parent->appendRow(row);
+ known[pid] = row[0];
+}
+
+void AttachRemoteDialog::rebuildProcessList()
+{
+ QStringList procnames = QDir("/proc/").entryList();
+ if (procnames.isEmpty()) {
+ procView->hide();
+ return;
+ }
+
+ typedef QMap<QString, ProcData> Procs;
+ Procs procs;
+
+ foreach (const QString &procname, procnames) {
+ if (!isProcessName(procname))
+ continue;
+ QString filename = "/proc/" + procname + "/stat";
+ QFile file(filename);
+ file.open(QIODevice::ReadOnly);
+ QStringList data = QString::fromLocal8Bit(file.readAll()).split(' ');
+ //qDebug() << filename << data;
+ ProcData proc;
+ proc.name = data.at(1);
+ if (proc.name.startsWith('(') && proc.name.endsWith(')'))
+ proc.name = proc.name.mid(1, proc.name.size() - 2);
+ proc.state = data.at(2);
+ proc.ppid = data.at(3);
+ procs[procname] = proc;
+ }
+
+ m_model->clear();
+ QMap<QString, QStandardItem *> known;
+ for (Procs::const_iterator it = procs.begin(); it != procs.end(); ++it)
+ insertItem(m_model->invisibleRootItem(), it.key(), procs, known);
+ m_model->setHeaderData(0, Qt::Horizontal, "Process ID", Qt::DisplayRole);
+ m_model->setHeaderData(1, Qt::Horizontal, "Name", Qt::DisplayRole);
+ //model->setHeaderData(2, Qt::Horizontal, "Parent", Qt::DisplayRole);
+ m_model->setHeaderData(2, Qt::Horizontal, "State", Qt::DisplayRole);
+
+ procView->setModel(m_model);
+ procView->expandAll();
+ procView->resizeColumnToContents(0);
+ procView->resizeColumnToContents(1);
+}
+
+#ifdef Q_OS_WINDOWS
+
+#include <windows.h>
+#include <tlhelp32.h>
+#include <tchar.h>
+#include <stdio.h>
+
+// Forward declarations:
+BOOL GetProcessList( );
+BOOL ListProcessModules( DWORD dwPID );
+BOOL ListProcessThreads( DWORD dwOwnerPID );
+void printError( TCHAR* msg );
+
+BOOL GetProcessList( )
+{
+ HANDLE hProcessSnap;
+ HANDLE hProcess;
+ PROCESSENTRY32 pe32;
+ DWORD dwPriorityClass;
+
+ // Take a snapshot of all processes in the system.
+ hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
+ if( hProcessSnap == INVALID_HANDLE_VALUE )
+ {
+ printError( TEXT("CreateToolhelp32Snapshot (of processes)") );
+ return( FALSE );
+ }
+
+ // Set the size of the structure before using it.
+ pe32.dwSize = sizeof( PROCESSENTRY32 );
+
+ // Retrieve information about the first process,
+ // and exit if unsuccessful
+ if( !Process32First( hProcessSnap, &pe32 ) )
+ {
+ printError( TEXT("Process32First") ); // show cause of failure
+ CloseHandle( hProcessSnap ); // clean the snapshot object
+ return( FALSE );
+ }
+
+ // Now walk the snapshot of processes, and
+ // display information about each process in turn
+ do
+ {
+ printf( "\n\n=====================================================" );
+ _tprintf( TEXT("\nPROCESS NAME: %s"), pe32.szExeFile );
+ printf( "\n-----------------------------------------------------" );
+
+ // Retrieve the priority class.
+ dwPriorityClass = 0;
+ hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID );
+ if( hProcess == NULL )
+ printError( TEXT("OpenProcess") );
+ else
+ {
+ dwPriorityClass = GetPriorityClass( hProcess );
+ if( !dwPriorityClass )
+ printError( TEXT("GetPriorityClass") );
+ CloseHandle( hProcess );
+ }
+
+ printf( "\n Process ID = 0x%08X", pe32.th32ProcessID );
+ printf( "\n Thread count = %d", pe32.cntThreads );
+ printf( "\n Parent process ID = 0x%08X", pe32.th32ParentProcessID );
+ printf( "\n Priority base = %d", pe32.pcPriClassBase );
+ if( dwPriorityClass )
+ printf( "\n Priority class = %d", dwPriorityClass );
+
+ // List the modules and threads associated with this process
+ ListProcessModules( pe32.th32ProcessID );
+ ListProcessThreads( pe32.th32ProcessID );
+
+ } while( Process32Next( hProcessSnap, &pe32 ) );
+
+ CloseHandle( hProcessSnap );
+ return( TRUE );
+}
+
+
+BOOL ListProcessModules( DWORD dwPID )
+{
+ HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
+ MODULEENTRY32 me32;
+
+ // Take a snapshot of all modules in the specified process.
+ hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID );
+ if( hModuleSnap == INVALID_HANDLE_VALUE )
+ {
+ printError( TEXT("CreateToolhelp32Snapshot (of modules)") );
+ return( FALSE );
+ }
+
+ // Set the size of the structure before using it.
+ me32.dwSize = sizeof( MODULEENTRY32 );
+
+ // Retrieve information about the first module,
+ // and exit if unsuccessful
+ if( !Module32First( hModuleSnap, &me32 ) )
+ {
+ printError( TEXT("Module32First") ); // show cause of failure
+ CloseHandle( hModuleSnap ); // clean the snapshot object
+ return( FALSE );
+ }
+
+ // Now walk the module list of the process,
+ // and display information about each module
+ do
+ {
+ _tprintf( TEXT("\n\n MODULE NAME: %s"), me32.szModule );
+ _tprintf( TEXT("\n Executable = %s"), me32.szExePath );
+ printf( "\n Process ID = 0x%08X", me32.th32ProcessID );
+ printf( "\n Ref count (g) = 0x%04X", me32.GlblcntUsage );
+ printf( "\n Ref count (p) = 0x%04X", me32.ProccntUsage );
+ printf( "\n Base address = 0x%08X", (DWORD) me32.modBaseAddr );
+ printf( "\n Base size = %d", me32.modBaseSize );
+
+ } while( Module32Next( hModuleSnap, &me32 ) );
+
+ CloseHandle( hModuleSnap );
+ return( TRUE );
+}
+
+BOOL ListProcessThreads( DWORD dwOwnerPID )
+{
+ HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
+ THREADENTRY32 te32;
+
+ // Take a snapshot of all running threads
+ hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
+ if( hThreadSnap == INVALID_HANDLE_VALUE )
+ return( FALSE );
+
+ // Fill in the size of the structure before using it.
+ te32.dwSize = sizeof(THREADENTRY32 );
+
+ // Retrieve information about the first thread,
+ // and exit if unsuccessful
+ if( !Thread32First( hThreadSnap, &te32 ) )
+ {
+ printError( TEXT("Thread32First") ); // show cause of failure
+ CloseHandle( hThreadSnap ); // clean the snapshot object
+ return( FALSE );
+ }
+
+ // Now walk the thread list of the system,
+ // and display information about each thread
+ // associated with the specified process
+ do
+ {
+ if( te32.th32OwnerProcessID == dwOwnerPID )
+ {
+ printf( "\n\n THREAD ID = 0x%08X", te32.th32ThreadID );
+ printf( "\n Base priority = %d", te32.tpBasePri );
+ printf( "\n Delta priority = %d", te32.tpDeltaPri );
+ }
+ } while( Thread32Next(hThreadSnap, &te32 ) );
+
+ CloseHandle( hThreadSnap );
+ return( TRUE );
+}
+
+void printError( TCHAR* msg )
+{
+ DWORD eNum;
+ TCHAR sysMsg[256];
+ TCHAR* p;
+
+ eNum = GetLastError( );
+ FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, eNum,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ sysMsg, 256, NULL );
+
+ // Trim the end of the line and terminate it with a null
+ p = sysMsg;
+ while( ( *p > 31 ) || ( *p == 9 ) )
+ ++p;
+ do { *p-- = 0; } while( ( p >= sysMsg ) &&
+ ( ( *p == '.' ) || ( *p < 33 ) ) );
+
+ // Display the message
+ _tprintf( TEXT("\n WARNING: %s failed with error %d (%s)"), msg, eNum, sysMsg );
+}
+
+#endif
+
+
+void AttachRemoteDialog::procSelected(const QModelIndex &index0)
+{
+ QModelIndex index = index0.sibling(index0.row(), 0);
+ QStandardItem *item = m_model->itemFromIndex(index);
+ if (!item)
+ return;
+ pidLineEdit->setText(item->text());
+ accept();
+}
+
+int AttachRemoteDialog::attachPID() const
+{
+ return pidLineEdit->text().toInt();
+}
diff --git a/src/plugins/debugger/attachremotedialog.h b/src/plugins/debugger/attachremotedialog.h
new file mode 100644
index 0000000000..ec732cd515
--- /dev/null
+++ b/src/plugins/debugger/attachremotedialog.h
@@ -0,0 +1,65 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef ATTACHREMOTE_DIALOG_H
+#define ATTACHREMOTE_DIALOG_H
+
+#include "ui_attachremotedialog.h"
+
+QT_BEGIN_NAMESPACE
+class QStandardItemModel;
+QT_END_NAMESPACE
+
+namespace Debugger {
+namespace Internal {
+
+class AttachRemoteDialog : public QDialog, Ui::AttachRemoteDialog
+{
+ Q_OBJECT
+
+public:
+ explicit AttachRemoteDialog(QWidget *parent, const QString &pid);
+ int attachPID() const;
+
+private slots:
+ void rebuildProcessList();
+ void procSelected(const QModelIndex &);
+
+private:
+ QString m_defaultPID;
+ QStandardItemModel *m_model;
+};
+
+} // namespace Debugger
+} // namespace Internal
+
+#endif // ATTACHREMOTEDIALOG_H
diff --git a/src/plugins/debugger/attachremotedialog.ui b/src/plugins/debugger/attachremotedialog.ui
new file mode 100644
index 0000000000..4d478e3b0f
--- /dev/null
+++ b/src/plugins/debugger/attachremotedialog.ui
@@ -0,0 +1,64 @@
+<ui version="4.0" >
+ <class>AttachRemoteDialog</class>
+ <widget class="QDialog" name="AttachRemoteDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>561</width>
+ <height>866</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Start Debugger</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout" >
+ <item>
+ <widget class="QLabel" name="pidLabel" >
+ <property name="text" >
+ <string>Attach to Process ID:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="pidLineEdit" />
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QTreeView" name="procView" >
+ <property name="editTriggers" >
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="Line" name="line" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/debugger/breakbyfunction.ui b/src/plugins/debugger/breakbyfunction.ui
new file mode 100644
index 0000000000..06cedb2e46
--- /dev/null
+++ b/src/plugins/debugger/breakbyfunction.ui
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BreakByFunctionDialog</class>
+ <widget class="QDialog" name="BreakByFunctionDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>337</width>
+ <height>101</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Start Debugger</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="functionLabel">
+ <property name="text">
+ <string>Function to break on:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="functionLineEdit"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/debugger/breakcondition.ui b/src/plugins/debugger/breakcondition.ui
new file mode 100644
index 0000000000..4de45fd29d
--- /dev/null
+++ b/src/plugins/debugger/breakcondition.ui
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BreakCondition</class>
+ <widget class="QDialog" name="BreakCondition">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>435</width>
+ <height>142</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelCondition">
+ <property name="text">
+ <string>Condition:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="lineEditCondition"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelIgnoreCount">
+ <property name="text">
+ <string>Ignore count:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="spinBoxIgnoreCount">
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="maximum">
+ <number>999999999</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>BreakCondition</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>BreakCondition</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp
new file mode 100644
index 0000000000..3d83b5e323
--- /dev/null
+++ b/src/plugins/debugger/breakhandler.cpp
@@ -0,0 +1,559 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "breakhandler.h"
+
+#include "assert.h"
+#include "imports.h" // TextEditor::BaseTextMark
+
+#include <QtCore/QDebug>
+#include <QtCore/QFileInfo>
+
+using namespace Debugger;
+using namespace Debugger::Internal;
+
+
+//////////////////////////////////////////////////////////////////
+//
+// BreakpointMarker
+//
+//////////////////////////////////////////////////////////////////
+
+namespace Debugger {
+namespace Internal {
+
+
+// The red blob on the left side in the cpp editor.
+class BreakpointMarker : public TextEditor::BaseTextMark
+{
+ Q_OBJECT
+public:
+ BreakpointMarker(BreakpointData *data, const QString &fileName, int lineNumber)
+ : BaseTextMark(fileName, lineNumber), m_data(data), m_pending(true)
+ {
+ //qDebug() << "CREATE MARKER " << fileName << lineNumber;
+ }
+
+ ~BreakpointMarker()
+ {
+ //qDebug() << "REMOVE MARKER ";
+ m_data = 0;
+ }
+
+ QIcon icon() const
+ {
+ static const QIcon icon(":/gdbdebugger/images/breakpoint.svg");
+ static const QIcon icon2(":/gdbdebugger/images/breakpoint_pending.svg");
+ return m_pending ? icon2 : icon;
+ }
+
+ void setPending(bool pending)
+ {
+ if (pending == m_pending)
+ return;
+ m_pending = pending;
+ updateMarker();
+ }
+
+ void updateBlock(const QTextBlock &)
+ {
+ //qDebug() << "BREAKPOINT MARKER UPDATE BLOCK";
+ }
+
+ void removedFromEditor()
+ {
+ if (!m_data)
+ return;
+
+ BreakHandler *handler = m_data->handler();
+ handler->removeBreakpoint(handler->indexOf(m_data));
+ handler->saveBreakpoints();
+ handler->updateMarkers();
+ }
+
+ void updateLineNumber(int lineNumber)
+ {
+ if (!m_data)
+ return;
+ //if (m_data->markerLineNumber == lineNumber)
+ // return;
+ if (m_data->markerLineNumber != lineNumber) {
+ m_data->markerLineNumber = lineNumber;
+ // FIXME: should we tell gdb about the change?
+ // Ignore it for now, as we would require re-compilation
+ // and debugger re-start anyway.
+ if (0 && !m_data->bpLineNumber.isEmpty()) {
+ if (!m_data->bpNumber.trimmed().isEmpty()) {
+ m_data->pending = true;
+ }
+ }
+ }
+ m_data->lineNumber = QString::number(lineNumber);
+ m_data->handler()->updateMarkers();
+ }
+
+private:
+ BreakpointData *m_data;
+ bool m_pending;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+
+
+//////////////////////////////////////////////////////////////////
+//
+// BreakpointData
+//
+//////////////////////////////////////////////////////////////////
+
+BreakpointData::BreakpointData(BreakHandler *handler)
+{
+ //qDebug() << "CREATE BREAKPOINTDATA" << this;
+ m_handler = handler;
+ pending = true;
+ marker = 0;
+ markerLineNumber = 0;
+ bpMultiple = false;
+}
+
+BreakpointData::~BreakpointData()
+{
+ removeMarker();
+ //qDebug() << "DESTROY BREAKPOINTDATA" << this;
+}
+
+void BreakpointData::removeMarker()
+{
+ BreakpointMarker *m = marker;
+ marker = 0;
+ delete m;
+}
+
+void BreakpointData::updateMarker()
+{
+ if (marker && (markerFileName != marker->fileName()
+ || markerLineNumber != marker->lineNumber()))
+ removeMarker();
+
+ if (!marker && !markerFileName.isEmpty() && markerLineNumber > 0)
+ marker = new BreakpointMarker(this, markerFileName, markerLineNumber);
+
+ if (marker)
+ marker->setPending(pending);
+}
+
+QString BreakpointData::toToolTip() const
+{
+ QString str;
+ str += "<table>";
+ str += "<tr><td>Marker File:</td><td>" + markerFileName + "</td></tr>";
+ str += "<tr><td>Marker Line:</td><td>" + QString::number(markerLineNumber) + "</td></tr>";
+ str += "<tr><td>BP Number:</td><td>" + bpNumber + "</td></tr>";
+ str += "<tr><td>BP Address:</td><td>" + bpAddress + "</td></tr>";
+ str += "<tr><td>----------</td><td></td><td></td></tr>";
+ str += "<tr><td>Property:</td><td>Wanted:</td><td>Actual:</td></tr>";
+ str += "<tr><td></td><td></td><td></td></tr>";
+ str += "<tr><td>Internal Number:</td><td>-</td><td>" + bpNumber + "</td></tr>";
+ str += "<tr><td>File Name:</td><td>" + fileName + "</td><td>" + bpFileName + "</td></tr>";
+ str += "<tr><td>Function Name:</td><td>" + funcName + "</td><td>" + bpFuncName + "</td></tr>";
+ str += "<tr><td>Line Number:</td><td>" + lineNumber + "</td><td>" + bpLineNumber + "</td></tr>";
+ str += "<tr><td>Condition:</td><td>" + condition + "</td><td>" + bpCondition + "</td></tr>";
+ str += "<tr><td>Ignore count:</td><td>" + ignoreCount + "</td><td>" + bpIgnoreCount + "</td></tr>";
+ str += "</table>";
+ return str;
+}
+
+bool BreakpointData::isLocatedAt(const QString &fileName_, int lineNumber_) const
+{
+ /*
+ if (lineNumber != QString::number(lineNumber_))
+ return false;
+ if (fileName == fileName_)
+ return true;
+ if (fileName_.endsWith(fileName))
+ return true;
+ return false;
+ */
+ return lineNumber_ == markerLineNumber && fileName_ == markerFileName;
+}
+
+bool BreakpointData::conditionsMatch() const
+{
+ // same versions of gdb "beautify" the passed condition
+ QString s1 = condition;
+ s1.remove(QChar(' '));
+ QString s2 = bpCondition;
+ s2.remove(QChar(' '));
+ return s1 == s2;
+}
+
+
+//////////////////////////////////////////////////////////////////
+//
+// BreakHandler
+//
+//////////////////////////////////////////////////////////////////
+
+BreakHandler::BreakHandler(QObject *parent)
+ : QAbstractItemModel(parent)
+{
+}
+
+int BreakHandler::columnCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : 6;
+}
+
+int BreakHandler::rowCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : size();
+}
+
+void BreakHandler::removeAt(int index)
+{
+ BreakpointData *data = at(index);
+ m_bp.removeAt(index);
+ delete data;
+}
+
+void BreakHandler::clear()
+{
+ for (int index = size(); --index >= 0; )
+ removeAt(index);
+}
+
+int BreakHandler::findBreakpoint(const BreakpointData &needle)
+{
+ // looks for a breakpoint we might refer to
+ for (int index = 0; index != size(); ++index) {
+ const BreakpointData *data = at(index);
+ // clear hit.
+ if (data->bpNumber == needle.bpNumber)
+ return index;
+ // at least at a position we were looking for
+ // FIXME: breaks multiple breakpoints at the same location
+ if (data->fileName == needle.bpFileName
+ && data->lineNumber == needle.bpLineNumber)
+ return index;
+ }
+ return -1;
+}
+
+int BreakHandler::findBreakpoint(int bpNumber)
+{
+ for (int index = 0; index != size(); ++index)
+ if (at(index)->bpNumber == QString::number(bpNumber))
+ return index;
+ return -1;
+}
+
+void BreakHandler::saveBreakpoints()
+{
+ QList<QVariant> list;
+ for (int index = 0; index != size(); ++index) {
+ const BreakpointData *data = at(index);
+ QMap<QString, QVariant> map;
+ if (!data->fileName.isEmpty())
+ map["filename"] = data->fileName;
+ if (!data->lineNumber.isEmpty())
+ map["linenumber"] = data->lineNumber;
+ if (!data->funcName.isEmpty())
+ map["funcname"] = data->funcName;
+ if (!data->condition.isEmpty())
+ map["condition"] = data->condition;
+ if (!data->ignoreCount.isEmpty())
+ map["ignorecount"] = data->ignoreCount;
+ list.append(map);
+ }
+ setSessionValueRequested("Breakpoints", list);
+}
+
+void BreakHandler::loadBreakpoints()
+{
+ QVariant value;
+ sessionValueRequested("Breakpoints", &value);
+ QList<QVariant> list = value.toList();
+
+ clear();
+ foreach (const QVariant &var, list) {
+ const QMap<QString, QVariant> map = var.toMap();
+ BreakpointData *data = new BreakpointData(this);
+ data->fileName = map["filename"].toString();
+ data->lineNumber = map["linenumber"].toString();
+ data->condition = map["condition"].toString();
+ data->ignoreCount = map["ignorecount"].toString();
+ data->funcName = map["funcname"].toString();
+ data->markerFileName = data->fileName;
+ data->markerLineNumber = data->lineNumber.toInt();
+ append(data);
+ }
+}
+
+void BreakHandler::resetBreakpoints()
+{
+ for (int index = size(); --index >= 0;) {
+ BreakpointData *data = at(index);
+ data->pending = true;
+ data->bpNumber.clear();
+ data->bpFuncName.clear();
+ data->bpFileName.clear();
+ data->bpLineNumber.clear();
+ data->bpCondition.clear();
+ data->bpIgnoreCount.clear();
+ // keep marker data if it was primary
+ if (data->markerFileName != data->fileName)
+ data->markerFileName.clear();
+ if (data->markerLineNumber != data->lineNumber.toInt())
+ data->markerLineNumber = 0;
+ }
+}
+
+void BreakHandler::updateMarkers()
+{
+ for (int index = 0; index != size(); ++index)
+ at(index)->updateMarker();
+ emit layoutChanged();
+}
+
+QVariant BreakHandler::headerData(int section,
+ Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ static QString headers[] = {
+ tr("Number"), tr("Function"), tr("File"), tr("Line"),
+ tr("Condition"), tr("Ignore")
+ };
+ return headers[section];
+ }
+ return QVariant();
+}
+
+QVariant BreakHandler::data(const QModelIndex &mi, int role) const
+{
+ static const QIcon icon(":/gdbdebugger/images/breakpoint.svg");
+ static const QIcon icon2(":/gdbdebugger/images/breakpoint_pending.svg");
+ static const QString empty = QString(QLatin1Char('-'));
+
+ QWB_ASSERT(mi.isValid(), return QVariant());
+
+ if (mi.row() >= size())
+ return QVariant();
+
+ const BreakpointData *data = at(mi.row());
+ switch (mi.column()) {
+ case 0:
+ if (role == Qt::DisplayRole) {
+ QString str = data->bpNumber;
+ return str.isEmpty() ? empty : str;
+ }
+ if (role == Qt::DecorationRole)
+ return data->pending ? icon2 : icon;
+ break;
+ case 1:
+ if (role == Qt::DisplayRole) {
+ QString str = data->pending ? data->funcName : data->bpFuncName;
+ return str.isEmpty() ? empty : str;
+ }
+ break;
+ case 2:
+ if (role == Qt::DisplayRole) {
+ QString str = data->pending ? data->fileName : data->bpFileName;
+ str = QFileInfo(str).fileName();
+ //if (data->bpMultiple && str.isEmpty() && !data->markerFileName.isEmpty())
+ // str = data->markerFileName;
+ return str.isEmpty() ? empty : str;
+ }
+ break;
+ case 3:
+ if (role == Qt::DisplayRole) {
+ QString str = data->pending ? data->lineNumber : data->bpLineNumber;
+ //if (data->bpMultiple && str.isEmpty() && !data->markerFileName.isEmpty())
+ // str = data->markerLineNumber;
+ return str.isEmpty() ? empty : str;
+ }
+ break;
+ case 4:
+ if (role == Qt::DisplayRole)
+ return data->pending ? data->condition : data->bpCondition;
+ if (role == Qt::ToolTipRole)
+ return tr("Breakpoint will only be hit if this condition is met.");
+ break;
+ case 5:
+ if (role == Qt::DisplayRole)
+ return data->pending ? data->ignoreCount : data->bpIgnoreCount;
+ if (role == Qt::ToolTipRole)
+ return tr("Breakpoint will only be hit after being ignored so many times.");
+ break;
+ }
+ if (role == Qt::ToolTipRole)
+ return data->toToolTip();
+ return QVariant();
+}
+
+bool BreakHandler::setData(const QModelIndex &mi, const QVariant &value, int role)
+{
+ if (role != Qt::EditRole)
+ return false;
+
+ BreakpointData *data = at(mi.row());
+ switch (mi.column()) {
+ case 4: {
+ QString val = value.toString();
+ if (val != data->condition) {
+ data->condition = val;
+ dataChanged(mi, mi);
+ }
+ return true;
+ }
+ case 5: {
+ QString val = value.toString();
+ if (val != data->ignoreCount) {
+ data->ignoreCount = val;
+ dataChanged(mi, mi);
+ }
+ return true;
+ }
+ default: {
+ return false;
+ }
+ }
+}
+
+QList<BreakpointData *> BreakHandler::takeRemovedBreakpoints()
+{
+ QList<BreakpointData *> result = m_removed;
+ m_removed.clear();
+ return result;
+}
+
+void BreakHandler::removeBreakpointHelper(int index)
+{
+ BreakpointData *data = m_bp.at(index);
+ m_bp.removeAt(index);
+ data->removeMarker();
+ m_removed.append(data);
+}
+
+
+void BreakHandler::removeBreakpoint(int index)
+{
+ if (index < 0 || index >= size())
+ return;
+ BreakHandler::removeBreakpointHelper(index);
+ emit layoutChanged();
+ saveBreakpoints();
+}
+
+
+int BreakHandler::indexOf(const QString &fileName, int lineNumber)
+{
+ for (int index = 0; index != size(); ++index)
+ if (at(index)->isLocatedAt(fileName, lineNumber))
+ return index;
+ return -1;
+}
+
+void BreakHandler::setBreakpoint(const QString &fileName, int lineNumber)
+{
+ QFileInfo fi(fileName);
+
+ BreakpointData *data = new BreakpointData(this);
+ data->fileName = fileName;
+ data->lineNumber = QString::number(lineNumber);
+ data->pending = true;
+ data->markerFileName = fileName;
+ data->markerLineNumber = lineNumber;
+ append(data);
+ emit layoutChanged();
+ saveBreakpoints();
+ updateMarkers();
+}
+
+void BreakHandler::removeAllBreakpoints()
+{
+ for (int index = size(); --index >= 0;)
+ removeBreakpointHelper(index);
+ emit layoutChanged();
+ saveBreakpoints();
+ updateMarkers();
+}
+
+void BreakHandler::setAllPending()
+{
+ loadBreakpoints();
+ for (int index = size(); --index >= 0;)
+ at(index)->pending = true;
+ saveBreakpoints();
+ updateMarkers();
+}
+
+void BreakHandler::saveSessionData()
+{
+ saveBreakpoints();
+ updateMarkers();
+}
+
+void BreakHandler::loadSessionData()
+{
+ //resetBreakpoints();
+ loadBreakpoints();
+ updateMarkers();
+}
+
+void BreakHandler::activateBreakPoint(int index)
+{
+ const BreakpointData *data = at(index);
+ //qDebug() << "BREAKPOINT ACTIVATED: " << data->fileName;
+ if (!data->markerFileName.isEmpty())
+ emit gotoLocation(data->markerFileName, data->markerLineNumber, false);
+}
+
+void BreakHandler::breakByFunction(const QString &functionName)
+{
+ // One per function is enough for now
+ for (int index = size(); --index >= 0;) {
+ const BreakpointData *data = at(index);
+ QWB_ASSERT(data, break);
+ if (data->funcName == functionName && data->condition.isEmpty()
+ && data->ignoreCount.isEmpty())
+ return;
+ }
+ BreakpointData *data = new BreakpointData(this);
+ data->funcName = functionName;
+ append(data);
+ saveBreakpoints();
+ updateMarkers();
+}
+
+#include "breakhandler.moc"
diff --git a/src/plugins/debugger/breakhandler.h b/src/plugins/debugger/breakhandler.h
new file mode 100644
index 0000000000..8ce63272b7
--- /dev/null
+++ b/src/plugins/debugger/breakhandler.h
@@ -0,0 +1,174 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_BREAKHANDLER_H
+#define DEBUGGER_BREAKHANDLER_H
+
+#include <QtCore/QObject>
+#include <QtCore/QAbstractItemModel>
+
+namespace Debugger {
+namespace Internal {
+
+class BreakpointMarker;
+class BreakHandler;
+
+//////////////////////////////////////////////////////////////////
+//
+// BreakpointData
+//
+//////////////////////////////////////////////////////////////////
+
+class BreakpointData
+{
+public:
+ explicit BreakpointData(BreakHandler *handler);
+ ~BreakpointData();
+
+ void removeMarker();
+ void updateMarker();
+ QString toToolTip() const;
+ BreakHandler *handler() { return m_handler; }
+
+ bool isLocatedAt(const QString &fileName, int lineNumber) const;
+ bool conditionsMatch() const;
+
+private:
+ // Intentionally unimplemented.
+ // Making it copiable is tricky because of the markers.
+ void operator=(const BreakpointData &);
+ BreakpointData(const BreakpointData &);
+
+ // Our owner
+ BreakHandler *m_handler; // not owned.
+
+public:
+ bool pending; // does the debugger engine know about us already?
+
+ // this "user requested information". will get stored in the session
+ QString fileName; // short name of source file
+ QString condition; // condition associated with breakpoint
+ QString ignoreCount; // ignore count associated with breakpoint
+ QString lineNumber; // line in source file
+ QString funcName; // name of containing function
+
+ // this is what gdb produced in response
+ QString bpNumber; // breakpoint number assigned by the debugger engine
+ QString bpCondition; // condition acknowledged by the debugger engine
+ QString bpIgnoreCount; // ignore count acknowledged by the debugger engine
+ QString bpFileName; // file name acknowledged by the debugger engine
+ QString bpLineNumber; // line number acknowledged by the debugger engine
+ QString bpFuncName; // function name acknowledged by the debugger engine
+ QString bpAddress; // address acknowledged by the debugger engine
+ bool bpMultiple; // happens in constructors/gdb
+
+ // taken from either user input or gdb responses
+ QString markerFileName; // used to locate the marker
+ int markerLineNumber;
+
+ // our red blob in the editor
+ BreakpointMarker *marker;
+};
+
+
+//////////////////////////////////////////////////////////////////
+//
+// BreakHandler
+//
+//////////////////////////////////////////////////////////////////
+
+class BreakHandler : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ explicit BreakHandler(QObject *parent = 0);
+
+ void removeAllBreakpoints();
+ void setAllPending();
+ void loadSessionData();
+ void saveSessionData();
+
+ QAbstractItemModel *model() { return this; }
+
+ BreakpointData *at(int index) const { return index < size() ? m_bp.at(index) : 0; }
+ int size() const { return m_bp.size(); }
+ void append(BreakpointData *data) { m_bp.append(data); }
+ void removeAt(int index); // also deletes the marker
+ void clear(); // also deletes all the marker
+ int indexOf(BreakpointData *data) { return m_bp.indexOf(data); }
+ int indexOf(const QString &fileName, int lineNumber);
+ int findBreakpoint(const BreakpointData &data); // returns index
+ int findBreakpoint(int bpNumber); // returns index
+ void updateMarkers();
+
+ QList<BreakpointData *> takeRemovedBreakpoints();
+
+public slots:
+ void setBreakpoint(const QString &fileName, int lineNumber);
+ void breakByFunction(const QString &functionName);
+ void activateBreakPoint(int index);
+ void removeBreakpoint(int index);
+
+signals:
+ void gotoLocation(const QString &fileName, int lineNumber, bool setMarker);
+
+ void sessionValueRequested(const QString &name, QVariant *value);
+ void setSessionValueRequested(const QString &name, const QVariant &value);
+
+private:
+ friend class BreakpointMarker;
+
+ // QAbstractItemModel
+ int columnCount(const QModelIndex &parent) const;
+ int rowCount(const QModelIndex &parent) const;
+ QVariant data(const QModelIndex &index, int role) const;
+ bool setData(const QModelIndex &index, const QVariant &, int role);
+ QModelIndex parent(const QModelIndex &) const { return QModelIndex(); }
+ QModelIndex index(int row, int column, const QModelIndex &) const
+ { return createIndex(row, column); }
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+
+ void markerUpdated(BreakpointMarker *, int lineNumber);
+ void loadBreakpoints();
+ void saveBreakpoints();
+ void resetBreakpoints();
+ void removeBreakpointHelper(int index);
+
+ QList<BreakpointData *> m_bp;
+ QList<BreakpointData *> m_removed;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_BREAKHANDLER_H
diff --git a/src/plugins/debugger/breakwindow.cpp b/src/plugins/debugger/breakwindow.cpp
new file mode 100644
index 0000000000..11248f9a58
--- /dev/null
+++ b/src/plugins/debugger/breakwindow.cpp
@@ -0,0 +1,164 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "breakwindow.h"
+
+#include "ui_breakcondition.h"
+
+#include <QAction>
+#include <QDir>
+#include <QFileInfo>
+#include <QFileInfoList>
+#include <QHeaderView>
+#include <QKeyEvent>
+#include <QMenu>
+#include <QResizeEvent>
+#include <QToolButton>
+#include <QTreeView>
+
+using Debugger::Internal::BreakWindow;
+
+
+BreakWindow::BreakWindow(QWidget *parent)
+ : QTreeView(parent), m_alwaysResizeColumnsToContents(false)
+{
+ setWindowTitle(tr("Breakpoints"));
+ setWindowIcon(QIcon(":/gdbdebugger/images/debugger_breakpoints.png"));
+ setAlternatingRowColors(true);
+ setRootIsDecorated(false);
+ setIconSize(QSize(10, 10));
+
+ connect(this, SIGNAL(activated(QModelIndex)),
+ this, SLOT(rowActivated(QModelIndex)));
+}
+
+void BreakWindow::keyPressEvent(QKeyEvent *event)
+{
+ if (event->key() == Qt::Key_Delete)
+ deleteBreakpoint(currentIndex());
+ QTreeView::keyPressEvent(event);
+}
+
+void BreakWindow::resizeEvent(QResizeEvent *event)
+{
+ QHeaderView *hv = header();
+ int totalSize = event->size().width() - 180;
+ hv->resizeSection(0, 60);
+ hv->resizeSection(1, (totalSize * 30) / 100);
+ hv->resizeSection(2, (totalSize * 30) / 100);
+ hv->resizeSection(3, (totalSize * 30) / 100);
+ hv->resizeSection(4, 70);
+ hv->resizeSection(5, 50);
+ QTreeView::resizeEvent(event);
+}
+
+void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QMenu menu;
+ QModelIndex index = indexAt(ev->pos());
+ QAction *act0 = new QAction("Delete breakpoint", &menu);
+ QAction *act1 = new QAction("Adjust column widths to contents", &menu);
+ QAction *act2 = new QAction("Always adjust column widths to contents", &menu);
+ QAction *act3 = new QAction("Edit condition...", &menu);
+ act2->setCheckable(true);
+ act2->setChecked(m_alwaysResizeColumnsToContents);
+ if (index.isValid()) {
+ menu.addAction(act0);
+ menu.addAction(act3);
+ menu.addSeparator();
+ }
+ menu.addAction(act1);
+ menu.addAction(act2);
+
+ QAction *act = menu.exec(ev->globalPos());
+
+ if (act == act0)
+ deleteBreakpoint(index);
+ else if (act == act1)
+ resizeColumnsToContents();
+ else if (act == act2)
+ setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
+ else if (act == act3)
+ editCondition(index);
+}
+
+void BreakWindow::deleteBreakpoint(const QModelIndex &idx)
+{
+ int row = idx.row();
+ if (row == model()->rowCount() - 1)
+ --row;
+ setCurrentIndex(idx.sibling(row, 0));
+ emit breakPointDeleted(idx.row());
+}
+
+void BreakWindow::editCondition(const QModelIndex &idx)
+{
+ QDialog dlg(this);
+ Ui::BreakCondition ui;
+ ui.setupUi(&dlg);
+
+ int row = idx.row();
+ dlg.setWindowTitle(tr("Conditions on Breakpoint %1").arg(row));
+ ui.lineEditCondition->setText(model()->data(idx.sibling(row, 4)).toString());
+ ui.spinBoxIgnoreCount->setValue(model()->data(idx.sibling(row, 5)).toInt());
+
+ if (dlg.exec() == QDialog::Rejected)
+ return;
+
+ model()->setData(idx.sibling(row, 4), ui.lineEditCondition->text());
+ model()->setData(idx.sibling(row, 5), ui.spinBoxIgnoreCount->value());
+}
+
+void BreakWindow::resizeColumnsToContents()
+{
+ resizeColumnToContents(0);
+ resizeColumnToContents(1);
+ resizeColumnToContents(2);
+ resizeColumnToContents(3);
+}
+
+void BreakWindow::setAlwaysResizeColumnsToContents(bool on)
+{
+ m_alwaysResizeColumnsToContents = on;
+ QHeaderView::ResizeMode mode = on
+ ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
+ header()->setResizeMode(0, mode);
+ header()->setResizeMode(1, mode);
+ header()->setResizeMode(2, mode);
+ header()->setResizeMode(3, mode);
+}
+
+void BreakWindow::rowActivated(const QModelIndex &index)
+{
+ emit breakPointActivated(index.row());
+}
+
diff --git a/src/plugins/debugger/breakwindow.h b/src/plugins/debugger/breakwindow.h
new file mode 100644
index 0000000000..8b17b55145
--- /dev/null
+++ b/src/plugins/debugger/breakwindow.h
@@ -0,0 +1,76 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_BREAKWINDOW_H
+#define DEBUGGER_BREAKWINDOW_H
+
+#include <QTreeView>
+
+namespace Debugger {
+namespace Internal {
+
+class BreakWindow : public QTreeView
+{
+ Q_OBJECT
+
+public:
+ BreakWindow(QWidget *parent = 0);
+
+public slots:
+ void resizeColumnsToContents();
+ void setAlwaysResizeColumnsToContents(bool on);
+
+signals:
+ void breakPointDeleted(int index);
+ void breakPointActivated(int index);
+
+private slots:
+ void rowActivated(const QModelIndex &index);
+
+protected:
+ void resizeEvent(QResizeEvent *ev);
+ void contextMenuEvent(QContextMenuEvent *ev);
+ void keyPressEvent(QKeyEvent *ev);
+
+private:
+ void deleteBreakpoint(const QModelIndex &idx);
+ void editCondition(const QModelIndex &idx);
+
+ bool m_alwaysResizeColumnsToContents;
+};
+
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_BREAKWINDOW
+
diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro
new file mode 100644
index 0000000000..d21eb7cdc0
--- /dev/null
+++ b/src/plugins/debugger/debugger.pro
@@ -0,0 +1,92 @@
+TEMPLATE = lib
+TARGET = Debugger
+
+# CONFIG += single
+include(../../qworkbenchplugin.pri)
+include(../../plugins/projectexplorer/projectexplorer.pri)
+include(../../plugins/find/find.pri)
+include(../../plugins/coreplugin/coreplugin.pri)
+include(../../plugins/texteditor/texteditor.pri)
+include(../../plugins/cpptools/cpptools.pri)
+include(../../libs/cplusplus/cplusplus.pri)
+
+# DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII
+QT += gui network script
+
+HEADERS += assert.h \
+ attachexternaldialog.h \
+ attachremotedialog.h \
+ breakhandler.h \
+ breakwindow.h \
+ debuggerconstants.h \
+ debuggermanager.h \
+ debuggeroutputwindow.h \
+ debuggerplugin.h \
+ debuggerrunner.h \
+ mode.h \
+ disassemblerhandler.h \
+ disassemblerwindow.h \
+ gdbengine.h \
+ gdbmi.h \
+ gdboptionpage.h \
+ idebuggerengine.h \
+ imports.h \
+ moduleshandler.h \
+ moduleswindow.h \
+ procinterrupt.h \
+ registerhandler.h \
+ registerwindow.h \
+ scriptengine.h \
+ stackhandler.h \
+ stackwindow.h \
+ startexternaldialog.h \
+ threadswindow.h \
+ watchhandler.h \
+ watchwindow.h
+
+SOURCES += attachexternaldialog.cpp \
+ attachremotedialog.cpp \
+ breakhandler.cpp \
+ breakwindow.cpp \
+ breakwindow.h \
+ debuggermanager.cpp \
+ debuggeroutputwindow.cpp \
+ debuggerplugin.cpp \
+ debuggerrunner.cpp \
+ mode.cpp \
+ disassemblerhandler.cpp \
+ disassemblerwindow.cpp \
+ gdbengine.cpp \
+ gdbmi.cpp \
+ gdboptionpage.cpp \
+ gdbtypemacros.cpp \
+ gdbengine.h \
+ moduleshandler.cpp \
+ moduleswindow.cpp \
+ procinterrupt.cpp \
+ registerhandler.cpp \
+ registerwindow.cpp \
+ scriptengine.cpp \
+ stackhandler.cpp \
+ stackwindow.cpp \
+ startexternaldialog.cpp \
+ threadswindow.cpp \
+ watchhandler.cpp \
+ watchwindow.cpp
+
+FORMS += attachexternaldialog.ui \
+ attachremotedialog.ui \
+ breakbyfunction.ui \
+ breakcondition.ui \
+ mode.ui \
+ gdboptionpage.ui \
+ gdbtypemacros.ui \
+ startexternaldialog.ui \
+
+RESOURCES += debugger.qrc
+
+false {
+SOURCES += $$PWD/modeltest.cpp
+HEADERS += $$PWD/modeltest.h
+DEFINES += USE_MODEL_TEST=1
+}
diff --git a/src/plugins/debugger/debugger.qrc b/src/plugins/debugger/debugger.qrc
new file mode 100644
index 0000000000..548c27ac9f
--- /dev/null
+++ b/src/plugins/debugger/debugger.qrc
@@ -0,0 +1,24 @@
+<RCC>
+ <qresource prefix="/gdbdebugger" >
+ <file>images/breakpoint.svg</file>
+ <file>images/breakpoint_pending.svg</file>
+ <file>images/debugger_breakpoints.png</file>
+ <file>images/debugger_continue_small.png</file>
+ <file>images/debugger_interrupt_small.png</file>
+ <file>images/debugger_start.png</file>
+ <file>images/debugger_start_small.png</file>
+ <file>images/debugger_stepinto_small.png</file>
+ <file>images/debugger_stepout_small.png</file>
+ <file>images/debugger_stepover_small.png</file>
+ <file>images/debugger_steponeproc_small.png</file>
+ <file>images/debugger_stepoverproc_small.png</file>
+ <file>images/debugger_stop_small.png</file>
+ <file>images/delete.png</file>
+ <file>images/done.png</file>
+ <file>images/empty.svg</file>
+ <file>images/error.png</file>
+ <file>images/location.svg</file>
+ <file>images/newitem.png</file>
+ <file>images/running.png</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h
new file mode 100644
index 0000000000..56d790e074
--- /dev/null
+++ b/src/plugins/debugger/debuggerconstants.h
@@ -0,0 +1,65 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGERCONSTANTS_H
+#define DEBUGGERCONSTANTS_H
+
+namespace Debugger {
+namespace Constants {
+
+// modes and their priorities
+const char * const MODE_DEBUG = "Debugger.Mode.Debug";
+const int P_MODE_DEBUG = 85;
+
+// common actions
+const char * const INTERRUPT = "Debugger.Interrupt";
+const char * const RESET = "Debugger.Reset";
+const char * const STEP = "Debugger.StepLine";
+const char * const STEPOUT = "Debugger.StepOut";
+const char * const NEXT = "Debugger.NextLine";
+const char * const STEPI = "Debugger.StepInstruction";
+const char * const NEXTI = "Debugger.NextInstruction";
+
+const char * const M_VIEW_DEBUG = "Debugger.Menu.View.Debug";
+const char * const G_DEBUG = "Debugger.Group.Debug";
+const char * const G_VIEW_DEBUG = "Debugger.Group.View.Debug";
+
+const char * const C_GDBDEBUGGER = "Gdb Debugger";
+const char * const GDBRUNNING = "Gdb.Running";
+
+const char * const PROPERTY_REGISTER_FORMAT = "Debugger.Property.RegisterFormat";
+
+} // namespace Constants
+} // namespace Debugger
+
+#endif // DEBUGGERCONSTANTS_H
+
diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp
new file mode 100644
index 0000000000..bcf314ae41
--- /dev/null
+++ b/src/plugins/debugger/debuggermanager.cpp
@@ -0,0 +1,1298 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+
+#include "debuggermanager.h"
+
+#include "assert.h"
+#include "debuggerconstants.h"
+#include "idebuggerengine.h"
+
+#include "breakwindow.h"
+#include "disassemblerwindow.h"
+#include "debuggeroutputwindow.h"
+#include "moduleswindow.h"
+#include "registerwindow.h"
+#include "stackwindow.h"
+#include "threadswindow.h"
+#include "watchwindow.h"
+
+#include "ui_breakbyfunction.h"
+
+#include "disassemblerhandler.h"
+#include "breakhandler.h"
+#include "moduleshandler.h"
+#include "registerhandler.h"
+#include "stackhandler.h"
+#include "watchhandler.h"
+
+#include "startexternaldialog.h"
+#include "attachexternaldialog.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTime>
+
+#include <QtGui/QAction>
+#include <QtGui/QComboBox>
+#include <QtGui/QDockWidget>
+#include <QtGui/QErrorMessage>
+#include <QtGui/QFileDialog>
+#include <QtGui/QLabel>
+#include <QtGui/QMainWindow>
+#include <QtGui/QMessageBox>
+#include <QtGui/QPlainTextEdit>
+#include <QtGui/QStatusBar>
+#include <QtGui/QTextBlock>
+#include <QtGui/QTextCursor>
+#include <QtGui/QToolBar>
+#include <QtGui/QToolButton>
+#include <QtGui/QToolTip>
+
+using namespace Debugger;
+using namespace Debugger::Internal;
+using namespace Debugger::Constants;
+
+static const QString tooltipIName = "tooltip";
+
+///////////////////////////////////////////////////////////////////////
+//
+// BreakByFunctionDialog
+//
+///////////////////////////////////////////////////////////////////////
+
+class BreakByFunctionDialog : public QDialog, Ui::BreakByFunctionDialog
+{
+ Q_OBJECT
+
+public:
+ explicit BreakByFunctionDialog(QWidget *parent)
+ : QDialog(parent)
+ {
+ setupUi(this);
+ connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
+ connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+ }
+ QString functionName() const { return functionLineEdit->text(); }
+};
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// DebuggerManager
+//
+///////////////////////////////////////////////////////////////////////
+
+static IDebuggerEngine *gdbEngine = 0;
+static IDebuggerEngine *winEngine = 0;
+static IDebuggerEngine *scriptEngine = 0;
+
+extern IDebuggerEngine *createGdbEngine(DebuggerManager *parent);
+extern IDebuggerEngine *createWinEngine(DebuggerManager *) { return 0; }
+extern IDebuggerEngine *createScriptEngine(DebuggerManager *parent);
+
+DebuggerManager::DebuggerManager()
+{
+ init();
+}
+
+DebuggerManager::~DebuggerManager()
+{
+ delete gdbEngine;
+ delete winEngine;
+ delete scriptEngine;
+}
+
+void DebuggerManager::init()
+{
+ m_status = -1;
+ m_busy = false;
+
+ m_attachedPID = 0;
+ m_startMode = startInternal;
+
+ m_disassemblerHandler = 0;
+ m_modulesHandler = 0;
+ m_registerHandler = 0;
+
+ m_breakWindow = new BreakWindow;
+ m_disassemblerWindow = new DisassemblerWindow;
+ m_modulesWindow = new ModulesWindow;
+ m_outputWindow = new DebuggerOutputWindow;
+ m_registerWindow = new RegisterWindow;
+ m_stackWindow = new StackWindow;
+ m_threadsWindow = new ThreadsWindow;
+ m_localsWindow = new WatchWindow(WatchWindow::LocalsType);
+ m_watchersWindow = new WatchWindow(WatchWindow::WatchersType);
+ //m_tooltipWindow = new WatchWindow(WatchWindow::TooltipType);
+ //m_watchersWindow = new QTreeView;
+ m_tooltipWindow = new QTreeView;
+
+ m_mainWindow = new QMainWindow;
+ m_mainWindow->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
+ m_mainWindow->setDocumentMode(true);
+
+ // Stack
+ m_stackHandler = new StackHandler;
+ QAbstractItemView *stackView =
+ qobject_cast<QAbstractItemView *>(m_stackWindow);
+ stackView->setModel(m_stackHandler->stackModel());
+ connect(stackView, SIGNAL(frameActivated(int)),
+ this, SLOT(activateFrame(int)));
+
+ // Threads
+ m_threadsHandler = new ThreadsHandler;
+ QAbstractItemView *threadsView =
+ qobject_cast<QAbstractItemView *>(m_threadsWindow);
+ threadsView->setModel(m_threadsHandler->threadsModel());
+ connect(threadsView, SIGNAL(threadSelected(int)),
+ this, SLOT(selectThread(int)));
+
+ // Disassembler
+ m_disassemblerHandler = new DisassemblerHandler;
+ QAbstractItemView *disassemblerView =
+ qobject_cast<QAbstractItemView *>(m_disassemblerWindow);
+ disassemblerView->setModel(m_disassemblerHandler->model());
+
+ // Breakpoints
+ m_breakHandler = new BreakHandler;
+ QAbstractItemView *breakView =
+ qobject_cast<QAbstractItemView *>(m_breakWindow);
+ breakView->setModel(m_breakHandler->model());
+ connect(breakView, SIGNAL(breakPointActivated(int)),
+ m_breakHandler, SLOT(activateBreakPoint(int)));
+ connect(breakView, SIGNAL(breakPointDeleted(int)),
+ m_breakHandler, SLOT(removeBreakpoint(int)));
+ connect(m_breakHandler, SIGNAL(gotoLocation(QString,int,bool)),
+ this, SLOT(gotoLocation(QString,int,bool)));
+ connect(m_breakHandler, SIGNAL(sessionValueRequested(QString,QVariant*)),
+ this, SIGNAL(sessionValueRequested(QString,QVariant*)));
+ connect(m_breakHandler, SIGNAL(setSessionValueRequested(QString,QVariant)),
+ this, SIGNAL(setSessionValueRequested(QString,QVariant)));
+
+ // Modules
+ QAbstractItemView *modulesView =
+ qobject_cast<QAbstractItemView *>(m_modulesWindow);
+ m_modulesHandler = new ModulesHandler;
+ modulesView->setModel(m_modulesHandler->model());
+ connect(modulesView, SIGNAL(reloadModulesRequested()),
+ this, SLOT(reloadModules()));
+ connect(modulesView, SIGNAL(loadSymbolsRequested(QString)),
+ this, SLOT(loadSymbols(QString)));
+ connect(modulesView, SIGNAL(loadAllSymbolsRequested()),
+ this, SLOT(loadAllSymbols()));
+
+
+ // Registers
+ QAbstractItemView *registerView =
+ qobject_cast<QAbstractItemView *>(m_registerWindow);
+ m_registerHandler = new RegisterHandler;
+ registerView->setModel(m_registerHandler->model());
+
+
+ m_watchHandler = new WatchHandler;
+
+ // Locals
+ QTreeView *localsView = qobject_cast<QTreeView *>(m_localsWindow);
+ localsView->setModel(m_watchHandler->model());
+ connect(localsView, SIGNAL(requestExpandChildren(QModelIndex)),
+ this, SLOT(expandChildren(QModelIndex)));
+ connect(localsView, SIGNAL(requestCollapseChildren(QModelIndex)),
+ this, SLOT(collapseChildren(QModelIndex)));
+ connect(localsView, SIGNAL(requestAssignValue(QString,QString)),
+ this, SLOT(assignValueInDebugger(QString,QString)));
+ connect(localsView, SIGNAL(requestWatchExpression(QString)),
+ this, SLOT(watchExpression(QString)));
+
+ // Watchers
+ QTreeView *watchersView = qobject_cast<QTreeView *>(m_watchersWindow);
+ watchersView->setModel(m_watchHandler->model());
+ connect(watchersView, SIGNAL(requestAssignValue(QString,QString)),
+ this, SLOT(assignValueInDebugger(QString,QString)));
+ connect(watchersView, SIGNAL(requestExpandChildren(QModelIndex)),
+ this, SLOT(expandChildren(QModelIndex)));
+ connect(watchersView, SIGNAL(requestCollapseChildren(QModelIndex)),
+ this, SLOT(collapseChildren(QModelIndex)));
+ connect(watchersView, SIGNAL(requestWatchExpression(QString)),
+ this, SLOT(watchExpression(QString)));
+ connect(watchersView, SIGNAL(requestRemoveWatchExpression(QString)),
+ this, SLOT(removeWatchExpression(QString)));
+
+ // Tooltip
+ QTreeView *tooltipView = qobject_cast<QTreeView *>(m_tooltipWindow);
+ tooltipView->setModel(m_watchHandler->model());
+
+ connect(m_watchHandler, SIGNAL(watchModelUpdateRequested()),
+ this, SLOT(updateWatchModel()));
+
+ m_startExternalAction = new QAction(this);
+ m_startExternalAction->setText(tr("Start and Debug External Application..."));
+
+ m_attachExternalAction = new QAction(this);
+ m_attachExternalAction->setText(tr("Attach to Running External Application..."));
+
+ m_continueAction = new QAction(this);
+ m_continueAction->setText(tr("Continue"));
+ m_continueAction->setIcon(QIcon(":/gdbdebugger/images/debugger_continue_small.png"));
+
+ m_stopAction = new QAction(this);
+ m_stopAction->setText(tr("Interrupt"));
+ m_stopAction->setIcon(QIcon(":/gdbdebugger/images/debugger_interrupt_small.png"));
+
+ m_resetAction = new QAction(this);
+ m_resetAction->setText(tr("Reset Debugger"));
+
+ m_nextAction = new QAction(this);
+ m_nextAction->setText(tr("Step Over"));
+ //m_nextAction->setShortcut(QKeySequence(tr("F6")));
+ m_nextAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stepover_small.png"));
+
+ m_stepAction = new QAction(this);
+ m_stepAction->setText(tr("Step Into"));
+ //m_stepAction->setShortcut(QKeySequence(tr("F7")));
+ m_stepAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stepinto_small.png"));
+
+ m_nextIAction = new QAction(this);
+ m_nextIAction->setText(tr("Step Over Instruction"));
+ //m_nextIAction->setShortcut(QKeySequence(tr("Shift+F6")));
+ m_nextIAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stepoverproc_small.png"));
+
+ m_stepIAction = new QAction(this);
+ m_stepIAction->setText(tr("Step One Instruction"));
+ //m_stepIAction->setShortcut(QKeySequence(tr("Shift+F9")));
+ m_stepIAction->setIcon(QIcon(":/gdbdebugger/images/debugger_steponeproc_small.png"));
+
+ m_stepOutAction = new QAction(this);
+ m_stepOutAction->setText(tr("Step Out"));
+ //m_stepOutAction->setShortcut(QKeySequence(tr("Shift+F7")));
+ m_stepOutAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stepout_small.png"));
+
+ m_runToLineAction = new QAction(this);
+ m_runToLineAction->setText(tr("Run to Line"));
+
+ m_runToFunctionAction = new QAction(this);
+ m_runToFunctionAction->setText(tr("Run to Outermost Function"));
+
+ m_jumpToLineAction = new QAction(this);
+ m_jumpToLineAction->setText(tr("Jump to Line"));
+
+ m_breakAction = new QAction(this);
+ m_breakAction->setText(tr("Toggle Breakpoint"));
+
+ m_breakByFunctionAction = new QAction(this);
+ m_breakByFunctionAction->setText(tr("Set Breakpoint at Function..."));
+
+ m_breakAtMainAction = new QAction(this);
+ m_breakAtMainAction->setText(tr("Set Breakpoint at Function 'main'"));
+
+ m_debugDumpersAction = new QAction(this);
+ m_debugDumpersAction->setText(tr("Debug Custom Dumpers"));
+ m_debugDumpersAction->setCheckable(true);
+
+ m_skipKnownFramesAction = new QAction(this);
+ m_skipKnownFramesAction->setText(tr("Skip Known Frames When Stepping"));
+ m_skipKnownFramesAction->setCheckable(true);
+
+ m_useCustomDumpersAction = new QAction(this);
+ m_useCustomDumpersAction->setText(tr("Use Custom Display for Qt Objects"));
+ m_useCustomDumpersAction->setToolTip(tr("Checking this will make the debugger "
+ "try to use code to format certain data (QObject, QString, ...) nicely. "));
+ m_useCustomDumpersAction->setCheckable(true);
+ m_useCustomDumpersAction->setChecked(true);
+
+ m_useCustomDumpersAction = new QAction(this);
+ m_useCustomDumpersAction->setText(tr("Use Custom Display for Qt Objects"));
+ m_useCustomDumpersAction->setToolTip(tr("Checking this will make the debugger "
+ "try to use code to format certain data (QObject, QString, ...) nicely. "));
+ m_useCustomDumpersAction->setCheckable(true);
+ m_useCustomDumpersAction->setChecked(true);
+
+ m_useFastStartAction = new QAction(this);
+ m_useFastStartAction->setText(tr("Fast Debugger Start"));
+ m_useFastStartAction->setToolTip(tr("Checking this will make the debugger "
+ "start fast by loading only very few debug symbols on start up. This "
+ "might lead to situations where breakpoints can not be set properly. "
+ "So uncheck this option if you experience breakpoint related problems."));
+ m_useFastStartAction->setCheckable(true);
+ m_useFastStartAction->setChecked(true);
+
+ // FIXME
+ m_useFastStartAction->setChecked(false);
+ m_useFastStartAction->setEnabled(false);
+
+ m_dumpLogAction = new QAction(this);
+ m_dumpLogAction->setText(tr("Dump Log File for Debugging Purposes"));
+
+ m_watchAction = new QAction(this);
+ m_watchAction->setText(tr("Add to Watch Window"));
+
+ // For usuage hints oin focus{In,Out}
+ //connect(m_outputWindow, SIGNAL(statusMessageRequested(QString,int)),
+ // this, SLOT(showStatusMessage(QString,int)));
+
+ connect(m_continueAction, SIGNAL(triggered()),
+ this, SLOT(continueExec()));
+
+ connect(m_startExternalAction, SIGNAL(triggered()),
+ this, SLOT(startExternalApplication()));
+ connect(m_attachExternalAction, SIGNAL(triggered()),
+ this, SLOT(attachExternalApplication()));
+
+ connect(m_stopAction, SIGNAL(triggered()),
+ this, SLOT(interruptDebuggingRequest()));
+ connect(m_resetAction, SIGNAL(triggered()),
+ this, SLOT(exitDebugger()));
+ connect(m_nextAction, SIGNAL(triggered()),
+ this, SLOT(nextExec()));
+ connect(m_stepAction, SIGNAL(triggered()),
+ this, SLOT(stepExec()));
+ connect(m_nextIAction, SIGNAL(triggered()),
+ this, SLOT(nextIExec()));
+ connect(m_stepIAction, SIGNAL(triggered()),
+ this, SLOT(stepIExec()));
+ connect(m_stepOutAction, SIGNAL(triggered()),
+ this, SLOT(stepOutExec()));
+ connect(m_runToLineAction, SIGNAL(triggered()),
+ this, SLOT(runToLineExec()));
+ connect(m_runToFunctionAction, SIGNAL(triggered()),
+ this, SLOT(runToFunctionExec()));
+ connect(m_jumpToLineAction, SIGNAL(triggered()),
+ this, SLOT(jumpToLineExec()));
+ connect(m_watchAction, SIGNAL(triggered()),
+ this, SLOT(addToWatchWindow()));
+ connect(m_breakAction, SIGNAL(triggered()),
+ this, SLOT(toggleBreakpoint()));
+ connect(m_breakByFunctionAction, SIGNAL(triggered()),
+ this, SLOT(breakByFunction()));
+ connect(m_breakAtMainAction, SIGNAL(triggered()),
+ this, SLOT(breakAtMain()));
+
+ connect(m_useFastStartAction, SIGNAL(triggered()),
+ this, SLOT(saveSessionData()));
+ connect(m_useCustomDumpersAction, SIGNAL(triggered()),
+ this, SLOT(saveSessionData()));
+ connect(m_skipKnownFramesAction, SIGNAL(triggered()),
+ this, SLOT(saveSessionData()));
+ connect(m_dumpLogAction, SIGNAL(triggered()),
+ this, SLOT(dumpLog()));
+
+ connect(m_outputWindow, SIGNAL(commandExecutionRequested(QString)),
+ this, SLOT(executeDebuggerCommand(QString)));
+
+
+ m_breakDock = createDockForWidget(m_breakWindow);
+
+ m_disassemblerDock = createDockForWidget(m_disassemblerWindow);
+ connect(m_disassemblerDock->toggleViewAction(), SIGNAL(toggled(bool)),
+ this, SLOT(reloadDisassembler()), Qt::QueuedConnection);
+
+ m_modulesDock = createDockForWidget(m_modulesWindow);
+ connect(m_modulesDock->toggleViewAction(), SIGNAL(toggled(bool)),
+ this, SLOT(reloadModules()), Qt::QueuedConnection);
+
+ m_registerDock = createDockForWidget(m_registerWindow);
+ connect(m_registerDock->toggleViewAction(), SIGNAL(toggled(bool)),
+ this, SLOT(reloadRegisters()), Qt::QueuedConnection);
+
+ m_outputDock = createDockForWidget(m_outputWindow);
+
+ m_stackDock = createDockForWidget(m_stackWindow);
+
+ m_threadsDock = createDockForWidget(m_threadsWindow);
+
+ setStatus(DebuggerProcessNotReady);
+ gdbEngine = createGdbEngine(this);
+ winEngine = createWinEngine(this);
+ scriptEngine = createScriptEngine(this);
+ setDebuggerType(GdbDebugger);
+}
+
+void DebuggerManager::setDebuggerType(DebuggerType type)
+{
+ switch (type) {
+ case GdbDebugger:
+ m_engine = gdbEngine;
+ break;
+ case ScriptDebugger:
+ m_engine = scriptEngine;
+ break;
+ case WinDebugger:
+ m_engine = winEngine;
+ break;
+ }
+}
+
+IDebuggerEngine *DebuggerManager::engine()
+{
+ return m_engine;
+}
+
+IDebuggerManagerAccessForEngines *DebuggerManager::engineInterface()
+{
+ return dynamic_cast<IDebuggerManagerAccessForEngines *>(this);
+}
+
+IDebuggerManagerAccessForDebugMode *DebuggerManager::debugModeInterface()
+{
+ return dynamic_cast<IDebuggerManagerAccessForDebugMode *>(this);
+}
+
+void DebuggerManager::createDockWidgets()
+{
+ QSplitter *localsAndWatchers = new QSplitter(Qt::Vertical, 0);
+ localsAndWatchers->setWindowTitle(m_localsWindow->windowTitle());
+ localsAndWatchers->addWidget(m_localsWindow);
+ localsAndWatchers->addWidget(m_watchersWindow);
+ localsAndWatchers->setStretchFactor(0, 3);
+ localsAndWatchers->setStretchFactor(1, 1);
+ m_watchDock = createDockForWidget(localsAndWatchers);
+}
+
+QDockWidget *DebuggerManager::createDockForWidget(QWidget *widget)
+{
+ QDockWidget *dockWidget = new QDockWidget(widget->windowTitle(), m_mainWindow);
+ dockWidget->setObjectName(widget->windowTitle());
+ //dockWidget->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea);
+ dockWidget->setAllowedAreas(Qt::AllDockWidgetAreas); // that space is needed.
+ //dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
+ dockWidget->setFeatures(QDockWidget::AllDockWidgetFeatures);
+ dockWidget->setTitleBarWidget(new QWidget(dockWidget));
+ dockWidget->setWidget(widget);
+ connect(dockWidget->toggleViewAction(), SIGNAL(toggled(bool)),
+ this, SLOT(dockToggled(bool)), Qt::QueuedConnection);
+ m_dockWidgets.append(dockWidget);
+ return dockWidget;
+}
+
+void DebuggerManager::setSimpleDockWidgetArrangement()
+{
+ foreach (QDockWidget *dockWidget, m_dockWidgets)
+ m_mainWindow->removeDockWidget(dockWidget);
+
+ foreach (QDockWidget *dockWidget, m_dockWidgets) {
+ m_mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dockWidget);
+ dockWidget->show();
+ }
+
+ m_mainWindow->tabifyDockWidget(m_watchDock, m_breakDock);
+ m_mainWindow->tabifyDockWidget(m_watchDock, m_disassemblerDock);
+ m_mainWindow->tabifyDockWidget(m_watchDock, m_modulesDock);
+ m_mainWindow->tabifyDockWidget(m_watchDock, m_outputDock);
+ m_mainWindow->tabifyDockWidget(m_watchDock, m_registerDock);
+ m_mainWindow->tabifyDockWidget(m_watchDock, m_threadsDock);
+
+ // They are rarely used even in ordinary debugging. Hiding them also saves
+ // cycles since the corresponding information won't be retrieved.
+ m_registerDock->hide();
+ m_disassemblerDock->hide();
+ m_modulesDock->hide();
+ m_outputDock->hide();
+}
+
+void DebuggerManager::setLocked(bool locked)
+{
+ const QDockWidget::DockWidgetFeatures features =
+ (locked) ? QDockWidget::NoDockWidgetFeatures :
+ QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable;
+
+ foreach (QDockWidget *dockWidget, m_dockWidgets) {
+ QWidget *titleBarWidget = dockWidget->titleBarWidget();
+ if (locked && !titleBarWidget)
+ titleBarWidget = new QWidget(dockWidget);
+ else if (!locked && titleBarWidget) {
+ delete titleBarWidget;
+ titleBarWidget = 0;
+ }
+ dockWidget->setTitleBarWidget(titleBarWidget);
+ dockWidget->setFeatures(features);
+ }
+}
+
+void DebuggerManager::dockToggled(bool on)
+{
+ QDockWidget *dw = qobject_cast<QDockWidget *>(sender()->parent());
+ if (on && dw)
+ dw->raise();
+}
+
+QAbstractItemModel *DebuggerManager::threadsModel()
+{
+ return qobject_cast<ThreadsWindow*>(m_threadsWindow)->model();
+}
+
+void DebuggerManager::showStatusMessage(const QString &msg, int timeout)
+{
+ Q_UNUSED(timeout)
+ //qDebug() << "STATUS: " << msg;
+ showDebuggerOutput("status:", msg);
+ mainWindow()->statusBar()->showMessage(msg, timeout);
+#if 0
+ QString currentTime = QTime::currentTime().toString("hh:mm:ss.zzz");
+
+ ICore *core = m_pm->getObject<Core::ICore>();
+ //qDebug() << qPrintable(currentTime) << "Setting status: " << msg;
+ if (msg.isEmpty())
+ core->messageManager()->displayStatusBarMessage(msg);
+ else if (timeout == -1)
+ core->messageManager()->displayStatusBarMessage(tr("Debugger: ") + msg);
+ else
+ core->messageManager()->displayStatusBarMessage(tr("Debugger: ") + msg, timeout);
+#endif
+}
+
+void DebuggerManager::notifyStartupFinished()
+{
+ setStatus(DebuggerProcessReady);
+ showStatusMessage(tr("Startup finished. Debugger ready."), -1);
+ if (m_startMode == attachExternal) {
+ // we continue the execution
+ engine()->continueInferior();
+ } else {
+ engine()->runInferior();
+ }
+}
+
+void DebuggerManager::notifyInferiorStopped()
+{
+ resetLocation();
+ setStatus(DebuggerInferiorStopped);
+ showStatusMessage(tr("Stopped."), 5000);
+}
+
+void DebuggerManager::notifyInferiorUpdateFinished()
+{
+ setStatus(DebuggerInferiorReady);
+ showStatusMessage(tr("Stopped."), 5000);
+}
+
+void DebuggerManager::notifyInferiorRunningRequested()
+{
+ setStatus(DebuggerInferiorRunningRequested);
+ showStatusMessage(tr("Running..."), 5000);
+}
+
+void DebuggerManager::notifyInferiorRunning()
+{
+ setStatus(DebuggerInferiorRunning);
+ showStatusMessage(tr("Running..."), 5000);
+}
+
+void DebuggerManager::notifyInferiorExited()
+{
+ setStatus(DebuggerProcessReady);
+ showStatusMessage(tr("Stopped."), 5000);
+}
+
+void DebuggerManager::notifyInferiorPidChanged(int pid)
+{
+ //QMessageBox::warning(0, "PID", "PID: " + QString::number(pid));
+ //qDebug() << "PID: " << pid;
+ emit inferiorPidChanged(pid);
+}
+
+void DebuggerManager::showApplicationOutput(const QString &prefix, const QString &str)
+{
+ applicationOutputAvailable(prefix, str);
+}
+
+void DebuggerManager::shutdown()
+{
+ //qDebug() << "DEBUGGER_MANAGER SHUTDOWN START";
+ engine()->shutdown();
+ // Delete these manually before deleting the manager
+ // (who will delete the models for most views)
+ delete m_breakWindow;
+ delete m_disassemblerWindow;
+ delete m_modulesWindow;
+ delete m_outputWindow;
+ delete m_registerWindow;
+ delete m_stackWindow;
+ delete m_threadsWindow;
+ delete m_tooltipWindow;
+ delete m_watchersWindow;
+ delete m_localsWindow;
+ // These widgets are all in some layout which will take care of deletion.
+ m_breakWindow = 0;
+ m_disassemblerWindow = 0;
+ m_modulesWindow = 0;
+ m_outputWindow = 0;
+ m_registerWindow = 0;
+ m_stackWindow = 0;
+ m_threadsWindow = 0;
+ m_tooltipWindow = 0;
+ m_watchersWindow = 0;
+ m_localsWindow = 0;
+
+ delete m_breakHandler;
+ delete m_disassemblerHandler;
+ delete m_modulesHandler;
+ delete m_registerHandler;
+ delete m_stackHandler;
+ delete m_watchHandler;
+ m_breakHandler = 0;
+ m_disassemblerHandler = 0;
+ m_modulesHandler = 0;
+ m_registerHandler = 0;
+ m_stackHandler = 0;
+ m_watchHandler = 0;
+ //qDebug() << "DEBUGGER_MANAGER SHUTDOWN END";
+}
+
+void DebuggerManager::toggleBreakpoint()
+{
+ QString fileName;
+ int lineNumber = -1;
+ queryCurrentTextEditor(&fileName, &lineNumber, 0);
+ if (lineNumber == -1)
+ return;
+ toggleBreakpoint(fileName, lineNumber);
+}
+
+void DebuggerManager::toggleBreakpoint(const QString &fileName, int lineNumber)
+{
+ int index = m_breakHandler->indexOf(fileName, lineNumber);
+ if (index == -1)
+ breakHandler()->setBreakpoint(fileName, lineNumber);
+ else
+ breakHandler()->removeBreakpoint(index);
+ engine()->attemptBreakpointSynchronization();
+}
+
+void DebuggerManager::setToolTipExpression(const QPoint &pos, const QString &exp)
+{
+ engine()->setToolTipExpression(pos, exp);
+}
+
+void DebuggerManager::updateWatchModel()
+{
+ engine()->updateWatchModel();
+}
+
+void DebuggerManager::expandChildren(const QModelIndex &idx)
+{
+ watchHandler()->expandChildren(idx);
+}
+
+void DebuggerManager::collapseChildren(const QModelIndex &idx)
+{
+ watchHandler()->collapseChildren(idx);
+}
+
+void DebuggerManager::removeWatchExpression(const QString &iname)
+{
+ watchHandler()->removeWatchExpression(iname);
+}
+
+QVariant DebuggerManager::sessionValue(const QString &name)
+{
+ QVariant value;
+ emit sessionValueRequested(name, &value);
+ return value;
+}
+
+void DebuggerManager::querySessionValue(const QString &name, QVariant *value)
+{
+ emit sessionValueRequested(name, value);
+}
+
+void DebuggerManager::setSessionValue(const QString &name, const QVariant &value)
+{
+ emit setSessionValueRequested(name, value);
+}
+
+QVariant DebuggerManager::configValue(const QString &name)
+{
+ QVariant value;
+ emit configValueRequested(name, &value);
+ return value;
+}
+
+void DebuggerManager::queryConfigValue(const QString &name, QVariant *value)
+{
+ emit configValueRequested(name, value);
+}
+
+void DebuggerManager::setConfigValue(const QString &name, const QVariant &value)
+{
+ emit setConfigValueRequested(name, value);
+}
+
+void DebuggerManager::startExternalApplication()
+{
+ if (!startNewDebugger(startExternal))
+ emit debuggingFinished();
+}
+
+void DebuggerManager::attachExternalApplication()
+{
+ if (!startNewDebugger(attachExternal))
+ emit debuggingFinished();
+}
+
+bool DebuggerManager::startNewDebugger(StartMode mode)
+{
+ m_startMode = mode;
+ // FIXME: Clean up
+
+ if (startMode() == startExternal) {
+ StartExternalDialog dlg(mainWindow());
+ dlg.setExecutableFile(
+ configValue(QLatin1String("LastExternalExecutableFile")).toString());
+ dlg.setExecutableArguments(
+ configValue(QLatin1String("LastExternalExecutableArguments")).toString());
+ if (dlg.exec() != QDialog::Accepted)
+ return false;
+ setConfigValue(QLatin1String("LastExternalExecutableFile"),
+ dlg.executableFile());
+ setConfigValue(QLatin1String("LastExternalExecutableArguments"),
+ dlg.executableArguments());
+ m_executable = dlg.executableFile();
+ m_processArgs = dlg.executableArguments().split(' ');
+ m_workingDir = QString();
+ m_attachedPID = -1;
+ } else if (startMode() == attachExternal) {
+ QString pid;
+ AttachExternalDialog dlg(mainWindow(), pid);
+ if (dlg.exec() != QDialog::Accepted)
+ return false;
+ m_executable = QString();
+ m_processArgs = QStringList();
+ m_workingDir = QString();
+ m_attachedPID = dlg.attachPID();
+ } else if (startMode() == startInternal) {
+ if (m_executable.isEmpty()) {
+ QString startDirectory = m_executable;
+ if (m_executable.isEmpty()) {
+ QString fileName;
+ emit currentTextEditorRequested(&fileName, 0, 0);
+ if (!fileName.isEmpty()) {
+ const QFileInfo editorFile(fileName);
+ startDirectory = editorFile.dir().absolutePath();
+ }
+ }
+ StartExternalDialog dlg(mainWindow());
+ dlg.setExecutableFile(startDirectory);
+ if (dlg.exec() != QDialog::Accepted)
+ return false;
+ m_executable = dlg.executableFile();
+ m_processArgs = dlg.executableArguments().split(' ');
+ m_workingDir = QString();
+ m_attachedPID = 0;
+ } else {
+ //m_executable = QDir::convertSeparators(m_executable);
+ //m_processArgs = sd.processArgs.join(QLatin1String(" "));
+ m_attachedPID = 0;
+ }
+ }
+
+ emit debugModeRequested();
+
+ if (m_executable.endsWith(".js"))
+ setDebuggerType(ScriptDebugger);
+ else
+ setDebuggerType(GdbDebugger);
+
+ if (!engine()->startDebugger())
+ return false;
+
+ m_busy = false;
+ setStatus(DebuggerProcessStartingUp);
+ return true;
+}
+
+void DebuggerManager::cleanupViews()
+{
+ resetLocation();
+ breakHandler()->setAllPending();
+ stackHandler()->removeAll();
+ threadsHandler()->removeAll();
+ disassemblerHandler()->removeAll();
+ modulesHandler()->removeAll();
+ watchHandler()->cleanup();
+}
+
+void DebuggerManager::exitDebugger()
+{
+ engine()->exitDebugger();
+ cleanupViews();
+ setStatus(DebuggerProcessNotReady);
+ setBusyCursor(false);
+ emit debuggingFinished();
+}
+
+void DebuggerManager::assignValueInDebugger(const QString &expr, const QString &value)
+{
+ engine()->assignValueInDebugger(expr, value);
+}
+
+void DebuggerManager::activateFrame(int index)
+{
+ engine()->activateFrame(index);
+}
+
+void DebuggerManager::selectThread(int index)
+{
+ engine()->selectThread(index);
+}
+
+void DebuggerManager::loadAllSymbols()
+{
+ engine()->loadAllSymbols();
+}
+
+void DebuggerManager::loadSymbols(const QString &module)
+{
+ engine()->loadSymbols(module);
+}
+
+void DebuggerManager::stepExec()
+{
+ resetLocation();
+ engine()->stepExec();
+}
+
+void DebuggerManager::stepOutExec()
+{
+ resetLocation();
+ engine()->stepOutExec();
+}
+
+void DebuggerManager::nextExec()
+{
+ resetLocation();
+ engine()->nextExec();
+}
+
+void DebuggerManager::stepIExec()
+{
+ resetLocation();
+ engine()->stepIExec();
+}
+
+void DebuggerManager::nextIExec()
+{
+ resetLocation();
+ engine()->nextIExec();
+}
+
+void DebuggerManager::executeDebuggerCommand(const QString &command)
+{
+ engine()->executeDebuggerCommand(command);
+}
+
+void DebuggerManager::sessionLoaded()
+{
+ exitDebugger();
+ loadSessionData();
+}
+
+void DebuggerManager::aboutToSaveSession()
+{
+ saveSessionData();
+}
+
+void DebuggerManager::loadSessionData()
+{
+ m_breakHandler->loadSessionData();
+
+ QVariant value;
+ querySessionValue(QLatin1String("UseFastStart"), &value);
+ m_useFastStartAction->setChecked(value.toBool());
+ querySessionValue(QLatin1String("UseCustomDumpers"), &value);
+ m_useCustomDumpersAction->setChecked(!value.isValid() || value.toBool());
+ querySessionValue(QLatin1String("SkipKnownFrames"), &value);
+ m_skipKnownFramesAction->setChecked(value.toBool());
+ engine()->loadSessionData();
+}
+
+void DebuggerManager::saveSessionData()
+{
+ m_breakHandler->saveSessionData();
+
+ setSessionValue(QLatin1String("UseFastStart"),
+ m_useFastStartAction->isChecked());
+ setSessionValue(QLatin1String("UseCustomDumpers"),
+ m_useCustomDumpersAction->isChecked());
+ setSessionValue(QLatin1String("SkipKnownFrames"),
+ m_skipKnownFramesAction->isChecked());
+ engine()->saveSessionData();
+}
+
+void DebuggerManager::dumpLog()
+{
+ QString fileName = QFileDialog::getSaveFileName(mainWindow(),
+ tr("Save Debugger Log"), QDir::tempPath());
+ if (fileName.isEmpty())
+ return;
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly))
+ return;
+ QTextStream ts(&file);
+ ts << m_outputWindow->inputContents();
+ ts << "\n\n=======================================\n\n";
+ ts << m_outputWindow->combinedContents();
+}
+
+#if 0
+// call after m_gdbProc exited.
+void GdbEngine::procFinished()
+{
+ //qDebug() << "GDB PROCESS FINISHED";
+ setStatus(DebuggerProcessNotReady);
+ showStatusMessage(tr("Done"), 5000);
+ q->m_breakHandler->procFinished();
+ q->m_watchHandler->cleanup();
+ m_stackHandler->m_stackFrames.clear();
+ m_stackHandler->resetModel();
+ m_threadsHandler->resetModel();
+ if (q->m_modulesHandler)
+ q->m_modulesHandler->procFinished();
+ q->resetLocation();
+ setStatus(DebuggerProcessNotReady);
+ emit q->previousModeRequested();
+ emit q->debuggingFinished();
+ //exitDebugger();
+ //showStatusMessage("Gdb killed");
+ m_shortToFullName.clear();
+ m_fullToShortName.clear();
+ m_shared = 0;
+ q->m_busy = false;
+}
+#endif
+
+void DebuggerManager::addToWatchWindow()
+{
+ // requires a selection, but that's the only case we want...
+ QObject *ob = 0;
+ queryCurrentTextEditor(0, 0, &ob);
+ QPlainTextEdit *editor = qobject_cast<QPlainTextEdit*>(ob);
+ if (!editor)
+ return;
+ QTextCursor tc = editor->textCursor();
+ watchExpression(tc.selectedText());
+}
+
+void DebuggerManager::watchExpression(const QString &expression)
+{
+ watchHandler()->watchExpression(expression);
+ //engine()->updateWatchModel();
+}
+
+void DebuggerManager::setBreakpoint(const QString &fileName, int lineNumber)
+{
+ breakHandler()->setBreakpoint(fileName, lineNumber);
+ engine()->attemptBreakpointSynchronization();
+}
+
+void DebuggerManager::breakByFunction(const QString &functionName)
+{
+ breakHandler()->breakByFunction(functionName);
+ engine()->attemptBreakpointSynchronization();
+}
+
+void DebuggerManager::breakByFunction()
+{
+ BreakByFunctionDialog dlg(m_mainWindow);
+ if (dlg.exec())
+ breakByFunction(dlg.functionName());
+}
+
+void DebuggerManager::breakAtMain()
+{
+#ifdef Q_OS_WIN
+ breakByFunction("qMain");
+#else
+ breakByFunction("main");
+#endif
+}
+
+void DebuggerManager::setStatus(int status)
+{
+ //qDebug() << "STATUS CHANGE: from" << m_status << "to" << status;
+
+ if (status == m_status)
+ return;
+
+ m_status = status;
+
+ const bool started = status == DebuggerInferiorRunning
+ || status == DebuggerInferiorRunningRequested
+ || status == DebuggerInferiorStopRequested
+ || status == DebuggerInferiorStopped
+ || status == DebuggerInferiorUpdating
+ || status == DebuggerInferiorUpdateFinishing
+ || status == DebuggerInferiorReady;
+
+ const bool starting = status == DebuggerProcessStartingUp;
+ const bool running = status == DebuggerInferiorRunning;
+ const bool ready = status == DebuggerInferiorStopped
+ || status == DebuggerInferiorReady
+ || status == DebuggerProcessReady;
+
+ m_startExternalAction->setEnabled(!started && !starting);
+ m_attachExternalAction->setEnabled(!started && !starting);
+ m_watchAction->setEnabled(ready);
+ m_breakAction->setEnabled(true);
+
+ bool interruptIsExit = !running;
+ if (interruptIsExit) {
+ m_stopAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stop_small.png"));
+ m_stopAction->setText(tr("Stop Debugger"));
+ } else {
+ m_stopAction->setIcon(QIcon(":/gdbdebugger/images/debugger_interrupt_small.png"));
+ m_stopAction->setText(tr("Interrupt"));
+ }
+
+ m_stopAction->setEnabled(started);
+ m_resetAction->setEnabled(true);
+
+ m_stepAction->setEnabled(ready);
+ m_stepOutAction->setEnabled(ready);
+ m_runToLineAction->setEnabled(ready);
+ m_runToFunctionAction->setEnabled(ready);
+ m_jumpToLineAction->setEnabled(ready);
+ m_nextAction->setEnabled(ready);
+ m_stepIAction->setEnabled(ready);
+ m_nextIAction->setEnabled(ready);
+ //showStatusMessage(QString("started: %1, running: %2").arg(started).arg(running));
+ emit statusChanged(m_status);
+ const bool notbusy = ready || status == DebuggerProcessNotReady;
+ setBusyCursor(!notbusy);
+}
+
+void DebuggerManager::setBusyCursor(bool busy)
+{
+ if (busy == m_busy)
+ return;
+ //qDebug() << "BUSY: " << busy;
+ m_busy = busy;
+
+ QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor);
+ m_breakWindow->setCursor(cursor);
+ m_disassemblerWindow->setCursor(cursor);
+ m_localsWindow->setCursor(cursor);
+ m_modulesWindow->setCursor(cursor);
+ m_outputWindow->setCursor(cursor);
+ m_registerWindow->setCursor(cursor);
+ m_stackWindow->setCursor(cursor);
+ m_threadsWindow->setCursor(cursor);
+ m_tooltipWindow->setCursor(cursor);
+ m_watchersWindow->setCursor(cursor);
+}
+
+bool DebuggerManager::skipKnownFrames() const
+{
+ return m_skipKnownFramesAction->isChecked();
+}
+
+bool DebuggerManager::debugDumpers() const
+{
+ return m_debugDumpersAction->isChecked();
+}
+
+bool DebuggerManager::useCustomDumpers() const
+{
+ return m_useCustomDumpersAction->isChecked();
+}
+
+bool DebuggerManager::useFastStart() const
+{
+ return 0; // && m_useFastStartAction->isChecked();
+}
+
+void DebuggerManager::queryCurrentTextEditor(QString *fileName, int *lineNumber,
+ QObject **object)
+{
+ emit currentTextEditorRequested(fileName, lineNumber, object);
+}
+
+void DebuggerManager::continueExec()
+{
+ engine()->continueInferior();
+}
+
+void DebuggerManager::interruptDebuggingRequest()
+{
+ //qDebug() << "INTERRUPTING AT" << status();
+ bool interruptIsExit = (status() != DebuggerInferiorRunning);
+ if (interruptIsExit)
+ exitDebugger();
+ else {
+ setStatus(DebuggerInferiorStopRequested);
+ engine()->interruptInferior();
+ }
+}
+
+
+void DebuggerManager::runToLineExec()
+{
+ QString fileName;
+ int lineNumber = -1;
+ emit currentTextEditorRequested(&fileName, &lineNumber, 0);
+ if (!fileName.isEmpty())
+ engine()->runToLineExec(fileName, lineNumber);
+}
+
+void DebuggerManager::runToFunctionExec()
+{
+ QString fileName;
+ int lineNumber = -1;
+ QObject *object = 0;
+ emit currentTextEditorRequested(&fileName, &lineNumber, &object);
+ QPlainTextEdit *ed = qobject_cast<QPlainTextEdit*>(object);
+ if (!ed)
+ return;
+ QTextCursor cursor = ed->textCursor();
+ QString functionName = cursor.selectedText();
+ if (functionName.isEmpty()) {
+ const QTextBlock block = cursor.block();
+ const QString line = block.text();
+ foreach (const QString &str, line.trimmed().split('(')) {
+ QString a;
+ for (int i = str.size(); --i >= 0; ) {
+ if (!str.at(i).isLetterOrNumber())
+ break;
+ a = str.at(i) + a;
+ }
+ if (!a.isEmpty()) {
+ functionName = a;
+ break;
+ }
+ }
+ }
+ //qDebug() << "RUN TO FUNCTION " << functionName;
+ if (!functionName.isEmpty())
+ engine()->runToFunctionExec(functionName);
+}
+
+void DebuggerManager::jumpToLineExec()
+{
+ QString fileName;
+ int lineNumber = -1;
+ emit currentTextEditorRequested(&fileName, &lineNumber, 0);
+ if (!fileName.isEmpty())
+ engine()->jumpToLineExec(fileName, lineNumber);
+}
+
+void DebuggerManager::resetLocation()
+{
+ //m_watchHandler->removeMouseMoveCatcher(editor->widget());
+ emit resetLocationRequested();
+}
+
+void DebuggerManager::gotoLocation(const QString &fileName, int line,
+ bool setMarker)
+{
+ emit gotoLocationRequested(fileName, line, setMarker);
+ //m_watchHandler->installMouseMoveCatcher(editor->widget());
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Disassembler specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+void DebuggerManager::reloadDisassembler()
+{
+ if (!m_disassemblerDock || !m_disassemblerDock->isVisible())
+ return;
+ engine()->reloadDisassembler();
+}
+
+void DebuggerManager::disassemblerDockToggled(bool on)
+{
+ if (on)
+ reloadDisassembler();
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Modules specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+void DebuggerManager::reloadModules()
+{
+ if (!m_modulesDock || !m_modulesDock->isVisible())
+ return;
+ engine()->reloadModules();
+}
+
+void DebuggerManager::modulesDockToggled(bool on)
+{
+ if (on)
+ reloadModules();
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Output specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+void DebuggerManager::showDebuggerOutput(const QString &prefix, const QString &msg)
+{
+ m_outputWindow->showOutput(prefix, msg);
+}
+
+void DebuggerManager::showDebuggerInput(const QString &prefix, const QString &msg)
+{
+ m_outputWindow->showInput(prefix, msg);
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Register specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+void DebuggerManager::registerDockToggled(bool on)
+{
+ if (on)
+ reloadRegisters();
+}
+
+void DebuggerManager::reloadRegisters()
+{
+ if (!m_registerDock || !m_registerDock->isVisible())
+ return;
+ engine()->reloadRegisters();
+}
+
+
+#include "debuggermanager.moc"
diff --git a/src/plugins/debugger/debuggermanager.h b/src/plugins/debugger/debuggermanager.h
new file mode 100644
index 0000000000..beaf876d4b
--- /dev/null
+++ b/src/plugins/debugger/debuggermanager.h
@@ -0,0 +1,451 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_DEBUGGERMANAGER_H
+#define DEBUGGER_DEBUGGERMANAGER_H
+
+#include <QtCore/QByteArray>
+#include <QtCore/QObject>
+#include <QtCore/QPoint>
+#include <QtCore/QStringList>
+#include <QtCore/QVariant>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QAbstractItemModel;
+class QDockWidget;
+class QMainWindow;
+class QModelIndex;
+class QSplitter;
+class QWidget;
+QT_END_NAMESPACE
+
+namespace Debugger {
+namespace Internal {
+
+class DebuggerOutputWindow;
+class DebuggerPlugin;
+class DebugMode;
+
+class BreakHandler;
+class DisassemblerHandler;
+class ModulesHandler;
+class RegisterHandler;
+class StackHandler;
+class ThreadsHandler;
+class WatchHandler;
+class WatchData;
+class BreakpointData;
+
+
+// Note: the Debugger process itself is referred to as 'Debugger',
+// whereas the debugged process is referred to as 'Inferior' or 'Debuggee'.
+
+// DebuggerProcessNotReady
+// |
+// DebuggerProcessStartingUp
+// |
+// DebuggerReady [R] [N]
+// | <-------------------------------------.
+// DebuggerInferiorRunningRequested |
+// | |
+// DebuggerInferiorRunning |
+// | |
+// DebuggerInferiorStopRequested |
+// | |
+// DebuggerInferiorStopped |
+// | |
+// DebuggerInferiorUpdating |
+// | |
+// DebuggerInferiorUpdateFinishing |
+// | |
+// DebuggerInferiorReady [C] [N] |
+// | |
+// `---------------------------------------'
+//
+// Allowed actions:
+// [R] : Run
+// [C] : Continue
+// [N] : Step, Next
+
+
+
+enum DebuggerStatus
+{
+ DebuggerProcessNotReady, // Debugger not started
+ DebuggerProcessStartingUp, // Debugger starting up
+ DebuggerProcessReady, // Debugger started, Inferior not yet
+ // running or already finished
+
+ DebuggerInferiorRunningRequested, // Debuggee requested to run
+ DebuggerInferiorRunning, // Debuggee running
+ DebuggerInferiorStopRequested, // Debuggee running, stop requested
+ DebuggerInferiorStopped, // Debuggee stopped
+
+ DebuggerInferiorUpdating, // Debuggee updating data views
+ DebuggerInferiorUpdateFinishing, // Debuggee updating data views aborting
+ DebuggerInferiorReady,
+};
+
+
+class IDebuggerEngine;
+class GdbEngine;
+class ScriptEngine;
+class WinEngine;
+
+// The construct below is not nice but enforces a bit of order. The
+// DebuggerManager interfaces a lots of thing: The DebuggerPlugin,
+// the DebuggerEngines, the RunMode, the handlers and views.
+// Instead of making the whole interface public, we split in into
+// smaller parts and grant friend access only to the classes that
+// need it.
+
+
+//
+// IDebuggerManagerAccessForEngines
+//
+
+class IDebuggerManagerAccessForEngines
+{
+public:
+ virtual ~IDebuggerManagerAccessForEngines() {}
+
+private:
+ // This is the part of the interface that's exclusively seen by the
+ // debugger engines.
+ friend class GdbEngine;
+ friend class ScriptEngine;
+ friend class WinEngine;
+
+ // called from the engines after successful startup
+ virtual void notifyStartupFinished() = 0;
+ virtual void notifyInferiorStopped() = 0;
+ virtual void notifyInferiorUpdateFinished() = 0;
+ virtual void notifyInferiorRunningRequested() = 0;
+ virtual void notifyInferiorRunning() = 0;
+ virtual void notifyInferiorExited() = 0;
+ virtual void notifyInferiorPidChanged(int) = 0;
+
+ virtual DisassemblerHandler *disassemblerHandler() = 0;
+ virtual ModulesHandler *modulesHandler() = 0;
+ virtual BreakHandler *breakHandler() = 0;
+ virtual RegisterHandler *registerHandler() = 0;
+ virtual StackHandler *stackHandler() = 0;
+ virtual ThreadsHandler *threadsHandler() = 0;
+ virtual WatchHandler *watchHandler() = 0;
+
+ virtual void showApplicationOutput(const QString &prefix, const QString &data) = 0;
+ virtual QAction *useCustomDumpersAction() const = 0;
+ virtual QAction *debugDumpersAction() const = 0;
+ virtual bool skipKnownFrames() const = 0;
+ virtual bool debugDumpers() const = 0;
+ virtual bool useCustomDumpers() const = 0;
+ virtual bool useFastStart() const = 0;
+
+ virtual void reloadDisassembler() = 0;
+ virtual void reloadModules() = 0;
+ virtual void reloadRegisters() = 0;
+};
+
+
+//
+// IDebuggerManagerAccessForDebugMode
+//
+
+class IDebuggerManagerAccessForDebugMode
+{
+public:
+ virtual ~IDebuggerManagerAccessForDebugMode() {}
+
+private:
+ friend class DebugMode;
+
+ virtual QWidget *threadsWindow() = 0;
+ virtual QList<QDockWidget*> dockWidgets() const = 0;
+ virtual void createDockWidgets() = 0;
+};
+
+
+//
+// DebuggerManager
+//
+
+class DebuggerManager : public QObject,
+ public IDebuggerManagerAccessForEngines,
+ public IDebuggerManagerAccessForDebugMode
+{
+ Q_OBJECT
+
+public:
+ DebuggerManager();
+ ~DebuggerManager();
+
+ IDebuggerManagerAccessForEngines *engineInterface();
+ IDebuggerManagerAccessForDebugMode *debugModeInterface();
+ QMainWindow *mainWindow() const { return m_mainWindow; }
+
+ enum StartMode { startInternal, startExternal, attachExternal };
+ enum DebuggerType { GdbDebugger, ScriptDebugger, WinDebugger };
+
+public slots:
+ bool startNewDebugger(StartMode mode);
+ void exitDebugger();
+
+ void setSimpleDockWidgetArrangement();
+ void setLocked(bool locked);
+ void dockToggled(bool on);
+
+ void setBusyCursor(bool on);
+ void queryCurrentTextEditor(QString *fileName, int *lineNumber, QObject **ed);
+ void querySessionValue(const QString &name, QVariant *value);
+ void setSessionValue(const QString &name, const QVariant &value);
+ QVariant configValue(const QString &name);
+ void queryConfigValue(const QString &name, QVariant *value);
+ void setConfigValue(const QString &name, const QVariant &value);
+ QVariant sessionValue(const QString &name);
+
+ void gotoLocation(const QString &file, int line, bool setLocationMarker);
+ void resetLocation();
+
+ void interruptDebuggingRequest();
+ void startExternalApplication();
+ void attachExternalApplication();
+
+ void jumpToLineExec();
+ void runToLineExec();
+ void runToFunctionExec();
+ void toggleBreakpoint();
+ void breakByFunction();
+ void breakByFunction(const QString &functionName);
+ void setBreakpoint(const QString &fileName, int lineNumber);
+ void watchExpression(const QString &expression);
+ void breakAtMain();
+ void activateFrame(int index);
+ void selectThread(int index);
+
+ void stepExec();
+ void stepOutExec();
+ void nextExec();
+ void stepIExec();
+ void nextIExec();
+ void continueExec();
+
+ void addToWatchWindow();
+ void updateWatchModel();
+ void removeWatchExpression(const QString &iname);
+ void expandChildren(const QModelIndex &idx);
+ void collapseChildren(const QModelIndex &idx);
+
+ void sessionLoaded();
+ void aboutToSaveSession();
+
+ void assignValueInDebugger(const QString &expr, const QString &value);
+ void executeDebuggerCommand(const QString &command);
+
+ void showStatusMessage(const QString &msg, int timeout); // -1 forever
+
+private slots:
+ void showDebuggerOutput(const QString &prefix, const QString &msg);
+ void showDebuggerInput(const QString &prefix, const QString &msg);
+ void showApplicationOutput(const QString &prefix, const QString &msg);
+
+ void reloadDisassembler();
+ void disassemblerDockToggled(bool on);
+
+ void reloadModules();
+ void modulesDockToggled(bool on);
+ void loadSymbols(const QString &moduleName);
+ void loadAllSymbols();
+
+ void reloadRegisters();
+ void registerDockToggled(bool on);
+ void setStatus(int status);
+
+private:
+ //
+ // Implementation of IDebuggerManagerAccessForEngines
+ //
+ DisassemblerHandler *disassemblerHandler() { return m_disassemblerHandler; }
+ ModulesHandler *modulesHandler() { return m_modulesHandler; }
+ BreakHandler *breakHandler() { return m_breakHandler; }
+ RegisterHandler *registerHandler() { return m_registerHandler; }
+ StackHandler *stackHandler() { return m_stackHandler; }
+ ThreadsHandler *threadsHandler() { return m_threadsHandler; }
+ WatchHandler *watchHandler() { return m_watchHandler; }
+ QAction *useCustomDumpersAction() const { return m_useCustomDumpersAction; }
+ QAction *debugDumpersAction() const { return m_debugDumpersAction; }
+ bool skipKnownFrames() const;
+ bool debugDumpers() const;
+ bool useCustomDumpers() const;
+ bool useFastStart() const;
+
+ void notifyStartupFinished();
+ void notifyInferiorStopped();
+ void notifyInferiorUpdateFinished();
+ void notifyInferiorRunningRequested();
+ void notifyInferiorRunning();
+ void notifyInferiorExited();
+ void notifyInferiorPidChanged(int);
+
+ void cleanupViews();
+
+ //
+ // Implementation of IDebuggerManagerAccessForDebugMode
+ //
+ QWidget *threadsWindow() { return m_threadsWindow; }
+ QList<QDockWidget*> dockWidgets() const { return m_dockWidgets; }
+ void createDockWidgets();
+
+ //
+ // internal implementation
+ //
+ Q_SLOT void loadSessionData();
+ Q_SLOT void saveSessionData();
+ Q_SLOT void dumpLog();
+
+public:
+ // stuff in this block should be made private by moving it to
+ // one of the interfaces
+ QAbstractItemModel *threadsModel();
+ int status() const { return m_status; }
+ StartMode startMode() const { return m_startMode; }
+
+signals:
+ void debuggingFinished();
+ void inferiorPidChanged(qint64 pid);
+ void statusChanged(int newstatus);
+ void debugModeRequested();
+ void previousModeRequested();
+ void statusMessageRequested(const QString &msg, int timeout); // -1 for 'forever'
+ void gotoLocationRequested(const QString &file, int line, bool setLocationMarker);
+ void resetLocationRequested();
+ void currentTextEditorRequested(QString *fileName, int *lineNumber, QObject **ob);
+ void currentMainWindowRequested(QWidget **);
+ void sessionValueRequested(const QString &name, QVariant *value);
+ void setSessionValueRequested(const QString &name, const QVariant &value);
+ void configValueRequested(const QString &name, QVariant *value);
+ void setConfigValueRequested(const QString &name, const QVariant &value);
+ void applicationOutputAvailable(const QString &prefix, const QString &msg);
+
+
+public:
+ // FIXME: make private
+ QString m_executable;
+ QStringList m_environment;
+ QString m_workingDir;
+ QString m_buildDir;
+ QStringList m_processArgs;
+ int m_attachedPID;
+
+private:
+ void init();
+ void setDebuggerType(DebuggerType type);
+ QDockWidget *createDockForWidget(QWidget *widget);
+
+ void shutdown();
+
+ void toggleBreakpoint(const QString &fileName, int lineNumber);
+ void setToolTipExpression(const QPoint &pos, const QString &exp0);
+
+ StartMode m_startMode;
+ DebuggerType m_debuggerType;
+
+ /// Views
+ QMainWindow *m_mainWindow;
+ QDockWidget *m_breakDock;
+ QDockWidget *m_disassemblerDock;
+ QDockWidget *m_modulesDock;
+ QDockWidget *m_outputDock;
+ QDockWidget *m_registerDock;
+ QDockWidget *m_stackDock;
+ QDockWidget *m_threadsDock;
+ QDockWidget *m_watchDock;
+ QList<QDockWidget*> m_dockWidgets;
+
+ BreakHandler *m_breakHandler;
+ DisassemblerHandler *m_disassemblerHandler;
+ ModulesHandler *m_modulesHandler;
+ RegisterHandler *m_registerHandler;
+ StackHandler *m_stackHandler;
+ ThreadsHandler *m_threadsHandler;
+ WatchHandler *m_watchHandler;
+
+ /// Actions
+ friend class DebuggerPlugin;
+ QAction *m_startExternalAction;
+ QAction *m_attachExternalAction;
+ QAction *m_continueAction;
+ QAction *m_stopAction;
+ QAction *m_resetAction; // FIXME: Should not be needed in a stable release
+ QAction *m_stepAction;
+ QAction *m_stepOutAction;
+ QAction *m_runToLineAction;
+ QAction *m_runToFunctionAction;
+ QAction *m_jumpToLineAction;
+ QAction *m_nextAction;
+ QAction *m_watchAction;
+ QAction *m_breakAction;
+ QAction *m_breakByFunctionAction;
+ QAction *m_breakAtMainAction;
+ QAction *m_sepAction;
+ QAction *m_stepIAction;
+ QAction *m_nextIAction;
+ QAction *m_skipKnownFramesAction;
+
+ QAction *m_debugDumpersAction;
+ QAction *m_useCustomDumpersAction;
+ QAction *m_useFastStartAction;
+ QAction *m_dumpLogAction;
+
+ QWidget *m_breakWindow;
+ QWidget *m_disassemblerWindow;
+ QWidget *m_localsWindow;
+ QWidget *m_registerWindow;
+ QWidget *m_modulesWindow;
+ QWidget *m_tooltipWindow;
+ QWidget *m_stackWindow;
+ QWidget *m_threadsWindow;
+ QWidget *m_watchersWindow;
+ DebuggerOutputWindow *m_outputWindow;
+
+ int m_status;
+ bool m_busy;
+
+ IDebuggerEngine *engine();
+ IDebuggerEngine *m_engine;
+};
+
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_DEBUGGERMANAGER_H
diff --git a/src/plugins/debugger/debuggeroutputwindow.cpp b/src/plugins/debugger/debuggeroutputwindow.cpp
new file mode 100644
index 0000000000..7f6bef85d7
--- /dev/null
+++ b/src/plugins/debugger/debuggeroutputwindow.cpp
@@ -0,0 +1,318 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "debuggeroutputwindow.h"
+
+#include <QtCore/QDebug>
+
+#include <QtGui/QAction>
+#include <QtGui/QHBoxLayout>
+#include <QtGui/QVBoxLayout>
+#include <QtGui/QKeyEvent>
+#include <QtGui/QLabel>
+#include <QtGui/QLineEdit>
+#include <QtGui/QMenu>
+#include <QtGui/QSpacerItem>
+#include <QtGui/QSplitter>
+#include <QtGui/QTextBlock>
+
+#ifndef GDBDEBUGGERLEAN
+
+#include <aggregation/aggregate.h>
+#include <find/basetextfind.h>
+
+using namespace Find;
+
+#endif // GDBDEBUGGERLEAN
+
+using Debugger::Internal::DebuggerOutputWindow;
+
+/////////////////////////////////////////////////////////////////////
+//
+// InputPane
+//
+/////////////////////////////////////////////////////////////////////
+
+class DebuggerPane : public QTextEdit
+{
+public:
+ DebuggerPane(QWidget *parent)
+ : QTextEdit(parent)
+ {
+ m_clearContentsAction = new QAction(this);
+ m_clearContentsAction->setText("Clear contents");
+ m_clearContentsAction->setEnabled(true);
+ m_clearContentsAction->setShortcut(Qt::ControlModifier + Qt::Key_R);
+ connect(m_clearContentsAction, SIGNAL(triggered(bool)),
+ parent, SLOT(clearContents()));
+
+ m_saveContentsAction = new QAction(this);
+ m_saveContentsAction->setText("Save contents");
+ m_saveContentsAction->setEnabled(true);
+ }
+
+ void contextMenuEvent(QContextMenuEvent *ev)
+ {
+ QMenu *menu = createStandardContextMenu();
+ menu->addAction(m_clearContentsAction);
+ //menu->addAction(m_saveContentsAction);
+ addContextActions(menu);
+ menu->exec(ev->globalPos());
+ delete menu;
+ }
+
+ virtual void addContextActions(QMenu *) {}
+
+public:
+ QAction *m_clearContentsAction;
+ QAction *m_saveContentsAction;
+};
+
+class InputPane : public DebuggerPane
+{
+ Q_OBJECT
+public:
+ InputPane(QWidget *parent) : DebuggerPane(parent)
+ {
+ m_commandExecutionAction = new QAction(this);
+ m_commandExecutionAction->setText("Execute line");
+ m_commandExecutionAction->setEnabled(true);
+ //m_commandExecutionAction->setShortcut
+ // (Qt::ControlModifier + Qt::Key_Return);
+
+ connect(m_commandExecutionAction, SIGNAL(triggered(bool)),
+ this, SLOT(executeCommand()));
+ }
+
+signals:
+ void commandExecutionRequested(const QString &);
+ void clearContentsRequested();
+ void statusMessageRequested(const QString &, int);
+ void commandSelected(int);
+
+private slots:
+ void executeCommand()
+ {
+ emit commandExecutionRequested(textCursor().block().text());
+ }
+
+private:
+ void keyPressEvent(QKeyEvent *ev)
+ {
+ if (ev->modifiers() == Qt::ControlModifier && ev->key() == Qt::Key_Return)
+ emit commandExecutionRequested(textCursor().block().text());
+ else if (ev->modifiers() == Qt::ControlModifier && ev->key() == Qt::Key_R)
+ emit clearContentsRequested();
+ else
+ QTextEdit::keyPressEvent(ev);
+ }
+
+ void mouseDoubleClickEvent(QMouseEvent *ev)
+ {
+ QString line = cursorForPosition(ev->pos()).block().text();
+ int n = 0;
+
+ // cut time string
+ if (line.size() > 18 && line.at(0) == '[')
+ line = line.mid(18);
+ //qDebug() << line;
+
+ for (int i = 0; i != line.size(); ++i) {
+ QChar c = line.at(i);
+ if (!c.isDigit())
+ break;
+ n = 10 * n + c.unicode() - '0';
+ }
+ emit commandSelected(n);
+ }
+
+ void addContextActions(QMenu *menu)
+ {
+ menu->addAction(m_commandExecutionAction);
+ }
+
+ void focusInEvent(QFocusEvent *ev)
+ {
+ emit statusMessageRequested("Type Ctrl-<Return> to execute a line.", -1);
+ QTextEdit::focusInEvent(ev);
+ }
+
+ void focusOutEvent(QFocusEvent *ev)
+ {
+ emit statusMessageRequested(QString(), -1);
+ QTextEdit::focusOutEvent(ev);
+ }
+
+ QAction *m_commandExecutionAction;
+};
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// CombinedPane
+//
+/////////////////////////////////////////////////////////////////////
+
+class CombinedPane : public DebuggerPane
+{
+ Q_OBJECT
+public:
+ CombinedPane(QWidget *parent)
+ : DebuggerPane(parent)
+ {}
+
+public slots:
+ void gotoResult(int i)
+ {
+ QString needle = QString::number(i) + '^';
+ QString needle2 = "stdout:" + needle;
+ QTextCursor cursor(document());
+ do {
+ const QString line = cursor.block().text();
+ if (line.startsWith(needle) || line.startsWith(needle2)) {
+ setFocus();
+ setTextCursor(cursor);
+ ensureCursorVisible();
+ cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor);
+ setTextCursor(cursor);
+ break;
+ }
+ } while (cursor.movePosition(QTextCursor::Down));
+ }
+};
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// DebuggerOutputWindow
+//
+/////////////////////////////////////////////////////////////////////
+
+DebuggerOutputWindow::DebuggerOutputWindow(QWidget *parent)
+ : QWidget(parent)
+{
+ setWindowTitle(tr("Gdb"));
+
+ QSplitter *m_splitter = new QSplitter(Qt::Horizontal, this);
+ // mixed input/output
+ m_combinedText = new CombinedPane(this);
+ m_combinedText->setReadOnly(true);
+ m_combinedText->setReadOnly(false);
+ m_combinedText->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+
+ // input only
+ m_inputText = new InputPane(this);
+ m_inputText->setReadOnly(false);
+ m_inputText->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
+
+ m_splitter->addWidget(m_inputText);
+ m_splitter->addWidget(m_combinedText);
+
+ QGridLayout *layout = new QGridLayout(this);
+ layout->setMargin(0);
+ layout->addWidget(m_splitter);
+ setLayout(layout);
+
+#ifndef GDBDEBUGGERLEAN
+ Aggregation::Aggregate *aggregate = new Aggregation::Aggregate;
+ aggregate->add(m_combinedText);
+ aggregate->add(new BaseTextFind(m_combinedText));
+
+ aggregate = new Aggregation::Aggregate;
+ aggregate->add(m_inputText);
+ aggregate->add(new BaseTextFind(m_inputText));
+#endif
+
+ connect(m_inputText, SIGNAL(commandExecutionRequested(QString)),
+ this, SIGNAL(commandExecutionRequested(QString)));
+ connect(m_inputText, SIGNAL(statusMessageRequested(QString,int)),
+ this, SIGNAL(statusMessageRequested(QString,int)));
+ connect(m_inputText, SIGNAL(commandSelected(int)),
+ m_combinedText, SLOT(gotoResult(int)));
+};
+
+void DebuggerOutputWindow::onReturnPressed()
+{
+ emit commandExecutionRequested(m_commandEdit->text());
+}
+
+void DebuggerOutputWindow::showOutput(const QString &prefix, const QString &output)
+{
+ if (output.isEmpty())
+ return;
+ foreach (QString line, output.split("\n")) {
+ // FIXME: QTextEdit asserts on really long lines...
+ const int n = 3000;
+ if (line.size() > n)
+ line = line.left(n) + " [...] <cut off>";
+ m_combinedText->append(prefix + line);
+ }
+ QTextCursor cursor = m_combinedText->textCursor();
+ cursor.movePosition(QTextCursor::End);
+ m_combinedText->setTextCursor(cursor);
+ m_combinedText->ensureCursorVisible();
+}
+
+void DebuggerOutputWindow::showInput(const QString &prefix, const QString &input)
+{
+ m_inputText->append(input);
+ QTextCursor cursor = m_inputText->textCursor();
+ cursor.movePosition(QTextCursor::End);
+ m_inputText->setTextCursor(cursor);
+ m_inputText->ensureCursorVisible();
+ showOutput("input:", input);
+}
+
+void DebuggerOutputWindow::clearContents()
+{
+ m_combinedText->clear();
+ m_inputText->clear();
+}
+
+void DebuggerOutputWindow::setCursor(const QCursor &cursor)
+{
+ m_combinedText->setCursor(cursor);
+ m_inputText->setCursor(cursor);
+ QWidget::setCursor(cursor);
+}
+
+QString DebuggerOutputWindow::combinedContents() const
+{
+ return m_combinedText->toPlainText();
+}
+
+QString DebuggerOutputWindow::inputContents() const
+{
+ return m_inputText->toPlainText();
+}
+
+#include "debuggeroutputwindow.moc"
diff --git a/src/plugins/debugger/debuggeroutputwindow.h b/src/plugins/debugger/debuggeroutputwindow.h
new file mode 100644
index 0000000000..f844c0fff9
--- /dev/null
+++ b/src/plugins/debugger/debuggeroutputwindow.h
@@ -0,0 +1,87 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_OUTPUTWINDOW_H
+#define DEBUGGER_OUTPUTWINDOW_H
+
+#include <QtGui/QLineEdit>
+#include <QtGui/QSplitter>
+#include <QtGui/QTextEdit>
+#include <QtGui/QWidget>
+
+namespace Debugger {
+namespace Internal {
+
+class DebuggerOutputWindow : public QWidget
+{
+ Q_OBJECT
+
+public:
+ DebuggerOutputWindow(QWidget *parent = 0);
+
+ QWidget *outputWidget(QWidget *) { return this; }
+ QList<QWidget*> toolBarWidgets(void) const { return QList<QWidget *>(); }
+
+ QString name() const { return windowTitle(); }
+ void visibilityChanged(bool /*visible*/) {}
+
+ void bringPaneToForeground() { emit showPage(); }
+ void setCursor(const QCursor &cursor);
+
+ QString combinedContents() const;
+ QString inputContents() const;
+
+public slots:
+ void clearContents();
+ void showOutput(const QString &prefix, const QString &output);
+ void showInput(const QString &prefix, const QString &input);
+
+signals:
+ void showPage();
+ void statusMessageRequested(const QString &msg, int);
+ void commandExecutionRequested(const QString &cmd);
+
+private slots:
+ void onReturnPressed();
+
+private:
+ QTextEdit *m_combinedText; // combined input/output
+ QTextEdit *m_inputText; // scriptable input alone
+ QLineEdit *m_commandEdit;
+};
+
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_OUTPUTWINDOW_H
+
diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp
new file mode 100644
index 0000000000..8fe6cb0250
--- /dev/null
+++ b/src/plugins/debugger/debuggerplugin.cpp
@@ -0,0 +1,610 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "debuggerplugin.h"
+
+#include "assert.h"
+#include "debuggerconstants.h"
+#include "debuggermanager.h"
+#include "debuggerrunner.h"
+#include "gdboptionpage.h"
+#include "gdbengine.h"
+#include "mode.h"
+
+#include <coreplugin/actionmanager/actionmanagerinterface.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/messagemanager.h>
+#include <coreplugin/modemanager.h>
+#include <coreplugin/uniqueidmanager.h>
+#include <cplusplus/ExpressionUnderCursor.h>
+#include <cppeditor/cppeditorconstants.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/session.h>
+#include <texteditor/basetextmark.h>
+#include <texteditor/itexteditor.h>
+#include <texteditor/texteditorconstants.h>
+#include <texteditor/basetexteditor.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/qplugin.h>
+#include <QtCore/QObject>
+#include <QtCore/QPoint>
+#include <QtCore/QSettings>
+#include <QtGui/QPlainTextEdit>
+#include <QtGui/QTextBlock>
+#include <QtGui/QTextCursor>
+
+
+using namespace Debugger::Internal;
+using namespace Debugger::Constants;
+using namespace TextEditor;
+using namespace Core;
+using namespace ProjectExplorer;
+using namespace CPlusPlus;
+
+
+namespace Debugger {
+namespace Constants {
+
+const char * const STARTEXTERNAL = "Debugger.StartExternal";
+const char * const ATTACHEXTERNAL = "Debugger.AttachExternal";
+
+const char * const RUN_TO_LINE = "Debugger.RunToLine";
+const char * const RUN_TO_FUNCTION = "Debugger.RunToFunction";
+const char * const JUMP_TO_LINE = "Debugger.JumpToLine";
+const char * const TOGGLE_BREAK = "Debugger.ToggleBreak";
+const char * const BREAK_BY_FUNCTION = "Debugger.BreakByFunction";
+const char * const BREAK_AT_MAIN = "Debugger.BreakAtMain";
+const char * const DEBUG_DUMPERS = "Debugger.DebugDumpers";
+const char * const ADD_TO_WATCH = "Debugger.AddToWatch";
+const char * const USE_CUSTOM_DUMPERS = "Debugger.UseCustomDumpers";
+const char * const USE_FAST_START = "Debugger.UseFastStart";
+const char * const SKIP_KNOWN_FRAMES = "Debugger.SkipKnownFrames";
+const char * const DUMP_LOG = "Debugger.DumpLog";
+
+#ifdef Q_OS_MAC
+const char * const INTERRUPT_KEY = "Shift+F5";
+const char * const RESET_KEY = "Ctrl+Shift+F5";
+const char * const STEP_KEY = "F7";
+const char * const STEPOUT_KEY = "Shift+F7";
+const char * const NEXT_KEY = "F6";
+const char * const STEPI_KEY = "Shift+F9";
+const char * const NEXTI_KEY = "Shift+F6";
+const char * const RUN_TO_LINE_KEY = "Shift+F8";
+const char * const RUN_TO_FUNCTION_KEY = "Ctrl+F6";
+const char * const JUMP_TO_LINE_KEY = "Alt+D,Alt+L";
+const char * const TOGGLE_BREAK_KEY = "F8";
+const char * const BREAK_BY_FUNCTION_KEY = "Alt+D,Alt+F";
+const char * const BREAK_AT_MAIN_KEY = "Alt+D,Alt+M";
+const char * const ADD_TO_WATCH_KEY = "Alt+D,Alt+W";
+#else
+const char * const INTERRUPT_KEY = "Shift+F5";
+const char * const RESET_KEY = "Ctrl+Shift+F5";
+const char * const STEP_KEY = "F11";
+const char * const STEPOUT_KEY = "Shift+F11";
+const char * const NEXT_KEY = "F10";
+const char * const STEPI_KEY = "";
+const char * const NEXTI_KEY = "";
+const char * const RUN_TO_LINE_KEY = "";
+const char * const RUN_TO_FUNCTION_KEY = "";
+const char * const JUMP_TO_LINE_KEY = "";
+const char * const TOGGLE_BREAK_KEY = "F9";
+const char * const BREAK_BY_FUNCTION_KEY = "";
+const char * const BREAK_AT_MAIN_KEY = "";
+const char * const ADD_TO_WATCH_KEY = "Ctrl+Alt+Q";
+#endif
+
+} // namespace Constants
+} // namespace Debugger
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// LocationMark
+//
+///////////////////////////////////////////////////////////////////////
+
+class Debugger::Internal::LocationMark
+ : public TextEditor::BaseTextMark
+{
+ Q_OBJECT
+
+public:
+ LocationMark(const QString &fileName, int linenumber)
+ : BaseTextMark(fileName, linenumber)
+ {
+ }
+ ~LocationMark();
+
+ QIcon icon() const;
+ void updateLineNumber(int /*lineNumber*/) {}
+ void updateBlock(const QTextBlock & /*block*/) {}
+ void removedFromEditor() { deleteLater(); }
+private:
+};
+
+LocationMark::~LocationMark()
+{
+ //qDebug() << "LOCATIONMARK DESTRUCTOR" << m_editor;
+}
+
+QIcon LocationMark::icon() const
+{
+ static const QIcon icon(":/gdbdebugger/images/location.svg");
+ return icon;
+}
+
+///////////////////////////////////////////////////////////////////////
+//
+// DebuggerPlugin
+//
+///////////////////////////////////////////////////////////////////////
+
+DebuggerPlugin::DebuggerPlugin()
+{
+ m_pm = 0;
+ m_generalOptionPage = 0;
+ m_typeMacroPage = 0;
+ m_locationMark = 0;
+ m_manager = 0;
+}
+
+DebuggerPlugin::~DebuggerPlugin()
+{}
+
+void DebuggerPlugin::shutdown()
+{
+ if (m_debugMode)
+ m_debugMode->shutdown(); // saves state including manager information
+ QWB_ASSERT(m_manager, /**/);
+ if (m_manager)
+ m_manager->shutdown();
+
+ //qDebug() << "DebuggerPlugin::~DebuggerPlugin";
+ removeObject(m_debugMode);
+ removeObject(m_generalOptionPage);
+ removeObject(m_typeMacroPage);
+
+ // FIXME: when using the line below, BreakWindow etc gets deleted twice.
+ // so better leak for now...
+ delete m_debugMode;
+ m_debugMode = 0;
+
+ delete m_generalOptionPage;
+ m_generalOptionPage = 0;
+
+ delete m_typeMacroPage;
+ m_typeMacroPage = 0;
+
+ delete m_locationMark;
+ m_locationMark = 0;
+
+ delete m_manager;
+ m_manager = 0;
+}
+
+bool DebuggerPlugin::initialize(const QStringList &arguments, QString *error_message)
+{
+ Q_UNUSED(arguments);
+ Q_UNUSED(error_message);
+
+ m_manager = new DebuggerManager;
+
+ m_pm = ExtensionSystem::PluginManager::instance();
+
+ ICore *core = m_pm->getObject<Core::ICore>();
+ QWB_ASSERT(core, return false);
+
+ Core::ActionManagerInterface *actionManager = core->actionManager();
+ QWB_ASSERT(actionManager, return false);
+
+ Core::UniqueIDManager *uidm = core->uniqueIDManager();
+ QWB_ASSERT(uidm, return false);
+
+ QList<int> globalcontext;
+ globalcontext << Core::Constants::C_GLOBAL_ID;
+
+ QList<int> cppcontext;
+ cppcontext << uidm->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX);
+
+ QList<int> debuggercontext;
+ debuggercontext << uidm->uniqueIdentifier(C_GDBDEBUGGER);
+
+ QList<int> cppeditorcontext;
+ cppeditorcontext << uidm->uniqueIdentifier(CppEditor::Constants::C_CPPEDITOR);
+
+ QList<int> texteditorcontext;
+ texteditorcontext << uidm->uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR);
+
+ m_gdbRunningContext = uidm->uniqueIdentifier(Constants::GDBRUNNING);
+
+ //Core::IActionContainer *mcppcontext =
+ // actionManager->actionContainer(CppEditor::Constants::M_CONTEXT);
+
+ Core::IActionContainer *mdebug =
+ actionManager->actionContainer(ProjectExplorer::Constants::M_DEBUG);
+
+ Core::ICommand *cmd = 0;
+ cmd = actionManager->registerAction(m_manager->m_startExternalAction,
+ Constants::STARTEXTERNAL, globalcontext);
+ mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
+
+#ifndef Q_OS_WIN
+ cmd = actionManager->registerAction(m_manager->m_attachExternalAction,
+ Constants::ATTACHEXTERNAL, globalcontext);
+ mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
+#endif
+
+ cmd = actionManager->registerAction(m_manager->m_continueAction,
+ ProjectExplorer::Constants::DEBUG, QList<int>()<< m_gdbRunningContext);
+
+ cmd = actionManager->registerAction(m_manager->m_stopAction,
+ Constants::INTERRUPT, globalcontext);
+ cmd->setAttribute(Core::ICommand::CA_UpdateText);
+ cmd->setAttribute(Core::ICommand::CA_UpdateIcon);
+ cmd->setDefaultKeySequence(QKeySequence(Constants::INTERRUPT_KEY));
+ cmd->setDefaultText(tr("Stop Debugger/Interrupt Debugger"));
+ mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
+
+ cmd = actionManager->registerAction(m_manager->m_resetAction,
+ Constants::RESET, globalcontext);
+ cmd->setAttribute(Core::ICommand::CA_UpdateText);
+ cmd->setDefaultKeySequence(QKeySequence(Constants::RESET_KEY));
+ cmd->setDefaultText(tr("Reset Debugger"));
+ //disabled mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
+
+ QAction *sep = new QAction(this);
+ sep->setSeparator(true);
+ cmd = actionManager->registerAction(sep,
+ QLatin1String("GdbDebugger.Sep1"), globalcontext);
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_nextAction,
+ Constants::NEXT, debuggercontext);
+ cmd->setDefaultKeySequence(QKeySequence(Constants::NEXT_KEY));
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_stepAction,
+ Constants::STEP, debuggercontext);
+ cmd->setDefaultKeySequence(QKeySequence(Constants::STEP_KEY));
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_stepOutAction,
+ Constants::STEPOUT, debuggercontext);
+ cmd->setDefaultKeySequence(QKeySequence(Constants::STEPOUT_KEY));
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_nextIAction,
+ Constants::NEXTI, debuggercontext);
+ cmd->setDefaultKeySequence(QKeySequence(Constants::NEXTI_KEY));
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_stepIAction,
+ Constants::STEPI, debuggercontext);
+ cmd->setDefaultKeySequence(QKeySequence(Constants::STEPI_KEY));
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_runToLineAction,
+ Constants::RUN_TO_LINE, debuggercontext);
+ cmd->setDefaultKeySequence(QKeySequence(Constants::RUN_TO_LINE_KEY));
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_runToFunctionAction,
+ Constants::RUN_TO_FUNCTION, debuggercontext);
+ cmd->setDefaultKeySequence(QKeySequence(Constants::RUN_TO_FUNCTION_KEY));
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_jumpToLineAction,
+ Constants::JUMP_TO_LINE, debuggercontext);
+ mdebug->addAction(cmd);
+
+ sep = new QAction(this);
+ sep->setSeparator(true);
+ cmd = actionManager->registerAction(sep,
+ QLatin1String("GdbDebugger.Sep3"), globalcontext);
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_breakAction,
+ Constants::TOGGLE_BREAK, cppeditorcontext);
+ cmd->setDefaultKeySequence(QKeySequence(Constants::TOGGLE_BREAK_KEY));
+ mdebug->addAction(cmd);
+ //mcppcontext->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_breakByFunctionAction,
+ Constants::BREAK_BY_FUNCTION, globalcontext);
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_breakAtMainAction,
+ Constants::BREAK_AT_MAIN, globalcontext);
+ mdebug->addAction(cmd);
+
+ sep = new QAction(this);
+ sep->setSeparator(true);
+ cmd = actionManager->registerAction(sep,
+ QLatin1String("GdbDebugger.Sep2"), globalcontext);
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_skipKnownFramesAction,
+ Constants::SKIP_KNOWN_FRAMES, globalcontext);
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_useCustomDumpersAction,
+ Constants::USE_CUSTOM_DUMPERS, globalcontext);
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_useFastStartAction,
+ Constants::USE_FAST_START, globalcontext);
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_dumpLogAction,
+ Constants::DUMP_LOG, globalcontext);
+ //cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+D,Ctrl+L")));
+ cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+F11")));
+ mdebug->addAction(cmd);
+
+#ifdef QT_DEBUG
+ cmd = actionManager->registerAction(m_manager->m_debugDumpersAction,
+ Constants::DEBUG_DUMPERS, debuggercontext);
+ mdebug->addAction(cmd);
+#endif
+
+ sep = new QAction(this);
+ sep->setSeparator(true);
+ cmd = actionManager->registerAction(sep,
+ QLatin1String("GdbDebugger.Sep4"), globalcontext);
+ mdebug->addAction(cmd);
+
+ cmd = actionManager->registerAction(m_manager->m_watchAction,
+ Constants::ADD_TO_WATCH, cppeditorcontext);
+ //cmd->setDefaultKeySequence(QKeySequence(tr("ALT+D,ALT+W")));
+ mdebug->addAction(cmd);
+
+ m_generalOptionPage = 0;
+ m_typeMacroPage = 0;
+
+ // FIXME:
+ m_generalOptionPage = new GdbOptionPage(&theGdbSettings());
+ addObject(m_generalOptionPage);
+ m_typeMacroPage = new TypeMacroPage(&theGdbSettings());
+ addObject(m_typeMacroPage);
+
+ m_locationMark = 0;
+
+ m_debugMode = new DebugMode(m_manager, this);
+ //addAutoReleasedObject(m_debugMode);
+ addObject(m_debugMode);
+
+ addAutoReleasedObject(new DebuggerRunner(m_manager));
+
+ // ProjectExplorer
+ connect(projectExplorer()->session(), SIGNAL(sessionLoaded()),
+ m_manager, SLOT(sessionLoaded()));
+ connect(projectExplorer()->session(), SIGNAL(aboutToSaveSession()),
+ m_manager, SLOT(aboutToSaveSession()));
+
+ // EditorManager
+ QObject *editorManager = core->editorManager();
+ connect(editorManager, SIGNAL(editorAboutToClose(Core::IEditor*)),
+ this, SLOT(editorAboutToClose(Core::IEditor*)));
+ connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)),
+ this, SLOT(editorOpened(Core::IEditor*)));
+
+ // Application interaction
+ connect(m_manager, SIGNAL(currentTextEditorRequested(QString*,int*,QObject**)),
+ this, SLOT(queryCurrentTextEditor(QString*,int*,QObject**)));
+
+ connect(m_manager, SIGNAL(setSessionValueRequested(QString,QVariant)),
+ this, SLOT(setSessionValue(QString,QVariant)));
+ connect(m_manager, SIGNAL(sessionValueRequested(QString,QVariant*)),
+ this, SLOT(querySessionValue(QString,QVariant*)));
+ connect(m_manager, SIGNAL(setConfigValueRequested(QString,QVariant)),
+ this, SLOT(setConfigValue(QString,QVariant)));
+ connect(m_manager, SIGNAL(configValueRequested(QString,QVariant*)),
+ this, SLOT(queryConfigValue(QString,QVariant*)));
+
+ connect(m_manager, SIGNAL(resetLocationRequested()),
+ this, SLOT(resetLocation()));
+ connect(m_manager, SIGNAL(gotoLocationRequested(QString,int,bool)),
+ this, SLOT(gotoLocation(QString,int,bool)));
+ connect(m_manager, SIGNAL(statusChanged(int)),
+ this, SLOT(changeStatus(int)));
+ connect(m_manager, SIGNAL(previousModeRequested()),
+ this, SLOT(activatePreviousMode()));
+ connect(m_manager, SIGNAL(debugModeRequested()),
+ this, SLOT(activateDebugMode()));
+
+ return true;
+}
+
+void DebuggerPlugin::extensionsInitialized()
+{
+}
+
+ProjectExplorer::ProjectExplorerPlugin *DebuggerPlugin::projectExplorer() const
+{
+ return m_pm->getObject<ProjectExplorer::ProjectExplorerPlugin>();
+}
+
+/*! Activates the previous mode when the current mode is the debug mode. */
+void DebuggerPlugin::activatePreviousMode()
+{
+ ICore *core = m_pm->getObject<Core::ICore>();
+ Core::ModeManager *const modeManager = core->modeManager();
+
+ if (modeManager->currentMode() == modeManager->mode(Constants::MODE_DEBUG)
+ && !m_previousMode.isEmpty()) {
+ modeManager->activateMode(m_previousMode);
+ m_previousMode.clear();
+ }
+}
+
+void DebuggerPlugin::activateDebugMode()
+{
+ ICore *core = m_pm->getObject<Core::ICore>();
+ Core::ModeManager *modeManager = core->modeManager();
+ m_previousMode = QLatin1String(modeManager->currentMode()->uniqueModeName());
+ modeManager->activateMode(QLatin1String(MODE_DEBUG));
+}
+
+void DebuggerPlugin::queryCurrentTextEditor(QString *fileName, int *lineNumber, QObject **object)
+{
+ ICore *core = m_pm->getObject<Core::ICore>();
+ if (!core || !core->editorManager())
+ return;
+ Core::IEditor *editor = core->editorManager()->currentEditor();
+ ITextEditor *textEditor = qobject_cast<ITextEditor*>(editor);
+ if (!textEditor)
+ return;
+ if (fileName)
+ *fileName = textEditor->file()->fileName();
+ if (lineNumber)
+ *lineNumber = textEditor->currentLine();
+ if (object)
+ *object = textEditor->widget();
+}
+
+void DebuggerPlugin::editorOpened(Core::IEditor *editor)
+{
+ if (ITextEditor *textEditor = qobject_cast<ITextEditor *>(editor)) {
+ connect(textEditor, SIGNAL(markRequested(TextEditor::ITextEditor*,int)),
+ this, SLOT(requestMark(TextEditor::ITextEditor*,int)));
+ connect(editor, SIGNAL(tooltipRequested(TextEditor::ITextEditor*,QPoint,int)),
+ this, SLOT(showToolTip(TextEditor::ITextEditor*,QPoint,int)));
+ }
+}
+
+void DebuggerPlugin::editorAboutToClose(Core::IEditor *editor)
+{
+ if (ITextEditor *textEditor = qobject_cast<ITextEditor *>(editor)) {
+ disconnect(textEditor, SIGNAL(markRequested(TextEditor::ITextEditor*,int)),
+ this, SLOT(requestMark(TextEditor::ITextEditor*,int)));
+ disconnect(editor, SIGNAL(tooltipRequested(TextEditor::ITextEditor*,QPoint,int)),
+ this, SLOT(showToolTip(TextEditor::ITextEditor*,QPoint,int)));
+ }
+}
+
+void DebuggerPlugin::requestMark(TextEditor::ITextEditor *editor, int lineNumber)
+{
+ m_manager->toggleBreakpoint(editor->file()->fileName(), lineNumber);
+}
+
+void DebuggerPlugin::showToolTip(TextEditor::ITextEditor *editor,
+ const QPoint &point, int pos)
+{
+ QPlainTextEdit *plaintext = qobject_cast<QPlainTextEdit*>(editor->widget());
+ if (!plaintext)
+ return;
+
+ QString expr = plaintext->textCursor().selectedText();
+ if (expr.isEmpty()) {
+ QTextCursor tc(plaintext->document());
+ tc.setPosition(pos);
+
+ const QChar ch = editor->characterAt(pos);
+ if (ch.isLetterOrNumber() || ch == QLatin1Char('_'))
+ tc.movePosition(QTextCursor::EndOfWord);
+
+ // Fetch the expression's code.
+ ExpressionUnderCursor expressionUnderCursor;
+ expr = expressionUnderCursor(tc);
+ }
+ //qDebug() << " TOOLTIP EXPR " << expr;
+ m_manager->setToolTipExpression(point, expr);
+}
+
+void DebuggerPlugin::setSessionValue(const QString &name, const QVariant &value)
+{
+ //qDebug() << "SET SESSION VALUE" << name << value;
+ ProjectExplorerPlugin *pe = projectExplorer();
+ if (pe->session())
+ pe->session()->setValue(name, value);
+ else
+ qDebug() << "FIXME: Session does not exist yet";
+}
+
+void DebuggerPlugin::querySessionValue(const QString &name, QVariant *value)
+{
+ ProjectExplorerPlugin *pe = projectExplorer();
+ *value = pe->session()->value(name);
+ //qDebug() << "GET SESSION VALUE: " << name << value;
+}
+
+
+void DebuggerPlugin::setConfigValue(const QString &name, const QVariant &value)
+{
+ QWB_ASSERT(m_debugMode, return);
+ m_debugMode->settings()->setValue(name, value);
+}
+
+void DebuggerPlugin::queryConfigValue(const QString &name, QVariant *value)
+{
+ QWB_ASSERT(m_debugMode, return);
+ *value = m_debugMode->settings()->value(name);
+}
+
+void DebuggerPlugin::resetLocation()
+{
+ //qDebug() << "RESET_LOCATION: current:" << currentTextEditor();
+ //qDebug() << "RESET_LOCATION: locations:" << m_locationMark;
+ //qDebug() << "RESET_LOCATION: stored:" << m_locationMark->editor();
+ delete m_locationMark;
+ m_locationMark = 0;
+}
+
+void DebuggerPlugin::gotoLocation(const QString &fileName, int lineNumber,
+ bool setMarker)
+{
+ TextEditor::BaseTextEditor::openEditorAt(fileName, lineNumber);
+ if (setMarker) {
+ resetLocation();
+ m_locationMark = new LocationMark(fileName, lineNumber);
+ }
+}
+
+void DebuggerPlugin::changeStatus(int status)
+{
+ bool startIsContinue = (status == DebuggerInferiorStopped);
+ ICore *core = m_pm->getObject<Core::ICore>();
+ if (startIsContinue) {
+ core->addAdditionalContext(m_gdbRunningContext);
+ core->updateContext();
+ } else {
+ core->removeAdditionalContext(m_gdbRunningContext);
+ core->updateContext();
+ }
+}
+
+#include "debuggerplugin.moc"
+
+Q_EXPORT_PLUGIN(DebuggerPlugin)
diff --git a/src/plugins/debugger/debuggerplugin.h b/src/plugins/debugger/debuggerplugin.h
new file mode 100644
index 0000000000..0d7cf83bd3
--- /dev/null
+++ b/src/plugins/debugger/debuggerplugin.h
@@ -0,0 +1,111 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGERPLUGIN_H
+#define DEBUGGERPLUGIN_H
+
+#include <projectexplorer/projectexplorer.h>
+#include <extensionsystem/iplugin.h>
+
+#include <QtCore/QObject>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QCursor;
+class QAbstractItemView;
+QT_END_NAMESPACE
+
+namespace Core { class IEditor; }
+namespace TextEditor { class ITextEditor; }
+
+namespace Debugger {
+namespace Internal {
+
+class DebuggerManager;
+class DebugMode;
+class GdbOptionPage;
+class TypeMacroPage;
+class LocationMark;
+
+class DebuggerPlugin : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+
+public:
+ DebuggerPlugin();
+ ~DebuggerPlugin();
+
+private:
+ bool initialize(const QStringList &arguments, QString *error_message);
+ void shutdown();
+ void extensionsInitialized();
+
+private slots:
+ void activatePreviousMode();
+ void activateDebugMode();
+ void queryCurrentTextEditor(QString *fileName, int *line, QObject **object);
+ void editorOpened(Core::IEditor *);
+ void editorAboutToClose(Core::IEditor *);
+ void changeStatus(int status);
+ void requestMark(TextEditor::ITextEditor *editor, int lineNumber);
+ void showToolTip(TextEditor::ITextEditor *editor, const QPoint &pnt, int pos);
+
+ void querySessionValue(const QString &name, QVariant *value);
+ void setSessionValue(const QString &name, const QVariant &value);
+ void queryConfigValue(const QString &name, QVariant *value);
+ void setConfigValue(const QString &name, const QVariant &value);
+
+ void resetLocation();
+ void gotoLocation(const QString &fileName, int line, bool setMarker);
+
+private:
+ friend class DebuggerManager;
+ friend class DebugMode; // FIXME: Just a hack now so that it can access the views
+
+ ProjectExplorer::ProjectExplorerPlugin *projectExplorer() const;
+
+ DebuggerManager *m_manager;
+ DebugMode *m_debugMode;
+
+ ExtensionSystem::PluginManager *m_pm;
+ GdbOptionPage *m_generalOptionPage;
+ TypeMacroPage *m_typeMacroPage;
+
+ QString m_previousMode;
+ LocationMark *m_locationMark;
+ int m_gdbRunningContext;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGERPLUGIN_H
diff --git a/src/plugins/debugger/debuggerrunner.cpp b/src/plugins/debugger/debuggerrunner.cpp
new file mode 100644
index 0000000000..0254bebb9b
--- /dev/null
+++ b/src/plugins/debugger/debuggerrunner.cpp
@@ -0,0 +1,161 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "debuggerrunner.h"
+
+#include "assert.h"
+#include "debuggermanager.h"
+
+#include <projectexplorer/applicationrunconfiguration.h>
+#include <projectexplorer/environment.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectexplorerconstants.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+
+using namespace Debugger::Internal;
+
+using ProjectExplorer::RunConfiguration;
+using ProjectExplorer::RunControl;
+using ProjectExplorer::ApplicationRunConfiguration;
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// DebuggerRunner
+//
+////////////////////////////////////////////////////////////////////////
+
+DebuggerRunner::DebuggerRunner(DebuggerManager *manager)
+ : m_manager(manager)
+{}
+
+bool DebuggerRunner::canRun(RunConfigurationPtr runConfiguration, const QString &mode)
+{
+ return mode == ProjectExplorer::Constants::DEBUGMODE
+ && !qSharedPointerCast<ApplicationRunConfiguration>(runConfiguration).isNull();
+}
+
+QString DebuggerRunner::displayName() const
+{
+ return QObject::tr("Debug");
+}
+
+RunControl* DebuggerRunner::run(RunConfigurationPtr runConfiguration, const QString &mode)
+{
+ Q_UNUSED(mode);
+ Q_ASSERT(mode == ProjectExplorer::Constants::DEBUGMODE);
+ ApplicationRunConfigurationPtr rc =
+ qSharedPointerCast<ApplicationRunConfiguration>(runConfiguration);
+ Q_ASSERT(rc);
+ //qDebug() << "***** Debugging" << rc->name() << rc->executable();
+ return new DebuggerRunControl(m_manager, rc);
+}
+
+QWidget *DebuggerRunner::configurationWidget(RunConfigurationPtr runConfiguration)
+{
+ // NBS TODO: Add GDB-specific configuration widget
+ Q_UNUSED(runConfiguration);
+ return 0;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// DebuggerRunControl
+//
+////////////////////////////////////////////////////////////////////////
+
+
+DebuggerRunControl::DebuggerRunControl(DebuggerManager *manager,
+ QSharedPointer<ApplicationRunConfiguration> runConfiguration)
+ : RunControl(runConfiguration), m_manager(manager), m_running(false)
+{
+ connect(m_manager, SIGNAL(debuggingFinished()),
+ this, SLOT(debuggingFinished()));
+ connect(m_manager, SIGNAL(applicationOutputAvailable(QString, QString)),
+ this, SLOT(slotAddToOutputWindow(QString, QString)));
+ connect(m_manager, SIGNAL(inferiorPidChanged(qint64)),
+ this, SLOT(bringApplicationToForeground(qint64)));
+}
+
+void DebuggerRunControl::start()
+{
+ m_running = true;
+ ApplicationRunConfigurationPtr rc =
+ qSharedPointerCast<ApplicationRunConfiguration>(runConfiguration());
+ QWB_ASSERT(rc, return);
+ ProjectExplorer::Project *project = rc->project();
+ QWB_ASSERT(project, return);
+
+ m_manager->m_executable = rc->executable();
+ m_manager->m_environment = rc->environment().toStringList();
+ m_manager->m_workingDir = rc->workingDirectory();
+ m_manager->m_processArgs = rc->commandLineArguments();
+ m_manager->m_buildDir =
+ project->buildDirectory(project->activeBuildConfiguration());
+ //<daniel> andre: + "\qtc-gdbmacros\"
+
+ //emit addToOutputWindow(this, tr("Debugging %1").arg(m_executable));
+ if (m_manager->startNewDebugger(DebuggerManager::startInternal))
+ emit started();
+ else
+ debuggingFinished();
+}
+
+void DebuggerRunControl::slotAddToOutputWindow(const QString &prefix, const QString &line)
+{
+ Q_UNUSED(prefix);
+ foreach (const QString &l, line.split('\n'))
+ emit addToOutputWindow(this, prefix + l);
+ //emit addToOutputWindow(this, prefix + line);
+}
+
+void DebuggerRunControl::stop()
+{
+ m_manager->exitDebugger();
+}
+
+void DebuggerRunControl::debuggingFinished()
+{
+ m_running = false;
+ //emit addToOutputWindow(this, tr("Debugging %1 finished").arg(m_executable));
+ emit finished();
+}
+
+bool DebuggerRunControl::isRunning() const
+{
+ return m_running;
+}
diff --git a/src/plugins/debugger/debuggerrunner.h b/src/plugins/debugger/debuggerrunner.h
new file mode 100644
index 0000000000..cf8a8d2184
--- /dev/null
+++ b/src/plugins/debugger/debuggerrunner.h
@@ -0,0 +1,96 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGERRUNNER_H
+#define DEBUGGERRUNNER_H
+
+#include <projectexplorer/runconfiguration.h>
+
+namespace ProjectExplorer {
+class ApplicationRunConfiguration;
+}
+
+namespace Debugger {
+namespace Internal {
+
+class DebuggerManager;
+class StartData;
+
+typedef QSharedPointer<ProjectExplorer::RunConfiguration>
+ RunConfigurationPtr;
+
+typedef QSharedPointer<ProjectExplorer::ApplicationRunConfiguration>
+ ApplicationRunConfigurationPtr;
+
+class DebuggerRunner : public ProjectExplorer::IRunConfigurationRunner
+{
+ Q_OBJECT
+
+public:
+ explicit DebuggerRunner(DebuggerManager *manager);
+
+ virtual bool canRun(RunConfigurationPtr runConfiguration, const QString &mode);
+ virtual QString displayName() const;
+ virtual ProjectExplorer::RunControl *run(RunConfigurationPtr runConfiguration,
+ const QString &mode);
+ virtual QWidget *configurationWidget(RunConfigurationPtr runConfiguration);
+
+private:
+ DebuggerManager *m_manager;
+};
+
+
+class DebuggerRunControl : public ProjectExplorer::RunControl
+{
+ Q_OBJECT
+
+public:
+ DebuggerRunControl(DebuggerManager *manager,
+ ApplicationRunConfigurationPtr runConfiguration);
+
+ virtual void start();
+ virtual void stop();
+ virtual bool isRunning() const;
+
+private slots:
+ void debuggingFinished();
+ void slotAddToOutputWindow(const QString &prefix, const QString &line);
+
+private:
+ DebuggerManager *m_manager;
+ bool m_running;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGERRUNNER_H
diff --git a/src/plugins/debugger/disassemblerhandler.cpp b/src/plugins/debugger/disassemblerhandler.cpp
new file mode 100644
index 0000000000..02d3172255
--- /dev/null
+++ b/src/plugins/debugger/disassemblerhandler.cpp
@@ -0,0 +1,187 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "disassemblerhandler.h"
+
+#include "assert.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QAbstractTableModel>
+
+#include <QtGui/QIcon>
+
+using namespace Debugger;
+using namespace Debugger::Internal;
+
+
+//////////////////////////////////////////////////////////////////
+//
+// DisassemblerModel
+//
+//////////////////////////////////////////////////////////////////
+
+/*! A model to represent the stack in a QTreeView. */
+class Debugger::Internal::DisassemblerModel : public QAbstractTableModel
+{
+public:
+ DisassemblerModel(QObject *parent);
+
+ // ItemModel
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+
+ // Properties
+ void setLines(const QList<DisassemblerLine> &lines);
+ QList<DisassemblerLine> lines() const;
+ void setCurrentLine(int line) { m_currentLine = line; }
+
+private:
+ friend class DisassemblerHandler;
+ QList<DisassemblerLine> m_lines;
+ int m_currentLine;
+ QIcon m_positionIcon;
+ QIcon m_emptyIcon;
+};
+
+DisassemblerModel::DisassemblerModel(QObject *parent)
+ : QAbstractTableModel(parent), m_currentLine(0)
+{
+ m_emptyIcon = QIcon(":/gdbdebugger/images/empty.svg");
+ m_positionIcon = QIcon(":/gdbdebugger/images/location.svg");
+}
+
+int DisassemblerModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ return m_lines.size();
+}
+
+int DisassemblerModel::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ return 3;
+}
+
+QVariant DisassemblerModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || index.row() >= m_lines.size())
+ return QVariant();
+
+ const DisassemblerLine &line = m_lines.at(index.row());
+
+ if (role == Qt::DisplayRole) {
+ switch (index.column()) {
+ case 0:
+ return line.addressDisplay;
+ case 1:
+ return line.symbolDisplay;
+ case 2:
+ return line.mnemonic;
+ }
+ } else if (role == Qt::ToolTipRole) {
+ return QString();
+ } else if (role == Qt::DecorationRole && index.column() == 0) {
+ // Return icon that indicates whether this is the active stack frame
+ return (index.row() == m_currentLine) ? m_positionIcon : m_emptyIcon;
+ }
+
+ return QVariant();
+}
+
+QVariant DisassemblerModel::headerData(int section, Qt::Orientation orientation,
+ int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ static const char * const headers[] = {
+ QT_TR_NOOP("Address"),
+ QT_TR_NOOP("Symbol"),
+ QT_TR_NOOP("Mnemonic"),
+ };
+ if (section < 3)
+ return tr(headers[section]);
+ }
+ return QVariant();
+}
+
+void DisassemblerModel::setLines(const QList<DisassemblerLine> &lines)
+{
+ m_lines = lines;
+ if (m_currentLine >= m_lines.size())
+ m_currentLine = m_lines.size() - 1;
+ reset();
+}
+
+QList<DisassemblerLine> DisassemblerModel::lines() const
+{
+ return m_lines;
+}
+
+
+
+//////////////////////////////////////////////////////////////////
+//
+// DisassemblerHandler
+//
+//////////////////////////////////////////////////////////////////
+
+DisassemblerHandler::DisassemblerHandler()
+{
+ m_model = new DisassemblerModel(this);
+}
+
+void DisassemblerHandler::removeAll()
+{
+ m_model->m_lines.clear();
+}
+
+QAbstractItemModel *DisassemblerHandler::model() const
+{
+ return m_model;
+}
+
+void DisassemblerHandler::setLines(const QList<DisassemblerLine> &lines)
+{
+ m_model->setLines(lines);
+}
+
+QList<DisassemblerLine> DisassemblerHandler::lines() const
+{
+ return m_model->lines();
+}
+
+void DisassemblerHandler::setCurrentLine(int line)
+{
+ m_model->setCurrentLine(line);
+}
diff --git a/src/plugins/debugger/disassemblerhandler.h b/src/plugins/debugger/disassemblerhandler.h
new file mode 100644
index 0000000000..9de497a1b4
--- /dev/null
+++ b/src/plugins/debugger/disassemblerhandler.h
@@ -0,0 +1,78 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DISASSEMBLERHANDLER_H
+#define DISASSEMBLERHANDLER_H
+
+#include <QtCore/QObject>
+#include <QtCore/QAbstractItemModel>
+
+#include <QtGui/QIcon>
+
+namespace Debugger {
+namespace Internal {
+
+class DisassemblerLine
+{
+public:
+ QString address;
+ QString symbol;
+ QString addressDisplay;
+ QString symbolDisplay;
+ QString mnemonic;
+};
+
+class DisassemblerModel;
+
+class DisassemblerHandler : public QObject
+{
+ Q_OBJECT
+
+public:
+ DisassemblerHandler();
+ QAbstractItemModel *model() const;
+
+public slots:
+ void removeAll();
+
+ void setLines(const QList<DisassemblerLine> &lines);
+ QList<DisassemblerLine> lines() const;
+ void setCurrentLine(int line);
+
+private:
+ DisassemblerModel *m_model;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DISASSEMBLERHANDLER_H
diff --git a/src/plugins/debugger/disassemblerwindow.cpp b/src/plugins/debugger/disassemblerwindow.cpp
new file mode 100644
index 0000000000..8130031d70
--- /dev/null
+++ b/src/plugins/debugger/disassemblerwindow.cpp
@@ -0,0 +1,140 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "disassemblerwindow.h"
+
+#include <QAction>
+#include <QDebug>
+#include <QHeaderView>
+#include <QMenu>
+#include <QResizeEvent>
+
+
+using namespace Debugger::Internal;
+
+DisassemblerWindow::DisassemblerWindow()
+ : m_alwaysResizeColumnsToContents(true), m_alwaysReloadContents(false)
+{
+ setWindowTitle(tr("Disassembler"));
+ setSortingEnabled(false);
+ setAlternatingRowColors(true);
+ setRootIsDecorated(false);
+ header()->hide();
+ //setIconSize(QSize(10, 10));
+ //setWindowIcon(QIcon(":/gdbdebugger/images/debugger_breakpoints.png"));
+ //QHeaderView *hv = header();
+ //hv->setDefaultAlignment(Qt::AlignLeft);
+ //hv->setClickable(true);
+ //hv->setSortIndicatorShown(true);
+}
+
+void DisassemblerWindow::resizeEvent(QResizeEvent *ev)
+{
+ //QHeaderView *hv = header();
+ //int totalSize = ev->size().width() - 110;
+ //hv->resizeSection(0, 60);
+ //hv->resizeSection(1, (totalSize * 50) / 100);
+ //hv->resizeSection(2, (totalSize * 50) / 100);
+ //hv->resizeSection(3, 50);
+ QTreeView::resizeEvent(ev);
+}
+
+void DisassemblerWindow::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QMenu menu;
+ //QTreeWidgetItem *item = itemAt(ev->pos());
+ QAction *act1 = new QAction("Adjust column widths to contents", &menu);
+ QAction *act2 = new QAction("Always adjust column widths to contents", &menu);
+ act2->setCheckable(true);
+ act2->setChecked(m_alwaysResizeColumnsToContents);
+ QAction *act3 = new QAction("Reload disassembler listing", &menu);
+ QAction *act4 = new QAction("Always reload disassembler listing", &menu);
+ act4->setCheckable(true);
+ act4->setChecked(m_alwaysReloadContents);
+ //if (item) {
+ // menu.addAction(act0);
+ // menu.addSeparator();
+ //}
+ menu.addAction(act3);
+ //menu.addAction(act4);
+ menu.addSeparator();
+ menu.addAction(act1);
+ menu.addAction(act2);
+
+ QAction *act = menu.exec(ev->globalPos());
+
+ if (act == act1)
+ resizeColumnsToContents();
+ else if (act == act2)
+ setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
+ else if (act == act3)
+ reloadContents();
+ else if (act == act2)
+ setAlwaysReloadContents(!m_alwaysReloadContents);
+}
+
+void DisassemblerWindow::resizeColumnsToContents()
+{
+ resizeColumnToContents(0);
+ resizeColumnToContents(1);
+ resizeColumnToContents(2);
+}
+
+void DisassemblerWindow::setAlwaysResizeColumnsToContents(bool on)
+{
+ m_alwaysResizeColumnsToContents = on;
+ QHeaderView::ResizeMode mode = on
+ ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
+ header()->setResizeMode(0, mode);
+ header()->setResizeMode(1, mode);
+ header()->setResizeMode(2, mode);
+}
+
+void DisassemblerWindow::setAlwaysReloadContents(bool on)
+{
+ m_alwaysReloadContents = on;
+ if (m_alwaysReloadContents)
+ reloadContents();
+}
+
+void DisassemblerWindow::reloadContents()
+{
+ emit reloadDisassemblerRequested();
+}
+
+
+void DisassemblerWindow::setModel(QAbstractItemModel *model)
+{
+ QTreeView::setModel(model);
+ setAlwaysResizeColumnsToContents(true);
+}
+
diff --git a/src/plugins/debugger/disassemblerwindow.h b/src/plugins/debugger/disassemblerwindow.h
new file mode 100644
index 0000000000..3715afca21
--- /dev/null
+++ b/src/plugins/debugger/disassemblerwindow.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_DISASSEMBLERWINDOW_H
+#define DEBUGGER_DISASSEMBLERWINDOW_H
+
+#include <QTreeView>
+
+namespace Debugger {
+namespace Internal {
+
+class DisassemblerWindow : public QTreeView
+{
+ Q_OBJECT
+
+public:
+ DisassemblerWindow();
+ void setModel(QAbstractItemModel *model);
+
+signals:
+ void reloadDisassemblerRequested();
+
+public slots:
+ void resizeColumnsToContents();
+ void setAlwaysResizeColumnsToContents(bool on);
+ void reloadContents();
+ void setAlwaysReloadContents(bool on);
+
+protected:
+ void resizeEvent(QResizeEvent *ev);
+ void contextMenuEvent(QContextMenuEvent *ev);
+
+private:
+ bool m_alwaysResizeColumnsToContents;
+ bool m_alwaysReloadContents;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_DISASSEMBLERWINDOW_H
+
diff --git a/src/plugins/debugger/gdbengine.cpp b/src/plugins/debugger/gdbengine.cpp
new file mode 100644
index 0000000000..9a29bc8675
--- /dev/null
+++ b/src/plugins/debugger/gdbengine.cpp
@@ -0,0 +1,4035 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+
+#include "gdbengine.h"
+
+#include "assert.h"
+#include "debuggerconstants.h"
+#include "debuggermanager.h"
+#include "gdbmi.h"
+#include "procinterrupt.h"
+
+#include "disassemblerhandler.h"
+#include "breakhandler.h"
+#include "moduleshandler.h"
+#include "registerhandler.h"
+#include "stackhandler.h"
+#include "watchhandler.h"
+
+#include "startexternaldialog.h"
+#include "attachexternaldialog.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTime>
+#include <QtCore/QTimer>
+
+#include <QtGui/QAction>
+#include <QtGui/QLabel>
+#include <QtGui/QMainWindow>
+#include <QtGui/QMessageBox>
+#include <QtGui/QToolTip>
+
+#if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
+#include <unistd.h>
+#include <dlfcn.h>
+#endif
+
+using namespace Debugger;
+using namespace Debugger::Internal;
+using namespace Debugger::Constants;
+
+Q_DECLARE_METATYPE(Debugger::Internal::GdbMi);
+
+//#define DEBUG_PENDING 1
+//#define DEBUG_SUBITEM 1
+
+#if DEBUG_PENDING
+# define PENDING_DEBUG(s) qDebug() << s
+#else
+# define PENDING_DEBUG(s)
+#endif
+
+static const QString tooltipIName = "tooltip";
+
+///////////////////////////////////////////////////////////////////////
+//
+// GdbSettings
+//
+///////////////////////////////////////////////////////////////////////
+
+GdbSettings &Debugger::Internal::theGdbSettings()
+{
+ static GdbSettings settings;
+ return settings;
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// GdbCommandType
+//
+///////////////////////////////////////////////////////////////////////
+
+enum GdbCommandType
+{
+ GdbInvalidCommand = 0,
+
+ GdbShowVersion = 100,
+ GdbFileExecAndSymbols,
+ GdbQueryPwd,
+ GdbQuerySources,
+ GdbQuerySources2,
+ GdbAsyncOutput2,
+ GdbExecRun,
+ GdbExecRunToFunction,
+ GdbExecStep,
+ GdbExecNext,
+ GdbExecStepI,
+ GdbExecNextI,
+ GdbExecContinue,
+ GdbExecFinish,
+ GdbExecJumpToLine,
+ GdbExecInterrupt,
+ GdbInfoShared,
+ GdbInfoProc,
+ GdbQueryDataDumper1,
+ GdbQueryDataDumper2,
+ GdbInitializeSocket1,
+
+ BreakCondition = 200,
+ BreakEnablePending,
+ BreakSetAnnotate,
+ BreakDelete,
+ BreakList,
+ BreakIgnore,
+ BreakInfo,
+ BreakInsert,
+ BreakInsert1,
+
+ DisassemblerList = 300,
+
+ ModulesList = 400,
+
+ RegisterListNames = 500,
+ RegisterListValues,
+
+ StackSelectThread = 600,
+ StackListThreads,
+ StackListFrames,
+ StackListLocals,
+ StackListArguments,
+
+ WatchVarAssign = 700, // data changed by user
+ WatchVarListChildren,
+ WatchVarCreate,
+ WatchEvaluateExpression,
+ WatchToolTip,
+ WatchDumpCustomSetup,
+ WatchDumpCustomValue1, // waiting for gdb ack
+ WatchDumpCustomValue2, // waiting for actual data
+ WatchDumpCustomEditValue,
+};
+
+QString dotEscape(QString str)
+{
+ str.replace(' ', '.');
+ str.replace('\\', '.');
+ str.replace('/', '.');
+ return str;
+}
+
+QString currentTime()
+{
+ return QTime::currentTime().toString("hh:mm:ss.zzz");
+}
+
+static int &currentToken()
+{
+ static int token = 0;
+ return token;
+}
+
+static bool isSkippableFunction(const QString &funcName, const QString &fileName)
+{
+ if (fileName.endsWith("kernel/qobject.cpp"))
+ return true;
+ if (fileName.endsWith("kernel/moc_qobject.cpp"))
+ return true;
+ if (fileName.endsWith("kernel/qmetaobject.cpp"))
+ return true;
+ if (fileName.endsWith(".moc"))
+ return true;
+
+ if (funcName.endsWith("::qt_metacall"))
+ return true;
+
+ return false;
+}
+
+static bool isLeavableFunction(const QString &funcName, const QString &fileName)
+{
+ if (funcName.endsWith("QObjectPrivate::setCurrentSender"))
+ return true;
+ if (fileName.endsWith("kernel/qmetaobject.cpp")
+ && funcName.endsWith("QMetaObject::methodOffset"))
+ return true;
+ if (fileName.endsWith("kernel/qobject.h"))
+ return true;
+ if (fileName.endsWith("kernel/qobject.cpp")
+ && funcName.endsWith("QObjectConnectionListVector::at"))
+ return true;
+ if (fileName.endsWith("kernel/qobject.cpp")
+ && funcName.endsWith("~QObject"))
+ return true;
+ if (fileName.endsWith("thread/qmutex.cpp"))
+ return true;
+ if (fileName.endsWith("thread/qthread.cpp"))
+ return true;
+ if (fileName.endsWith("thread/qthread_unix.cpp"))
+ return true;
+ if (fileName.endsWith("thread/qmutex.h"))
+ return true;
+ if (fileName.contains("thread/qbasicatomic"))
+ return true;
+ if (fileName.contains("thread/qorderedmutexlocker_p"))
+ return true;
+ if (fileName.contains("arch/qatomic"))
+ return true;
+ if (fileName.endsWith("tools/qvector.h"))
+ return true;
+ if (fileName.endsWith("tools/qlist.h"))
+ return true;
+ if (fileName.endsWith("tools/qhash.h"))
+ return true;
+ if (fileName.endsWith("tools/qmap.h"))
+ return true;
+ if (fileName.endsWith("tools/qstring.h"))
+ return true;
+ if (fileName.endsWith("global/qglobal.h"))
+ return true;
+
+ return false;
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// GdbEngine
+//
+///////////////////////////////////////////////////////////////////////
+
+GdbEngine::GdbEngine(DebuggerManager *parent)
+{
+ q = parent;
+ qq = parent->engineInterface();
+ init();
+}
+
+GdbEngine::~GdbEngine()
+{
+}
+
+void GdbEngine::init()
+{
+ m_pendingRequests = 0;
+ m_gdbVersion = 100;
+ m_shared = 0;
+ qq->debugDumpersAction()->setChecked(false);
+
+ m_oldestAcceptableToken = -1;
+
+ // Gdb Process interaction
+ connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)), this,
+ SLOT(gdbProcError(QProcess::ProcessError)));
+ connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()), this,
+ SLOT(readGdbStandardOutput()));
+ connect(&m_gdbProc, SIGNAL(readyReadStandardError()), this,
+ SLOT(readGdbStandardError()));
+ connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)), q,
+ SLOT(exitDebugger()));
+
+ // Custom dumpers
+ //m_dumperServerConnection = 0;
+ //m_dumperServer = new DumperServer(this);
+ //QString name = "gdb-" +
+ // QDateTime::currentDateTime().toString("yyyy_MM_dd-hh_mm_ss_zzz");
+ //m_dumperServer->listen(name);
+ //connect(m_dumperServer, SIGNAL(newConnection()),
+ // this, SLOT(acceptConnection()));
+
+ //if (!m_dumperServer->isListening()) {
+ // QMessageBox::critical(q->mainWindow(), tr("Dumper Server Setup Failed"),
+ // tr("Unable to create server listening for data: %1.\n"
+ // "Server name: %2").arg(m_dumperServer->errorString(), name),
+ // QMessageBox::Retry | QMessageBox::Cancel);
+ // }
+
+ connect(qq->debugDumpersAction(), SIGNAL(toggled(bool)),
+ this, SLOT(setDebugDumpers(bool)));
+
+ connect(qq->useCustomDumpersAction(), SIGNAL(toggled(bool)),
+ this, SLOT(setCustomDumpersWanted(bool)));
+
+ // Output
+ connect(this, SIGNAL(gdbResponseAvailable()),
+ this, SLOT(handleResponse()), Qt::QueuedConnection);
+
+ connect(this, SIGNAL(gdbOutputAvailable(QString,QString)),
+ q, SLOT(showDebuggerOutput(QString,QString)),
+ Qt::QueuedConnection);
+ connect(this, SIGNAL(gdbInputAvailable(QString,QString)),
+ q, SLOT(showDebuggerInput(QString,QString)),
+ Qt::QueuedConnection);
+ connect(this, SIGNAL(applicationOutputAvailable(QString,QString)),
+ q, SLOT(showApplicationOutput(QString,QString)),
+ Qt::QueuedConnection);
+}
+
+void GdbEngine::gdbProcError(QProcess::ProcessError error)
+{
+ QString msg;
+ switch (error) {
+ case QProcess::FailedToStart:
+ msg = QString(tr("The Gdb process failed to start. Either the "
+ "invoked program '%1' is missing, or you may have insufficient "
+ "permissions to invoke the program.")).arg(theGdbSettings().m_gdbCmd);
+ break;
+ case QProcess::Crashed:
+ msg = tr("The Gdb process crashed some time after starting "
+ "successfully.");
+ break;
+ case QProcess::Timedout:
+ msg = tr("The last waitFor...() function timed out. "
+ "The state of QProcess is unchanged, and you can try calling "
+ "waitFor...() again.");
+ break;
+ case QProcess::WriteError:
+ msg = tr("An error occurred when attempting to write "
+ "to the Gdb process. For example, the process may not be running, "
+ "or it may have closed its input channel.");
+ break;
+ case QProcess::ReadError:
+ msg = tr("An error occurred when attempting to read from "
+ "the Gdb process. For example, the process may not be running.");
+ break;
+ default:
+ msg = tr("An unknown error in the Gdb process occurred. "
+ "This is the default return value of error().");
+ }
+
+ q->showStatusMessage(msg, 5000);
+ QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
+ // act as if it was closed by the core
+ q->exitDebugger();
+}
+
+static void skipSpaces(const char *&from, const char *to)
+{
+ while (from != to && QChar(*from).isSpace())
+ ++from;
+}
+
+static inline bool isNameChar(char c)
+{
+ // could be 'stopped' or 'shlibs-added'
+ return (c >= 'a' && c <= 'z') || c == '-';
+}
+
+#if 0
+static void dump(const char *first, const char *middle, const QString & to)
+{
+ QByteArray ba(first, middle - first);
+ Q_UNUSED(to);
+ // note that qDebug cuts off output after a certain size... (bug?)
+ qDebug("\n>>>>> %s\n%s\n====\n%s\n<<<<<\n",
+ qPrintable(currentTime()),
+ qPrintable(QString(ba).trimmed()),
+ qPrintable(to.trimmed()));
+ //qDebug() << "";
+ //qDebug() << qPrintable(currentTime())
+ // << " Reading response: " << QString(ba).trimmed() << "\n";
+}
+#endif
+
+static void skipTerminator(const char *&from, const char *to)
+{
+ skipSpaces(from, to);
+ // skip '(gdb)'
+ if (from[0] == '(' && from[1] == 'g' && from[3] == 'b' && from[4] == ')')
+ from += 5;
+ skipSpaces(from, to);
+}
+
+// called asyncronously as response to Gdb stdout output in
+// gdbResponseAvailable()
+void GdbEngine::handleResponse()
+{
+ static QTime lastTime;
+
+ emit gdbOutputAvailable(" ", currentTime());
+ emit gdbOutputAvailable("stdout:", m_inbuffer);
+
+#if 0
+ qDebug() // << "#### start response handling #### "
+ << currentTime()
+ << lastTime.msecsTo(QTime::currentTime()) << "ms,"
+ << "buf: " << m_inbuffer.left(1500) << "..."
+ //<< "buf: " << m_inbuffer
+ << "size:" << m_inbuffer.size();
+#else
+ //qDebug() << "buf: " << m_inbuffer;
+#endif
+
+ lastTime = QTime::currentTime();
+
+ while (1) {
+ if (m_inbuffer.isEmpty())
+ break;
+
+ const char *from = m_inbuffer.constData();
+ // FIXME: check for line ending in '\n(gdb)\n'
+ const char *to = from + m_inbuffer.size();
+ const char *inner;
+
+ //const char *oldfrom = from;
+
+ //skipSpaces(from, to);
+ skipTerminator(from, to);
+ int token = -1;
+
+ // token is a sequence of numbers
+ for (inner = from; inner != to; ++inner)
+ if (*inner < '0' || *inner > '9')
+ break;
+ if (from != inner) {
+ token = QString(QByteArray(from, inner - from)).toInt();
+ from = inner;
+ //qDebug() << "found token " << token;
+ }
+
+ if (from == to) {
+ //qDebug() << "Returning: " << toString();
+ break;
+ }
+
+ if (token == -1 && *from != '&' && *from != '~') {
+ // FIXME: On Linux the application's std::out is merged in here.
+ // High risk of falsely interpreting this as MI output.
+ // We assume that we _always_ use tokens, so not finding a token
+ // is a positive indication for the presence of application output.
+ QString s;
+ while (from != to && *from != '\n')
+ s += *from++;
+ //qDebug() << "UNREQUESTED DATA " << s << " TAKEN AS APPLICATION OUTPUT";
+ s += '\n';
+
+ m_inbuffer = QByteArray(from, to - from);
+ emit applicationOutputAvailable("app-stdout: ", s);
+ continue;
+ }
+
+ // next char decides kind of record
+ const char c = *from++;
+ //qDebug() << "CODE:" << c;
+
+ switch (c) {
+ case '*':
+ case '+':
+ case '=': {
+ QByteArray asyncClass;
+ for (; from != to; ++from) {
+ const char c = *from;
+ if (!isNameChar(c))
+ break;
+ asyncClass += *from;
+ }
+ //qDebug() << "ASYNCCLASS" << asyncClass;
+
+ GdbMi record;
+ while (from != to && *from == ',') {
+ ++from; // skip ','
+ GdbMi data;
+ data.parseResultOrValue(from, to);
+ if (data.isValid()) {
+ //qDebug() << "parsed response: " << data.toString();
+ record.m_children += data;
+ record.m_type = GdbMi::Tuple;
+ }
+ }
+ //dump(oldfrom, from, record.toString());
+ skipTerminator(from, to);
+ m_inbuffer = QByteArray(from, to - from);
+ if (asyncClass == "stopped") {
+ handleAsyncOutput(record);
+ } else {
+ qDebug() << "INGNORED ASYNC OUTPUT " << record.toString();
+ }
+ break;
+ }
+
+ case '~':
+ case '@':
+ case '&': {
+ QString data = GdbMi::parseCString(from, to);
+ handleStreamOutput(data, c);
+ //dump(oldfrom, from, record.toString());
+ m_inbuffer = QByteArray(from, to - from);
+ break;
+ }
+
+ case '#': {
+ //qDebug() << "CUSTOM OUTPUT, TOKEN" << token;
+ QString str;
+ for (; from != to && *from >= '0' && *from <= '9'; ++from)
+ str += QLatin1Char(*from);
+ ++from; // skip the ' '
+ int len = str.toInt();
+ QByteArray ba(from, len);
+ from += len;
+ m_inbuffer = QByteArray(from, to - from);
+ m_customOutputForToken[token] += QString(ba);
+ break;
+ }
+
+ case '^': {
+ GdbResultRecord record;
+
+ record.token = token;
+
+ for (inner = from; inner != to; ++inner)
+ if (*inner < 'a' || *inner > 'z')
+ break;
+
+ QByteArray resultClass(from, inner - from);
+
+ if (resultClass == "done")
+ record.resultClass = GdbResultDone;
+ else if (resultClass == "running")
+ record.resultClass = GdbResultRunning;
+ else if (resultClass == "connected")
+ record.resultClass = GdbResultConnected;
+ else if (resultClass == "error")
+ record.resultClass = GdbResultError;
+ else if (resultClass == "exit")
+ record.resultClass = GdbResultExit;
+ else
+ record.resultClass = GdbResultUnknown;
+
+ from = inner;
+ skipSpaces(from, to);
+ if (from != to && *from == ',') {
+ ++from;
+ record.data.parseTuple_helper(from, to);
+ record.data.m_type = GdbMi::Tuple;
+ record.data.m_name = "data";
+ }
+ skipSpaces(from, to);
+ skipTerminator(from, to);
+
+ //qDebug() << "\nLOG STREAM:" + m_pendingLogStreamOutput;
+ //qDebug() << "\nTARGET STREAM:" + m_pendingTargetStreamOutput;
+ //qDebug() << "\nCONSOLE STREAM:" + m_pendingConsoleStreamOutput;
+ record.data.setStreamOutput("logstreamoutput",
+ m_pendingLogStreamOutput);
+ record.data.setStreamOutput("targetstreamoutput",
+ m_pendingTargetStreamOutput);
+ record.data.setStreamOutput("consolestreamoutput",
+ m_pendingConsoleStreamOutput);
+ QByteArray custom = m_customOutputForToken[token];
+ if (!custom.isEmpty())
+ record.data.setStreamOutput("customvaluecontents",
+ '{' + custom + '}');
+ //m_customOutputForToken.remove(token);
+ m_pendingLogStreamOutput.clear();
+ m_pendingTargetStreamOutput.clear();
+ m_pendingConsoleStreamOutput.clear();
+
+ //dump(oldfrom, from, record.toString());
+ m_inbuffer = QByteArray(from, to - from);
+ handleResultRecord(record);
+ break;
+ }
+ default: {
+ qDebug() << "FIXME: UNKNOWN CODE: " << c << " IN " << m_inbuffer;
+ m_inbuffer = QByteArray(from, to - from);
+ break;
+ }
+ }
+ }
+
+ //qDebug() << "##### end response handling ####\n\n\n"
+ // << currentTime() << lastTime.msecsTo(QTime::currentTime());
+ lastTime = QTime::currentTime();
+}
+
+#ifdef Q_OS_MAC
+static void fixMac(QByteArray &out)
+{
+ // HACK: gdb on Mac mixes MI1 and MI2 syntax. Not nice.
+ // it returns: 9^done,locals={{name="a"},{name="w"}}
+ // instead of: 9^done,locals=[{name="a"},{name="w"}]
+ if (!out.contains("locals={{name"))
+ return;
+
+ static const QByteArray termArray("(gdb) ");
+ int pos = out.indexOf(termArray);
+ if (pos == -1)
+ return;
+
+ int pos1 = out.indexOf("={{");
+ if (pos1 == -1)
+ return;
+
+ int pos2 = out.indexOf("]]");
+ if (pos2 == -1)
+ return;
+
+ if (pos1 < pos && pos2 < pos) {
+ out[pos1 + 1] = '[';
+ out[pos2 + 1] = ']';
+ }
+}
+#endif
+
+void GdbEngine::readGdbStandardError()
+{
+ QByteArray err = m_gdbProc.readAllStandardError();
+ emit applicationOutputAvailable("app-stderr:", err);
+}
+
+void GdbEngine::readGdbStandardOutput()
+{
+ // This is the function called whenever the Gdb process created
+ // output. As a rule of thumb, stdout contains _real_ Gdb output
+ // as responses to our command (with exception of the data dumpers)
+ // and "spontaneous" events like messages on loaded shared libraries.
+ // Otoh, stderr contains application output produced by qDebug etc.
+ // There is no organized way to pass application stdout output
+
+ // The result of custom data dumpers arrives over the socket _before_
+ // the corresponding Gdb "^done" message arrives here over stdout
+ // and is merged into the response via m_pendingCustomValueContents.
+
+ // Note that this code here runs syncronized to the arriving
+ // output. The completed response will be signalled by a queued
+ // connection to the handlers.
+
+ QByteArray out = m_gdbProc.readAllStandardOutput();
+
+ //qDebug() << "\n\n\nPLUGIN OUT: '" << out.data() << "'\n\n\n";
+
+ #ifdef Q_OS_MAC
+ fixMac(out);
+ #endif
+
+ m_inbuffer.append(out);
+ //QWB_ASSERT(!m_inbuffer.isEmpty(), return);
+
+ char c = m_inbuffer[m_inbuffer.size() - 1];
+ static const QByteArray termArray("(gdb) ");
+ if (out.indexOf(termArray) == -1 && c != 10 && c != 13) {
+ //qDebug() << "\n\nBuffer not yet filled, waiting for more data to arrive";
+ //qDebug() << m_inbuffer.data() << m_inbuffer.size();
+ //qDebug() << "\n\n";
+ return;
+ }
+
+ emit gdbResponseAvailable();
+}
+
+void GdbEngine::interruptInferior()
+{
+ if (m_gdbProc.state() == QProcess::NotRunning)
+ return;
+
+ if (q->m_attachedPID) {
+ if (interruptProcess(q->m_attachedPID))
+ qq->notifyInferiorStopped();
+ return;
+ }
+
+#ifdef Q_OS_MAC
+ sendCommand("-exec-interrupt", GdbExecInterrupt);
+ qq->notifyInferiorStopped();
+#else
+ if (interruptChildProcess(m_gdbProc.pid()))
+ qq->notifyInferiorStopped();
+#endif
+}
+
+void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
+{
+ int pid = pid0.toInt();
+ if (pid == 0) {
+ qDebug() << "Cannot parse PID from " << pid0;
+ return;
+ }
+ if (pid == m_inferiorPid)
+ return;
+ m_inferiorPid = pid;
+ qq->notifyInferiorPidChanged(pid);
+}
+
+void GdbEngine::sendSynchronizedCommand(const QString & command,
+ int type, const QVariant &cookie, bool needStop)
+{
+ sendCommand(command, type, cookie, needStop, true);
+}
+
+void GdbEngine::sendCommand(const QString &command, int type,
+ const QVariant &cookie, bool needStop, bool synchronized)
+{
+ if (m_gdbProc.state() == QProcess::NotRunning) {
+ //qDebug() << "NO GDB PROCESS RUNNING, CMD IGNORED:" << command;
+ return;
+ }
+
+ bool temporarilyStopped = false;
+ if (needStop && q->status() == DebuggerInferiorRunning) {
+ q->showStatusMessage(tr("Temporarily stopped"), -1);
+ interruptInferior();
+ temporarilyStopped = true;
+ }
+
+ ++currentToken();
+ if (synchronized) {
+ ++m_pendingRequests;
+ PENDING_DEBUG(" TYPE " << type << " INCREMENTS PENDING TO: "
+ << m_pendingRequests << command);
+ } else {
+ PENDING_DEBUG(" UNKNOWN TYPE " << type << " LEAVES PENDING AT: "
+ << m_pendingRequests << command);
+ }
+
+ GdbCookie cmd;
+ cmd.synchronized = synchronized;
+ cmd.command = command;
+ cmd.command = QString::number(currentToken()) + cmd.command;
+ if (cmd.command.contains("%1"))
+ cmd.command = cmd.command.arg(currentToken());
+ cmd.type = type;
+ cmd.cookie = cookie;
+
+ m_cookieForToken[currentToken()] = cmd;
+
+ //qDebug() << "";
+ if (!command.isEmpty()) {
+ //qDebug() << qPrintable(currentTime()) << "RUNNING << cmd.command;
+ m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
+ //emit gdbInputAvailable(QString(), " " + currentTime());
+ emit gdbInputAvailable(QString(), "[" + currentTime() + "] " + cmd.command);
+ //emit gdbInputAvailable(QString(), cmd.command);
+ }
+
+ if (temporarilyStopped)
+ sendCommand("-exec-continue");
+
+ // slows down
+ //qApp->processEvents();
+}
+
+void GdbEngine::handleResultRecord(const GdbResultRecord &record)
+{
+ //qDebug() << "TOKEN: " << record.token
+ // << " ACCEPTABLE: " << m_oldestAcceptableToken;
+ //qDebug() << "";
+ //qDebug() << qPrintable(currentTime()) << "Reading response: "
+ // << record.toString() << "\n";
+ //qDebug() << "\nRESULT" << record.token << record.toString();
+
+ int token = record.token;
+ if (token == -1)
+ return;
+
+ if (!m_cookieForToken.contains(token)) {
+ qDebug() << "NO SUCH TOKEN (ANYMORE): " << token;
+ return;
+ }
+
+ GdbCookie cmd = m_cookieForToken.take(token);
+
+ // FIXME: this falsely rejects results from the custom dumper recognition
+ // procedure, too...
+ if (record.token < m_oldestAcceptableToken) {
+ //qDebug() << "### SKIPPING OLD RESULT " << record.toString();
+ //QMessageBox::information(m_mainWindow, tr("Skipped"), "xxx");
+ return;
+ }
+
+ // We get _two_ results for a '-exec-foo' command: First a
+ // 'running' notification, then a 'stopped' or similar.
+ // So put it back.
+ if (record.resultClass == GdbResultRunning)
+ m_cookieForToken[token] = cmd;
+
+#if 0
+ qDebug() << "# handleOutput, "
+ << "cmd type: " << cmd.type
+ << "cmd synchronized: " << cmd.synchronized
+ << "\n record: " << record.toString();
+#endif
+
+ // << "\n data: " << record.data.toString(true);
+
+ if (cmd.type != GdbInvalidCommand)
+ handleResult(record, cmd.type, cmd.cookie);
+
+ if (cmd.synchronized) {
+ --m_pendingRequests;
+ PENDING_DEBUG(" TYPE " << cmd.type << " DECREMENTS PENDING TO: "
+ << m_pendingRequests << cmd.command);
+ if (m_pendingRequests == 0)
+ updateWatchModel2();
+ } else {
+ PENDING_DEBUG(" UNKNOWN TYPE " << cmd.type << " LEAVES PENDING AT: "
+ << m_pendingRequests << cmd.command);
+ }
+}
+
+void GdbEngine::handleResult(const GdbResultRecord & record, int type,
+ const QVariant & cookie)
+{
+ switch (type) {
+ case GdbExecNext:
+ case GdbExecStep:
+ case GdbExecNextI:
+ case GdbExecStepI:
+ case GdbExecContinue:
+ case GdbExecFinish:
+ // evil code sharing
+ case GdbExecRun:
+ handleExecRun(record);
+ break;
+ case GdbInfoProc:
+ handleInfoProc(record);
+ break;
+
+ case GdbShowVersion:
+ handleShowVersion(record);
+ break;
+ case GdbFileExecAndSymbols:
+ handleFileExecAndSymbols(record);
+ break;
+ case GdbExecRunToFunction:
+ // that should be "^running". We need to handle the resulting
+ // "Stopped"
+ //handleExecRunToFunction(record);
+ break;
+ case GdbExecInterrupt:
+ break;
+ case GdbExecJumpToLine:
+ handleExecJumpToLine(record);
+ break;
+ case GdbQueryPwd:
+ handleQueryPwd(record);
+ break;
+ case GdbQuerySources:
+ handleQuerySources(record);
+ break;
+ case GdbQuerySources2:
+ handleQuerySources2(record, cookie);
+ break;
+ case GdbAsyncOutput2:
+ handleAsyncOutput2(cookie.value<GdbMi>());
+ break;
+ case GdbInfoShared:
+ handleInfoShared(record);
+ break;
+ case GdbInitializeSocket1:
+ //qDebug() << " INIT SOCKET" << record.toString();
+ break;
+ case GdbQueryDataDumper1:
+ handleQueryDataDumper1(record);
+ break;
+ case GdbQueryDataDumper2:
+ handleQueryDataDumper2(record);
+ break;
+
+ case BreakList:
+ handleBreakList(record);
+ break;
+ case BreakInsert:
+ handleBreakInsert(record, cookie.toInt());
+ break;
+ case BreakInsert1:
+ handleBreakInsert1(record, cookie.toInt());
+ break;
+ case BreakInfo:
+ handleBreakInfo(record, cookie.toInt());
+ break;
+ case BreakEnablePending:
+ case BreakDelete:
+ // nothing
+ break;
+ case BreakIgnore:
+ handleBreakIgnore(record, cookie.toInt());
+ break;
+ case BreakCondition:
+ handleBreakCondition(record, cookie.toInt());
+ break;
+
+ case DisassemblerList:
+ handleDisassemblerList(record, cookie.toString());
+ break;
+
+ case ModulesList:
+ handleModulesList(record);
+ break;
+
+ case RegisterListNames:
+ handleRegisterListNames(record);
+ break;
+ case RegisterListValues:
+ handleRegisterListValues(record);
+ break;
+
+ case StackListFrames:
+ handleStackListFrames(record);
+ break;
+ case StackListThreads:
+ handleStackListThreads(record, cookie.toInt());
+ break;
+ case StackSelectThread:
+ handleStackSelectThread(record, cookie.toInt());
+ break;
+ case StackListLocals:
+ handleStackListLocals(record);
+ break;
+ case StackListArguments:
+ handleStackListArguments(record);
+ break;
+
+ case WatchVarListChildren:
+ handleVarListChildren(record, cookie.value<WatchData>());
+ break;
+ case WatchVarCreate:
+ handleVarCreate(record, cookie.value<WatchData>());
+ break;
+ case WatchVarAssign:
+ handleVarAssign();
+ break;
+ case WatchEvaluateExpression:
+ handleEvaluateExpression(record, cookie.value<WatchData>());
+ break;
+ case WatchToolTip:
+ handleToolTip(record, cookie.toString());
+ break;
+ case WatchDumpCustomValue1:
+ handleDumpCustomValue1(record, cookie.value<WatchData>());
+ break;
+ case WatchDumpCustomValue2:
+ handleDumpCustomValue2(record, cookie.value<WatchData>());
+ break;
+ case WatchDumpCustomSetup:
+ handleDumpCustomSetup(record);
+ break;
+
+ default:
+ qDebug() << "FIXME: GdbEngine::handleResult: "
+ "should not happen" << type;
+ break;
+ }
+}
+
+void GdbEngine::executeDebuggerCommand(const QString &command)
+{
+ //createGdbProcessIfNeeded();
+ if (m_gdbProc.state() == QProcess::NotRunning) {
+ qDebug() << "NO GDB PROCESS RUNNING, PLAIN CMD IGNORED: " << command;
+ return;
+ }
+
+ GdbCookie cmd;
+ cmd.command = command;
+ cmd.type = -1;
+
+ //m_cookieForToken[currentToken()] = cmd;
+ //++currentToken();
+
+ //qDebug() << "";
+ //qDebug() << currentTime() << "Running command: " << cmd.command;
+ emit gdbInputAvailable(QString(), cmd.command);
+ m_gdbProc.write(cmd.command.toLatin1() + "\r\n");
+}
+
+void GdbEngine::handleQueryPwd(const GdbResultRecord &record)
+{
+ // FIXME: remove this special case as soon as 'pwd'
+ // is supported by MI
+ //qDebug() << "PWD OUTPUT:" << record.toString();
+ // Gdb responses _unless_ we get an error first.
+ if (record.resultClass == GdbResultDone) {
+#ifdef Q_OS_LINUX
+ // "5^done,{logstreamoutput="pwd ",consolestreamoutput
+ // ="Working directory /home/apoenitz/dev/work/test1. "}
+ m_pwd = record.data.findChild("consolestreamoutput").data();
+ int pos = m_pwd.indexOf("Working directory");
+ m_pwd = m_pwd.mid(pos + 18);
+ m_pwd = m_pwd.trimmed();
+ if (m_pwd.endsWith('.'))
+ m_pwd.chop(1);
+#endif
+#ifdef Q_OS_WIN
+ // ~"Working directory C:\\Users\\Thomas\\Documents\\WBTest3\\debug.\n"
+ m_pwd = record.data.findChild("consolestreamoutput").data();
+ m_pwd = m_pwd.trimmed();
+#endif
+ //qDebug() << "PWD RESULT:" << m_pwd;
+ }
+}
+
+void GdbEngine::handleQuerySources(const GdbResultRecord &record)
+{
+ if (record.resultClass == GdbResultDone) {
+ m_shortToFullName.clear();
+ m_fullToShortName.clear();
+ // "^done,files=[{file="../../../../bin/gdbmacros/gdbmacros.cpp",
+ // fullname="/data5/dev/ide/main/bin/gdbmacros/gdbmacros.cpp"},
+ GdbMi files = record.data.findChild("files");
+ foreach (const GdbMi &item, files.children()) {
+ QString fileName = item.findChild("file").data();
+ GdbMi fullName = item.findChild("fullname");
+ QString full = fullName.data();
+ #ifdef Q_OS_WIN
+ full = QDir::cleanPath(full);
+ #endif
+ if (fullName.isValid() && QFileInfo(full).isReadable()) {
+ //qDebug() << "STORING 2: " << fileName << full;
+ m_shortToFullName[fileName] = full;
+ m_fullToShortName[full] = fileName;
+ }
+ }
+ }
+}
+
+void GdbEngine::handleInfoProc(const GdbResultRecord &record)
+{
+ if (record.resultClass == GdbResultDone) {
+ #if defined(Q_OS_MAC)
+ //^done,process-id="85075"
+ maybeHandleInferiorPidChanged(record.data.findChild("process-id").data());
+ #endif
+
+ #if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
+ // FIXME: use something more robust
+ QRegExp re(QLatin1String("process (\\d+)"));
+ QString data = record.data.findChild("consolestreamoutput").data();
+ if (re.indexIn(data) != -1)
+ maybeHandleInferiorPidChanged(re.cap(1));
+ #endif
+ }
+}
+
+void GdbEngine::handleInfoShared(const GdbResultRecord &record)
+{
+ if (record.resultClass == GdbResultDone) {
+ // let the modules handler do the parsing
+ handleModulesList(record);
+ QList<Module> modules = qq->modulesHandler()->modules();
+ bool reloadNeeded = false;
+ foreach (const Module &module, modules) {
+ // FIXME: read this from some list
+ if (!module.symbolsRead && !module.moduleName.contains("Q")) {
+ reloadNeeded = true;
+ sendCommand("sharedlibrary " + dotEscape(module.moduleName));
+ }
+ }
+ if (reloadNeeded)
+ reloadModules();
+ continueInferior();
+ }
+}
+
+void GdbEngine::handleQuerySources2(const GdbResultRecord &record,
+ const QVariant &cookie)
+{
+ if (record.resultClass == GdbResultDone)
+ handleAsyncOutput2(cookie.value<GdbMi>());
+}
+
+void GdbEngine::handleExecJumpToLine(const GdbResultRecord &record)
+{
+ // FIXME: remove this special case as soon as 'jump'
+ // is supported by MI
+ // "&"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
+ // ~"Continuing at 0x4058f3."
+ // ~"run1 (argc=1, argv=0x7fffb213a478) at test1.cpp:242"
+ // ~"242\t x *= 2;"
+ //109^done"
+ qq->notifyInferiorStopped();
+ q->showStatusMessage(tr("Jumped. Stopped."), -1);
+ QString output = record.data.findChild("logstreamoutput").data();
+ if (!output.isEmpty())
+ return;
+ QString fileAndLine = output.section(' ', 1, 1);
+ QString file = fileAndLine.section(':', 0, 0);
+ int line = fileAndLine.section(':', 1, 1).toInt();
+ q->gotoLocation(file, line, true);
+}
+
+void GdbEngine::handleExecRunToFunction(const GdbResultRecord &record)
+{
+ // FIXME: remove this special case as soon as there's a real
+ // reason given when the temporary breakpoint is hit.
+ // reight now we get:
+ // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
+ // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
+ // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
+ qq->notifyInferiorStopped();
+ q->showStatusMessage(tr("Run to Function finished. Stopped."), -1);
+ GdbMi frame = record.data.findChild("frame");
+ QString file = frame.findChild("fullname").data();
+ int line = frame.findChild("line").data().toInt();
+ qDebug() << "HIT: " << file << line << " IN " << frame.toString()
+ << " -- " << record.toString();
+ q->gotoLocation(file, line, true);
+}
+
+void GdbEngine::handleStreamOutput(const QString &data, char code)
+{
+ // Linux
+ if (data.contains("[New Thread")) {
+ QRegExp re("\\[New Thread 0x([0-9a-f]*) \\(LWP ([0-9]*)\\)\\]");
+ if (re.indexIn(data) != -1)
+ maybeHandleInferiorPidChanged(re.cap(2));
+ }
+
+ // Mac
+ if (data.contains("[Switching to process ")) {
+ QRegExp re("\\[Switching to process ([0-9]*) local thread 0x([0-9a-f]*)\\]");
+ if (re.indexIn(data) != -1)
+ maybeHandleInferiorPidChanged(re.cap(1));
+ }
+
+ // present it twice: now and together with the next 'real' result
+ switch (code) {
+ case '~':
+ m_pendingConsoleStreamOutput += data;
+ break;
+ case '@':
+ m_pendingTargetStreamOutput += data;
+ break;
+ case '&':
+ m_pendingLogStreamOutput += data;
+ // On Windows, the contents seem to depend on the debugger
+ // version and/or OS version used.
+ if (data.startsWith("warning:"))
+ qq->showApplicationOutput(QString(), data);
+ break;
+ }
+
+#ifdef Q_OS_LINUX
+ if (data.startsWith("Pending break") && data.contains("\" resolved")) {
+ qDebug() << "SCHEDULING -break-list";
+ //m_breakListOnStopNeeded = true;
+ }
+#endif
+
+#if 0
+ if (m_slurpingPTypeOutput)
+ qDebug() << "SLURP: " << output.data;
+
+ // "No symbol \"__dlopen\" in current context."
+ // "No symbol \"dlopen\" in current context."
+ if (output.data.startsWith("No symbol ")
+ && output.data.contains("dlopen")) {
+ m_dlopened = true;
+ return;
+ }
+
+ // output of 'ptype <foo>'
+ if (output.data.startsWith("type = ")) {
+ if (output.data.endsWith("{") || output.data.endsWith("{\\n")) {
+ // multi-line output started here...
+ m_slurpingPTypeOutput = true;
+ m_slurpedPTypeOutput = output.data;
+ } else {
+ // Happens for simple types. Process it immediately
+ m_watchHandler->handleTypeContents(output.data);
+ }
+ return;
+ }
+ if (m_slurpingPTypeOutput) {
+ m_slurpedPTypeOutput += '\n';
+ m_slurpedPTypeOutput += output.data;
+ if (output.data.startsWith("}")) {
+ // this is the last line...
+ m_slurpingPTypeOutput = false;
+ m_watchHandler->handleTypeContents(m_slurpedPTypeOutput);
+ m_slurpedPTypeOutput.clear();
+ }
+ return;
+ }
+#endif
+}
+
+static bool isExitedReason(const QString &reason)
+{
+ return reason == QLatin1String("exited-normally") // inferior exited normally
+ || reason == QLatin1String("exited-signalled") // inferior exited because of a signal
+ //|| reason == QLatin1String("signal-received") // inferior received signal
+ || reason == QLatin1String("exited"); // inferior exited
+}
+
+static bool isStoppedReason(const QString &reason)
+{
+ return reason == QLatin1String("function-finished") // -exec-finish
+ || reason == QLatin1String("signal-received") // handled as "isExitedReason"
+ || reason == QLatin1String("breakpoint-hit") // -exec-continue
+ || reason == QLatin1String("end-stepping-range") // -exec-next, -exec-step
+ || reason == QLatin1String("location-reached") // -exec-until
+ || reason == QLatin1String("access-watchpoint-trigger")
+ || reason == QLatin1String("read-watchpoint-trigger")
+#ifdef Q_OS_MAC
+ || reason.isEmpty()
+#endif
+ ;
+}
+
+void GdbEngine::handleAsyncOutput(const GdbMi &data)
+{
+ const QString reason = data.findChild("reason").data();
+
+ QString console = data.findChild("consolestreamoutput").data();
+ if (console.contains("Stopped due to shared library event") || reason.isEmpty()) {
+ ++m_shared;
+ //if (m_shared == 2)
+ // tryLoadCustomDumpers();
+ //qDebug() << "SHARED LIBRARY EVENT " << data.toString() << m_shared;
+ if (qq->useFastStart()) {
+ if (1 || m_shared <= 16) { // libpthread?
+ sendCommand("info shared", GdbInfoShared);
+ //sendCommand("sharedlibrary gdbdebugger ");
+ //continueInferior();
+ } else {
+ // auto-load from now on
+ sendCommand("info shared");
+ sendCommand("set auto-solib-add on");
+ sendCommand("-file-list-exec-source-files", GdbQuerySources);
+ sendCommand("-break-list", BreakList);
+ //sendCommand("bt");
+ //QVariant var = QVariant::fromValue<GdbMi>(data);
+ //sendCommand("p 1", GdbAsyncOutput2, var); // dummy
+ continueInferior();
+ }
+ } else {
+ // slow start requested.
+ q->showStatusMessage("Loading " + data.toString(), -1);
+ continueInferior();
+ }
+ return;
+ }
+
+ if (isExitedReason(reason)) {
+ qq->notifyInferiorExited();
+ QString msg = "Program exited normally";
+ if (reason == "exited") {
+ msg = "Program exited with exit code "
+ + data.findChild("exit-code").toString();
+ } else if (reason == "exited-signalled") {
+ msg = "Program exited after receiving signal "
+ + data.findChild("signal-name").toString();
+ } else if (reason == "signal-received") {
+ msg = "Program exited after receiving signal "
+ + data.findChild("signal-name").toString();
+ }
+ q->showStatusMessage(msg, -1);
+ q->exitDebugger();
+ return;
+ }
+
+ tryLoadCustomDumpers();
+
+ // jump over well-known frames
+ static int stepCounter = 0;
+ if (qq->skipKnownFrames()) {
+ if (reason == "end-stepping-range" || reason == "function-finished") {
+ GdbMi frame = data.findChild("frame");
+ //qDebug() << frame.toString();
+ m_currentFrame = frame.findChild("addr").data() + '%' +
+ frame.findChild("func").data() + '%';
+
+ QString funcName = frame.findChild("func").data();
+ QString fileName = frame.findChild("file").data();
+ if (isLeavableFunction(funcName, fileName)) {
+ //qDebug() << "LEAVING" << funcName;
+ ++stepCounter;
+ q->stepOutExec();
+ //stepExec();
+ return;
+ }
+ if (isSkippableFunction(funcName, fileName)) {
+ //qDebug() << "SKIPPING" << funcName;
+ ++stepCounter;
+ q->stepExec();
+ return;
+ }
+ //if (stepCounter)
+ // qDebug() << "STEPCOUNTER:" << stepCounter;
+ stepCounter = 0;
+ }
+ }
+
+ if (isStoppedReason(reason) || reason.isEmpty()) {
+ // Need another round trip
+ if (reason == "breakpoint-hit") {
+ GdbMi frame = data.findChild("frame");
+ //qDebug() << frame.toString();
+ m_currentFrame = frame.findChild("addr").data() + '%' +
+ frame.findChild("func").data() + '%';
+
+ QApplication::alert(q->mainWindow(), 200);
+ sendCommand("-file-list-exec-source-files", GdbQuerySources);
+ sendCommand("-break-list", BreakList);
+ QVariant var = QVariant::fromValue<GdbMi>(data);
+ sendCommand("p 0", GdbAsyncOutput2, var); // dummy
+ } else {
+ handleAsyncOutput2(data);
+ }
+ return;
+ }
+
+ qDebug() << "STOPPED FOR UNKNOWN REASON" << data.toString();
+ // Ignore it. Will be handled with full response later in the
+ // JumpToLine or RunToFunction handlers
+#if 1
+ // FIXME: remove this special case as soon as there's a real
+ // reason given when the temporary breakpoint is hit.
+ // reight now we get:
+ // 14*stopped,thread-id="1",frame={addr="0x0000000000403ce4",
+ // func="foo",args=[{name="str",value="@0x7fff0f450460"}],
+ // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
+ //
+ // MAC yields sometimes:
+ // stdout:3661*stopped,time={wallclock="0.00658",user="0.00142",
+ // system="0.00136",start="1218810678.805432",end="1218810678.812011"}
+ q->resetLocation();
+ qq->notifyInferiorStopped();
+ q->showStatusMessage(tr("Run to Function finished. Stopped."), -1);
+ GdbMi frame = data.findChild("frame");
+ QString file = frame.findChild("fullname").data();
+ int line = frame.findChild("line").data().toInt();
+ qDebug() << "HIT: " << file << line << " IN " << frame.toString()
+ << " -- " << data.toString();
+ q->gotoLocation(file, line, true);
+#endif
+}
+
+
+void GdbEngine::handleAsyncOutput2(const GdbMi &data)
+{
+ qq->notifyInferiorStopped();
+
+ //
+ // Breakpoints
+ //
+ //qDebug() << "BREAK ASYNC: " << output.toString();
+ //sendListBreakpoints();
+ //attemptBreakpointSynchronization();
+ //if (m_breakListOnStopNeeded)
+ // sendListBreakpoints();
+
+ // something reasonably 'invariant'
+ // Linux:
+ //"79*stopped,reason="end-stepping-range",reason="breakpoint-hit",bkptno="1",
+ //thread-id="1",frame={addr="0x0000000000405d8f",func="run1",
+ //args=[{name="argc",value="1"},{name="argv",value="0x7fffb7c23058"}],
+ //file="test1.cpp",fullname="/home/apoenitz/dev/work/test1/test1.cpp"
+ //,line="261"}"
+ // Mac: (but only sometimes)
+ // "82*stopped,bkpt={number="0",type="step
+ // resume",disp="keep",enabled="y",addr="0x43127171",at="<Find::
+ // Internal::FindToolWindow::invokeFindIncremental()
+ // +225>",thread="1",shlib="/Users/epreuss/dev/ide/main/bin/
+ // workbench.app/Contents/PlugIns/Trolltech/libFind.1.0.0.dylib",
+ // frame="0xbfffd800",thread="1",times="1"},
+
+ //
+ // Stack
+ //
+ qq->stackHandler()->setCurrentIndex(0);
+ updateLocals(); // Quick shot
+
+ int currentId = data.findChild("thread-id").data().toInt();
+ sendSynchronizedCommand("-stack-list-frames", StackListFrames);
+ if (supportsThreads())
+ sendSynchronizedCommand("-thread-list-ids", StackListThreads, currentId);
+
+ //
+ // Disassembler
+ //
+ // Linux:
+ //"79*stopped,reason="end-stepping-range",reason="breakpoint-hit",bkptno="1",
+ //thread-id="1",frame={addr="0x0000000000405d8f",func="run1",
+ //args=[{name="argc",value="1"},{name="argv",value="0x7fffb7c23058"}],
+ //file="test1.cpp",fullname="/home/apoenitz/dev/work/test1/test1.cpp",line="261"}"
+ // Mac: (but only sometimes)
+ m_address = data.findChild("frame").findChild("addr").data();
+ qq->reloadDisassembler();
+
+ //
+ // Registers
+ //
+ qq->reloadRegisters();
+}
+
+void GdbEngine::handleShowVersion(const GdbResultRecord &response)
+{
+ if (response.resultClass == GdbResultDone) {
+ m_gdbVersion = 100;
+ QString msg = response.data.findChild("consolestreamoutput").data();
+ QRegExp supported("GNU gdb 6.[6789]");
+ if (msg.indexOf(supported) == -1) {
+ QStringList list = msg.split("\n");
+ while (list.size() > 2)
+ list.removeLast();
+ msg = tr("The debugger you are using identifies itself as:")
+ + "<p><p>" + list.join("<br>") + "<p><p>"
+ + tr("This version is not officially supported by Qt Creator.\n"
+ "Debugging will most likely not work well.\n"
+ "Using gdb 6.7 or later is strongly recommended.");
+#if 0
+ // ugly, but 'Show again' check box...
+ static QErrorMessage *err = new QErrorMessage(m_mainWindow);
+ err->setMinimumSize(400, 300);
+ err->showMessage(msg);
+#else
+ //QMessageBox::information(m_mainWindow, tr("Warning"), msg);
+#endif
+ }
+ int pos = msg.indexOf("GNU gdb 6.");
+ if (pos != -1) {
+ m_gdbVersion = 600 + (msg.at(pos + 10).unicode() - '0') * 10;
+ //qDebug() << "GDB VERSION " << m_gdbVersion << msg;
+ }
+ }
+}
+
+void GdbEngine::handleFileExecAndSymbols
+ (const GdbResultRecord &response)
+{
+ if (response.resultClass == GdbResultDone) {
+ //m_breakHandler->clearBreakMarkers();
+ } else if (response.resultClass == GdbResultError) {
+ QString msg = response.data.findChild("msg").data();
+ QMessageBox::critical(q->mainWindow(), tr("Error"),
+ tr("Starting executable failed:\n") + msg);
+ QWB_ASSERT(q->status() == DebuggerInferiorRunning, /**/);
+ interruptInferior();
+ }
+}
+
+void GdbEngine::handleExecRun(const GdbResultRecord &response)
+{
+ if (response.resultClass == GdbResultRunning) {
+ qq->notifyInferiorRunning();
+ q->showStatusMessage(tr("Running..."), -1);
+ //reloadModules();
+ } else if (response.resultClass == GdbResultError) {
+ QString msg = response.data.findChild("msg").data();
+ if (msg == "Cannot find bounds of current function") {
+ qq->notifyInferiorStopped();
+ //q->showStatusMessage(tr("No debug information available. "
+ // "Leaving function..."), -1);
+ //stepOutExec();
+ } else {
+ QMessageBox::critical(q->mainWindow(), tr("Error"),
+ tr("Starting executable failed:\n") + msg);
+ QWB_ASSERT(q->status() == DebuggerInferiorRunning, /**/);
+ interruptInferior();
+ }
+ }
+}
+
+void GdbEngine::queryFullName(const QString &fileName, QString *full)
+{
+ *full = fullName(fileName);
+}
+
+QString GdbEngine::shortName(const QString &fullName)
+{
+ return m_fullToShortName.value(fullName, QString());
+}
+
+QString GdbEngine::fullName(const QString &fileName)
+{
+ //QString absName = m_manager->currentWorkingDirectory() + "/" + file; ??
+ if (fileName.isEmpty())
+ return QString();
+ QString full = m_shortToFullName.value(fileName, QString());
+ //qDebug() << "RESOLVING: " << fileName << full;
+ if (!full.isEmpty())
+ return full;
+ QFileInfo fi(fileName);
+ if (!fi.isReadable())
+ return QString();
+ full = fi.absoluteFilePath();
+ #ifdef Q_OS_WIN
+ full = QDir::cleanPath(full);
+ #endif
+ //qDebug() << "STORING: " << fileName << full;
+ m_shortToFullName[fileName] = full;
+ m_fullToShortName[full] = fileName;
+ return full;
+}
+
+QString GdbEngine::fullName(const QStringList &candidates)
+{
+ QString full;
+ foreach (const QString &fileName, candidates) {
+ full = fullName(fileName);
+ if (!full.isEmpty())
+ return full;
+ }
+ foreach (const QString &fileName, candidates) {
+ if (!fileName.isEmpty())
+ return fileName;
+ }
+ return full;
+}
+
+void GdbEngine::shutdown()
+{
+ exitDebugger();
+}
+
+void GdbEngine::exitDebugger()
+{
+ //qDebug() << "EXITING: " << m_gdbProc.state();
+ if (m_gdbProc.state() == QProcess::Starting)
+ m_gdbProc.waitForStarted();
+ if (m_gdbProc.state() == QProcess::Running) {
+ interruptInferior();
+ sendCommand("kill");
+ sendCommand("-gdb-exit");
+ // 20s can easily happen when loading webkit debug information
+ m_gdbProc.waitForFinished(20000);
+ if (m_gdbProc.state() != QProcess::Running) {
+ m_gdbProc.terminate();
+ m_gdbProc.waitForFinished(20000);
+ }
+ }
+ if (m_gdbProc.state() != QProcess::NotRunning)
+ qDebug() << "PROBLEM STOPPING DEBUGGER";
+
+ m_shortToFullName.clear();
+ m_fullToShortName.clear();
+ m_varToType.clear();
+ m_dataDumperState = DataDumperUninitialized;
+ m_shared = 0;
+ qq->debugDumpersAction()->setChecked(false);
+}
+
+
+int GdbEngine::currentFrame() const
+{
+ return qq->stackHandler()->currentIndex();
+}
+
+
+bool GdbEngine::startDebugger()
+{
+ m_inferiorPid = 0;
+ QStringList gdbArgs;
+
+ QFileInfo fi(q->m_executable);
+ QString fileName = '"' + fi.absoluteFilePath() + '"';
+
+ if (m_gdbProc.state() != QProcess::NotRunning) {
+ qDebug() << "GDB IS ALREADY RUNNING!";
+ return false;
+ }
+
+ //gdbArgs.prepend(QLatin1String("--quiet"));
+ gdbArgs.prepend(QLatin1String("mi"));
+ gdbArgs.prepend(QLatin1String("-i"));
+
+ if (!q->m_workingDir.isEmpty())
+ m_gdbProc.setWorkingDirectory(q->m_workingDir);
+ if (!q->m_environment.isEmpty())
+ m_gdbProc.setEnvironment(q->m_environment);
+
+ #if 0
+ qDebug() << "Command: " << theGdbSettings().m_gdbCmd;
+ qDebug() << "WorkingDirectory: " << m_gdbProc.workingDirectory();
+ qDebug() << "Environment: " << m_gdbProc.environment();
+ qDebug() << "Arguments: " << gdbArgs;
+ qDebug() << "BuildDir: " << q->m_buildDir;
+ qDebug() << "ExeFile: " << q->m_executable;
+ #endif
+
+ q->showStatusMessage("Starting Debugger", -1);
+ emit gdbInputAvailable(QString(), theGdbSettings().m_gdbCmd + ' ' + gdbArgs.join(" "));
+
+ m_gdbProc.start(theGdbSettings().m_gdbCmd, gdbArgs);
+ m_gdbProc.waitForStarted();
+
+ if (m_gdbProc.state() != QProcess::Running)
+ return false;
+
+ q->showStatusMessage(tr("Gdb Running"), -1);
+
+ sendCommand("show version", GdbShowVersion);
+ if (qq->useFastStart()) {
+ sendCommand("set auto-solib-add off");
+ sendCommand("set stop-on-solib-events 1");
+ }
+ //sendCommand("-enable-timings");
+ //sendCommand("set stop-on-solib-events 1");
+ sendCommand("set print static-members off"); // Seemingly doesn't work.
+ //sendCommand("define hook-stop\n-thread-list-ids\n-stack-list-frames\nend");
+ //sendCommand("define hook-stop\nprint 4\nend");
+ //sendCommand("define hookpost-stop\nprint 5\nend");
+ //sendCommand("define hook-call\nprint 6\nend");
+ //sendCommand("define hookpost-call\nprint 7\nend");
+ //sendCommand("set print object on"); // works with CLI, but not MI
+ //sendCommand("set step-mode on"); // we can't work with that yes
+ //sendCommand("set exec-done-display on");
+ //sendCommand("set print pretty on");
+ //sendCommand("set confirm off");
+ //sendCommand("set pagination off");
+ sendCommand("set breakpoint pending on", BreakEnablePending);
+
+ // one of the following is needed to prevent crashes in gdb on code like:
+ // template <class T> T foo() { return T(0); }
+ // int main() { return foo<int>(); }
+ // (gdb) call 'int foo<int>'()
+ // /build/buildd/gdb-6.8/gdb/valops.c:2069: internal-error:
+ sendCommand("set overload-resolution off");
+ //sendCommand("set demangle-style none");
+
+ // From the docs:
+ // Stop means reenter debugger if this signal happens (implies print).
+ // Print means print a message if this signal happens.
+ // Pass means let program see this signal;
+ // otherwise program doesn't know.
+ // Pass and Stop may be combined.
+ // We need "print" as otherwise we would get no feedback whatsoever
+ // Custom Dumper crashs which happen regularily for when accessing
+ // uninitialized variables.
+ sendCommand("handle SIGSEGV nopass stop print");
+
+ // This is useful to kill the inferior whenever gdb dies.
+ //sendCommand("handle SIGTERM pass nostop print");
+
+ sendCommand("set unwindonsignal on");
+ sendCommand("pwd", GdbQueryPwd);
+
+ #ifdef Q_OS_MAC
+ sendCommand("-gdb-set inferior-auto-start-cfm off");
+ sendCommand("-gdb-set sharedLibrary load-rules "
+ "dyld \".*libSystem.*\" "
+ "all dyld \".*libauto.*\" "
+ "all dyld \".*AppKit.*\" "
+ "all dyld \".*PBGDBIntrospectionSupport.*\" "
+ "all dyld \".*Foundation.*\" "
+ "all dyld \".*CFDataFormatters.*\" "
+ "all dyld \".*libobjc.*\" "
+ "all dyld \".*CarbonDataFormatters");
+ #endif
+
+ if (q->startMode() == q->attachExternal) {
+ sendCommand("attach " + QString::number(q->m_attachedPID));
+ }
+
+ if (q->startMode() == q->startInternal) {
+ sendCommand("-file-exec-and-symbols " + fileName, GdbFileExecAndSymbols);
+ #ifdef Q_OS_MAC
+ sendCommand("sharedlibrary apply-load-rules all");
+ #endif
+ sendCommand("-file-list-exec-source-files", GdbQuerySources);
+ //sendCommand("-gdb-set stop-on-solib-events 1");
+ }
+
+ sendCommand("-data-list-register-names", RegisterListNames);
+
+ // set all to "pending"
+ if (q->startMode() == q->attachExternal)
+ qq->breakHandler()->removeAllBreakpoints();
+ else
+ qq->breakHandler()->setAllPending();
+
+ QTimer::singleShot(0, this, SLOT(attemptBreakpointSynchronization()));
+
+ return true;
+}
+
+void GdbEngine::continueInferior()
+{
+ q->resetLocation();
+ setTokenBarrier();
+ qq->notifyInferiorRunningRequested();
+ emit gdbInputAvailable(QString(), QString());
+ sendCommand("-exec-continue", GdbExecContinue);
+}
+
+void GdbEngine::runInferior()
+{
+ q->resetLocation();
+ // FIXME: this ignores important startup messages
+ setTokenBarrier();
+ if (!q->m_processArgs.isEmpty())
+ sendCommand("-exec-arguments " + q->m_processArgs.join(" "));
+ qq->notifyInferiorRunningRequested();
+ emit gdbInputAvailable(QString(), QString());
+ sendCommand("-exec-run", GdbExecRun);
+#if defined(Q_OS_WIN)
+ sendCommand("info proc", GdbInfoProc);
+#endif
+#if defined(Q_OS_LINUX)
+ sendCommand("info proc", GdbInfoProc);
+#endif
+#if defined(Q_OS_MAC)
+ sendCommand("info pid", GdbInfoProc, QVariant(), true);
+#endif
+}
+
+void GdbEngine::stepExec()
+{
+ setTokenBarrier();
+ qq->notifyInferiorRunningRequested();
+ emit gdbInputAvailable(QString(), QString());
+ sendCommand("-exec-step", GdbExecStep);
+}
+
+void GdbEngine::stepIExec()
+{
+ setTokenBarrier();
+ qq->notifyInferiorRunningRequested();
+ sendCommand("-exec-step-instruction", GdbExecStepI);
+}
+
+void GdbEngine::stepOutExec()
+{
+ setTokenBarrier();
+ qq->notifyInferiorRunningRequested();
+ sendCommand("-exec-finish", GdbExecFinish);
+}
+
+void GdbEngine::nextExec()
+{
+ setTokenBarrier();
+ qq->notifyInferiorRunningRequested();
+ emit gdbInputAvailable(QString(), QString());
+ sendCommand("-exec-next", GdbExecNext);
+}
+
+void GdbEngine::nextIExec()
+{
+ setTokenBarrier();
+ qq->notifyInferiorRunningRequested();
+ sendCommand("-exec-next-instruction", GdbExecNextI);
+}
+
+void GdbEngine::runToLineExec(const QString &fileName, int lineNumber)
+{
+ setTokenBarrier();
+ qq->notifyInferiorRunningRequested();
+ sendCommand("-exec-until " + fileName + ":" + QString::number(lineNumber));
+}
+
+void GdbEngine::runToFunctionExec(const QString &functionName)
+{
+ setTokenBarrier();
+ sendCommand("-break-insert -t " + functionName);
+ qq->notifyInferiorRunningRequested();
+ sendCommand("-exec-continue", GdbExecRunToFunction);
+}
+
+void GdbEngine::jumpToLineExec(const QString &fileName, int lineNumber)
+{
+#if 1
+ // not available everywhere?
+ //sendCliCommand("tbreak " + fileName + ":" + QString::number(lineNumber));
+ sendCommand("-break-insert -t " + fileName + ":" + QString::number(lineNumber));
+ sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
+ // will produce something like
+ // &"jump /home/apoenitz/dev/work/test1/test1.cpp:242"
+ // ~"Continuing at 0x4058f3."
+ // ~"run1 (argc=1, argv=0x7fffbf1f5538) at test1.cpp:242"
+ // ~"242\t x *= 2;"
+ // 23^done"
+ q->gotoLocation(fileName, lineNumber, true);
+ //setBreakpoint();
+ //sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
+#else
+ q->gotoLocation(fileName, lineNumber, true);
+ setBreakpoint(fileName, lineNumber);
+ sendCommand("jump " + fileName + ":" + QString::number(lineNumber));
+#endif
+}
+
+/*!
+ \fn void GdbEngine::setTokenBarrier()
+ \brief Sets up internal structures to handle a new debugger turn.
+
+ This method is called at the beginnign of all step/next/finish etc.
+ debugger functions.
+*/
+
+void GdbEngine::setTokenBarrier()
+{
+ m_oldestAcceptableToken = currentToken();
+}
+
+void GdbEngine::setDebugDumpers(bool on)
+{
+ if (on) {
+ qDebug() << "SWITCHING ON DUMPER DEBUGGING";
+ sendCommand("set unwindonsignal off");
+ q->breakByFunction("qDumpObjectData440");
+ //updateLocals();
+ } else {
+ qDebug() << "SWITCHING OFF DUMPER DEBUGGING";
+ sendCommand("set unwindonsignal on");
+ }
+}
+
+//QByteArray GdbEngine::dumperChannel() const
+//{
+// return m_dumperServer->serverName().toLatin1();
+// //QByteArray ba;
+// //ba.setNum(m_dumperServer->serverPort());
+// //return ba;
+//}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Breakpoint specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+void GdbEngine::breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt)
+{
+ if (!bkpt.isValid())
+ return;
+ if (!data)
+ return;
+ data->pending = false;
+ data->bpMultiple = false;
+ data->bpCondition.clear();
+ QStringList files;
+ foreach (const GdbMi &child, bkpt.children()) {
+ if (child.hasName("number")) {
+ data->bpNumber = child.data();
+ } else if (child.hasName("func")) {
+ data->bpFuncName = child.data();
+ } else if (child.hasName("addr")) {
+ // <MULTIPLE> happens in constructors. In this case there are
+ // _two_ fields named "addr" in the response. On Linux that is...
+ if (child.data() == "<MULTIPLE>")
+ data->bpMultiple = true;
+ else
+ data->bpAddress = child.data();
+ } else if (child.hasName("file")) {
+ files.append(child.data());
+ } else if (child.hasName("fullname")) {
+ QString fullName = child.data();
+ #ifdef Q_OS_WIN
+ fullName = QDir::cleanPath(fullName);
+ #endif
+ files.prepend(fullName);
+ } else if (child.hasName("line")) {
+ data->bpLineNumber = child.data();
+ if (child.data().toInt())
+ data->markerLineNumber = child.data().toInt();
+ } else if (child.hasName("cond")) {
+ data->bpCondition = child.data();
+ // gdb 6.3 likes to "rewrite" conditions. Just accept that fact.
+ if (data->bpCondition != data->condition && data->conditionsMatch())
+ data->condition = data->bpCondition;
+ }
+ else if (child.hasName("pending")) {
+ data->pending = true;
+ int pos = child.data().lastIndexOf(':');
+ if (pos > 0) {
+ data->bpLineNumber = child.data().mid(pos + 1);
+ data->markerLineNumber = child.data().mid(pos + 1).toInt();
+ files.prepend(child.data().left(pos));
+ } else {
+ files.prepend(child.data());
+ }
+ }
+ }
+ // This field is not present. Contents needs to be parsed from
+ // the plain "ignore" response.
+ //else if (child.hasName("ignore"))
+ // data->bpIgnoreCount = child.data();
+
+ QString name = fullName(files);
+ if (data->bpFileName.isEmpty())
+ data->bpFileName = name;
+ if (data->markerFileName.isEmpty())
+ data->markerFileName = name;
+}
+
+void GdbEngine::sendInsertBreakpoint(int index)
+{
+ const BreakpointData *data = qq->breakHandler()->at(index);
+ QString where;
+ if (data->funcName.isEmpty()) {
+ where = data->fileName;
+#ifdef Q_OS_MAC
+ // full names do not work on Mac/MI
+ QFileInfo fi(data->fileName);
+ where = fi.fileName();
+ //where = fi.absoluteFilePath();
+#endif
+#ifdef Q_OS_WIN
+ // full names do not work on Mac/MI
+ QFileInfo fi(data->fileName);
+ where = fi.fileName();
+ //where = m_manager->shortName(data->fileName);
+ //if (where.isEmpty())
+ // where = data->fileName;
+#endif
+ // we need something like "\"file name.cpp\":100" to
+ // survive the gdb command line parser with file names intact
+ where = "\"\\\"" + where + "\\\":" + data->lineNumber + "\"";
+ } else {
+ where = data->funcName;
+ }
+
+ // set up fallback in case of pending breakpoints which aren't handled
+ // by the MI interface
+#ifdef Q_OS_LINUX
+ QString cmd = "-break-insert ";
+ //if (!data->condition.isEmpty())
+ // cmd += "-c " + data->condition + " ";
+ cmd += where;
+#endif
+#ifdef Q_OS_MAC
+ QString cmd = "-break-insert -l -1 ";
+ //if (!data->condition.isEmpty())
+ // cmd += "-c " + data->condition + " ";
+ cmd += where;
+#endif
+#ifdef Q_OS_WIN
+ QString cmd = "-break-insert ";
+ //if (!data->condition.isEmpty())
+ // cmd += "-c " + data->condition + " ";
+ cmd += where;
+#endif
+ sendCommand(cmd, BreakInsert, index, true);
+ //processQueueAndContinue();
+}
+
+void GdbEngine::handleBreakList(const GdbResultRecord &record)
+{
+ // 45^done,BreakpointTable={nr_rows="2",nr_cols="6",hdr=[
+ // {width="3",alignment="-1",col_name="number",colhdr="Num"}, ...
+ // body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
+ // addr="0x000000000040109e",func="main",file="app.cpp",
+ // fullname="/home/apoenitz/dev/work/plugintest/app/app.cpp",
+ // line="11",times="1"},
+ // bkpt={number="2",type="breakpoint",disp="keep",enabled="y",
+ // addr="<PENDING>",pending="plugin.cpp:7",times="0"}] ... }
+
+ if (record.resultClass == GdbResultDone) {
+ GdbMi table = record.data.findChild("BreakpointTable");
+ if (table.isValid())
+ handleBreakList(table);
+ }
+}
+
+void GdbEngine::handleBreakList(const GdbMi &table)
+{
+ //qDebug() << "GdbEngine::handleOutput: table: "
+ // << table.toString();
+ GdbMi body = table.findChild("body");
+ //qDebug() << "GdbEngine::handleOutput: body: "
+ // << body.toString();
+ QList<GdbMi> bkpts;
+ if (body.isValid()) {
+ // Non-Mac
+ bkpts = body.children();
+ } else {
+ // Mac
+ bkpts = table.children();
+ // remove the 'hdr' and artificial items
+ //qDebug() << "FOUND " << bkpts.size() << " BREAKPOINTS";
+ for (int i = bkpts.size(); --i >= 0; ) {
+ int num = bkpts.at(i).findChild("number").data().toInt();
+ if (num <= 0) {
+ //qDebug() << "REMOVING " << i << bkpts.at(i).toString();
+ bkpts.removeAt(i);
+ }
+ }
+ //qDebug() << "LEFT " << bkpts.size() << " BREAKPOINTS";
+ }
+
+ BreakHandler *handler = qq->breakHandler();
+ for (int index = 0; index != bkpts.size(); ++index) {
+ BreakpointData temp(handler);
+ breakpointDataFromOutput(&temp, bkpts.at(index));
+ int found = handler->findBreakpoint(temp);
+ if (found != -1)
+ breakpointDataFromOutput(handler->at(found), bkpts.at(index));
+ //else
+ //qDebug() << "CANNOT HANDLE RESPONSE " << bkpts.at(index).toString();
+ }
+
+ attemptBreakpointSynchronization();
+ handler->updateMarkers();
+}
+
+
+void GdbEngine::handleBreakIgnore(const GdbResultRecord &record, int index)
+{
+ // gdb 6.8:
+ // ignore 2 0:
+ // ~"Will stop next time breakpoint 2 is reached.\n"
+ // 28^done
+ // ignore 2 12:
+ // &"ignore 2 12\n"
+ // ~"Will ignore next 12 crossings of breakpoint 2.\n"
+ // 29^done
+ //
+ // gdb 6.3 does not produce any console output
+ BreakHandler *handler = qq->breakHandler();
+ if (record.resultClass == GdbResultDone && index < handler->size()) {
+ QString msg = record.data.findChild("consolestreamoutput").data();
+ BreakpointData *data = handler->at(index);
+ //if (msg.contains("Will stop next time breakpoint")) {
+ // data->bpIgnoreCount = "0";
+ //} else if (msg.contains("Will ignore next")) {
+ // data->bpIgnoreCount = data->ignoreCount;
+ //}
+ // FIXME: this assumes it is doing the right thing...
+ data->bpIgnoreCount = data->ignoreCount;
+ attemptBreakpointSynchronization();
+ handler->updateMarkers();
+ }
+}
+
+void GdbEngine::handleBreakCondition(const GdbResultRecord &record, int index)
+{
+ BreakHandler *handler = qq->breakHandler();
+ if (record.resultClass == GdbResultDone) {
+ // we just assume it was successful. otherwise we had to parse
+ // the output stream data
+ BreakpointData *data = handler->at(index);
+ //qDebug() << "HANDLE BREAK CONDITION " << index << data->condition;
+ data->bpCondition = data->condition;
+ attemptBreakpointSynchronization();
+ handler->updateMarkers();
+ } else if (record.resultClass == GdbResultError) {
+ QString msg = record.data.findChild("msg").data();
+ // happens on Mac
+ if (1 || msg.startsWith("Error parsing breakpoint condition. "
+ " Will try again when we hit the breakpoint.")) {
+ BreakpointData *data = handler->at(index);
+ //qDebug() << "ERROR BREAK CONDITION " << index << data->condition;
+ data->bpCondition = data->condition;
+ attemptBreakpointSynchronization();
+ handler->updateMarkers();
+ }
+ }
+}
+
+void GdbEngine::handleBreakInsert(const GdbResultRecord &record, int index)
+{
+ BreakHandler *handler = qq->breakHandler();
+ if (record.resultClass == GdbResultDone) {
+ //qDebug() << "HANDLE BREAK INSERT " << index;
+//#ifdef Q_OS_MAC
+ // interesting only on Mac?
+ BreakpointData *data = handler->at(index);
+ GdbMi bkpt = record.data.findChild("bkpt");
+ //qDebug() << "BKPT: " << bkpt.toString() << " DATA" << data->toToolTip();
+ breakpointDataFromOutput(data, bkpt);
+//#endif
+ attemptBreakpointSynchronization();
+ handler->updateMarkers();
+ } else if (record.resultClass == GdbResultError) {
+ const BreakpointData *data = handler->at(index);
+#ifdef Q_OS_LINUX
+ //QString where = "\"\\\"" + data->fileName + "\\\":"
+ // + data->lineNumber + "\"";
+ QString where = "\"" + data->fileName + "\":"
+ + data->lineNumber;
+ sendCommand("break " + where, BreakInsert1, index);
+#endif
+#ifdef Q_OS_MAC
+ QFileInfo fi(data->fileName);
+ QString where = "\"" + fi.fileName() + "\":"
+ + data->lineNumber;
+ sendCommand("break " + where, BreakInsert1, index);
+#endif
+#ifdef Q_OS_WIN
+ QFileInfo fi(data->fileName);
+ QString where = "\"" + fi.fileName() + "\":"
+ + data->lineNumber;
+ //QString where = m_data->fileName + QLatin1Char(':') + data->lineNumber;
+ sendCommand("break " + where, BreakInsert1, index);
+#endif
+ }
+}
+
+void GdbEngine::extractDataFromInfoBreak(const QString &output, BreakpointData *data)
+{
+ data->bpFileName = "<MULTIPLE>";
+
+ //qDebug() << output;
+ if (output.isEmpty())
+ return;
+ // "Num Type Disp Enb Address What
+ // 4 breakpoint keep y <MULTIPLE> 0x00000000004066ad
+ // 4.1 y 0x00000000004066ad in CTorTester
+ // at /data5/dev/ide/main/tests/manual/gdbdebugger/simple/app.cpp:124
+ // - or -
+ // everything on a single line on Windows for constructors of classes
+ // within namespaces.
+ // Sometimes the path is relative too.
+
+ QRegExp re("MULTIPLE.*(0x[0-9a-f]+) in (.*)\\s+at (.*):([\\d]+)([^\\d]|$)");
+ re.setMinimal(true);
+
+ if (re.indexIn(output) != -1) {
+ data->bpAddress = re.cap(1);
+ data->bpFuncName = re.cap(2).trimmed();
+ data->bpLineNumber = re.cap(4);
+ QString full = fullName(re.cap(3));
+ data->markerLineNumber = data->bpLineNumber.toInt();
+ data->markerFileName = full;
+ data->bpFileName = full;
+ //qDebug() << "FOUND BREAKPOINT\n" << output
+ // << re.cap(1) << "\n" << re.cap(2) << "\n"
+ // << re.cap(3) << "\n" << re.cap(4) << "\n";
+ } else {
+ qDebug() << "COULD NOT MATCH " << re.pattern() << " AND " << output;
+ data->bpNumber = "<unavailable>";
+ }
+}
+
+void GdbEngine::handleBreakInfo(const GdbResultRecord &record, int bpNumber)
+{
+ BreakHandler *handler = qq->breakHandler();
+ if (record.resultClass == GdbResultDone) {
+ // Old-style output for multiple breakpoints, presumably in a
+ // constructor
+ int found = handler->findBreakpoint(bpNumber);
+ if (found != -1) {
+ QString str = record.data.findChild("consolestreamoutput").data();
+ extractDataFromInfoBreak(str, handler->at(found));
+ handler->updateMarkers();
+ attemptBreakpointSynchronization(); // trigger "ready"
+ }
+ }
+}
+
+void GdbEngine::handleBreakInsert1(const GdbResultRecord &record, int index)
+{
+ BreakHandler *handler = qq->breakHandler();
+ if (record.resultClass == GdbResultDone) {
+ // Pending breakpoints in dylibs on Mac only?
+ BreakpointData *data = handler->at(index);
+ GdbMi bkpt = record.data.findChild("bkpt");
+ breakpointDataFromOutput(data, bkpt);
+ attemptBreakpointSynchronization(); // trigger "ready"
+ handler->updateMarkers();
+ } else if (record.resultClass == GdbResultError) {
+ qDebug() << "INSERTING BREAKPOINT WITH BASE NAME FAILED. GIVING UP";
+ BreakpointData *data = handler->at(index);
+ data->bpNumber = "<unavailable>";
+ attemptBreakpointSynchronization(); // trigger "ready"
+ handler->updateMarkers();
+ }
+}
+
+void GdbEngine::attemptBreakpointSynchronization()
+{
+ BreakHandler *handler = qq->breakHandler();
+ //qDebug() << "BREAKPOINT SYNCHRONIZATION ";
+
+ foreach (BreakpointData *data, handler->takeRemovedBreakpoints()) {
+ //qDebug() << " SYNCHRONIZATION REMOVING" << data;
+ QString bpNumber = data->bpNumber;
+ if (!bpNumber.trimmed().isEmpty())
+ sendCommand("-break-delete " + bpNumber, BreakDelete, 0, true);
+ //else
+ // qDebug() << "BP HAS NO NUMBER: " << data->markerFileName;
+ delete data;
+ }
+
+ bool updateNeeded = false;
+
+ for (int index = 0; index != handler->size(); ++index) {
+ BreakpointData *data = handler->at(index);
+ // multiple breakpoints?
+ if (data->bpMultiple && data->bpFileName.isEmpty()) {
+ sendCommand(QString("info break %1").arg(data->bpNumber),
+ BreakInfo, data->bpNumber.toInt());
+ updateNeeded = true;
+ break;
+ }
+ }
+
+ for (int index = 0; index != handler->size(); ++index) {
+ BreakpointData *data = handler->at(index);
+ // unset breakpoints?
+ if (data->bpNumber.isEmpty()) {
+ data->bpNumber = " ";
+ sendInsertBreakpoint(index);
+ //qDebug() << "UPDATE NEEDED BECAUSE OF UNKNOWN BREAKPOINT";
+ updateNeeded = true;
+ break;
+ }
+ }
+
+ if (!updateNeeded) {
+ for (int index = 0; index != handler->size(); ++index) {
+ BreakpointData *data = handler->at(index);
+ // update conditions if needed
+ if (data->bpNumber.toInt() && data->condition != data->bpCondition
+ && !data->conditionsMatch()) {
+ sendCommand(QString("condition %1 %2").arg(data->bpNumber)
+ .arg(data->condition), BreakCondition, index);
+ //qDebug() << "UPDATE NEEDED BECAUSE OF CONDITION"
+ // << data->condition << data->bpCondition;
+ updateNeeded = true;
+ break;
+ }
+ // update ignorecount if needed
+ if (data->bpNumber.toInt() && data->ignoreCount != data->bpIgnoreCount) {
+ sendCommand(QString("ignore %1 %2").arg(data->bpNumber)
+ .arg(data->ignoreCount), BreakIgnore, index);
+ updateNeeded = true;
+ break;
+ }
+ }
+ }
+
+ for (int index = 0; index != handler->size(); ++index) {
+ // happens sometimes on Mac. Brush over symptoms
+ BreakpointData *data = handler->at(index);
+ if (data->markerFileName.startsWith("../")) {
+ data->markerFileName = fullName(data->markerFileName);
+ handler->updateMarkers();
+ }
+ }
+
+ if (updateNeeded) {
+ //interruptAndContinue();
+ //sendListBreakpoints();
+ }
+
+ if (!updateNeeded && q->status() == DebuggerProcessStartingUp)
+ qq->notifyStartupFinished();
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Disassembler specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+void GdbEngine::reloadDisassembler()
+{
+ emit sendCommand("disassemble", DisassemblerList, m_address);
+}
+
+void GdbEngine::handleDisassemblerList(const GdbResultRecord &record,
+ const QString &cookie)
+{
+ QList<DisassemblerLine> lines;
+ static const QString pad = QLatin1String(" ");
+ int currentLine = -1;
+ if (record.resultClass == GdbResultDone) {
+ QString res = record.data.findChild("consolestreamoutput").data();
+ QTextStream ts(&res, QIODevice::ReadOnly);
+ while (!ts.atEnd()) {
+ //0x0000000000405fd8 <_ZN11QTextStreamD1Ev@plt+0>:
+ // jmpq *2151890(%rip) # 0x6135b0 <_GLOBAL_OFFSET_TABLE_+640>
+ //0x0000000000405fde <_ZN11QTextStreamD1Ev@plt+6>:
+ // pushq $0x4d
+ //0x0000000000405fe3 <_ZN11QTextStreamD1Ev@plt+11>:
+ // jmpq 0x405af8 <_init+24>
+ //0x0000000000405fe8 <_ZN9QHashData6rehashEi@plt+0>:
+ // jmpq *2151882(%rip) # 0x6135b8 <_GLOBAL_OFFSET_TABLE_+648>
+ QString str = ts.readLine();
+ if (!str.startsWith(QLatin1String("0x"))) {
+ //qDebug() << "IGNORING DISASSEMBLER" << str;
+ continue;
+ }
+ DisassemblerLine line;
+ QTextStream ts(&str, QIODevice::ReadOnly);
+ ts >> line.address >> line.symbol;
+ line.mnemonic = ts.readLine().trimmed();
+ if (line.symbol.endsWith(QLatin1Char(':')))
+ line.symbol.chop(1);
+ line.addressDisplay = line.address + pad;
+ if (line.addressDisplay.startsWith(QLatin1String("0x00000000")))
+ line.addressDisplay.replace(2, 8, QString());
+ line.symbolDisplay = line.symbol + pad;
+
+ if (line.address == cookie)
+ currentLine = lines.size();
+
+ lines.append(line);
+ }
+ } else {
+ DisassemblerLine line;
+ line.addressDisplay = tr("<could not retreive module information>");
+ lines.append(line);
+ }
+
+ qq->disassemblerHandler()->setLines(lines);
+ if (currentLine != -1)
+ qq->disassemblerHandler()->setCurrentLine(currentLine);
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Modules specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+void GdbEngine::loadSymbols(const QString &moduleName)
+{
+ // FIXME: gdb does not understand quoted names here (tested with 6.8)
+ sendCommand("sharedlibrary " + dotEscape(moduleName));
+ reloadModules();
+}
+
+void GdbEngine::loadAllSymbols()
+{
+ sendCommand("sharedlibrary .*");
+ reloadModules();
+}
+
+void GdbEngine::reloadModules()
+{
+ sendCommand("info shared", ModulesList, QVariant());
+}
+
+void GdbEngine::handleModulesList(const GdbResultRecord &record)
+{
+ QList<Module> modules;
+ if (record.resultClass == GdbResultDone) {
+ QString data = record.data.findChild("consolestreamoutput").data();
+ QTextStream ts(&data, QIODevice::ReadOnly);
+ while (!ts.atEnd()) {
+ QString line = ts.readLine();
+ if (!line.startsWith("0x"))
+ continue;
+ Module module;
+ QString symbolsRead;
+ QTextStream ts(&line, QIODevice::ReadOnly);
+ ts >> module.startAddress >> module.endAddress >> symbolsRead;
+ module.moduleName = ts.readLine().trimmed();
+ module.symbolsRead = (symbolsRead == "Yes");
+ modules.append(module);
+ }
+ }
+ qq->modulesHandler()->setModules(modules);
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Stack specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+void GdbEngine::handleStackSelectThread(const GdbResultRecord &record, int)
+{
+ Q_UNUSED(record);
+ qDebug("FIXME: StackHandler::handleOutput: SelectThread");
+ q->showStatusMessage(tr("Retrieving data for stack view..."), -1);
+ sendCommand("-stack-list-frames", StackListFrames);
+}
+
+
+void GdbEngine::handleStackListFrames(const GdbResultRecord &record)
+{
+ QList<StackFrame> stackFrames;
+
+ const GdbMi stack = record.data.findChild("stack");
+ QString dummy = stack.toString();
+ if (!stack.isValid()) {
+ qDebug() << "FIXME: stack: " << stack.toString();
+ return;
+ }
+
+ int topFrame = -1;
+
+ for (int i = 0; i != stack.childCount(); ++i) {
+ //qDebug() << "HANDLING FRAME: " << stack.childAt(i).toString();
+ const GdbMi frameMi = stack.childAt(i);
+ StackFrame frame;
+ frame.level = i;
+ QStringList files;
+ files.append(frameMi.findChild("fullname").data());
+ files.append(frameMi.findChild("file").data());
+ frame.file = fullName(files);
+ frame.function = frameMi.findChild("func").data();
+ frame.from = frameMi.findChild("from").data();
+ frame.line = frameMi.findChild("line").data().toInt();
+ frame.address = frameMi.findChild("addr").data();
+
+ stackFrames.append(frame);
+
+#ifdef Q_OS_WIN
+ const bool isBogus =
+ // Assume this is wrong and points to some strange stl_algobase
+ // implementation. Happens on Karsten's XP system with Gdb 5.50
+ (frame.file.endsWith("/bits/stl_algobase.h") && frame.line == 150)
+ // Also wrong. Happens on Vista with Gdb 5.50
+ || (frame.function == "operator new" && frame.line == 151);
+
+ // immediately leave bogus frames
+ if (topFrame == -1 && isBogus) {
+ sendCommand("-exec-finish");
+ return;
+ }
+
+#endif
+
+ // Initialize top frame to the first valid frame
+ const bool isValid = !frame.file.isEmpty() && !frame.function.isEmpty();
+ if (isValid && topFrame == -1)
+ topFrame = i;
+ }
+
+ qq->stackHandler()->setFrames(stackFrames);
+
+#if 0
+ if (0 && topFrame != -1) {
+ // updates of locals already triggered early
+ const StackFrame &frame = qq->stackHandler()->currentFrame();
+ bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
+ if (usable)
+ q->gotoLocation(frame.file, frame.line, true);
+ else
+ qDebug() << "FULL NAME NOT USABLE 0: " << frame.file;
+ } else {
+ activateFrame(topFrame);
+ }
+#else
+ if (topFrame != -1) {
+ // updates of locals already triggered early
+ const StackFrame &frame = qq->stackHandler()->currentFrame();
+ bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
+ if (usable)
+ q->gotoLocation(frame.file, frame.line, true);
+ else
+ qDebug() << "FULL NAME NOT USABLE 0: " << frame.file;
+ }
+#endif
+}
+
+void GdbEngine::selectThread(int index)
+{
+ //reset location arrow
+ q->resetLocation();
+
+ ThreadsHandler *threadsHandler = qq->threadsHandler();
+ threadsHandler->setCurrentThread(index);
+
+ QList<ThreadData> threads = threadsHandler->threads();
+ QWB_ASSERT(index < threads.size(), return);
+ int id = threads.at(index).id;
+ q->showStatusMessage(tr("Retrieving data for stack view..."), -1);
+ sendCommand(QLatin1String("-thread-select ") + QString::number(id),
+ StackSelectThread);
+}
+
+void GdbEngine::activateFrame(int frameIndex)
+{
+ if (q->status() != DebuggerInferiorStopped)
+ return;
+
+ StackHandler *stackHandler = qq->stackHandler();
+ int oldIndex = stackHandler->currentIndex();
+ //qDebug() << "ACTIVATE FRAME: " << frameIndex << oldIndex
+ // << stackHandler->currentIndex();
+
+ QWB_ASSERT(frameIndex < stackHandler->stackSize(), return);
+
+ if (oldIndex != frameIndex) {
+ // Assuming this always succeeds saves a roundtrip.
+ // Otherwise the lines below would need to get triggered
+ // after a response to this -stack-select-frame here.
+ sendCommand("-stack-select-frame " + QString::number(frameIndex));
+
+ stackHandler->setCurrentIndex(frameIndex);
+ updateLocals();
+ }
+
+ const StackFrame &frame = stackHandler->currentFrame();
+
+ bool usable = !frame.file.isEmpty() && QFileInfo(frame.file).isReadable();
+ if (usable)
+ q->gotoLocation(frame.file, frame.line, true);
+ else
+ qDebug() << "FULL NAME NOT USABLE: " << frame.file;
+}
+
+void GdbEngine::handleStackListThreads(const GdbResultRecord &record, int id)
+{
+ // "72^done,{thread-ids={thread-id="2",thread-id="1"},number-of-threads="2"}
+ const QList<GdbMi> items = record.data.findChild("thread-ids").children();
+ QList<ThreadData> threads;
+ int currentIndex = -1;
+ for (int index = 0, n = items.size(); index != n; ++index) {
+ ThreadData thread;
+ thread.id = items.at(index).data().toInt();
+ threads.append(thread);
+ if (thread.id == id) {
+ //qDebug() << "SETTING INDEX TO: " << index << " ID: "<< id << "RECOD: "<< record.toString();
+ currentIndex = index;
+ }
+ }
+ ThreadsHandler *threadsHandler = qq->threadsHandler();
+ threadsHandler->setThreads(threads);
+ threadsHandler->setCurrentThread(currentIndex);
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Register specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+void GdbEngine::reloadRegisters()
+{
+ QString format = qq->registerHandler()->model()->property(PROPERTY_REGISTER_FORMAT).toString();
+ sendCommand("-data-list-register-values " + format, RegisterListValues);
+}
+
+void GdbEngine::handleRegisterListNames(const GdbResultRecord &record)
+{
+ if (record.resultClass != GdbResultDone)
+ return;
+
+ QList<Register> registers;
+ foreach (const GdbMi &item, record.data.findChild("register-names").children())
+ registers.append(Register(item.data()));
+
+ qq->registerHandler()->setRegisters(registers);
+}
+
+void GdbEngine::handleRegisterListValues(const GdbResultRecord &record)
+{
+ if (record.resultClass != GdbResultDone)
+ return;
+
+ QList<Register> registers = qq->registerHandler()->registers();
+
+ // 24^done,register-values=[{number="0",value="0xf423f"},...]
+ foreach (const GdbMi &item, record.data.findChild("register-values").children()) {
+ int index = item.findChild("number").data().toInt();
+ if (index < registers.size()) {
+ Register &reg = registers[index];
+ QString value = item.findChild("value").data();
+ reg.changed = (value != reg.value);
+ if (reg.changed)
+ reg.value = value;
+ }
+ }
+ qq->registerHandler()->setRegisters(registers);
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Thread specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+bool GdbEngine::supportsThreads() const
+{
+ // 6.3 crashes happily on -thread-list-ids. So don't use it.
+ // The test below is a semi-random pick, 6.8 works fine
+ return m_gdbVersion > 650;
+}
+
+//////////////////////////////////////////////////////////////////////
+//
+// Tooltip specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+static WatchData m_toolTip;
+static QString m_toolTipExpression;
+static QPoint m_toolTipPos;
+static QHash<QString, WatchData> m_toolTipCache;
+
+static bool hasLetterOrNumber(const QString &exp)
+{
+ for (int i = exp.size(); --i >= 0; )
+ if (exp[i].isLetterOrNumber() || exp[i] == '_')
+ return true;
+ return false;
+}
+
+static bool hasSideEffects(const QString &exp)
+{
+ // FIXME: complete?
+ return exp.contains("-=")
+ || exp.contains("+=")
+ || exp.contains("/=")
+ || exp.contains("*=")
+ || exp.contains("&=")
+ || exp.contains("|=")
+ || exp.contains("^=")
+ || exp.contains("--")
+ || exp.contains("++");
+}
+
+static bool isKeyWord(const QString &exp)
+{
+ // FIXME: incomplete
+ return exp == QLatin1String("class")
+ || exp == QLatin1String("const")
+ || exp == QLatin1String("do")
+ || exp == QLatin1String("if")
+ || exp == QLatin1String("return")
+ || exp == QLatin1String("struct")
+ || exp == QLatin1String("template")
+ || exp == QLatin1String("void")
+ || exp == QLatin1String("volatile")
+ || exp == QLatin1String("while");
+}
+
+void GdbEngine::setToolTipExpression(const QPoint &pos, const QString &exp0)
+{
+ //qDebug() << "SET TOOLTIP EXP" << pos << exp0;
+ if (q->status() != DebuggerInferiorStopped) {
+ //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED";
+ return;
+ }
+
+ if (qq->debugDumpersAction()->isChecked()) {
+ // minimize interference
+ return;
+ }
+
+ m_toolTipPos = pos;
+ m_toolTipExpression = exp0;
+ QString exp = exp0;
+/*
+ if (m_toolTip.isTypePending()) {
+ qDebug() << "suppressing duplicated tooltip creation";
+ return;
+ }
+*/
+ if (m_toolTipCache.contains(exp)) {
+ const WatchData & data = m_toolTipCache[exp];
+ // FIXME: qq->watchHandler()->collapseChildren(data.iname);
+ insertData(data);
+ return;
+ }
+
+ QToolTip::hideText();
+ if (exp.isEmpty() || exp.startsWith("#")) {
+ QToolTip::hideText();
+ return;
+ }
+
+ if (!hasLetterOrNumber(exp)) {
+ QToolTip::showText(m_toolTipPos,
+ "'" + exp + "' contains no identifier");
+ return;
+ }
+
+ if (isKeyWord(exp))
+ return;
+
+ if (exp.startsWith('"') && exp.endsWith('"')) {
+ QToolTip::showText(m_toolTipPos, "String literal " + exp);
+ return;
+ }
+
+ if (exp.startsWith("++") || exp.startsWith("--"))
+ exp = exp.mid(2);
+
+ if (exp.endsWith("++") || exp.endsWith("--"))
+ exp = exp.mid(2);
+
+ if (exp.startsWith("<") || exp.startsWith("["))
+ return;
+
+ if (hasSideEffects(exp)) {
+ QToolTip::showText(m_toolTipPos,
+ "Cowardly refusing to evaluate expression '" + exp
+ + "' with potential side effects");
+ return;
+ }
+
+ // Gdb crashes when creating a variable object with the name
+ // of the type of 'this'
+/*
+ for (int i = 0; i != m_currentLocals.childCount(); ++i) {
+ if (m_currentLocals.childAt(i).exp == "this") {
+ qDebug() << "THIS IN ROW " << i;
+ if (m_currentLocals.childAt(i).type.startsWith(exp)) {
+ QToolTip::showText(m_toolTipPos,
+ exp + ": type of current 'this'");
+ qDebug() << " TOOLTIP CRASH SUPPRESSED";
+ return;
+ }
+ break;
+ }
+ }
+*/
+
+ //if (m_manager->status() != DebuggerInferiorStopped)
+ // return;
+
+ // FIXME: 'exp' can contain illegal characters
+ m_toolTip = WatchData();
+ //m_toolTip.level = 0;
+ // m_toolTip.row = 0;
+ // m_toolTip.parentIndex = 2;
+ m_toolTip.exp = exp;
+ m_toolTip.name = exp;
+ m_toolTip.iname = tooltipIName;
+ insertData(m_toolTip);
+ updateWatchModel2();
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Watch specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+static const QString strNotInScope = QLatin1String("<not in scope>");
+
+static bool isPointerType(const QString &type)
+{
+ return type.endsWith("*") || type.endsWith("* const");
+}
+
+static bool isAccessSpecifier(const QString &str)
+{
+ static const QStringList items =
+ QStringList() << "private" << "protected" << "public";
+ return items.contains(str);
+}
+
+static bool startsWithDigit(const QString &str)
+{
+ return !str.isEmpty() && str[0] >= '0' && str[0] <= '9';
+}
+
+QString stripPointerType(QString type)
+{
+ if (type.endsWith("*"))
+ type.chop(1);
+ if (type.endsWith("* const"))
+ type.chop(7);
+ if (type.endsWith(' '))
+ type.chop(1);
+ return type;
+}
+
+static QString gdbQuoteTypes(const QString &type)
+{
+ // gdb does not understand sizeof(Core::IFile*).
+ // "sizeof('Core::IFile*')" is also not acceptable,
+ // it needs to be "sizeof('Core::IFile'*)"
+ //
+ // We never will have a perfect solution here (even if we had a full blown
+ // C++ parser as we do not have information on what is a type and what is
+ // a vriable name. So "a<b>::c" could either be two comparisons of values
+ // 'a', 'b' and '::c', or a nested type 'c' in a template 'a<b>'. We
+ // assume here it is the latter.
+ //return type;
+
+ // (*('myns::QPointer<myns::QObject>*'*)0x684060)" is not acceptable
+ // (*('myns::QPointer<myns::QObject>'**)0x684060)" is acceptable
+ if (isPointerType(type))
+ return gdbQuoteTypes(stripPointerType(type)) + "*";
+
+ QString accu;
+ QString result;
+ int templateLevel = 0;
+ for (int i = 0; i != type.size(); ++i) {
+ QChar c = type.at(i);
+ if (c.isLetterOrNumber() || c == '_' || c == ':' || c == ' ') {
+ accu += c;
+ } else if (c == '<') {
+ ++templateLevel;
+ accu += c;
+ } else if (c == '<') {
+ --templateLevel;
+ accu += c;
+ } else if (templateLevel > 0) {
+ accu += c;
+ } else {
+ if (accu.contains(':') || accu.contains('<'))
+ result += '\'' + accu + '\'';
+ else
+ result += accu;
+ accu.clear();
+ result += c;
+ }
+ }
+ if (accu.contains(':') || accu.contains('<'))
+ result += '\'' + accu + '\'';
+ else
+ result += accu;
+ //qDebug() << "GDB_QUOTING" << type << " TO " << result;
+
+ return result;
+}
+
+static void setWatchDataValue(WatchData &data, const GdbMi &mi,
+ int encoding = 0)
+{
+ if (mi.isValid()) {
+ QByteArray ba;
+ switch (encoding) {
+ case 0: // unencoded 8 bit data
+ ba = mi.data();
+ break;
+ case 1: // base64 encoded 8 bit data
+ ba = QByteArray::fromBase64(mi.data());
+ break;
+ case 2: // base64 encoded 16 bit data
+ ba = QByteArray::fromBase64(mi.data());
+ ba = QString::fromUtf16((ushort *)ba.data(), ba.size() / 2).toUtf8();
+ break;
+ case 3: // base64 encoded 32 bit data
+ ba = QByteArray::fromBase64(mi.data());
+ ba = QString::fromUcs4((uint *)ba.data(), ba.size() / 4).toUtf8();
+ break;
+ }
+ data.setValue(ba);
+ } else {
+ data.setValueNeeded();
+ }
+}
+
+static void setWatchDataEditValue(WatchData &data, const GdbMi &mi)
+{
+ if (mi.isValid())
+ data.editvalue = mi.data();
+}
+
+static void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi)
+{
+ if (mi.isValid())
+ data.setValueToolTip(mi.data());
+}
+
+static void setWatchDataChildCount(WatchData &data, const GdbMi &mi)
+{
+ if (mi.isValid()) {
+ data.childCount = mi.data().toInt();
+ data.setChildCountUnneeded();
+ if (data.childCount == 0)
+ data.setChildrenUnneeded();
+ } else {
+ data.childCount = -1;
+ }
+}
+
+static void setWatchDataValueDisabled(WatchData &data, const GdbMi &mi)
+{
+ if (mi.data() == "true")
+ data.valuedisabled = true;
+ else if (mi.data() == "false")
+ data.valuedisabled = false;
+}
+
+static void setWatchDataExpression(WatchData &data, const GdbMi &mi)
+{
+ if (mi.isValid())
+ data.exp = "(" + mi.data() + ")";
+}
+
+static void setWatchDataAddress(WatchData &data, const GdbMi &mi)
+{
+ if (mi.isValid()) {
+ data.addr = mi.data();
+ if (data.exp.isEmpty())
+ data.exp = "(*(" + gdbQuoteTypes(data.type) + "*)" + data.addr + ")";
+ }
+}
+
+static bool extractTemplate(const QString &type, QString *tmplate, QString *inner)
+{
+ // Input "Template<Inner1,Inner2,...>::Foo" will return "Template::Foo" in
+ // 'tmplate' and "Inner1@Inner2@..." etc in 'inner'. Result indicates
+ // whether parsing was successful
+ int level = 0;
+ for (int i = 0; i != type.size(); ++i) {
+ QChar c = type[i];
+ if (c == '<') {
+ *(level == 0 ? tmplate : inner) += c;
+ ++level;
+ } else if (c == '>') {
+ --level;
+ *(level == 0 ? tmplate : inner) += c;
+ } else if (c == ',') {
+ *inner += (level == 1) ? '@' : ',';
+ } else {
+ *(level == 0 ? tmplate : inner) += c;
+ }
+ }
+ *tmplate = tmplate->trimmed();
+ *tmplate = tmplate->remove("<>");
+ *inner = inner->trimmed();
+ //qDebug() << "EXTRACT TEMPLATE: " << *tmplate << *inner;
+ return !inner->isEmpty();
+}
+
+static QString extractTypeFromPTypeOutput(const QString &str)
+{
+ int pos0 = str.indexOf('=');
+ int pos1 = str.indexOf('{');
+ int pos2 = str.lastIndexOf('}');
+ QString res = str;
+ if (pos0 != -1 && pos1 != -1 && pos2 != -1)
+ res = str.mid(pos0 + 2, pos1 - 1 - pos0)
+ + " ... " + str.right(str.size() - pos2);
+ return res.simplified();
+}
+
+static bool isIntOrFloatType(const QString &type)
+{
+ static const QStringList types = QStringList()
+ << "char" << "int" << "short" << "float" << "double" << "long"
+ << "bool" << "signed char" << "unsigned" << "unsigned char"
+ << "unsigned int" << "unsigned long" << "long long"
+ << "unsigned long long";
+ return types.contains(type);
+}
+
+static QString sizeofTypeExpression(const QString &type)
+{
+ if (type.endsWith('*'))
+ return "sizeof(void*)";
+ if (type.endsWith('>'))
+ return "sizeof(" + type + ")";
+ return "sizeof(" + gdbQuoteTypes(type) + ")";
+}
+
+void GdbEngine::setCustomDumpersWanted(bool on)
+{
+ Q_UNUSED(on);
+ // FIXME: a bit too harsh, but otherwise the treeview
+ // sometimes look funny
+ //m_expandedINames.clear();
+ updateLocals();
+}
+
+bool GdbEngine::isCustomValueDumperAvailable(const QString &type) const
+{
+ if (!qq->useCustomDumpers())
+ return false;
+ if (qq->debugDumpersAction()->isChecked()
+ && qq->stackHandler()->isDebuggingDumpers())
+ return false;
+ if (m_dataDumperState != DataDumperAvailable)
+ return false;
+ if (m_availableSimpleDumpers.contains(type))
+ return true;
+
+ QString tmplate;
+ QString inner;
+ if (extractTemplate(type, &tmplate, &inner)) {
+ if (type.startsWith(m_namespace)) {
+ tmplate = tmplate.mid(m_namespace.size());
+ if (tmplate == "QList")
+ return true;
+ if (tmplate == "QVector")
+ return true;
+ if (tmplate == "QHash")
+ return true;
+ if (tmplate == "QHashNode")
+ return true;
+ if (tmplate == "QMap")
+ return true;
+ if (tmplate == "QMapNode")
+ return true;
+ if (tmplate == "QSet")
+ return true;
+ }
+ if (tmplate == "std::vector" && inner != "bool")
+ return true;
+ if (tmplate == "std::basic_string") {
+ if (inner.startsWith("char@") || inner.startsWith("wchar_t@"))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void GdbEngine::runCustomDumper(const WatchData & data0, bool dumpChildren)
+{
+ WatchData data = data0;
+ QWB_ASSERT(!data.exp.isEmpty(), return);
+ QString tmplate;
+ QString inner;
+ bool isTemplate = extractTemplate(data.type, &tmplate, &inner);
+ QStringList inners = inner.split('@');
+ if (inners.at(0).isEmpty())
+ inners.clear();
+
+ QString outertype = isTemplate ? tmplate : data.type;
+
+ if (outertype == "QWidget")
+ outertype = "QObject";
+
+ QString extraArgs[4];
+ extraArgs[0] = "0";
+ extraArgs[1] = "0";
+ extraArgs[2] = "0";
+ extraArgs[3] = "0";
+ int extraArgCount = 0;
+
+ // "generic" template dumpers: passing sizeof(argument)
+ // gives already most information the dumpers need
+ foreach (const QString &arg, inners)
+ extraArgs[extraArgCount++] = sizeofTypeExpression(arg);
+
+ // in rare cases we need more or less:
+ if (outertype == m_namespace + "QObject") {
+ extraArgs[extraArgCount++] = "(char*)&((('"
+ + m_namespace + "QObjectPrivate'*)&"
+ + data.exp + ")->children)-(char*)&" + data.exp;
+ } else if (outertype == m_namespace + "QVector") {
+ extraArgs[extraArgCount++] = "(char*)&(("
+ + data.exp + ").d->array)-(char*)" + data.exp + ".d";
+ } else if (outertype == m_namespace + "QObjectSlot"
+ || outertype == m_namespace + "QObjectSignal") {
+ // we need the number out of something like
+ // iname="local.ob.slots.[2]deleteLater()"
+ int lastOpened = data.iname.lastIndexOf('[');
+ int lastClosed = data.iname.lastIndexOf(']');
+ QString slotNumber = "-1";
+ if (lastOpened != -1 && lastClosed != -1)
+ slotNumber = data.iname.mid(lastOpened + 1, lastClosed - lastOpened - 1);
+ extraArgs[extraArgCount++] = slotNumber;
+ } else if (outertype == m_namespace + "QMap") {
+ QString nodetype = m_namespace + "QMapNode";
+ nodetype += data.type.mid(m_namespace.size() + 4);
+ //qDebug() << "OUTERTYPE: " << outertype << " NODETYPE: " << nodetype;
+ extraArgs[extraArgCount++] = sizeofTypeExpression(nodetype);
+ extraArgs[extraArgCount++] = "(size_t)&(('" + nodetype + "'*)0)->value";
+ } else if (outertype == m_namespace + "QMapNode") {
+ extraArgs[extraArgCount++] = sizeofTypeExpression(data.type);
+ extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value";
+ } else if (outertype == "std::vector") {
+ //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
+ if (inners.at(0) == "bool") {
+ outertype = "std::vector::bool";
+ } else {
+ //extraArgs[extraArgCount++] = sizeofTypeExpression(data.type);
+ //extraArgs[extraArgCount++] = "(size_t)&(('" + data.type + "'*)0)->value";
+ }
+ } else if (outertype == "std::basic_string") {
+ //qDebug() << "EXTRACT TEMPLATE: " << outertype << inners;
+ if (inners.at(0) == "char") {
+ outertype = "std::string";
+ } else if (inners.at(0) == "wchar_t") {
+ outertype = "std::wstring";
+ }
+ extraArgs[0] = "0";
+ extraArgs[1] = "0";
+ extraArgs[2] = "0";
+ extraArgs[3] = "0";
+ }
+
+ //int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
+ //int protocol = data.iname.startsWith("watch") ? 3 : 2;
+ int protocol = 2;
+ //int protocol = isDisplayedIName(data.iname) ? 3 : 2;
+
+ QString addr;
+ if (data.addr.startsWith("0x"))
+ addr = "(void*)" + data.addr;
+ else
+ addr = "&(" + data.exp + ")";
+
+ QByteArray params;
+ params.append(outertype);
+ params.append('\0');
+ params.append(data.iname);
+ params.append('\0');
+ params.append(data.exp);
+ params.append('\0');
+ params.append(inner);
+ params.append('\0');
+ params.append(data.iname);
+ params.append('\0');
+
+ sendWatchParameters(params);
+
+ QString cmd ="call "
+ + QString("qDumpObjectData440(")
+ + QString::number(protocol)
+ + ',' + "%1+1" // placeholder for token
+ + ',' + addr
+ + ',' + (dumpChildren ? "1" : "0")
+ + ',' + extraArgs[0]
+ + ',' + extraArgs[1]
+ + ',' + extraArgs[2]
+ + ',' + extraArgs[3] + ')';
+
+ sendSynchronizedCommand(cmd, WatchDumpCustomValue1, QVariant::fromValue(data));
+
+ q->showStatusMessage(
+ tr("Retrieving data for watch view (%1 requests pending)...")
+ .arg(m_pendingRequests + 1), -1);
+ // create response slot for socket data
+ QVariant var;
+ var.setValue(data);
+ sendSynchronizedCommand(QString(), WatchDumpCustomValue2, var);
+
+ // this increases the probability that gdb spits out output it
+ // has collected so far
+ //sendCommand("p qDumpInBuffer");
+}
+
+void GdbEngine::createGdbVariable(const WatchData &data)
+{
+ sendSynchronizedCommand("-var-delete \"" + data.iname + '"');
+ QString exp = data.exp;
+ if (exp.isEmpty() && data.addr.startsWith("0x"))
+ exp = "*(" + gdbQuoteTypes(data.type) + "*)" + data.addr;
+ QVariant val = QVariant::fromValue<WatchData>(data);
+ sendSynchronizedCommand("-var-create \"" + data.iname + '"' + " * "
+ + '"' + exp + '"', WatchVarCreate, val);
+}
+
+void GdbEngine::updateSubItem(const WatchData &data0)
+{
+ WatchData data = data0;
+ #if DEBUG_SUBITEM
+ qDebug() << "UPDATE SUBITEM: " << data.toString();
+ #endif
+ QWB_ASSERT(data.isValid(), return);
+
+ // in any case we need the type first
+ if (data.isTypeNeeded()) {
+ // This should only happen if we don't have a variable yet.
+ // Let's play safe, though.
+ if (!data.variable.isEmpty()) {
+ // Update: It does so for out-of-scope watchers.
+ #if 1
+ qDebug() << "FIXME: GdbEngine::updateSubItem: "
+ << data.toString() << "should not happen";
+ #else
+ data.setType("<out of scope>");
+ data.setValue("<out of scope>");
+ data.setChildCount(0);
+ insertData(data);
+ return;
+ #endif
+ }
+ // The WatchVarCreate handler will receive type information
+ // and re-insert a WatchData item with correct type, so
+ // we will not re-enter this bit.
+ // FIXME: Concurrency issues?
+ createGdbVariable(data);
+ return;
+ }
+
+ // we should have a type now. this is relied upon further below
+ QWB_ASSERT(!data.type.isEmpty(), return);
+
+ // a common case that can be easily solved
+ if (data.isChildrenNeeded() && isPointerType(data.type)
+ && !isCustomValueDumperAvailable(data.type)) {
+ // We sometimes know what kind of children pointers have
+ #if DEBUG_SUBITEM
+ qDebug() << "IT'S A POINTER";
+ #endif
+#if 1
+ WatchData data1;
+ data1.iname = data.iname + ".*";
+ data1.name = "*" + data.name;
+ data1.exp = "(*(" + data.exp + "))";
+ data1.type = stripPointerType(data.type);
+ data1.setValueNeeded();
+ insertData(data1);
+ data.setChildrenUnneeded();
+ insertData(data);
+#else
+ // Try automatic dereferentiation
+ data.exp = "*(" + data.exp + ")";
+ data.type = data.type + "."; // FIXME: fragile HACK to avoid recursion
+ insertData(data);
+#endif
+ return;
+ }
+
+ if (data.isValueNeeded() && isCustomValueDumperAvailable(data.type)) {
+ #if DEBUG_SUBITEM
+ qDebug() << "UPDATE SUBITEM: CUSTOMVALUE";
+ #endif
+ runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname));
+ return;
+ }
+
+/*
+ if (data.isValueNeeded() && data.exp.isEmpty()) {
+ #if DEBUG_SUBITEM
+ qDebug() << "UPDATE SUBITEM: NO EXPRESSION?";
+ #endif
+ data.setError("<no expression given>");
+ insertData(data);
+ return;
+ }
+*/
+
+ if (data.isValueNeeded() && data.variable.isEmpty()) {
+ #if DEBUG_SUBITEM
+ qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE";
+ #endif
+ createGdbVariable(data);
+ // the WatchVarCreate handler will re-insert a WatchData
+ // item, with valueNeeded() set.
+ return;
+ }
+
+ if (data.isValueNeeded()) {
+ QWB_ASSERT(!data.variable.isEmpty(), return); // tested above
+ #if DEBUG_SUBITEM
+ qDebug() << "UPDATE SUBITEM: VALUE";
+ #endif
+ QString cmd = "-var-evaluate-expression \"" + data.iname + "\"";
+ sendSynchronizedCommand(cmd, WatchEvaluateExpression,
+ QVariant::fromValue(data));
+ return;
+ }
+
+ if (data.isChildrenNeeded() && isCustomValueDumperAvailable(data.type)) {
+ #if DEBUG_SUBITEM
+ qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
+ #endif
+ runCustomDumper(data, true);
+ return;
+ }
+
+ if (data.isChildrenNeeded() && data.variable.isEmpty()) {
+ #if DEBUG_SUBITEM
+ qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN";
+ #endif
+ createGdbVariable(data);
+ // the WatchVarCreate handler will re-insert a WatchData
+ // item, with childrenNeeded() set.
+ return;
+ }
+
+ if (data.isChildrenNeeded()) {
+ QWB_ASSERT(!data.variable.isEmpty(), return); // tested above
+ QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
+ sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
+ return;
+ }
+
+ if (data.isChildCountNeeded() && isCustomValueDumperAvailable(data.type)) {
+ #if DEBUG_SUBITEM
+ qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
+ #endif
+ runCustomDumper(data, qq->watchHandler()->isExpandedIName(data.iname));
+ return;
+ }
+
+ if (data.isChildCountNeeded() && data.variable.isEmpty()) {
+ #if DEBUG_SUBITEM
+ qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT";
+ #endif
+ createGdbVariable(data);
+ // the WatchVarCreate handler will re-insert a WatchData
+ // item, with childrenNeeded() set.
+ return;
+ }
+
+ if (data.isChildCountNeeded()) {
+ QWB_ASSERT(!data.variable.isEmpty(), return); // tested above
+ QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
+ sendCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
+ return;
+ }
+
+ qDebug() << "FIXME: UPDATE SUBITEM: " << data.toString();
+ QWB_ASSERT(false, return);
+}
+
+void GdbEngine::updateWatchModel()
+{
+ m_pendingRequests = 0;
+ PENDING_DEBUG("EXTERNAL TRIGGERING UPDATE WATCH MODEL");
+ updateWatchModel2();
+}
+
+void GdbEngine::updateWatchModel2()
+{
+ PENDING_DEBUG("UPDATE WATCH MODEL");
+ QList<WatchData> incomplete = qq->watchHandler()->takeCurrentIncompletes();
+ //QWB_ASSERT(incomplete.isEmpty(), /**/);
+ if (!incomplete.isEmpty()) {
+ #if DEBUG_PENDING
+ qDebug() << "##############################################";
+ qDebug() << "UPDATE MODEL, FOUND INCOMPLETES:";
+ foreach (const WatchData &data, incomplete)
+ qDebug() << data.toString();
+ #endif
+
+ // Bump requests to avoid model rebuilding during the nested
+ // updateWatchModel runs.
+ ++m_pendingRequests;
+ foreach (const WatchData &data, incomplete)
+ updateSubItem(data);
+ PENDING_DEBUG("INTERNAL TRIGGERING UPDATE WATCH MODEL");
+ updateWatchModel2();
+ --m_pendingRequests;
+
+ return;
+ }
+
+ if (m_pendingRequests > 0) {
+ PENDING_DEBUG("UPDATE MODEL, PENDING: " << m_pendingRequests);
+ return;
+ }
+
+ PENDING_DEBUG("REBUILDING MODEL")
+ emit gdbInputAvailable(QString(),
+ "[" + currentTime() + "] <Rebuild Watchmodel>");
+ q->showStatusMessage(tr("Finished retrieving data."), -1);
+ qq->watchHandler()->rebuildModel();
+
+ if (!m_toolTipExpression.isEmpty()) {
+ WatchData *data = qq->watchHandler()->findData(tooltipIName);
+ if (data) {
+ //m_toolTipCache[data->exp] = *data;
+ QToolTip::showText(m_toolTipPos,
+ "(" + data->type + ") " + data->exp + " = " + data->value);
+ } else {
+ QToolTip::showText(m_toolTipPos,
+ "Cannot evaluate expression: " + m_toolTipExpression);
+ }
+ }
+
+ //qDebug() << "INSERT DATA" << data0.toString();
+ //q->showStatusMessage(tr("Stopped."), 5000);
+}
+
+void GdbEngine::handleQueryDataDumper1(const GdbResultRecord &record)
+{
+ Q_UNUSED(record);
+}
+
+void GdbEngine::handleQueryDataDumper2(const GdbResultRecord &record)
+{
+ // is this the official gdb response. However, it won't contain
+ // interesting data other than the information that 'real' data
+ // either already arrived or is still in the pipe. So we do
+ // _not_ register this result for counting purposes, this will
+ // be done by the 'real' result (with resultClass == GdbResultCustomDone)
+ //qDebug() << "DATA DUMPER TRIAL:" << record.toString();
+ GdbMi output = record.data.findChild("customvaluecontents");
+ GdbMi contents(output.data());
+ GdbMi simple = contents.findChild("simpledumpers");
+ m_namespace = contents.findChild("namespace").data();
+ //qDebug() << "OUTPUT: " << output.toString();
+ //qDebug() << "CONTENTS: " << contents.toString();
+ //qDebug() << "SIMPLE DUMPERS: " << simple.toString();
+ m_availableSimpleDumpers.clear();
+ foreach (const GdbMi &item, simple.children())
+ m_availableSimpleDumpers.append(item.data());
+ if (m_availableSimpleDumpers.isEmpty()) {
+ m_dataDumperState = DataDumperUnavailable;
+ QMessageBox::warning(q->mainWindow(),
+ tr("Cannot find special data dumpers"),
+ tr("The debugged binary does not contain information needed for "
+ "nice display of Qt data types.\n\n"
+ "Try might want to try include the file\n\n"
+ ".../ide/main/bin/gdbmacros/gdbmacros.cpp'\n\n"
+ "into your project directly.")
+ );
+ } else {
+ m_dataDumperState = DataDumperAvailable;
+ }
+ //qDebug() << "DATA DUMPERS AVAILABLE" << m_availableSimpleDumpers;
+}
+
+void GdbEngine::sendWatchParameters(const QByteArray &params0)
+{
+ QByteArray params = params0;
+ params.append('\0');
+ char buf[50];
+ sprintf(buf, "set {char[%d]} qDumpInBuffer = {", params.size());
+ QByteArray encoded;
+ encoded.append(buf);
+ for (int i = 0; i != params.size(); ++i) {
+ sprintf(buf, "%d,", int(params[i]));
+ encoded.append(buf);
+ }
+ encoded[encoded.size() - 1] = '}';
+
+ sendCommand(encoded);
+}
+
+void GdbEngine::handleVarAssign()
+{
+ // everything might have changed, force re-evaluation
+ // FIXME: Speed this up by re-using variables and only
+ // marking values as 'unknown'
+ updateLocals();
+}
+
+void GdbEngine::setWatchDataType(WatchData &data, const GdbMi &mi)
+{
+ if (mi.isValid()) {
+ if (!data.framekey.isEmpty())
+ m_varToType[data.framekey] = mi.data();
+ data.setType(mi.data());
+ } else if (data.type.isEmpty()) {
+ data.setTypeNeeded();
+ }
+}
+
+void GdbEngine::handleVarCreate(const GdbResultRecord &record,
+ const WatchData &data0)
+{
+ WatchData data = data0;
+ // happens e.g. when we already issued a var-evaluate command
+ if (!data.isValid())
+ return;
+ //qDebug() << "HANDLE VARIABLE CREATION: " << data.toString();
+ if (record.resultClass == GdbResultDone) {
+ data.variable = data.iname;
+ setWatchDataType(data, record.data.findChild("type"));
+ if (isCustomValueDumperAvailable(data.type)) {
+ // we do not trust gdb if we have a custom dumper
+ if (record.data.findChild("children").isValid())
+ data.setChildrenUnneeded();
+ else if (qq->watchHandler()->isExpandedIName(data.iname))
+ data.setChildrenNeeded();
+ insertData(data);
+ } else {
+ if (record.data.findChild("children").isValid())
+ data.setChildrenUnneeded();
+ else if (qq->watchHandler()->isExpandedIName(data.iname))
+ data.setChildrenNeeded();
+ setWatchDataChildCount(data, record.data.findChild("numchild"));
+ //if (data.isValueNeeded() && data.childCount > 0)
+ // data.setValue(QByteArray());
+ insertData(data);
+ }
+ } else if (record.resultClass == GdbResultError) {
+ data.setError(record.data.findChild("msg").data());
+ if (data.isWatcher()) {
+ data.value = strNotInScope;
+ data.type = " ";
+ data.setAllUnneeded();
+ data.setChildCount(0);
+ data.valuedisabled = true;
+ insertData(data);
+ }
+ }
+}
+
+void GdbEngine::handleEvaluateExpression(const GdbResultRecord &record,
+ const WatchData &data0)
+{
+ WatchData data = data0;
+ QWB_ASSERT(data.isValid(), qDebug() << "HUH?");
+ if (record.resultClass == GdbResultDone) {
+ //if (col == 0)
+ // data.name = record.data.findChild("value").data();
+ //else
+ setWatchDataValue(data, record.data.findChild("value"));
+ } else if (record.resultClass == GdbResultError) {
+ data.setError(record.data.findChild("msg").data());
+ }
+ //qDebug() << "HANDLE EVALUATE EXPRESSION: " << data.toString();
+ insertData(data);
+ //updateWatchModel2();
+}
+
+void GdbEngine::handleDumpCustomSetup(const GdbResultRecord &record)
+{
+ qDebug() << "CUSTOM SETUP RESULT: " << record.toString();
+ if (record.resultClass == GdbResultDone) {
+ } else if (record.resultClass == GdbResultError) {
+ QString msg = record.data.findChild("msg").data();
+ qDebug() << "CUSTOM DUMPER SETUP ERROR MESSAGE: " << msg;
+ }
+}
+
+void GdbEngine::handleDumpCustomValue1(const GdbResultRecord &record,
+ const WatchData &data0)
+{
+ WatchData data = data0;
+ QWB_ASSERT(data.isValid(), return);
+ if (record.resultClass == GdbResultDone) {
+ // ignore this case, data will follow
+ } else if (record.resultClass == GdbResultError) {
+ // Record an extra result, as the socket result will be lost
+ // in transmission
+ --m_pendingRequests;
+ QString msg = record.data.findChild("msg").data();
+ //qDebug() << "CUSTOM DUMPER ERROR MESSAGE: " << msg;
+#ifdef QT_DEBUG
+ // Make debugging of dumers easier
+ if (qq->debugDumpersAction()->isChecked()
+ && msg.startsWith("The program being debugged stopped while")
+ && msg.contains("qDumpObjectData440")) {
+ // Fake full stop
+ sendCommand("-file-list-exec-source-files", GdbQuerySources);
+ sendCommand("-break-list", BreakList);
+ sendCommand("p 0", GdbAsyncOutput2); // dummy
+ return;
+ }
+#endif
+ if (msg.startsWith("The program being debugged was sig"))
+ msg = strNotInScope;
+ if (msg.startsWith("The program being debugged stopped while"))
+ msg = strNotInScope;
+ data.setError(msg);
+ insertData(data);
+ }
+}
+
+void GdbEngine::handleDumpCustomValue2(const GdbResultRecord &record,
+ const WatchData &data0)
+{
+ WatchData data = data0;
+ QWB_ASSERT(data.isValid(), return);
+ //qDebug() << "CUSTOM VALUE RESULT: " << record.toString();
+ //qDebug() << "FOR DATA: " << data.toString() << record.resultClass;
+ if (record.resultClass == GdbResultDone) {
+ GdbMi output = record.data.findChild("customvaluecontents");
+ //qDebug() << "HANDLE VALUE CONTENTS: " << output.toString(true);
+ if (!output.isValid()) {
+ //qDebug() << "INVALID";
+ // custom dumper produced no output
+ if (data.isValueNeeded())
+ data.setValue("<unknown>");
+ if (data.isTypeNeeded())
+ data.setType("<unknown>");
+ if (data.isChildrenNeeded())
+ data.setChildCount(0);
+ if (data.isChildCountNeeded())
+ data.setChildCount(0);
+ data.setValueToolTip("<custom dumper produced no output>");
+ insertData(data);
+ } else {
+ GdbMi contents;
+ //qDebug() << "OUTPUT" << output.toString(true);
+ contents.fromString(output.data());
+ //qDebug() << "CONTENTS" << contents.toString(true);
+ setWatchDataType(data, contents.findChild("type"));
+ setWatchDataValue(data, contents.findChild("value"),
+ contents.findChild("valueencoded").data().toInt());
+ setWatchDataAddress(data, contents.findChild("addr"));
+ setWatchDataChildCount(data, contents.findChild("numchild"));
+ setWatchDataValueToolTip(data, contents.findChild("valuetooltip"));
+ setWatchDataValueDisabled(data, contents.findChild("valuedisabled"));
+ setWatchDataEditValue(data, contents.findChild("editvalue"));
+ if (qq->watchHandler()->isDisplayedIName(data.iname)) {
+ GdbMi editvalue = contents.findChild("editvalue");
+ if (editvalue.isValid()) {
+ setWatchDataEditValue(data, editvalue);
+ qq->watchHandler()->showEditValue(data);
+ }
+ }
+ if (!qq->watchHandler()->isExpandedIName(data.iname))
+ data.setChildrenUnneeded();
+ GdbMi children = contents.findChild("children");
+ if (children.isValid() || !qq->watchHandler()->isExpandedIName(data.iname))
+ data.setChildrenUnneeded();
+ data.setValueUnneeded();
+
+ // try not to repeat data too often
+ WatchData childtemplate;
+ setWatchDataType(childtemplate, contents.findChild("childtype"));
+ setWatchDataChildCount(childtemplate, contents.findChild("childnumchild"));
+ //qDebug() << "DATA: " << data.toString();
+ insertData(data);
+ foreach (GdbMi item, children.children()) {
+ WatchData data1 = childtemplate;
+ data1.name = item.findChild("name").data();
+ data1.iname = data.iname + "." + data1.name;
+ //qDebug() << "NAMEENCODED: " << item.findChild("nameencoded").data()
+ // << item.findChild("nameencoded").data()[1];
+ if (item.findChild("nameencoded").data()[0] == '1')
+ data1.name = QByteArray::fromBase64(data1.name.toUtf8());
+ if (item.findChild("nameisindex").data()[0] == '1')
+ data1.name = '[' + data1.name + ']';
+ setWatchDataType(data1, item.findChild("type"));
+ setWatchDataExpression(data1, item.findChild("exp"));
+ setWatchDataChildCount(data1, item.findChild("numchild"));
+ setWatchDataValue(data1, item.findChild("value"),
+ item.findChild("valueencoded").data().toInt());
+ setWatchDataAddress(data1, item.findChild("addr"));
+ setWatchDataValueToolTip(data1, item.findChild("valuetooltip"));
+ setWatchDataValueDisabled(data1, item.findChild("valuedisabled"));
+ if (!qq->watchHandler()->isExpandedIName(data1.iname))
+ data1.setChildrenUnneeded();
+ //qDebug() << "HANDLE CUSTOM SUBCONTENTS:" << data1.toString();
+ insertData(data1);
+ }
+ }
+ //qDebug() << "HANDLE CUSTOM VALUE CONTENTS: " << data.toString();
+ } else if (record.resultClass == GdbResultError) {
+ // FIXME: Should not happen here, i.e. could be removed
+ QString msg = record.data.findChild("msg").data();
+ //qDebug() << "CUSTOM DUMPER ERROR MESSAGE: " << msg;
+ if (msg.startsWith("The program being debugged was sig"))
+ msg = strNotInScope;
+ if (msg.startsWith("The program being debugged stopped while"))
+ msg = strNotInScope;
+ data.setError(msg);
+ insertData(data);
+ } else {
+ qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA: " << data.toString();
+ }
+}
+
+void GdbEngine::updateLocals()
+{
+ setTokenBarrier();
+
+ m_pendingRequests = 0;
+ PENDING_DEBUG("\nRESET PENDING");
+ m_toolTipCache.clear();
+ m_toolTipExpression.clear();
+ qq->watchHandler()->reinitializeWatchers();
+
+ int level = currentFrame();
+ // '2' is 'list with type and value'
+ QString cmd = QString("-stack-list-arguments 2 %1 %2").arg(level).arg(level);
+ sendSynchronizedCommand(cmd, StackListArguments); // stage 1/2
+ // '2' is 'list with type and value'
+ sendSynchronizedCommand("-stack-list-locals 2", StackListLocals); // stage 2/2
+}
+
+void GdbEngine::handleStackListArguments(const GdbResultRecord &record)
+{
+ // stage 1/2
+
+ // Linux:
+ // 12^done,stack-args=
+ // [frame={level="0",args=[
+ // {name="argc",type="int",value="1"},
+ // {name="argv",type="char **",value="(char **) 0x7..."}]}]
+ // Mac:
+ // 78^done,stack-args=
+ // {frame={level="0",args={
+ // varobj=
+ // {exp="this",value="0x38a2fab0",name="var21",numchild="3",
+ // type="CurrentDocumentFind * const",typecode="PTR",
+ // dynamic_type="",in_scope="true",block_start_addr="0x3938e946",
+ // block_end_addr="0x3938eb2d"},
+ // varobj=
+ // {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}",
+ // name="var22",numchild="1",type="const QString ...} }}}
+ //
+ // In both cases, iterating over the children of stack-args/frame/args
+ // is ok.
+ m_currentFunctionArgs.clear();
+ if (record.resultClass == GdbResultDone) {
+ const GdbMi list = record.data.findChild("stack-args");
+ const GdbMi frame = list.findChild("frame");
+ const GdbMi args = frame.findChild("args");
+ m_currentFunctionArgs = args.children();
+ } else if (record.resultClass == GdbResultError) {
+ qDebug() << "FIXME: GdbEngine::handleStackListArguments: should not happen";
+ }
+}
+
+void GdbEngine::handleStackListLocals(const GdbResultRecord &record)
+{
+ // stage 2/2
+
+ // There could be shadowed variables
+ QHash<QString, int> seen;
+ QList<GdbMi> locals = record.data.findChild("locals").children();
+ locals += m_currentFunctionArgs;
+
+ //qDebug() << m_varToType;
+
+ foreach (const GdbMi &item, locals) {
+ #ifdef Q_OS_MAC
+ QString name = item.findChild("exp").data();
+ #else
+ QString name = item.findChild("name").data();
+ #endif
+ int n = seen.value(name);
+ if (n) {
+ seen[name] = n + 1;
+ WatchData data;
+ data.iname = "local." + name + QString::number(n + 1);
+ data.name = name + QString(" <shadowed %1>").arg(n);
+ //data.setValue("<shadowed>");
+ setWatchDataValue(data, item.findChild("value"));
+ data.setType("<shadowed>");
+ data.setChildCount(0);
+ insertData(data);
+ } else {
+ seen[name] = 1;
+ WatchData data;
+ data.iname = "local." + name;
+ data.name = name;
+ data.exp = name;
+ data.framekey = m_currentFrame + data.name;
+ setWatchDataType(data, item.findChild("type"));
+ // set value only directly if it is simple enough, otherwise
+ // pass through the insertData() machinery
+ if (isIntOrFloatType(data.type) || isPointerType(data.type))
+ setWatchDataValue(data, item.findChild("value"));
+ if (!qq->watchHandler()->isExpandedIName(data.iname))
+ data.setChildrenUnneeded();
+ if (isPointerType(data.type) || data.name == "this")
+ data.setChildCount(1);
+ if (0 && m_varToType.contains(data.framekey)) {
+ qDebug() << "RE-USING " << m_varToType.value(data.framekey);
+ data.setType(m_varToType.value(data.framekey));
+ }
+ insertData(data);
+ }
+ }
+}
+
+void GdbEngine::insertData(const WatchData &data0)
+{
+ //qDebug() << "INSERT DATA" << data0.toString();
+ WatchData data = data0;
+ if (data.value.startsWith("mi_cmd_var_create:")) {
+ qDebug() << "BOGUS VALUE: " << data.toString();
+ return;
+ }
+ qq->watchHandler()->insertData(data);
+}
+
+void GdbEngine::handleTypeContents(const QString &output)
+{
+ // output.startsWith("type = ") == true
+ // "type = int"
+ // "type = class QString {"
+ // "type = class QStringList : public QList<QString> {"
+ QString tip;
+ QString className;
+ if (output.startsWith("type = class")) {
+ int posBrace = output.indexOf('{');
+ QString head = output.mid(13, posBrace - 13 - 1);
+ int posColon = head.indexOf(": public");
+ if (posColon == -1)
+ posColon = head.indexOf(": protected");
+ if (posColon == -1)
+ posColon = head.indexOf(": private");
+ if (posColon == -1) {
+ className = head;
+ tip = "class " + className + " { ... }";
+ } else {
+ className = head.left(posColon - 1);
+ tip = "class " + head + " { ... }";
+ }
+ //qDebug() << "posColon: " << posColon;
+ //qDebug() << "posBrace: " << posBrace;
+ //qDebug() << "head: " << head;
+ } else {
+ className = output.mid(7);
+ tip = className;
+ }
+ //qDebug() << "output: " << output.left(100) + "...";
+ //qDebug() << "className: " << className;
+ //qDebug() << "tip: " << tip;
+ //m_toolTip.type = className;
+ m_toolTip.type.clear();
+ m_toolTip.value = tip;
+}
+
+void GdbEngine::handleVarListChildrenHelper(const GdbMi &item,
+ const WatchData &parent)
+{
+ //qDebug() << "VAR_LIST_CHILDREN: PARENT 2" << parent.toString();
+ //qDebug() << "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
+ QByteArray exp = item.findChild("exp").data();
+ QByteArray name = item.findChild("name").data();
+ if (isAccessSpecifier(exp)) {
+ // suppress 'private'/'protected'/'public' level
+ WatchData data;
+ data.variable = name;
+ data.iname = parent.iname;
+ //data.iname = data.variable;
+ data.exp = parent.exp;
+ data.setTypeUnneeded();
+ data.setValueUnneeded();
+ data.setChildCountUnneeded();
+ data.setChildrenUnneeded();
+ //qDebug() << "DATA" << data.toString();
+ QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
+ //iname += '.' + exp;
+ sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
+ } else if (item.findChild("numchild").data() == "0") {
+ // happens for structs without data, e.g. interfaces.
+ WatchData data;
+ data.iname = parent.iname + '.' + exp;
+ data.name = exp;
+ data.variable = name;
+ setWatchDataType(data, item.findChild("type"));
+ setWatchDataValue(data, item.findChild("value"));
+ setWatchDataAddress(data, item.findChild("addr"));
+ data.setChildCount(0);
+ insertData(data);
+ } else if (parent.iname.endsWith('.')) {
+ // Happens with anonymous unions
+ WatchData data;
+ data.iname = name;
+ QString cmd = "-var-list-children --all-values \"" + data.variable + "\"";
+ sendSynchronizedCommand(cmd, WatchVarListChildren, QVariant::fromValue(data));
+ } else if (exp == "staticMetaObject") {
+ // && item.findChild("type").data() == "const QMetaObject")
+ // FIXME: Namespaces?
+ // { do nothing } FIXME: make coinfigurable?
+ // special "clever" hack to avoid clutter in the GUI.
+ // I am not sure this is a good idea...
+ } else {
+ WatchData data;
+ data.iname = parent.iname + '.' + exp;
+ data.variable = name;
+ setWatchDataType(data, item.findChild("type"));
+ setWatchDataValue(data, item.findChild("value"));
+ setWatchDataAddress(data, item.findChild("addr"));
+ setWatchDataChildCount(data, item.findChild("numchild"));
+ if (!qq->watchHandler()->isExpandedIName(data.iname))
+ data.setChildrenUnneeded();
+
+ data.name = exp;
+ if (isPointerType(parent.type) && data.type == exp) {
+ data.exp = "*(" + parent.exp + ")";
+ data.name = "*" + parent.name;
+ } else if (data.type == exp) {
+ // A type we derive from? gdb crashes when creating variables here
+ data.exp = parent.exp;
+ } else if (exp.startsWith("*")) {
+ // A pointer
+ data.exp = "*(" + parent.exp + ")";
+ } else if (startsWithDigit(exp)) {
+ // An array. No variables needed?
+ data.name = "[" + data.name + "]";
+ data.exp = parent.exp + "[" + exp + "]";
+ } else if (0 && parent.name.endsWith('.')) {
+ // Happens with anonymous unions
+ data.exp = parent.exp + exp;
+ //data.name = "<anonymous union>";
+ } else if (exp.isEmpty()) {
+ // Happens with anonymous unions
+ data.exp = parent.exp;
+ data.name = "<n/a>";
+ data.iname = parent.iname + ".@";
+ data.type = "<anonymous union>";
+ } else {
+ // A structure. Hope there's nothing else...
+ data.exp = parent.exp + '.' + exp;
+ }
+
+ if (isCustomValueDumperAvailable(data.type)) {
+ // we do not trust gdb if we have a custom dumper
+ data.setValueNeeded();
+ data.setChildCountNeeded();
+ }
+
+ //qDebug() << "VAR_LIST_CHILDREN: PARENT 3" << parent.toString();
+ //qDebug() << "VAR_LIST_CHILDREN: APPENDEE " << data.toString();
+ insertData(data);
+ }
+}
+
+void GdbEngine::handleVarListChildren(const GdbResultRecord &record,
+ const WatchData &data0)
+{
+ //WatchResultCounter dummy(this, WatchVarListChildren);
+ WatchData data = data0;
+ if (!data.isValid())
+ return;
+ if (record.resultClass == GdbResultDone) {
+ //qDebug() << "VAR_LIST_CHILDREN: PARENT " << data.toString();
+ GdbMi children = record.data.findChild("children");
+
+ foreach (const GdbMi &child, children.children())
+ handleVarListChildrenHelper(child, data);
+
+ if (!isAccessSpecifier(data.variable.split('.').takeLast())) {
+ data.setChildrenUnneeded();
+ insertData(data);
+ }
+ } else if (record.resultClass == GdbResultError) {
+ data.setError(record.data.findChild("msg").data());
+ } else {
+ data.setError("Unknown error: " + record.toString());
+ }
+}
+
+void GdbEngine::handleToolTip(const GdbResultRecord &record,
+ const QString &what)
+{
+ //qDebug() << "HANDLE TOOLTIP: " << what << m_toolTip.toString();
+ // << "record: " << record.toString();
+ if (record.resultClass == GdbResultError) {
+ QString msg = record.data.findChild("msg").data();
+ if (what == "create") {
+ sendCommand("ptype " + m_toolTip.exp, WatchToolTip, "ptype");
+ return;
+ }
+ if (what == "evaluate") {
+ if (msg.startsWith("Cannot look up value of a typedef")) {
+ m_toolTip.value = m_toolTip.exp + " is a typedef.";
+ //return;
+ }
+ }
+ } else if (record.resultClass == GdbResultDone) {
+ if (what == "create") {
+ setWatchDataType(m_toolTip, record.data.findChild("type"));
+ setWatchDataChildCount(m_toolTip, record.data.findChild("numchild"));
+ if (isCustomValueDumperAvailable(m_toolTip.type))
+ runCustomDumper(m_toolTip, false);
+ else
+ q->showStatusMessage(tr("Retrieving data for tooltip..."), -1);
+ sendCommand("-data-evaluate-expression " + m_toolTip.exp,
+ WatchToolTip, "evaluate");
+ //sendToolTipCommand("-var-evaluate-expression tooltip")
+ return;
+ }
+ if (what == "evaluate") {
+ m_toolTip.value = m_toolTip.type + ' ' + m_toolTip.exp
+ + " = " + record.data.findChild("value").data();
+ //return;
+ }
+ if (what == "ptype") {
+ GdbMi mi = record.data.findChild("consolestreamoutput");
+ m_toolTip.value = extractTypeFromPTypeOutput(mi.data());
+ //return;
+ }
+ }
+
+ m_toolTip.iname = tooltipIName;
+ m_toolTip.setChildrenUnneeded();
+ m_toolTip.setChildCountUnneeded();
+ insertData(m_toolTip);
+ qDebug() << "DATA INSERTED";
+ QTimer::singleShot(0, this, SLOT(updateWatchModel2()));
+ qDebug() << "HANDLE TOOLTIP END";
+}
+
+#if 0
+void GdbEngine::handleChangedItem(QStandardItem *item)
+{
+ // HACK: Just store the item for the slot
+ // handleChangedItem(QWidget *widget) below.
+ QModelIndex index = item->index().sibling(item->index().row(), 0);
+ //WatchData data = m_currentSet.takeData(iname);
+ //m_editedData = inameFromItem(m_model.itemFromIndex(index)).exp;
+ //qDebug() << "HANDLE CHANGED EXPRESSION: " << m_editedData;
+}
+#endif
+
+void GdbEngine::assignValueInDebugger(const QString &expression, const QString &value)
+{
+ sendCommand("-var-delete assign");
+ sendCommand("-var-create assign * " + expression);
+ sendCommand("-var-assign assign " + value, WatchVarAssign);
+}
+
+
+void GdbEngine::tryLoadCustomDumpers()
+{
+ if (m_dataDumperState != DataDumperUninitialized)
+ return;
+
+ PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
+ m_dataDumperState = DataDumperLoadTried;
+
+#if defined(Q_OS_LINUX)
+ QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.so";
+ if (QFileInfo(lib).isExecutable()) {
+ //sendCommand("p dlopen");
+ if (qq->useFastStart())
+ sendCommand("set stop-on-solib-events 0");
+ QString flag = QString::number(RTLD_NOW);
+ sendCommand("call dlopen(\"" + lib + "\", " + flag + ")");
+ sendCommand("sharedlibrary " + dotEscape(lib));
+ if (qq->useFastStart())
+ sendCommand("set stop-on-solib-events 1");
+ }
+#endif
+#if defined(Q_OS_MAC)
+ QString lib = q->m_buildDir + "/qtc-gdbmacros/libgdbmacros.dylib";
+ if (QFileInfo(lib).isExecutable()) {
+ //sendCommand("p dlopen"); // FIXME: remove me
+ if (qq->useFastStart())
+ sendCommand("set stop-on-solib-events 0");
+ QString flag = QString::number(RTLD_NOW);
+ sendCommand("call dlopen(\"" + lib + "\", " + flag + ")");
+ sendCommand("sharedlibrary " + dotEscape(lib));
+ if (qq->useFastStart())
+ sendCommand("set stop-on-solib-events 1");
+ }
+#endif
+#if defined(Q_OS_WIN)
+ QString lib = q->m_buildDir + "/qtc-gdbmacros/debug/gdbmacros.dll";
+ if (QFileInfo(lib).exists()) {
+ if (qq->useFastStart())
+ sendCommand("set stop-on-solib-events 0");
+ //sendCommand("handle SIGSEGV pass stop print");
+ //sendCommand("set unwindonsignal off");
+ sendCommand("call LoadLibraryA(\"" + lib + "\")");
+ sendCommand("sharedlibrary " + dotEscape(lib));
+ if (qq->useFastStart())
+ sendCommand("set stop-on-solib-events 1");
+ }
+#endif
+
+ // retreive list of dumpable classes
+ sendCommand("call qDumpObjectData440(1,%1+1,0,0,0,0,0,0)",
+ GdbQueryDataDumper1);
+ // create response slot for socket data
+ sendCommand(QString(), GdbQueryDataDumper2);
+}
+
+
+IDebuggerEngine *createGdbEngine(DebuggerManager *parent)
+{
+ return new GdbEngine(parent);
+}
+
diff --git a/src/plugins/debugger/gdbengine.h b/src/plugins/debugger/gdbengine.h
new file mode 100644
index 0000000000..bdd59cbca6
--- /dev/null
+++ b/src/plugins/debugger/gdbengine.h
@@ -0,0 +1,351 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_GDBENGINE_H
+#define DEBUGGER_GDBENGINE_H
+
+#include <QtCore/QByteArray>
+#include <QtCore/QHash>
+#include <QtCore/QMap>
+#include <QtCore/QObject>
+#include <QtCore/QProcess>
+#include <QtCore/QPoint>
+#include <QtCore/QVariant>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QAbstractItemModel;
+class QWidget;
+QT_END_NAMESPACE
+
+#include "idebuggerengine.h"
+#include "gdbmi.h"
+
+namespace Debugger {
+namespace Internal {
+
+class DebuggerManager;
+class IDebuggerManagerAccessForEngines;
+class GdbResultRecord;
+class GdbMi;
+
+class WatchData;
+class BreakpointData;
+
+struct GdbCookie
+{
+ GdbCookie() : type(0), synchronized(false) {}
+
+ QString command;
+ int type;
+ bool synchronized;
+ QVariant cookie;
+};
+
+enum DataDumperState
+{
+ DataDumperUninitialized,
+ DataDumperLoadTried,
+ DataDumperAvailable,
+ DataDumperUnavailable,
+};
+
+// FIXME: Move to extra file?
+class GdbSettings
+{
+public:
+ GdbSettings() { m_autoRun = m_autoQuit = false; }
+
+public:
+ QString m_gdbCmd;
+ QString m_gdbEnv;
+ bool m_autoRun;
+ bool m_autoQuit;
+
+ QString m_scriptFile;
+ QMap<QString, QVariant> m_typeMacros;
+};
+
+GdbSettings &theGdbSettings();
+
+class GdbEngine : public IDebuggerEngine
+{
+ Q_OBJECT
+
+public:
+ GdbEngine(DebuggerManager *parent);
+ ~GdbEngine();
+
+signals:
+ void gdbResponseAvailable();
+ void gdbInputAvailable(const QString &prefix, const QString &msg);
+ void gdbOutputAvailable(const QString &prefix, const QString &msg);
+ void applicationOutputAvailable(const QString &prefix, const QString &msg);
+
+private:
+ //
+ // IDebuggerEngine implementation
+ //
+ void stepExec();
+ void stepOutExec();
+ void nextExec();
+ void stepIExec();
+ void nextIExec();
+
+ void shutdown();
+ void setToolTipExpression(const QPoint &pos, const QString &exp);
+ bool startDebugger();
+ void exitDebugger();
+
+ void continueInferior();
+ void runInferior();
+ void interruptInferior();
+
+ void runToLineExec(const QString &fileName, int lineNumber);
+ void runToFunctionExec(const QString &functionName);
+ void jumpToLineExec(const QString &fileName, int lineNumber);
+
+ void activateFrame(int index);
+ void selectThread(int index);
+
+ Q_SLOT void attemptBreakpointSynchronization();
+
+ void loadSessionData() {}
+ void saveSessionData() {}
+
+ void assignValueInDebugger(const QString &expr, const QString &value);
+ void executeDebuggerCommand(const QString & command);
+
+ void loadSymbols(const QString &moduleName);
+ void loadAllSymbols();
+
+ //
+ // Own stuff
+ //
+ int currentFrame() const;
+ QString currentWorkingDirectory() const { return m_pwd; }
+
+ bool supportsThreads() const;
+
+ void init(); // called by destructor
+ void queryFullName(const QString &fileName, QString *fullName);
+ QString fullName(const QString &fileName);
+ QString shortName(const QString &fullName);
+ // get one usable name out of these, try full names first
+ QString fullName(const QStringList &candidates);
+
+ void handleResult(const GdbResultRecord &, int type, const QVariant &);
+
+ // type and cookie are sender-internal data, opaque for the "event
+ // queue". resultNeeded == true increments m_pendingResults on
+ // send and decrements on receipt, effectively preventing
+ // watch model updates before everything is finished.
+ void sendCommand(const QString & command,
+ int type = 0, const QVariant &cookie = QVariant(),
+ bool needStop = false, bool synchronized = false);
+ void sendSynchronizedCommand(const QString & command,
+ int type = 0, const QVariant &cookie = QVariant(),
+ bool needStop = false);
+
+ void setTokenBarrier();
+
+ void updateLocals();
+
+private slots:
+ void setDebugDumpers(bool on);
+ void setCustomDumpersWanted(bool on);
+
+ void handleResponse();
+
+ void gdbProcError(QProcess::ProcessError error);
+ void readGdbStandardOutput();
+ void readGdbStandardError();
+
+private:
+ int terminationIndex(const QByteArray &buffer, int &length);
+ void handleStreamOutput(const QString &output, char code);
+ void handleAsyncOutput2(const GdbMi &data);
+ void handleAsyncOutput(const GdbMi &data);
+ void handleResultRecord(const GdbResultRecord &response);
+ void handleFileExecAndSymbols(const GdbResultRecord &response);
+ void handleExecRun(const GdbResultRecord &response);
+ void handleExecJumpToLine(const GdbResultRecord &response);
+ void handleExecRunToFunction(const GdbResultRecord &response);
+ void handleInfoShared(const GdbResultRecord &response);
+ void handleInfoProc(const GdbResultRecord &response);
+ void handleShowVersion(const GdbResultRecord &response);
+ void handleQueryPwd(const GdbResultRecord &response);
+ void handleQuerySources(const GdbResultRecord &response);
+ void handleQuerySources2(const GdbResultRecord &response,
+ const QVariant &);
+
+ QByteArray m_inbuffer;
+
+ QProcess m_gdbProc;
+
+ QHash<int, GdbCookie> m_cookieForToken;
+ QHash<int, QByteArray> m_customOutputForToken;
+
+ QByteArray m_pendingConsoleStreamOutput;
+ QByteArray m_pendingTargetStreamOutput;
+ QByteArray m_pendingLogStreamOutput;
+ //QByteArray m_pendingCustomValueContents;
+ QString m_pwd;
+
+ // contains the first token number for the current round
+ // of evaluation. Responses with older tokens are considers
+ // out of date and discarded.
+ int m_oldestAcceptableToken;
+
+ int m_gdbVersion; // 6.8.0 is 680
+ int m_shared;
+
+ // awful hack to keep track of used files
+ QHash<QString, QString> m_shortToFullName;
+ QHash<QString, QString> m_fullToShortName;
+
+ //
+ // Breakpoint specific stuff
+ //
+ void handleBreakList(const GdbResultRecord &record);
+ void handleBreakList(const GdbMi &table);
+ void handleBreakIgnore(const GdbResultRecord &record, int index);
+ void handleBreakInsert(const GdbResultRecord &record, int index);
+ void handleBreakInsert1(const GdbResultRecord &record, int index);
+ void handleBreakCondition(const GdbResultRecord &record, int index);
+ void handleBreakInfo(const GdbResultRecord &record, int index);
+ void extractDataFromInfoBreak(const QString &output, BreakpointData *data);
+ void breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt);
+ void sendInsertBreakpoint(int index);
+
+
+ //
+ // Disassembler specific stuff
+ //
+ void handleDisassemblerList(const GdbResultRecord &record,
+ const QString &cookie);
+ void reloadDisassembler();
+ QString m_address;
+
+
+ //
+ // Modules specific stuff
+ //
+ void reloadModules();
+ void handleModulesList(const GdbResultRecord &record);
+
+
+ //
+ // Register specific stuff
+ //
+ void reloadRegisters();
+ void handleRegisterListNames(const GdbResultRecord &record);
+ void handleRegisterListValues(const GdbResultRecord &record);
+
+
+ //
+ // Stack specific stuff
+ //
+ void handleStackListFrames(const GdbResultRecord &record);
+ void handleStackSelectThread(const GdbResultRecord &record, int cookie);
+ void handleStackListThreads(const GdbResultRecord &record, int cookie);
+
+
+ //
+ // Tooltip specific stuff
+ //
+ void sendToolTipCommand(const QString &command, const QString &cookie);
+
+
+ //
+ // Watch specific stuff
+ //
+ // FIXME: BaseClass. called to improve situation for a watch item
+ void updateSubItem(const WatchData &data);
+
+ void updateWatchModel();
+ Q_SLOT void updateWatchModel2();
+
+ void insertData(const WatchData &data);
+ void sendWatchParameters(const QByteArray &params0);
+ void createGdbVariable(const WatchData &data);
+
+ void handleTypeContents(const QString &output);
+ void maybeHandleInferiorPidChanged(const QString &pid);
+
+ void tryLoadCustomDumpers();
+ void runCustomDumper(const WatchData &data, bool dumpChildren);
+ bool isCustomValueDumperAvailable(const QString &type) const;
+
+ void handleVarListChildren(const GdbResultRecord &record,
+ const WatchData &cookie);
+ void handleVarCreate(const GdbResultRecord &record,
+ const WatchData &cookie);
+ void handleVarAssign();
+ void handleEvaluateExpression(const GdbResultRecord &record,
+ const WatchData &cookie);
+ void handleToolTip(const GdbResultRecord &record,
+ const QString &cookie);
+ void handleDumpCustomValue1(const GdbResultRecord &record,
+ const WatchData &cookie);
+ void handleQueryDataDumper1(const GdbResultRecord &record);
+ void handleQueryDataDumper2(const GdbResultRecord &record);
+ void handleDumpCustomValue2(const GdbResultRecord &record,
+ const WatchData &cookie);
+ void handleDumpCustomEditValue(const GdbResultRecord &record);
+ void handleDumpCustomSetup(const GdbResultRecord &record);
+ void handleStackListLocals(const GdbResultRecord &record);
+ void handleStackListArguments(const GdbResultRecord &record);
+ void handleVarListChildrenHelper(const GdbMi &child,
+ const WatchData &parent);
+ void setWatchDataType(WatchData &data, const GdbMi &mi);
+
+ QString m_editedData;
+ int m_pendingRequests;
+ int m_inferiorPid;
+
+ QStringList m_availableSimpleDumpers;
+ QString m_namespace; // namespace used in "namespaced Qt";
+
+ DataDumperState m_dataDumperState; // state of qt creator dumpers
+ QList<GdbMi> m_currentFunctionArgs;
+ QString m_currentFrame;
+ QMap<QString, QString> m_varToType;
+
+ DebuggerManager *q;
+ IDebuggerManagerAccessForEngines *qq;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_GDBENGINE_H
diff --git a/src/plugins/debugger/gdbmi.cpp b/src/plugins/debugger/gdbmi.cpp
new file mode 100644
index 0000000000..9091422ad4
--- /dev/null
+++ b/src/plugins/debugger/gdbmi.cpp
@@ -0,0 +1,473 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "gdbmi.h"
+#include "assert.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+#include <QtCore/QTextStream>
+
+namespace Debugger {
+namespace Internal {
+
+QTextStream & operator<<(QTextStream & os, const GdbMi & mi)
+{
+ return os << mi.toString();
+}
+
+//static void skipSpaces(const GdbMi::Char *&from, const GdbMi::Char *to)
+//{
+// while (from != to && QChar(*from).isSpace())
+// ++from;
+//}
+
+
+void GdbMi::parseResultOrValue(const Char *&from, const Char *to)
+{
+ //skipSpaces(from, to);
+ while (from != to && QChar(*from).isSpace())
+ ++from;
+
+ //qDebug() << "parseResultOrValue: " << QByteArray::fromLatin1(from, to - from);
+ parseValue(from, to);
+ if (isValid()) {
+ //qDebug() << "no valid result in " << QByteArray::fromLatin1(from, to - from);
+ return;
+ }
+ if (from == to || *from == '(')
+ return;
+ const Char *ptr = from;
+ while (ptr < to && *ptr != '=') {
+ //qDebug() << "adding" << QChar(*ptr) << "to name";
+ ++ptr;
+ }
+ m_name = QByteArray(from, ptr - from);
+ from = ptr;
+ if (from < to && *from == '=') {
+ ++from;
+ parseValue(from, to);
+ }
+}
+
+QByteArray GdbMi::parseCString(const Char *&from, const Char *to)
+{
+ QByteArray result;
+ //qDebug() << "parseCString: " << QByteArray::fromUtf16(from, to - from);
+ if (*from != '"') {
+ qDebug() << "MI Parse Error, double quote expected";
+ return QByteArray();
+ }
+ const Char *ptr = from;
+ ++ptr;
+ while (ptr < to) {
+ if (*ptr == '"') {
+ ++ptr;
+ result = QByteArray(from + 1, ptr - from - 2);
+ break;
+ }
+ if (*ptr == '\\' && ptr < to - 1)
+ ++ptr;
+ ++ptr;
+ }
+
+ if (result.contains('\\')) {
+ if (result.contains("\\032\\032"))
+ result.clear();
+ else {
+ result = result.replace("\\n", "\n");
+ result = result.replace("\\t", "\t");
+ result = result.replace("\\\"", "\"");
+ }
+ }
+
+ from = ptr;
+ return result;
+}
+
+void GdbMi::parseValue(const Char *&from, const Char *to)
+{
+ //qDebug() << "parseValue: " << QByteArray::fromUtf16(from, to - from);
+ switch (*from) {
+ case '{':
+ parseTuple(from, to);
+ break;
+ case '[':
+ parseList(from, to);
+ break;
+ case '"':
+ m_type = Const;
+ m_data = parseCString(from, to);
+ break;
+ default:
+ break;
+ }
+}
+
+
+void GdbMi::parseTuple(const Char *&from, const Char *to)
+{
+ //qDebug() << "parseTuple: " << QByteArray::fromUtf16(from, to - from);
+ QWB_ASSERT(*from == '{', /**/);
+ ++from;
+ parseTuple_helper(from, to);
+}
+
+void GdbMi::parseTuple_helper(const Char *&from, const Char *to)
+{
+ //qDebug() << "parseTuple_helper: " << QByteArray::fromUtf16(from, to - from);
+ m_type = Tuple;
+ while (from < to) {
+ if (*from == '}') {
+ ++from;
+ break;
+ }
+ GdbMi child;
+ child.parseResultOrValue(from, to);
+ //qDebug() << "\n=======\n" << qPrintable(child.toString()) << "\n========\n";
+ if (!child.isValid())
+ return;
+ m_children += child;
+ if (*from == ',')
+ ++from;
+ }
+}
+
+void GdbMi::parseList(const Char *&from, const Char *to)
+{
+ //qDebug() << "parseList: " << QByteArray::fromUtf16(from, to - from);
+ QWB_ASSERT(*from == '[', /**/);
+ ++from;
+ m_type = List;
+ while (from < to) {
+ if (*from == ']') {
+ ++from;
+ break;
+ }
+ GdbMi child;
+ child.parseResultOrValue(from, to);
+ if (child.isValid())
+ m_children += child;
+ if (*from == ',')
+ ++from;
+ }
+}
+
+void GdbMi::setStreamOutput(const QByteArray &name, const QByteArray &content)
+{
+ if (content.isEmpty())
+ return;
+ GdbMi child;
+ child.m_type = Const;
+ child.m_name = name;
+ child.m_data = content;
+ m_children += child;
+ if (m_type == Invalid)
+ m_type = Tuple;
+}
+
+static QByteArray ind(int indent)
+{
+ return QByteArray(2 * indent, ' ');
+}
+
+void GdbMi::dumpChildren(QByteArray * str, bool multiline, int indent) const
+{
+ for (int i = 0; i < m_children.size(); ++i) {
+ if (i != 0) {
+ *str += ',';
+ if (multiline)
+ *str += '\n';
+ }
+ if (multiline)
+ *str += ind(indent);
+ *str += m_children.at(i).toString(multiline, indent);
+ }
+}
+
+QByteArray GdbMi::toString(bool multiline, int indent) const
+{
+ QByteArray result;
+ switch (m_type) {
+ case Invalid:
+ if (multiline) {
+ result += ind(indent) + "Invalid\n";
+ } else {
+ result += "Invalid";
+ }
+ break;
+ case Const:
+ if (!m_name.isEmpty())
+ result += m_name + "=";
+ if (multiline) {
+ result += "\"" + m_data + "\"";
+ } else {
+ result += "\"" + m_data + "\"";
+ }
+ break;
+ case Tuple:
+ if (!m_name.isEmpty())
+ result += m_name + "=";
+ if (multiline) {
+ result += "{\n";
+ dumpChildren(&result, multiline, indent + 1);
+ result += '\n' + ind(indent) + "}";
+ } else {
+ result += "{";
+ dumpChildren(&result, multiline, indent + 1);
+ result += "}";
+ }
+ break;
+ case List:
+ if (!m_name.isEmpty())
+ result += m_name + "=";
+ if (multiline) {
+ result += "[\n";
+ dumpChildren(&result, multiline, indent + 1);
+ result += "]";
+ } else {
+ result += "[";
+ dumpChildren(&result, multiline, indent + 1);
+ result += '\n' + ind(indent) + "]";
+ }
+ break;
+ }
+ return result;
+}
+
+void GdbMi::fromString(const QByteArray &ba)
+{
+ const Char *from = ba.constBegin();
+ const Char *to = ba.constEnd();
+ parseResultOrValue(from, to);
+}
+
+GdbMi GdbMi::findChild(const QByteArray &name) const
+{
+ for (int i = 0; i < m_children.size(); ++i)
+ if (m_children.at(i).m_name == name)
+ return m_children.at(i);
+ return GdbMi();
+}
+
+
+GdbMi GdbMi::findChild(const QByteArray &name, const QByteArray &defaultData) const
+{
+ for (int i = 0; i < m_children.size(); ++i)
+ if (m_children.at(i).m_name == name)
+ return m_children.at(i);
+ GdbMi result;
+ result.m_data = defaultData;
+ return result;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////
+//
+// GdbResultRecord
+//
+//////////////////////////////////////////////////////////////////////////////////
+
+QByteArray stringFromResultClass(GdbResultClass resultClass)
+{
+ switch (resultClass) {
+ case GdbResultDone: return "done";
+ case GdbResultRunning: return "running";
+ case GdbResultConnected: return "connected";
+ case GdbResultError: return "error";
+ case GdbResultExit: return "exit";
+ default: return "unknown";
+ }
+};
+
+QByteArray GdbResultRecord::toString() const
+{
+ QByteArray result;
+ if (token != -1)
+ result = QByteArray::number(token);
+ result += '^';
+ result += stringFromResultClass(resultClass);
+ if (data.isValid())
+ result += ',' + data.toString();
+ result += '\n';
+ return result;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////
+//
+// GdbStreamOutput
+//
+//////////////////////////////////////////////////////////////////////////////////
+
+#if 0
+
+static const char test1[] =
+ "1^done,stack=[frame={level=\"0\",addr=\"0x00000000004061ca\","
+ "func=\"main\",file=\"test1.cpp\","
+ "fullname=\"/home/apoenitz/work/test1/test1.cpp\",line=\"209\"}]\n"
+ "(gdb)\n";
+
+static const char test2[] =
+ "2^done,stack=[frame={level=\"0\",addr=\"0x00002ac058675840\","
+ "func=\"QApplication\",file=\"/home/apoenitz/dev/qt/src/gui/kernel/qapplication.cpp\","
+ "fullname=\"/home/apoenitz/dev/qt/src/gui/kernel/qapplication.cpp\",line=\"592\"},"
+ "frame={level=\"1\",addr=\"0x00000000004061e0\",func=\"main\",file=\"test1.cpp\","
+ "fullname=\"/home/apoenitz/work/test1/test1.cpp\",line=\"209\"}]\n"
+ "(gdb)\n";
+
+static const char test3[] =
+ "3^done,stack=[frame={level=\"0\",addr=\"0x00000000004061ca\","
+ "func=\"main\",file=\"test1.cpp\","
+ "fullname=\"/home/apoenitz/work/test1/test1.cpp\",line=\"209\"}]\n"
+ "(gdb)\n";
+
+static const char test4[] =
+ "&\"source /home/apoenitz/dev/ide/main/bin/gdb/qt4macros\\n\"\n"
+ "4^done\n"
+ "(gdb)\n";
+
+
+static const char test5[] =
+ "1*stopped,reason=\"breakpoint-hit\",bkptno=\"1\",thread-id=\"1\","
+ "frame={addr=\"0x0000000000405738\",func=\"main\","
+ "args=[{name=\"argc\",value=\"1\"},{name=\"argv\",value=\"0x7fff1ac78f28\"}],"
+ "file=\"test1.cpp\",fullname=\"/home/apoenitz/work/test1/test1.cpp\","
+ "line=\"209\"}\n"
+ "(gdb)\n";
+
+static const char test6[] =
+ "{u = {u = 2048, v = 16788279, w = -689265400}, a = 1, b = -689265424, c = 11063, s = {static null = {<No data fields>}, static shared_null = {ref = {value = 2}, alloc = 0, size = 0, data = 0x6098da, clean = 0, simpletext = 0, righttoleft = 0, asciiCache = 0, capacity = 0, reserved = 0, array = {0}}, static shared_empty = {ref = {value = 1}, alloc = 0, size = 0, data = 0x2b37d84f8fba, clean = 0, simpletext = 0, righttoleft = 0, asciiCache = 0, capacity = 0, reserved = 0, array = {0}}, d = 0x6098c0, static codecForCStrings = 0x0}}";
+
+static const char test8[] =
+ "8^done,data={locals={{name=\"a\"},{name=\"w\"}}}\n"
+ "(gdb)\n";
+
+static const char test9[] =
+ "9^done,data={locals=[name=\"baz\",name=\"urgs\",name=\"purgs\"]}\n"
+ "(gdb)\n";
+
+
+static const char test10[] =
+ "16^done,name=\"urgs\",numchild=\"1\",type=\"Urgs\"\n"
+ "(gdb)\n"
+ "17^done,name=\"purgs\",numchild=\"1\",type=\"Urgs *\"\n"
+ "(gdb)\n"
+ "18^done,name=\"bar\",numchild=\"0\",type=\"int\"\n"
+ "(gdb)\n"
+ "19^done,name=\"z\",numchild=\"0\",type=\"int\"\n"
+ "(gdb)\n";
+
+static const char test11[] =
+ "[{name=\"size\",value=\"1\",type=\"size_t\",readonly=\"true\"},"
+ "{name=\"0\",value=\"one\",type=\"QByteArray\"}]";
+
+static const char test12[] =
+ "{iname=\"local.hallo\",value=\"\\\"\\\"\",type=\"QByteArray\",numchild=\"0\"}";
+
+static struct Tester {
+
+ Tester() {
+ //test(test10);
+ test2(test12);
+ //test(test4);
+ //apple();
+ exit(0);
+ }
+
+ void test(const char* input)
+ {
+ //qDebug("\n<<<<\n%s\n====\n%s\n>>>>\n", input,
+ //qPrintable(GdbResponse(input).toString()));
+ }
+
+ void test2(const char* input)
+ {
+ GdbMi mi(input);
+ qDebug("\n<<<<\n%s\n====\n%s\n>>>>\n", input,
+ qPrintable(mi.toString()));
+ }
+
+ void apple()
+ {
+ QByteArray input(test9);
+/*
+ qDebug() << "input: " << input;
+ input = input.replace("{{","[");
+ input = input.replace("},{",",");
+ input = input.replace("}}","]");
+ qDebug() << "input: " << input;
+ GdbResponse response(input);
+ qDebug() << "read: " << response.toString();
+ GdbMi list = response.results[0].data.findChild("data").findChild("locals");
+ QByteArrayList locals;
+ foreach (const GdbMi &item, list.children())
+ locals.append(item.string());
+ qDebug() << "Locals (new): " << locals;
+*/
+ }
+ void parse(const QByteArray &str)
+ {
+ QByteArray result;
+ result += "\n ";
+ int indent = 0;
+ int from = 0;
+ int to = str.size();
+ if (str.size() && str[0] == '{' /*'}'*/) {
+ ++from;
+ --to;
+ }
+ for (int i = from; i < to; ++i) {
+ if (str[i] == '{')
+ result += "{\n" + QByteArray(2*++indent + 1, QChar(' '));
+ else if (str[i] == '}') {
+ if (!result.isEmpty() && result[result.size() - 1] != '\n')
+ result += "\n";
+ result += QByteArray(2*--indent + 1, QChar(' ')) + "}\n";
+ }
+ else if (str[i] == ',') {
+ if (true || !result.isEmpty() && result[result.size() - 1] != '\n')
+ result += "\n";
+ result += QByteArray(2*indent, QChar(' '));
+ }
+ else
+ result += str[i];
+ }
+ qDebug() << "result:\n" << result;
+ }
+
+} dummy;
+
+#endif
+
+} // namespace Internal
+} // namespace Debugger
diff --git a/src/plugins/debugger/gdbmi.h b/src/plugins/debugger/gdbmi.h
new file mode 100644
index 0000000000..381b5ba86b
--- /dev/null
+++ b/src/plugins/debugger/gdbmi.h
@@ -0,0 +1,183 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+
+#ifndef DEBUGGER_GDBMI_H
+#define DEBUGGER_GDBMI_H
+
+#include <qglobal.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QList>
+
+namespace Debugger {
+namespace Internal {
+
+/*
+
+output ==>
+ ( out-of-band-record )* [ result-record ] "(gdb)" nl
+result-record ==>
+ [ token ] "^" result-class ( "," result )* nl
+out-of-band-record ==>
+ async-record | stream-record
+async-record ==>
+ exec-async-output | status-async-output | notify-async-output
+exec-async-output ==>
+ [ token ] "*" async-output
+status-async-output ==>
+ [ token ] "+" async-output
+notify-async-output ==>
+ [ token ] "=" async-output
+async-output ==>
+ async-class ( "," result )* nl
+result-class ==>
+ "done" | "running" | "connected" | "error" | "exit"
+async-class ==>
+ "stopped" | others (where others will be added depending on the needs--this is still in development).
+result ==>
+ variable "=" value
+variable ==>
+ string
+value ==>
+ const | tuple | list
+const ==>
+ c-string
+tuple ==>
+ "{}" | "{" result ( "," result )* "}"
+list ==>
+ "[]" | "[" value ( "," value )* "]" | "[" result ( "," result )* "]"
+stream-record ==>
+ console-stream-output | target-stream-output | log-stream-output
+console-stream-output ==>
+ "~" c-string
+target-stream-output ==>
+ "@" c-string
+log-stream-output ==>
+ "&" c-string
+nl ==>
+ CR | CR-LF
+token ==>
+ any sequence of digits.
+
+ */
+
+// FIXME: rename into GdbMiValue
+class GdbMi
+{
+public:
+ GdbMi() : m_type(Invalid) {}
+ explicit GdbMi(const QByteArray &str) { fromString(str); }
+
+ QByteArray m_name;
+ QByteArray m_data;
+ QList<GdbMi> m_children;
+
+ enum Type {
+ Invalid,
+ Const,
+ Tuple,
+ List,
+ };
+
+ Type m_type;
+
+ inline Type type() const { return m_type; }
+ inline QByteArray name() const { return m_name; }
+ inline bool hasName(const char *name) const { return m_name == name; }
+
+ inline bool isValid() const { return m_type != Invalid; }
+ inline bool isConst() const { return m_type == Const; }
+ inline bool isTuple() const { return m_type == Tuple; }
+ inline bool isList() const { return m_type == List; }
+
+
+ inline QByteArray data() const { return m_data; }
+ inline const QList<GdbMi> &children() const { return m_children; }
+ inline int childCount() const { return m_children.size(); }
+
+ const GdbMi & childAt(int index) const { return m_children[index]; }
+ GdbMi & childAt(int index) { return m_children[index]; }
+ GdbMi findChild(const QByteArray &name) const;
+ GdbMi findChild(const QByteArray &name, const QByteArray &defaultString) const;
+
+ QByteArray toString(bool multiline = false, int indent = 0) const;
+ void fromString(const QByteArray &str);
+ void setStreamOutput(const QByteArray &name, const QByteArray &content);
+
+private:
+ friend class GdbResultRecord;
+ friend class GdbEngine;
+
+ //typedef ushort Char;
+ typedef char Char;
+ static QByteArray parseCString(const Char *&from, const Char *to);
+ void parseResultOrValue(const Char *&from, const Char *to);
+ void parseValue(const Char *&from, const Char *to);
+ void parseTuple(const Char *&from, const Char *to);
+ void parseTuple_helper(const Char *&from, const Char *to);
+ void parseList(const Char *&from, const Char *to);
+
+ void dumpChildren(QByteArray *str, bool multiline, int indent) const;
+};
+
+enum GdbResultClass
+{
+ // "done" | "running" | "connected" | "error" | "exit"
+ GdbResultUnknown,
+ GdbResultDone,
+ GdbResultCustomDone,
+ GdbResultRunning,
+ GdbResultConnected,
+ GdbResultError,
+ GdbResultExit,
+};
+
+class GdbResultRecord
+{
+public:
+ GdbResultRecord() : token(-1), resultClass(GdbResultUnknown) {}
+ QByteArray toString() const;
+
+ int token;
+ GdbResultClass resultClass;
+ GdbMi data;
+private:
+ friend class GdbMi;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+//Q_DECLARE_METATYPE(GdbDebugger::Internal::GdbMi);
+
+#endif // DEBUGGER_GDBMI_H
diff --git a/src/plugins/debugger/gdboptionpage.cpp b/src/plugins/debugger/gdboptionpage.cpp
new file mode 100644
index 0000000000..bee68d1833
--- /dev/null
+++ b/src/plugins/debugger/gdboptionpage.cpp
@@ -0,0 +1,128 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "gdboptionpage.h"
+
+#include "gdbengine.h"
+
+#include <extensionsystem/pluginmanager.h>
+#include <coreplugin/icore.h>
+
+#include <QtCore/QSettings>
+#include <QtGui/QLineEdit>
+#include <QtGui/QFileDialog>
+
+using namespace Debugger::Internal;
+
+GdbOptionPage::GdbOptionPage(GdbSettings *settings)
+{
+ m_pm = ExtensionSystem::PluginManager::instance();
+ m_settings = settings;
+
+ Core::ICore *coreIFace = m_pm->getObject<Core::ICore>();
+ if (!coreIFace || !coreIFace->settings())
+ return;
+ QSettings *s = coreIFace->settings();
+ s->beginGroup("GdbOptions");
+ QString defaultCommand("gdb");
+#if defined(Q_OS_WIN32)
+ defaultCommand.append(".exe");
+#endif
+ m_settings->m_gdbCmd = s->value("Location", defaultCommand).toString();
+ m_settings->m_gdbEnv = s->value("Environment", "").toString();
+ m_settings->m_autoRun = s->value("AutoRun", true).toBool();
+ m_settings->m_autoQuit = s->value("AutoQuit", true).toBool();
+ s->endGroup();
+}
+
+QString GdbOptionPage::name() const
+{
+ return tr("Gdb");
+}
+
+QString GdbOptionPage::category() const
+{
+ return "Debugger|Gdb";
+}
+
+QString GdbOptionPage::trCategory() const
+{
+ return tr("Debugger|Gdb");
+}
+
+QWidget *GdbOptionPage::createPage(QWidget *parent)
+{
+ QWidget *w = new QWidget(parent);
+ m_ui.setupUi(w);
+ m_ui.gdbEdit->setText(m_settings->m_gdbCmd);
+ m_ui.envEdit->setText(m_settings->m_gdbEnv);
+ m_ui.autoStartBox->setChecked(m_settings->m_autoRun);
+ m_ui.autoQuitBox->setChecked(m_settings->m_autoQuit);
+ connect(m_ui.pushButtonBrowse, SIGNAL(clicked()),
+ this, SLOT(browse()));
+
+ return w;
+}
+
+void GdbOptionPage::browse()
+{
+ QString fileName = QFileDialog::getOpenFileName(m_ui.pushButtonBrowse,
+ "Browse for gdb executable");
+ if (fileName.isEmpty())
+ return;
+ m_settings->m_gdbCmd = fileName;
+ m_ui.gdbEdit->setText(fileName);
+}
+
+void GdbOptionPage::finished(bool accepted)
+{
+ if (!accepted)
+ return;
+
+ m_settings->m_gdbCmd = m_ui.gdbEdit->text();
+ m_settings->m_gdbEnv = m_ui.envEdit->text();
+ m_settings->m_autoRun = m_ui.autoStartBox->isChecked();
+ m_settings->m_autoQuit = m_ui.autoQuitBox->isChecked();
+
+ Core::ICore *coreIFace = m_pm->getObject<Core::ICore>();
+ if (!coreIFace || !coreIFace->settings())
+ return;
+
+ QSettings *s = coreIFace->settings();
+
+ s->beginGroup("GdbOptions");
+ s->setValue("Location", m_settings->m_gdbCmd);
+ s->setValue("Environment", m_settings->m_gdbEnv);
+ s->setValue("AutoRun", m_settings->m_autoRun);
+ s->setValue("AutoQuit", m_settings->m_autoQuit);
+ s->endGroup();
+}
diff --git a/src/plugins/debugger/gdboptionpage.h b/src/plugins/debugger/gdboptionpage.h
new file mode 100644
index 0000000000..0a83533742
--- /dev/null
+++ b/src/plugins/debugger/gdboptionpage.h
@@ -0,0 +1,106 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef GDBOPTIONPAGE_H
+#define GDBOPTIONPAGE_H
+
+#include "ui_gdboptionpage.h"
+#include "ui_gdbtypemacros.h"
+
+#include <coreplugin/dialogs/ioptionspage.h>
+
+#include <QtGui/QWidget>
+
+namespace ExtensionSystem { class PluginManager; }
+
+namespace Debugger {
+namespace Internal {
+
+class GdbSettings;
+
+class GdbOptionPage : public Core::IOptionsPage
+{
+ Q_OBJECT
+
+public:
+ GdbOptionPage(GdbSettings *settings);
+
+ QString name() const;
+ QString category() const;
+ QString trCategory() const;
+
+ QWidget *createPage(QWidget *parent);
+ void finished(bool accepted);
+
+public slots:
+ void browse();
+
+private:
+ ExtensionSystem::PluginManager *m_pm;
+ Ui::GdbOptionPage m_ui;
+
+ GdbSettings *m_settings;
+};
+
+class TypeMacroPage : public Core::IOptionsPage
+{
+ Q_OBJECT
+
+public:
+ TypeMacroPage(GdbSettings *settings);
+
+ QString name() const;
+ QString category() const;
+ QString trCategory() const;
+
+ QWidget *createPage(QWidget *parent);
+ void finished(bool accepted);
+
+private slots:
+ void onScriptButton();
+ void onAddButton();
+ void onDelButton();
+ void currentItemChanged(QTreeWidgetItem *item);
+ void updateButtonState();
+
+private:
+ ExtensionSystem::PluginManager *m_pm;
+ Ui::TypeMacroPage m_ui;
+
+ GdbSettings *m_settings;
+ QWidget *m_widget;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // GDBOPTIONPAGE_H
diff --git a/src/plugins/debugger/gdboptionpage.ui b/src/plugins/debugger/gdboptionpage.ui
new file mode 100644
index 0000000000..4b58d5d714
--- /dev/null
+++ b/src/plugins/debugger/gdboptionpage.ui
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>GdbOptionPage</class>
+ <widget class="QWidget" name="GdbOptionPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>433</width>
+ <height>216</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Gdb Debug Options</string>
+ </property>
+ <layout class="QGridLayout">
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="gdbEdit"/>
+ </item>
+ <item row="1" column="1" colspan="2">
+ <widget class="QLineEdit" name="envEdit"/>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Gdb Location:</string>
+ </property>
+ <property name="buddy">
+ <cstring>gdbEdit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Environment:</string>
+ </property>
+ <property name="buddy">
+ <cstring>envEdit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QPushButton" name="pushButtonBrowse">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset resource="../coreplugin/core.qrc">
+ <normaloff>:/qworkbench/images/fileopen.png</normaloff>:/qworkbench/images/fileopen.png</iconset>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="autoStartBox">
+ <property name="text">
+ <string>Auto run executable on debugger startup</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="autoQuitBox">
+ <property name="text">
+ <string>Quit debugger when the executable exits</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>415</width>
+ <height>41</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="../coreplugin/core.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/plugins/debugger/gdbtypemacros.cpp b/src/plugins/debugger/gdbtypemacros.cpp
new file mode 100644
index 0000000000..8a35720097
--- /dev/null
+++ b/src/plugins/debugger/gdbtypemacros.cpp
@@ -0,0 +1,214 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "gdboptionpage.h"
+#include "gdbengine.h"
+#include "imports.h"
+
+#include <extensionsystem/pluginmanager.h>
+#include <coreplugin/icore.h>
+
+#include <QtCore/QSettings>
+#include <QtCore/QByteArray>
+#include <QtGui/QFileDialog>
+
+using namespace Debugger::Internal;
+
+TypeMacroPage::TypeMacroPage(GdbSettings *settings)
+{
+ m_pm = ExtensionSystem::PluginManager::instance();
+ m_settings = settings;
+
+ Core::ICore *coreIFace = m_pm->getObject<Core::ICore>();
+ if (!coreIFace || !coreIFace->settings())
+ return;
+
+ QSettings *s = coreIFace->settings();
+ s->beginGroup("GdbOptions");
+ if (!s->contains("ScriptFile") && !s->contains("TypeMacros")) {
+ //insert qt4 defaults
+ m_settings->m_scriptFile = coreIFace->resourcePath() +
+ QLatin1String("/gdb/qt4macros");
+ for (int i=0; i<3; ++i) {
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+ switch(i) {
+ case 0:
+ stream << QString("printqstring") << (int)1;
+ m_settings->m_typeMacros.insert(QLatin1String("QString"), data);
+ break;
+ case 1:
+ stream << QString("printqcolor") << (int)0;
+ m_settings->m_typeMacros.insert(QLatin1String("QColor"), data);
+ break;
+ case 2:
+ stream << QString("printqfont") << (int)1;
+ m_settings->m_typeMacros.insert(QLatin1String("QFont"), data);
+ break;
+ }
+ }
+
+ s->setValue("ScriptFile", m_settings->m_scriptFile);
+ s->setValue("TypeMacros", m_settings->m_typeMacros);
+ } else {
+ m_settings->m_scriptFile = s->value("ScriptFile", QString()).toString();
+ m_settings->m_typeMacros = s->value("TypeMacros", QMap<QString,QVariant>()).toMap();
+ }
+ s->endGroup();
+}
+
+QString TypeMacroPage::name() const
+{
+ return tr("Type Macros");
+}
+
+QString TypeMacroPage::category() const
+{
+ return "Debugger|Gdb";
+}
+
+QString TypeMacroPage::trCategory() const
+{
+ return tr("Debugger|Gdb");
+}
+
+QWidget *TypeMacroPage::createPage(QWidget *parent)
+{
+ QString macro;
+ int index;
+
+ m_widget = new QWidget(parent);
+ m_ui.setupUi(m_widget);
+
+ connect(m_ui.addButton, SIGNAL(clicked()),
+ this, SLOT(onAddButton()));
+
+ connect(m_ui.delButton, SIGNAL(clicked()),
+ this, SLOT(onDelButton()));
+
+ connect(m_ui.scriptButton, SIGNAL(clicked()),
+ this, SLOT(onScriptButton()));
+
+ connect(m_ui.treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
+ this, SLOT(currentItemChanged(QTreeWidgetItem *)));
+
+ connect(m_ui.typeEdit, SIGNAL(textChanged(const QString &)),
+ this, SLOT(updateButtonState()));
+
+ connect(m_ui.macroEdit, SIGNAL(textChanged(const QString &)),
+ this, SLOT(updateButtonState()));
+
+ QMap<QString, QVariant>::const_iterator i = m_settings->m_typeMacros.constBegin();
+ while (i != m_settings->m_typeMacros.constEnd()) {
+ QTreeWidgetItem *item = new QTreeWidgetItem(m_ui.treeWidget);
+ QDataStream stream(i.value().toByteArray());
+ stream >> macro >> index;
+ item->setText(0, i.key());
+ item->setText(1, macro);
+ item->setData(0, Qt::UserRole, index);
+ ++i;
+ }
+
+ m_ui.scriptEdit->setText(m_settings->m_scriptFile);
+
+ updateButtonState();
+
+ return m_widget;
+}
+
+void TypeMacroPage::finished(bool accepted)
+{
+ if (!accepted)
+ return;
+
+ m_settings->m_typeMacros.clear();
+ m_settings->m_scriptFile = m_ui.scriptEdit->text();
+
+ for (int i=0; i<m_ui.treeWidget->topLevelItemCount(); ++i) {
+ QTreeWidgetItem *item = m_ui.treeWidget->topLevelItem(i);
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+ stream << item->text(1) << item->data(0, Qt::UserRole).toInt();
+ m_settings->m_typeMacros.insert(item->text(0), data);
+ }
+
+ Core::ICore *coreIFace = m_pm->getObject<Core::ICore>();
+ if (coreIFace && coreIFace->settings()) {
+ QSettings *s = coreIFace->settings();
+ s->beginGroup("GdbOptions");
+ s->setValue("ScriptFile", m_settings->m_scriptFile);
+ s->setValue("TypeMacros", m_settings->m_typeMacros);
+ s->endGroup();
+ }
+}
+
+void TypeMacroPage::onScriptButton()
+{
+ QString fileName = QFileDialog::getOpenFileName(m_widget, tr("Select Gdb Script"));
+ m_ui.scriptEdit->setText(fileName);
+ updateButtonState();
+}
+
+void TypeMacroPage::onAddButton()
+{
+ if (m_ui.typeEdit->text().isEmpty() || m_ui.macroEdit->text().isEmpty())
+ return;
+
+ QTreeWidgetItem *item = new QTreeWidgetItem(m_ui.treeWidget);
+ item->setText(0, m_ui.typeEdit->text());
+ item->setText(1, m_ui.macroEdit->text());
+ item->setData(0, Qt::UserRole, m_ui.parseAsBox->currentIndex());
+
+ updateButtonState();
+}
+
+void TypeMacroPage::onDelButton()
+{
+ if (QTreeWidgetItem *item = m_ui.treeWidget->currentItem())
+ delete item;
+ updateButtonState();
+}
+
+void TypeMacroPage::currentItemChanged(QTreeWidgetItem *item)
+{
+ m_ui.typeEdit->setText(item ? item->text(0) : QString());
+ m_ui.macroEdit->setText(item ? item->text(1) : QString());
+ m_ui.parseAsBox->setCurrentIndex(item ? item->data(0, Qt::UserRole).toInt() : 0);
+ updateButtonState();
+}
+
+void TypeMacroPage::updateButtonState()
+{
+ m_ui.delButton->setEnabled(m_ui.treeWidget->currentItem() != 0);
+ m_ui.addButton->setDisabled(m_ui.typeEdit->text().isEmpty()
+ || m_ui.macroEdit->text().isEmpty());
+}
diff --git a/src/plugins/debugger/gdbtypemacros.ui b/src/plugins/debugger/gdbtypemacros.ui
new file mode 100644
index 0000000000..aa7215577b
--- /dev/null
+++ b/src/plugins/debugger/gdbtypemacros.ui
@@ -0,0 +1,186 @@
+<ui version="4.0" >
+ <author></author>
+ <comment></comment>
+ <exportmacro></exportmacro>
+ <class>TypeMacroPage</class>
+ <widget class="QWidget" name="TypeMacroPage" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>519</width>
+ <height>238</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="title" >
+ <string>Script File</string>
+ </property>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLineEdit" name="scriptEdit" />
+ </item>
+ <item>
+ <widget class="QToolButton" name="scriptButton" >
+ <property name="minimumSize" >
+ <size>
+ <width>21</width>
+ <height>23</height>
+ </size>
+ </property>
+ <property name="text" >
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="0" column="0" colspan="2" >
+ <widget class="QTreeWidget" name="treeWidget" >
+ <property name="rootIsDecorated" >
+ <bool>false</bool>
+ </property>
+ <column>
+ <property name="text" >
+ <string>Type</string>
+ </property>
+ </column>
+ <column>
+ <property name="text" >
+ <string>Macro</string>
+ </property>
+ </column>
+ </widget>
+ </item>
+ <item row="1" column="2" >
+ <widget class="QToolButton" name="addButton" >
+ <property name="minimumSize" >
+ <size>
+ <width>21</width>
+ <height>23</height>
+ </size>
+ </property>
+ <property name="text" >
+ <string>+</string>
+ </property>
+ <property name="icon" >
+ <iconset resource="gdbdebugger.qrc" >:/gdbdebugger/images/newitem.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_2" >
+ <property name="text" >
+ <string>Macro Name:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>Parse as:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QLineEdit" name="macroEdit" />
+ </item>
+ <item row="0" column="2" >
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QToolButton" name="delButton" >
+ <property name="minimumSize" >
+ <size>
+ <width>21</width>
+ <height>23</height>
+ </size>
+ </property>
+ <property name="text" >
+ <string>-</string>
+ </property>
+ <property name="icon" >
+ <iconset resource="gdbdebugger.qrc" >:/gdbdebugger/images/delete.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLineEdit" name="typeEdit" />
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Type:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" >
+ <widget class="QComboBox" name="parseAsBox" >
+ <item>
+ <property name="text" >
+ <string>ASCII (char *)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Unicode (short)</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <pixmapfunction></pixmapfunction>
+ <resources>
+ <include location="gdbdebugger.qrc" />
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/plugins/debugger/idebuggerengine.h b/src/plugins/debugger/idebuggerengine.h
new file mode 100644
index 0000000000..84edcb6ca5
--- /dev/null
+++ b/src/plugins/debugger/idebuggerengine.h
@@ -0,0 +1,88 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_IDEBUGGERENGINE_H
+#define DEBUGGER_IDEBUGGERENGINE_H
+
+#include <QtCore/QObject>
+
+namespace Debugger {
+namespace Internal {
+
+class IDebuggerEngine : public QObject
+{
+public:
+ IDebuggerEngine(QObject *parent = 0) : QObject(parent) {}
+
+ virtual void shutdown() = 0;
+ virtual void setToolTipExpression(const QPoint &pos, const QString &exp) = 0;
+ virtual bool startDebugger() = 0;
+ virtual void exitDebugger() = 0;
+ virtual void updateWatchModel() = 0;
+
+ virtual void stepExec() = 0;
+ virtual void stepOutExec() = 0;
+ virtual void nextExec() = 0;
+ virtual void stepIExec() = 0;
+ virtual void nextIExec() = 0;
+
+ virtual void continueInferior() = 0;
+ virtual void runInferior() = 0;
+ virtual void interruptInferior() = 0;
+
+ virtual void runToLineExec(const QString &fileName, int lineNumber) = 0;
+ virtual void runToFunctionExec(const QString &functionName) = 0;
+ virtual void jumpToLineExec(const QString &fileName, int lineNumber) = 0;
+ virtual void assignValueInDebugger(const QString &expr, const QString &value) = 0;
+ virtual void executeDebuggerCommand(const QString &command) = 0;
+
+ virtual void activateFrame(int index) = 0;
+ virtual void selectThread(int index) = 0;
+
+ virtual void attemptBreakpointSynchronization() = 0;
+
+ virtual void loadSessionData() = 0;
+ virtual void saveSessionData() = 0;
+
+ virtual void reloadDisassembler() = 0;
+
+ virtual void reloadModules() = 0;
+ virtual void loadSymbols(const QString &moduleName) = 0;
+ virtual void loadAllSymbols() = 0;
+
+ virtual void reloadRegisters() = 0;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_IDEBUGGERENGINE_H
diff --git a/src/plugins/debugger/images/breakpoint.svg b/src/plugins/debugger/images/breakpoint.svg
new file mode 100644
index 0000000000..e8d63cc903
--- /dev/null
+++ b/src/plugins/debugger/images/breakpoint.svg
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="14"
+ height="14"
+ id="svg2270"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="D:\depot\research\main\editor\images"
+ sodipodi:docname="breakpoint.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs2272">
+ <linearGradient
+ id="linearGradient7029">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop7031" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop7033" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient17794">
+ <stop
+ style="stop-color:#f18383;stop-opacity:1;"
+ offset="0"
+ id="stop17798" />
+ <stop
+ id="stop8006"
+ offset="0.3807947"
+ style="stop-color:#ed6767;stop-opacity:1;" />
+ <stop
+ style="stop-color:#e62323;stop-opacity:1;"
+ offset="1"
+ id="stop17796" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient17794"
+ id="linearGradient24732"
+ gradientUnits="userSpaceOnUse"
+ x1="472.42236"
+ y1="436.79602"
+ x2="461.39169"
+ y2="424.95065" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient17794"
+ id="linearGradient2438"
+ gradientUnits="userSpaceOnUse"
+ x1="472.42236"
+ y1="436.79602"
+ x2="461.39169"
+ y2="424.95065" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient17794"
+ id="radialGradient6052"
+ cx="466.73566"
+ cy="431.19708"
+ fx="466.73566"
+ fy="431.19708"
+ r="9.3095722"
+ gradientTransform="matrix(1,0,0,1.0057859,0,-2.4948735)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient7029"
+ id="linearGradient7035"
+ x1="6.75"
+ y1="0.5"
+ x2="6.75"
+ y2="12.5"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="32"
+ inkscape:cx="8.6877264"
+ inkscape:cy="6.3888789"
+ inkscape:document-units="px"
+ inkscape:current-layer="g25843"
+ width="14px"
+ height="14px"
+ inkscape:window-width="1280"
+ inkscape:window-height="998"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ showgrid="true"
+ gridspacingx="0.5px"
+ gridspacingy="0.5px"
+ gridempspacing="2"
+ inkscape:grid-points="true" />
+ <metadata
+ id="metadata2275">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="g25843"
+ transform="matrix(0.7931251,0,0,0.7931251,-372.13374,-408.22195)">
+ <path
+ sodipodi:type="arc"
+ style="fill:url(#radialGradient6052);fill-opacity:1.0;stroke:#c80000;stroke-width:1.43637741;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path22737"
+ sodipodi:cx="466.73566"
+ sodipodi:cy="431.19708"
+ sodipodi:rx="8.5913839"
+ sodipodi:ry="8.6452484"
+ d="M 475.32704 431.19708 A 8.5913839 8.6452484 0 1 1 458.14427,431.19708 A 8.5913839 8.6452484 0 1 1 475.32704 431.19708 z"
+ transform="matrix(0.8805346,0,0,0.8750503,66.41784,145.57686)" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;fill:url(#linearGradient7035);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6058"
+ sodipodi:cx="6.75"
+ sodipodi:cy="6.5"
+ sodipodi:rx="5.75"
+ sodipodi:ry="6"
+ d="M 12.5 6.5 A 5.75 6 0 1 1 1,6.5 A 5.75 6 0 1 1 12.5 6.5 z"
+ transform="matrix(0.9867408,0,0,0.6304178,470.73423,515.01579)" />
+ </g>
+ </g>
+</svg>
diff --git a/src/plugins/debugger/images/breakpoint_pending.svg b/src/plugins/debugger/images/breakpoint_pending.svg
new file mode 100644
index 0000000000..e7094068b5
--- /dev/null
+++ b/src/plugins/debugger/images/breakpoint_pending.svg
@@ -0,0 +1,534 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="14"
+ height="14"
+ id="svg2270"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="c:\depot\research\main\editor\images"
+ sodipodi:docname="pendingbreakpoint.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs2272">
+ <linearGradient
+ id="linearGradient7029">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop7031" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop7033" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient17794">
+ <stop
+ style="stop-color:#f18383;stop-opacity:1;"
+ offset="0"
+ id="stop17798" />
+ <stop
+ id="stop8006"
+ offset="0.3807947"
+ style="stop-color:#ed6767;stop-opacity:1;" />
+ <stop
+ style="stop-color:#e62323;stop-opacity:1;"
+ offset="1"
+ id="stop17796" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient17794"
+ id="linearGradient24732"
+ gradientUnits="userSpaceOnUse"
+ x1="472.42236"
+ y1="436.79602"
+ x2="461.39169"
+ y2="424.95065" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient17794"
+ id="linearGradient2438"
+ gradientUnits="userSpaceOnUse"
+ x1="472.42236"
+ y1="436.79602"
+ x2="461.39169"
+ y2="424.95065" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient17794"
+ id="radialGradient6052"
+ cx="466.73566"
+ cy="431.19708"
+ fx="466.73566"
+ fy="431.19708"
+ r="9.3095722"
+ gradientTransform="matrix(1,0,0,1.0057859,0,-2.4948735)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient7029"
+ id="linearGradient7035"
+ x1="6.75"
+ y1="0.5"
+ x2="6.75"
+ y2="12.5"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ gradientUnits="userSpaceOnUse"
+ y2="12.5"
+ x2="6.75"
+ y1="0.5"
+ x1="6.75"
+ id="linearGradient2228"
+ xlink:href="#linearGradient7029"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1,0,0,1.0057859,0,-2.4948735)"
+ r="9.3095722"
+ fy="431.19708"
+ fx="466.73566"
+ cy="431.19708"
+ cx="466.73566"
+ id="radialGradient2226"
+ xlink:href="#linearGradient17794"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="424.95065"
+ x2="461.39169"
+ y1="436.79602"
+ x1="472.42236"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2224"
+ xlink:href="#linearGradient17794"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="424.95065"
+ x2="461.39169"
+ y1="436.79602"
+ x1="472.42236"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2222"
+ xlink:href="#linearGradient17794"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2214">
+ <stop
+ id="stop2216"
+ offset="0"
+ style="stop-color:#f18383;stop-opacity:1;" />
+ <stop
+ style="stop-color:#ed6767;stop-opacity:1;"
+ offset="0.3807947"
+ id="stop2218" />
+ <stop
+ id="stop2220"
+ offset="1"
+ style="stop-color:#e62323;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2208">
+ <stop
+ id="stop2210"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop2212"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="matrix(1.2661544,0,0,1.2608351,469.23729,510.59508)"
+ y2="10.60876"
+ x2="10.981011"
+ y1="9.9135647"
+ x1="10.946278"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4173"
+ xlink:href="#linearGradient4128"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="matrix(1.2608351,0,0,1.2608351,475.7834,516.59183)"
+ y2="9.578125"
+ x2="5.859375"
+ y1="6.609375"
+ x1="5.953125"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4159"
+ xlink:href="#linearGradient4128"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientUnits="userSpaceOnUse"
+ y2="11"
+ x2="11"
+ y1="10"
+ x1="11"
+ id="linearGradient4156"
+ xlink:href="#linearGradient4128"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="521.00476"
+ x2="472.35138"
+ y1="519.11353"
+ x1="472.35138"
+ gradientTransform="matrix(1,0,0,1.0000093,10.421461,10.71221)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4138"
+ xlink:href="#linearGradient4128"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="matrix(1,0,0,0.9687523,10.283691,16.9106)"
+ gradientUnits="userSpaceOnUse"
+ y2="521.00476"
+ x2="472.35138"
+ y1="519.11353"
+ x1="472.35138"
+ id="linearGradient4134"
+ xlink:href="#linearGradient4128"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="424.95065"
+ x2="461.39169"
+ y1="436.79602"
+ x1="472.42236"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2293"
+ xlink:href="#linearGradient17794"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="424.95065"
+ x2="461.39169"
+ y1="436.79602"
+ x1="472.42236"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2291"
+ xlink:href="#linearGradient17794"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2285">
+ <stop
+ id="stop2287"
+ offset="0"
+ style="stop-color:#c80000;stop-opacity:1;" />
+ <stop
+ id="stop2289"
+ offset="1"
+ style="stop-color:#ffa0a0;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ y2="424.95065"
+ x2="461.39169"
+ y1="436.79602"
+ x1="472.42236"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient25826"
+ xlink:href="#linearGradient17794"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4128">
+ <stop
+ id="stop4130"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:0.78431374;" />
+ <stop
+ id="stop4132"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0.23529412;" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="matrix(1.2661544,0,0,1.2608351,469.23729,510.59508)"
+ y2="10.60876"
+ x2="10.981011"
+ y1="9.9135647"
+ x1="10.946278"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2378"
+ xlink:href="#linearGradient4128"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientUnits="userSpaceOnUse"
+ y2="11.147234"
+ x2="11.02502"
+ y1="9.6892195"
+ x1="10.968282"
+ id="linearGradient2376"
+ xlink:href="#linearGradient4128"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="matrix(1.2608351,0,0,1.2608351,475.7834,516.59183)"
+ y2="9.578125"
+ x2="5.859375"
+ y1="6.609375"
+ x1="5.953125"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2374"
+ xlink:href="#linearGradient4128"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientUnits="userSpaceOnUse"
+ y2="11"
+ x2="11"
+ y1="10"
+ x1="11"
+ id="linearGradient2372"
+ xlink:href="#linearGradient4128"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="521.00476"
+ x2="472.35138"
+ y1="519.11353"
+ x1="472.35138"
+ gradientTransform="matrix(1,0,0,0.6666648,10.303108,184.09099)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2370"
+ xlink:href="#linearGradient4128"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="521.00476"
+ x2="472.35138"
+ y1="519.11353"
+ x1="472.35138"
+ gradientTransform="matrix(1,0,0,0.6666648,10.342666,173.98441)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2368"
+ xlink:href="#linearGradient4128"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="521.00476"
+ x2="472.35138"
+ y1="519.11353"
+ x1="472.35138"
+ gradientTransform="matrix(1,0,0,1.0000093,10.421461,10.71221)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2366"
+ xlink:href="#linearGradient4128"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="matrix(1,0,0,0.9687523,10.283691,16.9106)"
+ gradientUnits="userSpaceOnUse"
+ y2="521.00476"
+ x2="472.35138"
+ y1="519.11353"
+ x1="472.35138"
+ id="linearGradient2364"
+ xlink:href="#linearGradient4128"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="424.95065"
+ x2="461.39169"
+ y1="436.79602"
+ x1="472.42236"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2362"
+ xlink:href="#linearGradient17794"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="424.95065"
+ x2="461.39169"
+ y1="436.79602"
+ x1="472.42236"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2360"
+ xlink:href="#linearGradient17794"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2354">
+ <stop
+ id="stop2356"
+ offset="0"
+ style="stop-color:#c80000;stop-opacity:1;" />
+ <stop
+ id="stop2358"
+ offset="1"
+ style="stop-color:#ffa0a0;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ y2="424.95065"
+ x2="461.39169"
+ y1="436.79602"
+ x1="472.42236"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2352"
+ xlink:href="#linearGradient17794"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2346">
+ <stop
+ id="stop2348"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:0.78431374;" />
+ <stop
+ id="stop2350"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0.23529412;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4128"
+ id="linearGradient2392"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1,0,0,0.6666648,10.342666,173.98441)"
+ x1="472.35138"
+ y1="519.11353"
+ x2="472.35138"
+ y2="521.00476" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4128"
+ id="linearGradient2394"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1,0,0,0.6666648,10.303108,184.09099)"
+ x1="472.35138"
+ y1="519.11353"
+ x2="472.35138"
+ y2="521.00476" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4128"
+ id="linearGradient2396"
+ gradientUnits="userSpaceOnUse"
+ x1="10.968282"
+ y1="9.6892195"
+ x2="11.02502"
+ y2="11.147234" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="45.254834"
+ inkscape:cx="13.678175"
+ inkscape:cy="6.8291478"
+ inkscape:document-units="px"
+ inkscape:current-layer="g25843"
+ width="14px"
+ height="14px"
+ inkscape:window-width="1280"
+ inkscape:window-height="998"
+ inkscape:window-x="44"
+ inkscape:window-y="58"
+ showgrid="true"
+ gridspacingx="0.5px"
+ gridspacingy="0.5px"
+ gridempspacing="2"
+ inkscape:grid-points="true" />
+ <metadata
+ id="metadata2275">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="g25843"
+ transform="matrix(0.7931251,0,0,0.7931251,-372.13374,-408.22195)">
+ <path
+ sodipodi:type="arc"
+ style="fill:url(#radialGradient6052);fill-opacity:1.0;stroke:#c80000;stroke-width:1.43637741;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path22737"
+ sodipodi:cx="466.73566"
+ sodipodi:cy="431.19708"
+ sodipodi:rx="8.5913839"
+ sodipodi:ry="8.6452484"
+ d="M 475.32704 431.19708 A 8.5913839 8.6452484 0 1 1 458.14427,431.19708 A 8.5913839 8.6452484 0 1 1 475.32704 431.19708 z"
+ transform="matrix(0.8805346,0,0,0.8750503,66.41784,145.57686)" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;fill:url(#linearGradient7035);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6058"
+ sodipodi:cx="6.75"
+ sodipodi:cy="6.5"
+ sodipodi:rx="5.75"
+ sodipodi:ry="6"
+ d="M 12.5 6.5 A 5.75 6 0 1 1 1,6.5 A 5.75 6 0 1 1 12.5 6.5 z"
+ transform="matrix(0.9867408,0,0,0.6304178,470.73423,515.01579)" />
+ <g
+ id="g2380"
+ inkscape:label="Layer 1"
+ transform="matrix(1.2608365,0,0,1.2633098,469.19929,514.69071)">
+ <g
+ transform="matrix(0.7931251,0,0,0.7931251,-372.13374,-408.22195)"
+ id="g2382">
+ <path
+ style="fill:#7f7f8c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 481.17723,524.15684 C 481.17723,524.15684 482.11277,524.68766 483.0584,524.68766 C 484.00403,524.68766 484.95974,524.15684 484.95974,524.15684 L 483.4493,526.03249 L 483.4493,527.83201 L 484.8944,528.56151 L 484.95974,531.09144 L 481.17723,531.09144 L 481.16963,528.69627 L 482.71594,527.80266 L 482.72456,526.04216 L 481.17723,524.15684 z "
+ id="path2442"
+ sodipodi:nodetypes="czcccccccccc" />
+ <path
+ style="opacity:0.2;fill:#7f7f7f;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 481.17723,521.00476 L 481.17723,523.52643 L 482.43807,526.0481 L 481.17723,528.56977 L 481.17723,531.72186 L 484.95974,531.72186 L 484.95974,528.56977 L 483.6989,526.0481 L 484.95974,523.52643 L 484.95974,521.00476 L 481.17723,521.00476 z "
+ id="path2444"
+ sodipodi:nodetypes="ccccccccccc" />
+ <path
+ sodipodi:nodetypes="cccccccccccccccccccccc"
+ id="path2174"
+ d="M 480.54682,520.37434 L 480.54681,523.52643 L 481.80765,526.0481 L 480.54681,528.56977 L 480.54682,531.72185 L 485.59016,531.72185 L 485.59015,528.56977 L 484.32932,526.0481 L 485.59015,523.52643 L 485.59016,520.37434 L 480.54682,520.37434 z M 481.80766,521.63517 L 484.32932,521.63517 L 484.32932,523.52643 L 483.46249,526.0481 L 484.32932,528.56977 L 484.32932,530.46102 L 481.80766,530.46102 L 481.80765,528.56977 L 482.72372,526.0481 L 481.80765,523.52643 L 481.80766,521.63517 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <rect
+ ry="1.2608351"
+ y="519.7439"
+ x="479.28598"
+ height="2.5216701"
+ width="7.565001"
+ id="rect2172"
+ style="opacity:1;fill:#7f2aff;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ ry="1.2608351"
+ y="529.83063"
+ x="479.28598"
+ height="2.5216701"
+ width="7.565001"
+ id="rect2170"
+ style="opacity:1;fill:#6600ff;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ ry="0.63040882"
+ y="520.0589"
+ x="480.17267"
+ height="1.2608176"
+ width="5.6737618"
+ id="rect4140"
+ style="opacity:1;fill:url(#linearGradient2392);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ ry="0.63040882"
+ y="530.16577"
+ x="480.13324"
+ height="1.2608176"
+ width="5.6737618"
+ id="rect4144"
+ style="opacity:1;fill:url(#linearGradient2394);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cssz"
+ transform="matrix(1.2608351,0,0,1.2608351,469.1993,514.70058)"
+ id="path4161"
+ d="M 11.007811,9.9179701 C 10.386665,9.9257826 9.6414144,10.484263 9.5594939,10.992187 C 9.5017174,11.350414 12.494284,11.43273 12.441847,10.914063 C 12.40754,10.574731 11.628908,9.9101582 11.007811,9.9179701 z "
+ style="fill:url(#linearGradient2396);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/src/plugins/debugger/images/debugger_breakpoints.png b/src/plugins/debugger/images/debugger_breakpoints.png
new file mode 100644
index 0000000000..75bb5f15bf
--- /dev/null
+++ b/src/plugins/debugger/images/debugger_breakpoints.png
Binary files differ
diff --git a/src/plugins/debugger/images/debugger_continue_small.png b/src/plugins/debugger/images/debugger_continue_small.png
new file mode 100644
index 0000000000..4a3788c149
--- /dev/null
+++ b/src/plugins/debugger/images/debugger_continue_small.png
Binary files differ
diff --git a/src/plugins/debugger/images/debugger_interrupt_small.png b/src/plugins/debugger/images/debugger_interrupt_small.png
new file mode 100644
index 0000000000..815400cb58
--- /dev/null
+++ b/src/plugins/debugger/images/debugger_interrupt_small.png
Binary files differ
diff --git a/src/plugins/debugger/images/debugger_start.png b/src/plugins/debugger/images/debugger_start.png
new file mode 100644
index 0000000000..8eed81a899
--- /dev/null
+++ b/src/plugins/debugger/images/debugger_start.png
Binary files differ
diff --git a/src/plugins/debugger/images/debugger_start_small.png b/src/plugins/debugger/images/debugger_start_small.png
new file mode 100644
index 0000000000..4a3788c149
--- /dev/null
+++ b/src/plugins/debugger/images/debugger_start_small.png
Binary files differ
diff --git a/src/plugins/debugger/images/debugger_stepinto_small.png b/src/plugins/debugger/images/debugger_stepinto_small.png
new file mode 100644
index 0000000000..da36a5f670
--- /dev/null
+++ b/src/plugins/debugger/images/debugger_stepinto_small.png
Binary files differ
diff --git a/src/plugins/debugger/images/debugger_steponeproc_small.png b/src/plugins/debugger/images/debugger_steponeproc_small.png
new file mode 100644
index 0000000000..cf164c6604
--- /dev/null
+++ b/src/plugins/debugger/images/debugger_steponeproc_small.png
Binary files differ
diff --git a/src/plugins/debugger/images/debugger_stepout_small.png b/src/plugins/debugger/images/debugger_stepout_small.png
new file mode 100644
index 0000000000..e5eeeb32ad
--- /dev/null
+++ b/src/plugins/debugger/images/debugger_stepout_small.png
Binary files differ
diff --git a/src/plugins/debugger/images/debugger_stepover_small.png b/src/plugins/debugger/images/debugger_stepover_small.png
new file mode 100644
index 0000000000..e8a5d08046
--- /dev/null
+++ b/src/plugins/debugger/images/debugger_stepover_small.png
Binary files differ
diff --git a/src/plugins/debugger/images/debugger_stepoverproc_small.png b/src/plugins/debugger/images/debugger_stepoverproc_small.png
new file mode 100644
index 0000000000..34e712da06
--- /dev/null
+++ b/src/plugins/debugger/images/debugger_stepoverproc_small.png
Binary files differ
diff --git a/src/plugins/debugger/images/debugger_stop_small.png b/src/plugins/debugger/images/debugger_stop_small.png
new file mode 100644
index 0000000000..1063d08998
--- /dev/null
+++ b/src/plugins/debugger/images/debugger_stop_small.png
Binary files differ
diff --git a/src/plugins/debugger/images/delete.png b/src/plugins/debugger/images/delete.png
new file mode 100644
index 0000000000..e4139afc55
--- /dev/null
+++ b/src/plugins/debugger/images/delete.png
Binary files differ
diff --git a/src/plugins/debugger/images/done.png b/src/plugins/debugger/images/done.png
new file mode 100644
index 0000000000..b5238f7680
--- /dev/null
+++ b/src/plugins/debugger/images/done.png
Binary files differ
diff --git a/src/plugins/debugger/images/empty.svg b/src/plugins/debugger/images/empty.svg
new file mode 100644
index 0000000000..46209de391
--- /dev/null
+++ b/src/plugins/debugger/images/empty.svg
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="14"
+ height="14"
+ id="svg2243"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="c:\depot\research\main\editor\images"
+ sodipodi:docname="location.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs2245">
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="64"
+ inkscape:cx="8.3920091"
+ inkscape:cy="7.4257237"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ width="14px"
+ height="14px"
+ showborder="true"
+ inkscape:window-width="1600"
+ inkscape:window-height="1174"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ gridempspacing="2"
+ showgrid="true"
+ inkscape:grid-points="true"
+ gridspacingx="0.5px"
+ gridspacingy="0.5px" />
+ <metadata
+ id="metadata2248">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ </g>
+</svg>
diff --git a/src/plugins/debugger/images/error.png b/src/plugins/debugger/images/error.png
new file mode 100644
index 0000000000..700530438a
--- /dev/null
+++ b/src/plugins/debugger/images/error.png
Binary files differ
diff --git a/src/plugins/debugger/images/location.svg b/src/plugins/debugger/images/location.svg
new file mode 100644
index 0000000000..afb70052a1
--- /dev/null
+++ b/src/plugins/debugger/images/location.svg
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="14"
+ height="14"
+ id="svg2243"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="c:\depot\research\main\editor\images"
+ sodipodi:docname="location.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs2245">
+ <linearGradient
+ id="linearGradient3134">
+ <stop
+ style="stop-color:#dcdc23;stop-opacity:1;"
+ offset="0"
+ id="stop3136" />
+ <stop
+ id="stop5080"
+ offset="0.64285713"
+ style="stop-color:#e5d044;stop-opacity:1;" />
+ <stop
+ style="stop-color:#b89354;stop-opacity:1;"
+ offset="1"
+ id="stop3138" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3137">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.86274511;"
+ offset="0"
+ id="stop3139" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3141" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3137"
+ id="linearGradient3143"
+ x1="6.5"
+ y1="3"
+ x2="6.515625"
+ y2="12.180227"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3134"
+ id="linearGradient3140"
+ x1="6.5"
+ y1="3.015625"
+ x2="6.484375"
+ y2="11.984375"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="64"
+ inkscape:cx="8.3920091"
+ inkscape:cy="7.4257237"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ width="14px"
+ height="14px"
+ showborder="true"
+ inkscape:window-width="1600"
+ inkscape:window-height="1174"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ gridempspacing="2"
+ showgrid="true"
+ inkscape:grid-points="true"
+ gridspacingx="0.5px"
+ gridspacingy="0.5px" />
+ <metadata
+ id="metadata2248">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ style="fill:url(#linearGradient3140);fill-opacity:1.0;fill-rule:evenodd;stroke:#b18b1b;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 6.5,3 L 6.5,5.5 L 0.5,5.5 L 0.5,9.5 L 6.5,9.5 L 6.5,12 L 13.125,7.5 L 6.5,3 z "
+ id="path2216"
+ sodipodi:nodetypes="cccccccc" />
+ <path
+ style="fill:url(#linearGradient3143);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;opacity:1"
+ d="M 6.5,3 L 6.5,5.5 L 0.5,5.5 L 0.5,7.5 C 7,6.5 7.5,9.5 13,7.5 L 6.5,3 z "
+ id="path5066"
+ sodipodi:nodetypes="cccccc" />
+ </g>
+</svg>
diff --git a/src/plugins/debugger/images/newitem.png b/src/plugins/debugger/images/newitem.png
new file mode 100644
index 0000000000..7e26ea9b15
--- /dev/null
+++ b/src/plugins/debugger/images/newitem.png
Binary files differ
diff --git a/src/plugins/debugger/images/running.png b/src/plugins/debugger/images/running.png
new file mode 100644
index 0000000000..1cf8888ecb
--- /dev/null
+++ b/src/plugins/debugger/images/running.png
Binary files differ
diff --git a/src/plugins/debugger/imports.h b/src/plugins/debugger/imports.h
new file mode 100644
index 0000000000..8a2edd4c39
--- /dev/null
+++ b/src/plugins/debugger/imports.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_IMPORTS_H
+#define DEBUGGER_IMPORTS_H
+
+// FIXME: Plan is to remove this file. It's needed to get "harmless"
+// replacements for GH internals in the standalone version
+
+#ifndef GDBDEBUGGERLEAN
+
+#include <texteditor/basetextmark.h>
+
+#else
+
+#include "lean.h"
+
+#endif
+
+#endif // DEBUGGER_IMPORTS_H
diff --git a/src/plugins/debugger/mode.cpp b/src/plugins/debugger/mode.cpp
new file mode 100644
index 0000000000..2023cdd7ee
--- /dev/null
+++ b/src/plugins/debugger/mode.cpp
@@ -0,0 +1,233 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "mode.h"
+
+#include "assert.h"
+#include "debuggerconstants.h"
+#include "debuggermanager.h"
+
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/modemanager.h>
+#include <coreplugin/uniqueidmanager.h>
+#include <coreplugin/actionmanager/actionmanagerinterface.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/minisplitter.h>
+#include <coreplugin/findplaceholder.h>
+#include <coreplugin/outputpane.h>
+#include <coreplugin/navigationwidget.h>
+#include <coreplugin/rightpane.h>
+#include <projectexplorer/projectexplorerconstants.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QSettings>
+#include <QtGui/QDockWidget>
+#include <QtGui/QLabel>
+#include <QtGui/QMainWindow>
+#include <QtGui/QVBoxLayout>
+#include <QtGui/QWidget>
+
+using namespace Core;
+using namespace ExtensionSystem;
+using namespace Debugger;
+using namespace Debugger::Internal;
+using namespace Debugger::Constants;
+
+
+DebugMode::DebugMode(DebuggerManager *manager, QObject *parent)
+ : BaseMode(tr("Debug"), Constants::MODE_DEBUG,
+ QIcon(":/fancyactionbar/images/mode_Debug.png"),
+ Constants::P_MODE_DEBUG, 0, parent),
+ m_manager(manager)
+{
+ IDebuggerManagerAccessForDebugMode *managerAccess =
+ m_manager->debugModeInterface();
+ UniqueIDManager *uidm =
+ PluginManager::instance()->getObject<ICore>()->uniqueIDManager();
+ QList<int> context;
+ context.append(uidm->uniqueIdentifier(Core::Constants::C_EDITORMANAGER));
+ context.append(uidm->uniqueIdentifier(Constants::C_GDBDEBUGGER));
+ context.append(uidm->uniqueIdentifier(Core::Constants::C_NAVIGATION_PANE));
+ setContext(context);
+
+ QBoxLayout *editorHolderLayout = new QVBoxLayout;
+ editorHolderLayout->setMargin(0);
+ editorHolderLayout->setSpacing(0);
+ editorHolderLayout->addWidget(new EditorManagerPlaceHolder(this));
+ editorHolderLayout->addWidget(new FindToolBarPlaceHolder(this));
+
+ QWidget *editorAndFindWidget = new QWidget;
+ editorAndFindWidget->setLayout(editorHolderLayout);
+
+ MiniSplitter *rightPaneSplitter = new MiniSplitter;
+ rightPaneSplitter->addWidget(editorAndFindWidget);
+ rightPaneSplitter->addWidget(new RightPanePlaceHolder(this));
+ rightPaneSplitter->setStretchFactor(0, 1);
+ rightPaneSplitter->setStretchFactor(1, 0);
+
+ QWidget *centralWidget = new QWidget;
+ QBoxLayout *toolBarAddingLayout = new QVBoxLayout(centralWidget);
+ toolBarAddingLayout->setMargin(0);
+ toolBarAddingLayout->setSpacing(0);
+ toolBarAddingLayout->addWidget(rightPaneSplitter);
+
+ m_manager->mainWindow()->setCentralWidget(centralWidget);
+
+ MiniSplitter *splitter = new MiniSplitter;
+ splitter->addWidget(m_manager->mainWindow());
+ splitter->addWidget(new OutputPanePlaceHolder(this));
+ splitter->setStretchFactor(0, 10);
+ splitter->setStretchFactor(1, 0);
+ splitter->setOrientation(Qt::Vertical);
+
+ MiniSplitter *splitter2 = new MiniSplitter;
+ splitter2 = new MiniSplitter;
+ splitter2->addWidget(new NavigationWidgetPlaceHolder(this));
+ splitter2->addWidget(splitter);
+ splitter2->setStretchFactor(0, 0);
+ splitter2->setStretchFactor(1, 1);
+
+ setWidget(splitter2);
+
+ QToolBar *toolBar = createToolBar();
+ toolBarAddingLayout->addWidget(toolBar);
+
+ managerAccess->createDockWidgets();
+ m_manager->setSimpleDockWidgetArrangement();
+ readSettings();
+
+ connect(ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode*)),
+ this, SLOT(focusCurrentEditor(Core::IMode*)));
+ widget()->setFocusProxy(EditorManager::instance());
+}
+
+DebugMode::~DebugMode()
+{
+ // Make sure the editor manager does not get deleted
+ EditorManager::instance()->setParent(0);
+}
+
+void DebugMode::shutdown()
+{
+ writeSettings();
+}
+
+QToolBar *DebugMode::createToolBar()
+{
+ IDebuggerManagerAccessForDebugMode *managerAccess =
+ m_manager->debugModeInterface();
+
+ Core::ActionManagerInterface *am =
+ ExtensionSystem::PluginManager::instance()
+ ->getObject<Core::ICore>()->actionManager();
+ QToolBar *debugToolBar = new QToolBar;
+ debugToolBar->addAction(am->command(ProjectExplorer::Constants::DEBUG)->action());
+ debugToolBar->addAction(am->command(Constants::INTERRUPT)->action());
+ debugToolBar->addAction(am->command(Constants::NEXT)->action());
+ debugToolBar->addAction(am->command(Constants::STEP)->action());
+ debugToolBar->addAction(am->command(Constants::STEPOUT)->action());
+ debugToolBar->addSeparator();
+ debugToolBar->addAction(am->command(Constants::STEPI)->action());
+ debugToolBar->addAction(am->command(Constants::NEXTI)->action());
+ debugToolBar->addSeparator();
+ debugToolBar->addWidget(new QLabel(tr("Threads:")));
+
+ QComboBox *threadBox = new QComboBox;
+ threadBox->setModel(m_manager->threadsModel());
+ connect(threadBox, SIGNAL(activated(int)),
+ managerAccess->threadsWindow(), SIGNAL(threadSelected(int)));
+ debugToolBar->addWidget(threadBox);
+
+ QWidget *stretch = new QWidget;
+ stretch->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+ debugToolBar->addWidget(stretch);
+
+ QMenu *viewMenu = new QMenu(debugToolBar);
+ m_toggleLockedAction = new QAction(tr("Locked"), viewMenu);
+ m_toggleLockedAction->setCheckable(true);
+ m_toggleLockedAction->setChecked(true);
+ connect(m_toggleLockedAction, SIGNAL(toggled(bool)),
+ m_manager, SLOT(setLocked(bool)));
+ foreach (QDockWidget *dockWidget, managerAccess->dockWidgets())
+ viewMenu->addAction(dockWidget->toggleViewAction());
+ viewMenu->addSeparator();
+ viewMenu->addAction(m_toggleLockedAction);
+ viewMenu->addSeparator();
+
+ QAction *resetToSimpleAction = viewMenu->addAction(tr("Reset to default layout"));
+ connect(resetToSimpleAction, SIGNAL(triggered()),
+ m_manager, SLOT(setSimpleDockWidgetArrangement()));
+ QToolButton *viewMenuButton = new QToolButton(debugToolBar);
+ viewMenuButton->setText(tr("View "));
+ viewMenuButton->setPopupMode(QToolButton::InstantPopup);
+ viewMenuButton->setMenu(viewMenu);
+ debugToolBar->addWidget(viewMenuButton);
+
+ return debugToolBar;
+}
+
+void DebugMode::focusCurrentEditor(IMode *mode)
+{
+ if (mode != this)
+ return;
+
+ EditorManager *editorManager = EditorManager::instance();
+
+ if (editorManager->currentEditor())
+ editorManager->currentEditor()->widget()->setFocus();
+}
+
+void DebugMode::writeSettings() const
+{
+ QSettings *s = settings();
+ QWB_ASSERT(m_manager, return);
+ QWB_ASSERT(m_manager->mainWindow(), return);
+ s->beginGroup(QLatin1String("DebugMode"));
+ s->setValue(QLatin1String("State"), m_manager->mainWindow()->saveState());
+ s->setValue(QLatin1String("Locked"), m_toggleLockedAction->isChecked());
+ s->endGroup();
+}
+
+void DebugMode::readSettings()
+{
+ QSettings *s = settings();
+ s->beginGroup(QLatin1String("DebugMode"));
+ m_manager->mainWindow()->restoreState(s->value(QLatin1String("State"), QByteArray()).toByteArray());
+ m_toggleLockedAction->setChecked(s->value(QLatin1String("Locked"), true).toBool());
+ s->endGroup();
+}
+
+QSettings *DebugMode::settings()
+{
+ return PluginManager::instance()->getObject<ICore>()->settings();
+}
diff --git a/src/plugins/debugger/mode.h b/src/plugins/debugger/mode.h
new file mode 100644
index 0000000000..15506d0cac
--- /dev/null
+++ b/src/plugins/debugger/mode.h
@@ -0,0 +1,84 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_DEBUGMODE_H
+#define DEBUGGER_DEBUGMODE_H
+
+#include <coreplugin/basemode.h>
+
+#include <QtCore/QList>
+#include <QtCore/QPointer>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QDockWidget;
+class QMainWindow;
+class QSettings;
+class QSplitter;
+class QToolBar;
+class QWidget;
+QT_END_NAMESPACE
+
+namespace Debugger {
+namespace Internal {
+
+class DebuggerManager;
+
+class DebugMode : public Core::BaseMode
+{
+ Q_OBJECT
+
+public:
+ DebugMode(DebuggerManager *manager, QObject *parent = 0);
+ ~DebugMode();
+
+ // IMode
+ void activated();
+ void shutdown();
+ static QSettings *settings();
+
+private slots:
+ void focusCurrentEditor(Core::IMode *mode);
+
+private:
+ QToolBar *createToolBar();
+ void writeSettings() const;
+ void readSettings();
+
+ QPointer<DebuggerManager> m_manager;
+ QAction *m_toggleLockedAction;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_DEBUGMODE_H
diff --git a/src/plugins/debugger/mode.ui b/src/plugins/debugger/mode.ui
new file mode 100644
index 0000000000..da44cf38b4
--- /dev/null
+++ b/src/plugins/debugger/mode.ui
@@ -0,0 +1,76 @@
+<ui version="4.0" >
+ <class>DebugMode</class>
+ <widget class="QWidget" name="DebugMode" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>558</width>
+ <height>353</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3" >
+ <property name="spacing" >
+ <number>0</number>
+ </property>
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QSplitter" name="vSplitter" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <widget class="QWidget" native="1" name="editorHolder" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
+ <horstretch>10</horstretch>
+ <verstretch>10</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QWidget" name="layoutWidget" >
+ <layout class="QVBoxLayout" name="verticalLayout" >
+ <property name="spacing" >
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="toolbarLayout" >
+ <property name="spacing" >
+ <number>0</number>
+ </property>
+ </layout>
+ </item>
+ <item>
+ <widget class="QSplitter" name="hSplitter" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <widget class="QTabWidget" name="bottomTabWidget" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="tabPosition" >
+ <enum>QTabWidget::South</enum>
+ </property>
+ <property name="tabShape" >
+ <enum>QTabWidget::Rounded</enum>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/debugger/moduleshandler.cpp b/src/plugins/debugger/moduleshandler.cpp
new file mode 100644
index 0000000000..458482b385
--- /dev/null
+++ b/src/plugins/debugger/moduleshandler.cpp
@@ -0,0 +1,172 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "moduleshandler.h"
+
+#include "assert.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QList>
+#include <QtCore/QTextStream>
+
+#include <QtGui/QAction>
+#include <QtGui/QMainWindow>
+#include <QtGui/QStandardItemModel>
+#include <QtGui/QSortFilterProxyModel>
+
+using namespace Debugger;
+using namespace Debugger::Internal;
+
+
+//////////////////////////////////////////////////////////////////
+//
+// ModulesModel
+//
+//////////////////////////////////////////////////////////////////
+
+class Debugger::Internal::ModulesModel : public QAbstractItemModel
+{
+public:
+ ModulesModel(ModulesHandler *parent)
+ : QAbstractItemModel(parent)
+ {}
+
+ // QAbstractItemModel
+ int columnCount(const QModelIndex &parent) const
+ { return parent.isValid() ? 0 : 4; }
+ int rowCount(const QModelIndex &parent) const
+ { return parent.isValid() ? 0 : m_modules.size(); }
+ QModelIndex parent(const QModelIndex &) const { return QModelIndex(); }
+ QModelIndex index(int row, int column, const QModelIndex &) const
+ { return createIndex(row, column); }
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ QVariant data(const QModelIndex &index, int role) const;
+ bool setData(const QModelIndex &index, const QVariant &value, int role);
+
+ void clearModel() { if (!m_modules.isEmpty()) { m_modules.clear(); update(); } }
+ void update() { reset(); }
+
+public:
+ QList<Module> m_modules;
+};
+
+QVariant ModulesModel::headerData(int section,
+ Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ static QString headers[] = {
+ tr("Module name") + " ",
+ tr("Symbols read") + " ",
+ tr("Start address") + " ",
+ tr("End addAress") + " "
+ };
+ return headers[section];
+ }
+ return QVariant();
+}
+
+QVariant ModulesModel::data(const QModelIndex &index, int role) const
+{
+ //static const QIcon icon(":/gdbdebugger/images/breakpoint.svg");
+ //static const QIcon icon2(":/gdbdebugger/images/breakpoint_pending.svg");
+
+ int row = index.row();
+ if (row < 0 || row >= m_modules.size())
+ return QVariant();
+
+ const Module &module = m_modules.at(row);
+
+ switch (index.column()) {
+ case 0:
+ if (role == Qt::DisplayRole)
+ return module.moduleName;
+ // FIXME: add icons
+ //if (role == Qt::DecorationRole)
+ // return module.symbolsRead ? icon2 : icon;
+ break;
+ case 1:
+ if (role == Qt::DisplayRole)
+ return module.symbolsRead ? "yes" : "no";
+ break;
+ case 2:
+ if (role == Qt::DisplayRole)
+ return module.startAddress;
+ break;
+ case 3:
+ if (role == Qt::DisplayRole)
+ return module.endAddress;
+ break;
+ }
+ return QVariant();
+}
+
+bool ModulesModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ return QAbstractItemModel::setData(index, value, role);
+}
+
+
+//////////////////////////////////////////////////////////////////
+//
+// ModulesHandler
+//
+//////////////////////////////////////////////////////////////////
+
+ModulesHandler::ModulesHandler()
+{
+ m_model = new ModulesModel(this);
+ m_proxyModel = new QSortFilterProxyModel(this);
+ m_proxyModel->setSourceModel(m_model);
+}
+
+QAbstractItemModel *ModulesHandler::model() const
+{
+ return m_proxyModel;
+}
+
+void ModulesHandler::removeAll()
+{
+ m_model->clearModel();
+}
+
+
+void ModulesHandler::setModules(const QList<Module> &modules)
+{
+ m_model->m_modules = modules;
+ m_model->update();
+}
+
+QList<Module> ModulesHandler::modules() const
+{
+ return m_model->m_modules;
+}
diff --git a/src/plugins/debugger/moduleshandler.h b/src/plugins/debugger/moduleshandler.h
new file mode 100644
index 0000000000..49efe16a72
--- /dev/null
+++ b/src/plugins/debugger/moduleshandler.h
@@ -0,0 +1,104 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_MODULESHANDLER_H
+#define DEBUGGER_MODULESHANDLER_H
+
+#include <QtCore/QList>
+#include <QtCore/QObject>
+
+QT_BEGIN_NAMESPACE
+class QAbstractItemModel;
+class QSortFilterProxyModel;
+QT_END_NAMESPACE
+
+
+namespace Debugger {
+namespace Internal {
+
+class ModulesModel;
+
+enum ModulesModelRoles
+{
+ DisplaySourceRole = Qt::UserRole,
+ LoadSymbolsRole,
+ LoadAllSymbolsRole
+};
+
+
+//////////////////////////////////////////////////////////////////
+//
+// Module
+//
+//////////////////////////////////////////////////////////////////
+
+class Module
+{
+public:
+ Module() : symbolsRead(false) {}
+
+public:
+ QString moduleName;
+ bool symbolsRead;
+ QString startAddress;
+ QString endAddress;
+};
+
+
+//////////////////////////////////////////////////////////////////
+//
+// ModulesHandler
+//
+//////////////////////////////////////////////////////////////////
+
+class ModulesHandler : public QObject
+{
+ Q_OBJECT
+
+public:
+ ModulesHandler();
+
+ QAbstractItemModel *model() const;
+
+ void setModules(const QList<Module> &modules);
+ QList<Module> modules() const;
+ void removeAll();
+
+private:
+ ModulesModel *m_model;
+ QSortFilterProxyModel *m_proxyModel;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_MODULESHANDLER_H
diff --git a/src/plugins/debugger/moduleswindow.cpp b/src/plugins/debugger/moduleswindow.cpp
new file mode 100644
index 0000000000..f98db1ce1f
--- /dev/null
+++ b/src/plugins/debugger/moduleswindow.cpp
@@ -0,0 +1,136 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "moduleswindow.h"
+#include "moduleshandler.h" // for model roles
+
+#include <QAction>
+#include <QDebug>
+#include <QHeaderView>
+#include <QMenu>
+#include <QResizeEvent>
+#include <QToolButton>
+
+
+using Debugger::Internal::ModulesWindow;
+
+ModulesWindow::ModulesWindow(QWidget *parent)
+ : QTreeView(parent), m_alwaysResizeColumnsToContents(false)
+{
+ setWindowTitle(tr("Modules"));
+ setSortingEnabled(true);
+ setAlternatingRowColors(true);
+ setRootIsDecorated(false);
+ setIconSize(QSize(10, 10));
+}
+
+void ModulesWindow::resizeEvent(QResizeEvent *event)
+{
+ //QHeaderView *hv = header();
+ //int totalSize = event->size().width() - 110;
+ //hv->resizeSection(0, totalSize / 4);
+ //hv->resizeSection(1, totalSize / 4);
+ //hv->resizeSection(2, totalSize / 4);
+ //hv->resizeSection(3, totalSize / 4);
+ //hv->resizeSection(0, 60);
+ //hv->resizeSection(1, (totalSize * 50) / 100);
+ //hv->resizeSection(2, (totalSize * 50) / 100);
+ //hv->resizeSection(3, 50);
+ //setColumnHidden(3, true);
+ QTreeView::resizeEvent(event);
+}
+
+void ModulesWindow::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QModelIndex index = indexAt(ev->pos());
+ index = index.sibling(index.row(), 0);
+ QString name = model()->data(index).toString();
+
+ QMenu menu;
+ QAction *act0 = new QAction("Update module list", &menu);
+ QAction *act1 = new QAction("Adjust column widths to contents", &menu);
+ QAction *act2 = new QAction("Always adjust column widths to contents", &menu);
+ act2->setCheckable(true);
+ act2->setChecked(m_alwaysResizeColumnsToContents);
+ QAction *act3 = new QAction("Show source files for module " + name, &menu);
+ QAction *act4 = new QAction("Load symbols for all modules", &menu);
+ QAction *act5 = new QAction("Load symbols for module " + name, &menu);
+ act5->setDisabled(name.isEmpty());
+
+ menu.addAction(act0);
+ menu.addAction(act4);
+ menu.addAction(act5);
+ menu.addSeparator();
+ menu.addAction(act1);
+ menu.addAction(act2);
+
+ QAction *act = menu.exec(ev->globalPos());
+
+ if (act == act0)
+ emit reloadModulesRequested();
+ else if (act == act1)
+ resizeColumnsToContents();
+ else if (act == act2)
+ setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
+ else if (act == act3)
+ emit displaySourceRequested(name);
+ else if (act == act4)
+ emit loadAllSymbolsRequested();
+ else if (act == act5)
+ emit loadSymbolsRequested(name);
+}
+
+void ModulesWindow::resizeColumnsToContents()
+{
+ resizeColumnToContents(0);
+ resizeColumnToContents(1);
+ resizeColumnToContents(2);
+}
+
+void ModulesWindow::setAlwaysResizeColumnsToContents(bool on)
+{
+ m_alwaysResizeColumnsToContents = on;
+ QHeaderView::ResizeMode mode = on
+ ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
+ header()->setResizeMode(0, mode);
+ header()->setResizeMode(1, mode);
+ header()->setResizeMode(2, mode);
+ header()->setResizeMode(3, mode);
+ //setColumnHidden(3, true);
+}
+
+void ModulesWindow::setModel(QAbstractItemModel *model)
+{
+ QTreeView::setModel(model);
+ setAlwaysResizeColumnsToContents(true);
+}
+
diff --git a/src/plugins/debugger/moduleswindow.h b/src/plugins/debugger/moduleswindow.h
new file mode 100644
index 0000000000..a014ff8e6c
--- /dev/null
+++ b/src/plugins/debugger/moduleswindow.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_MODULESWINDOW_H
+#define DEBUGGER_MODULESWINDOW_H
+
+#include <QTreeView>
+
+namespace Debugger {
+namespace Internal {
+
+class ModulesWindow : public QTreeView
+{
+ Q_OBJECT
+
+public:
+ explicit ModulesWindow(QWidget *parent = 0);
+
+signals:
+ void reloadModulesRequested();
+ void displaySourceRequested(const QString &modulesName);
+ void loadSymbolsRequested(const QString &modulesName);
+ void loadAllSymbolsRequested();
+
+public slots:
+ void resizeColumnsToContents();
+ void setAlwaysResizeColumnsToContents(bool on);
+
+protected:
+ void resizeEvent(QResizeEvent *ev);
+ void contextMenuEvent(QContextMenuEvent *ev);
+ void setModel(QAbstractItemModel *model);
+
+private:
+ bool m_alwaysResizeColumnsToContents;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_MODULESWINDOW_H
+
diff --git a/src/plugins/debugger/procinterrupt.cpp b/src/plugins/debugger/procinterrupt.cpp
new file mode 100644
index 0000000000..29d4d5803d
--- /dev/null
+++ b/src/plugins/debugger/procinterrupt.cpp
@@ -0,0 +1,182 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "procinterrupt.h"
+
+#ifdef Q_OS_WIN
+#include <windows.h>
+#include <Tlhelp32.h>
+
+using namespace Debugger::Internal;
+
+typedef HANDLE (WINAPI *PtrCreateRemoteThread)(
+ HANDLE hProcess,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ SIZE_T dwStackSize,
+ LPTHREAD_START_ROUTINE lpStartAddress,
+ LPVOID lpParameter,
+ DWORD dwCreationFlags,
+ LPDWORD lpThreadId);
+
+PtrCreateRemoteThread resolveCreateRemoteThread()
+{
+ HINSTANCE hLib = LoadLibraryA("Kernel32");
+ return (PtrCreateRemoteThread)GetProcAddress(hLib, "CreateRemoteThread");
+}
+
+DWORD findProcessId(DWORD parentId)
+{
+ HANDLE hProcList = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+
+ PROCESSENTRY32 procEntry;
+ procEntry.dwSize = sizeof(PROCESSENTRY32);
+
+ DWORD procId = 0;
+
+ BOOL moreProc = Process32First(hProcList, &procEntry);
+ while (moreProc) {
+ if (procEntry.th32ParentProcessID == parentId) {
+ procId = procEntry.th32ProcessID;
+ break;
+ }
+ moreProc = Process32Next(hProcList, &procEntry);
+ }
+
+ CloseHandle(hProcList);
+ return procId;
+}
+#else
+
+#include <QtCore/QLatin1String>
+#include <QtCore/QString>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfoList>
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+
+#include <sys/types.h>
+#include <signal.h>
+
+#include <sys/sysctl.h>
+
+#define OPProcessValueUnknown UINT_MAX
+
+/* Mac OS X
+int OPParentIDForProcessID(int pid)
+ // Returns the parent process id for the given process id (pid)
+{
+ struct kinfo_proc info;
+ size_t length = sizeof(struct kinfo_proc);
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid };
+ if (sysctl(mib, 4, &info, &length, NULL, 0) < 0)
+ return OPProcessValueUnknown;
+ if (length == 0)
+ return OPProcessValueUnknown;
+ return info.kp_eproc.e_ppid;
+}
+*/
+
+int findParentProcess(int procId)
+{
+ QFile statFile(QLatin1String("/proc/") + QString::number(procId) +
+ QLatin1String("/stat"));
+ if (!statFile.open(QIODevice::ReadOnly))
+ return -1;
+
+ QByteArray line = statFile.readLine();
+ line = line.mid(line.indexOf(')') + 4);
+ //qDebug() << "1: " << line;
+ line = line.left(line.indexOf(' '));
+ //qDebug() << "2: " << line;
+
+ return QString(line).toInt();
+}
+
+int findChildProcess(int parentId)
+{
+ QDir proc(QLatin1String("/proc"));
+ QFileInfoList procList = proc.entryInfoList(QDir::Dirs);
+ foreach (const QFileInfo &info, procList) {
+ int procId = 0;
+ bool ok = false;
+ procId = info.baseName().toInt(&ok);
+ if (!ok || !procId)
+ continue;
+
+ if (findParentProcess(procId) == parentId)
+ return procId;
+ }
+
+ return -1;
+}
+
+#endif
+
+bool Debugger::Internal::interruptProcess(int pID)
+{
+#ifdef Q_OS_WIN
+ DWORD pid = pID;
+ if (!pid)
+ return false;
+
+ PtrCreateRemoteThread libFunc = resolveCreateRemoteThread();
+ if (libFunc) {
+ DWORD dwThreadId = 0;
+ HANDLE hproc = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
+ HANDLE hthread = libFunc(hproc, NULL, 0, (LPTHREAD_START_ROUTINE)DebugBreak, 0, 0, &dwThreadId);
+ CloseHandle(hthread);
+ if (dwThreadId)
+ return true;
+ }
+#else
+ int procId = pID;
+ if (procId != -1) {
+ if (kill(procId, 2) == 0)
+ return true;
+ }
+
+#endif
+
+ return false;
+}
+
+bool Debugger::Internal::interruptChildProcess(Q_PID parentPID)
+{
+#ifdef WIN32
+ DWORD pid = findProcessId(parentPID->dwProcessId);
+ return interruptProcess(pid);
+#else
+ int procId = findChildProcess(parentPID);
+ //qDebug() << "INTERRUPTING PROCESS" << procId;
+ return interruptProcess(procId);
+#endif
+}
diff --git a/src/plugins/debugger/procinterrupt.h b/src/plugins/debugger/procinterrupt.h
new file mode 100644
index 0000000000..0fcf27b0f1
--- /dev/null
+++ b/src/plugins/debugger/procinterrupt.h
@@ -0,0 +1,47 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_PROCINTERRUPT_H
+#define DEBUGGER_PROCINTERRUPT_H
+
+#include <QtCore/QProcess>
+
+namespace Debugger {
+namespace Internal {
+
+bool interruptProcess(int pID);
+bool interruptChildProcess(Q_PID parentPID);
+
+} // Internal
+} // GdbDebugger
+
+#endif // DEBUGGER_PROCINTERRUPT_H
diff --git a/src/plugins/debugger/registerhandler.cpp b/src/plugins/debugger/registerhandler.cpp
new file mode 100644
index 0000000000..4c70e2339c
--- /dev/null
+++ b/src/plugins/debugger/registerhandler.cpp
@@ -0,0 +1,128 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "registerhandler.h"
+
+#include "assert.h"
+#include "debuggerconstants.h"
+
+#include <QtCore/QAbstractTableModel>
+#include <QtCore/QDebug>
+
+#include <QtGui/QColor>
+
+using namespace Debugger;
+using namespace Debugger::Internal;
+using namespace Debugger::Constants;
+
+
+
+//////////////////////////////////////////////////////////////////
+//
+// RegisterHandler
+//
+//////////////////////////////////////////////////////////////////
+
+RegisterHandler::RegisterHandler(QObject *parent)
+ : QAbstractTableModel(parent)
+{
+ setProperty(PROPERTY_REGISTER_FORMAT, "x");
+}
+
+int RegisterHandler::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ return m_registers.size();
+}
+
+int RegisterHandler::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ return 2;
+}
+
+QVariant RegisterHandler::data(const QModelIndex &index, int role) const
+{
+ static const QVariant red = QColor(200, 0, 0);
+ if (!index.isValid() || index.row() >= m_registers.size())
+ return QVariant();
+
+ const Register &reg = m_registers.at(index.row());
+
+ if (role == Qt::DisplayRole) {
+ switch (index.column()) {
+ case 0:
+ return reg.name;
+ case 1:
+ return reg.value;
+ }
+ }
+ if (role == Qt::TextColorRole && reg.changed && index.column() == 1)
+ return red;
+ return QVariant();
+}
+
+QVariant RegisterHandler::headerData(int section, Qt::Orientation orientation,
+ int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ static const char * const headers[] = {
+ QT_TR_NOOP("Name"),
+ QT_TR_NOOP("Value"),
+ };
+ if (section < 2)
+ return tr(headers[section]);
+ }
+ return QVariant();
+}
+
+void RegisterHandler::removeAll()
+{
+ m_registers.clear();
+ reset();
+}
+
+bool RegisterHandler::isEmpty() const
+{
+ return m_registers.isEmpty();
+}
+
+void RegisterHandler::setRegisters(const QList<Register> &registers)
+{
+ m_registers = registers;
+ reset();
+}
+
+QList<Register> RegisterHandler::registers() const
+{
+ return m_registers;
+}
diff --git a/src/plugins/debugger/registerhandler.h b/src/plugins/debugger/registerhandler.h
new file mode 100644
index 0000000000..933e45aad4
--- /dev/null
+++ b/src/plugins/debugger/registerhandler.h
@@ -0,0 +1,81 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_REGISTERHANDLER_H
+#define DEBUGGER_REGISTERHANDLER_H
+
+#include <QtCore/QAbstractTableModel>
+
+namespace Debugger {
+namespace Internal {
+
+class Register
+{
+public:
+ Register() : changed(true) {}
+ Register(QString const &name_) : name(name_), changed(true) {}
+
+public:
+ QString name;
+ QString value;
+ bool changed;
+};
+
+class RegisterHandler : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ RegisterHandler(QObject *parent = 0);
+
+ void sessionClosed();
+ QAbstractItemModel *model() { return this; }
+
+ bool isEmpty() const; // nothing known so far?
+ void setRegisters(const QList<Register> &registers);
+ QList<Register> registers() const;
+ void removeAll();
+
+private:
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+
+ QList<Register> m_registers;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_REGISTERHANDLER_H
diff --git a/src/plugins/debugger/registerwindow.cpp b/src/plugins/debugger/registerwindow.cpp
new file mode 100644
index 0000000000..5e53139285
--- /dev/null
+++ b/src/plugins/debugger/registerwindow.cpp
@@ -0,0 +1,181 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "registerwindow.h"
+
+#include "debuggerconstants.h"
+
+#include <QAction>
+#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
+#include <QFileInfoList>
+#include <QHeaderView>
+#include <QMenu>
+#include <QResizeEvent>
+#include <QToolButton>
+
+
+using namespace Debugger::Internal;
+using namespace Debugger::Constants;
+
+RegisterWindow::RegisterWindow()
+ : m_alwaysResizeColumnsToContents(true), m_alwaysReloadContents(false)
+{
+ setWindowTitle(tr("Registers"));
+ setSortingEnabled(true);
+ setAlternatingRowColors(true);
+ setRootIsDecorated(false);
+ //header()->hide();
+ //setIconSize(QSize(10, 10));
+ //setWindowIcon(QIcon(":/gdbdebugger/images/debugger_breakpoints.png"));
+ //QHeaderView *hv = header();
+ //hv->setDefaultAlignment(Qt::AlignLeft);
+ //hv->setClickable(true);
+ //hv->setSortIndicatorShown(true);
+}
+
+void RegisterWindow::resizeEvent(QResizeEvent *ev)
+{
+ //QHeaderView *hv = header();
+ //int totalSize = ev->size().width() - 110;
+ //hv->resizeSection(0, totalSize / 4);
+ //hv->resizeSection(1, totalSize / 4);
+ QTreeView::resizeEvent(ev);
+}
+
+void RegisterWindow::contextMenuEvent(QContextMenuEvent *ev)
+{
+ enum { Hex, Bin, Dec, Raw, Oct, Nat,
+ Adjust, AlwaysAdjust, Reload, AlwaysReload, Count };
+
+ QMenu menu;
+ QAction *actions[Count];
+ //QTreeWidgetItem *item = itemAt(ev->pos());
+ QString format = model()->property(PROPERTY_REGISTER_FORMAT).toString();
+ qDebug() << "FORMAT: " << format;
+
+ actions[Adjust] = menu.addAction("Adjust column widths to contents");
+
+ actions[AlwaysAdjust] = menu.addAction("Always adjust column widths to contents");
+ actions[AlwaysAdjust]->setCheckable(true);
+ actions[AlwaysAdjust]->setChecked(m_alwaysResizeColumnsToContents);
+
+ actions[Reload] = menu.addAction("Reload register listing");
+
+ actions[AlwaysReload] = menu.addAction("Always reload register listing");
+ actions[AlwaysReload]->setCheckable(true);
+ actions[AlwaysReload]->setChecked(m_alwaysReloadContents);
+
+ menu.addSeparator();
+
+ actions[Hex] = menu.addAction("Hexadecimal");
+ actions[Hex]->setCheckable(true);
+ actions[Hex]->setChecked(format == "h");
+
+ actions[Bin] = menu.addAction("Binary");
+ actions[Bin]->setCheckable(true);
+ actions[Bin]->setChecked(format == "t");
+
+ actions[Dec] = menu.addAction("Decimal");
+ actions[Dec]->setCheckable(true);
+ actions[Dec]->setChecked(format == "d");
+
+ actions[Raw] = menu.addAction("Raw");
+ actions[Raw]->setCheckable(true);
+ actions[Raw]->setChecked(format == "r");
+
+ actions[Nat] = menu.addAction("Natural");
+ actions[Nat]->setCheckable(true);
+ actions[Nat]->setChecked(format == "N");
+
+ actions[Oct] = menu.addAction("Octal");
+ actions[Oct]->setCheckable(true);
+ actions[Oct]->setChecked(format == "o");
+
+ QAction *act = menu.exec(ev->globalPos());
+
+ if (act == actions[Adjust])
+ resizeColumnsToContents();
+ else if (act == actions[AlwaysAdjust])
+ setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
+ else if (act == actions[Reload])
+ reloadContents();
+ else if (act == actions[AlwaysReload])
+ setAlwaysReloadContents(!m_alwaysReloadContents);
+ else if (act == actions[Hex])
+ model()->setProperty(PROPERTY_REGISTER_FORMAT, "h");
+ else if (act == actions[Oct])
+ model()->setProperty(PROPERTY_REGISTER_FORMAT, "o");
+ else if (act == actions[Bin])
+ model()->setProperty(PROPERTY_REGISTER_FORMAT, "t");
+ else if (act == actions[Dec])
+ model()->setProperty(PROPERTY_REGISTER_FORMAT, "d");
+ else if (act == actions[Nat])
+ model()->setProperty(PROPERTY_REGISTER_FORMAT, "N");
+
+}
+
+void RegisterWindow::resizeColumnsToContents()
+{
+ resizeColumnToContents(0);
+ resizeColumnToContents(1);
+}
+
+void RegisterWindow::setAlwaysResizeColumnsToContents(bool on)
+{
+ m_alwaysResizeColumnsToContents = on;
+ QHeaderView::ResizeMode mode = on
+ ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
+ header()->setResizeMode(0, mode);
+ header()->setResizeMode(1, mode);
+}
+
+void RegisterWindow::setAlwaysReloadContents(bool on)
+{
+ m_alwaysReloadContents = on;
+ if (m_alwaysReloadContents)
+ reloadContents();
+}
+
+void RegisterWindow::reloadContents()
+{
+ emit reloadRegisterRequested();
+}
+
+
+void RegisterWindow::setModel(QAbstractItemModel *model)
+{
+ QTreeView::setModel(model);
+ setAlwaysResizeColumnsToContents(true);
+}
+
diff --git a/src/plugins/debugger/registerwindow.h b/src/plugins/debugger/registerwindow.h
new file mode 100644
index 0000000000..4c2cc7b7a6
--- /dev/null
+++ b/src/plugins/debugger/registerwindow.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_REGISTERWINDOW_H
+#define DEBUGGER_REGISTERWINDOW_H
+
+#include <QTreeView>
+
+namespace Debugger {
+namespace Internal {
+
+class RegisterWindow : public QTreeView
+{
+ Q_OBJECT
+
+public:
+ RegisterWindow();
+ void setModel(QAbstractItemModel *model);
+
+signals:
+ void reloadRegisterRequested();
+
+public slots:
+ void resizeColumnsToContents();
+ void setAlwaysResizeColumnsToContents(bool on);
+ void reloadContents();
+ void setAlwaysReloadContents(bool on);
+
+protected:
+ void resizeEvent(QResizeEvent *ev);
+ void contextMenuEvent(QContextMenuEvent *ev);
+
+private:
+ bool m_alwaysResizeColumnsToContents;
+ bool m_alwaysReloadContents;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_REGISTERWINDOW_H
+
diff --git a/src/plugins/debugger/scriptengine.cpp b/src/plugins/debugger/scriptengine.cpp
new file mode 100644
index 0000000000..c6c7dd65a5
--- /dev/null
+++ b/src/plugins/debugger/scriptengine.cpp
@@ -0,0 +1,677 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+
+#include "scriptengine.h"
+
+#include "assert.h"
+#include "debuggerconstants.h"
+#include "debuggermanager.h"
+
+#include "disassemblerhandler.h"
+#include "breakhandler.h"
+#include "moduleshandler.h"
+#include "registerhandler.h"
+#include "stackhandler.h"
+#include "watchhandler.h"
+
+#include "startexternaldialog.h"
+#include "attachexternaldialog.h"
+
+#include <QtCore/QDateTime>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTimer>
+
+#include <QtGui/QAction>
+#include <QtGui/QToolTip>
+
+#include <QtScript/QScriptContext>
+#include <QtScript/QScriptClassPropertyIterator>
+#include <QtScript/QScriptContextInfo>
+#include <QtScript/QScriptEngine>
+#include <QtScript/QScriptEngineAgent>
+#include <QtScript/QScriptValue>
+#include <QtScript/QScriptValueIterator>
+
+using namespace Debugger;
+using namespace Debugger::Internal;
+using namespace Debugger::Constants;
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// ScriptEngine
+//
+///////////////////////////////////////////////////////////////////////
+
+class Debugger::Internal::ScriptAgent : public QScriptEngineAgent
+{
+public:
+ ScriptAgent(ScriptEngine *debugger, QScriptEngine *script);
+ ~ScriptAgent() {}
+
+ void contextPop();
+ void contextPush();
+ void exceptionCatch(qint64 scriptId, const QScriptValue &exception);
+ void exceptionThrow(qint64 scriptId, const QScriptValue & exception,
+ bool hasHandler);
+ void functionEntry(qint64 scriptId);
+ void functionExit(qint64 scriptId, const QScriptValue &returnValue);
+ void positionChange(qint64 scriptId, int lineNumber, int columnNumber);
+ void scriptLoad(qint64 id, const QString &program, const QString &fileName,
+ int baseLineNumber);
+ void scriptUnload(qint64 id);
+
+private:
+ void maybeBreakNow(bool byFunction);
+
+ ScriptEngine *q;
+};
+
+ScriptAgent::ScriptAgent(ScriptEngine *debugger, QScriptEngine *script)
+ : QScriptEngineAgent(script), q(debugger)
+{}
+
+void ScriptAgent::contextPop()
+{
+ qDebug() << "ScriptAgent::contextPop: ";
+}
+
+void ScriptAgent::contextPush()
+{
+ qDebug() << "ScriptAgent::contextPush: ";
+}
+
+void ScriptAgent::exceptionCatch(qint64 scriptId, const QScriptValue & exception)
+{
+ qDebug() << "ScriptAgent::exceptionCatch: " << scriptId << &exception;
+}
+
+void ScriptAgent::exceptionThrow(qint64 scriptId, const QScriptValue &exception,
+ bool hasHandler)
+{
+ qDebug() << "ScriptAgent::exceptionThrow: " << scriptId << &exception
+ << hasHandler;
+}
+
+void ScriptAgent::functionEntry(qint64 scriptId)
+{
+ Q_UNUSED(scriptId);
+ q->maybeBreakNow(true);
+}
+
+void ScriptAgent::functionExit(qint64 scriptId, const QScriptValue &returnValue)
+{
+ qDebug() << "ScriptAgent::functionExit: " << scriptId << &returnValue;
+}
+
+void ScriptAgent::positionChange(qint64 scriptId, int lineNumber, int columnNumber)
+{
+ //qDebug() << "ScriptAgent::position: " << lineNumber;
+ Q_UNUSED(scriptId);
+ Q_UNUSED(lineNumber);
+ Q_UNUSED(columnNumber);
+ q->maybeBreakNow(false);
+}
+
+void ScriptAgent::scriptLoad(qint64 scriptId, const QString &program,
+ const QString &fileName, int baseLineNumber)
+{
+ Q_UNUSED(scriptId);
+ Q_UNUSED(program);
+ Q_UNUSED(fileName);
+ Q_UNUSED(baseLineNumber);
+ //qDebug() << "ScriptAgent::scriptLoad: " << program << fileName
+ // << baseLineNumber;
+}
+
+void ScriptAgent::scriptUnload(qint64 scriptId)
+{
+ Q_UNUSED(scriptId);
+ //qDebug() << "ScriptAgent::scriptUnload: " << scriptId;
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// ScriptEngine
+//
+///////////////////////////////////////////////////////////////////////
+
+ScriptEngine::ScriptEngine(DebuggerManager *parent)
+{
+ q = parent;
+ qq = parent->engineInterface();
+ m_scriptEngine = new QScriptEngine(this);
+ m_scriptAgent = new ScriptAgent(this, m_scriptEngine);
+ m_scriptEngine->setAgent(m_scriptAgent);
+ m_scriptEngine->setProcessEventsInterval(1 /*ms*/);
+}
+
+ScriptEngine::~ScriptEngine()
+{
+}
+
+void ScriptEngine::executeDebuggerCommand(const QString &command)
+{
+ Q_UNUSED(command);
+ qDebug() << "FIXME: ScriptEngine::executeDebuggerCommand()";
+}
+
+void ScriptEngine::shutdown()
+{
+ exitDebugger();
+}
+
+void ScriptEngine::exitDebugger()
+{
+ //qDebug() << " ScriptEngine::exitDebugger()";
+ m_stopped = false;
+ m_stopOnNextLine = false;
+ m_scriptEngine->abortEvaluation();
+ qq->notifyInferiorExited();
+}
+
+bool ScriptEngine::startDebugger()
+{
+ m_stopped = false;
+ m_stopOnNextLine = false;
+ m_scriptEngine->abortEvaluation();
+ QFileInfo fi(q->m_executable);
+ m_scriptFileName = fi.absoluteFilePath();
+ QFile scriptFile(m_scriptFileName);
+ if (!scriptFile.open(QIODevice::ReadOnly))
+ return false;
+ QTextStream stream(&scriptFile);
+ m_scriptContents = stream.readAll();
+ scriptFile.close();
+ attemptBreakpointSynchronization();
+ QTimer::singleShot(0, q, SLOT(notifyStartupFinished()));
+ return true;
+}
+
+void ScriptEngine::continueInferior()
+{
+ //qDebug() << "ScriptEngine::continueInferior()";
+ m_stopped = false;
+ m_stopOnNextLine = false;
+}
+
+void ScriptEngine::runInferior()
+{
+ //qDebug() << "ScriptEngine::runInferior()";
+ QScriptValue result = m_scriptEngine->evaluate(m_scriptContents, m_scriptFileName);
+}
+
+void ScriptEngine::interruptInferior()
+{
+ m_stopped = false;
+ m_stopOnNextLine = true;
+ qDebug() << "FIXME: ScriptEngine::interruptInferior()";
+}
+
+void ScriptEngine::stepExec()
+{
+ //qDebug() << "FIXME: ScriptEngine::stepExec()";
+ m_stopped = false;
+ m_stopOnNextLine = true;
+}
+
+void ScriptEngine::stepIExec()
+{
+ //qDebug() << "FIXME: ScriptEngine::stepIExec()";
+ m_stopped = false;
+ m_stopOnNextLine = true;
+}
+
+void ScriptEngine::stepOutExec()
+{
+ //qDebug() << "FIXME: ScriptEngine::stepOutExec()";
+ m_stopped = false;
+ m_stopOnNextLine = true;
+}
+
+void ScriptEngine::nextExec()
+{
+ //qDebug() << "FIXME: ScriptEngine::nextExec()";
+ m_stopped = false;
+ m_stopOnNextLine = true;
+}
+
+void ScriptEngine::nextIExec()
+{
+ //qDebug() << "FIXME: ScriptEngine::nextIExec()";
+ m_stopped = false;
+ m_stopOnNextLine = true;
+}
+
+void ScriptEngine::runToLineExec(const QString &fileName, int lineNumber)
+{
+ Q_UNUSED(fileName);
+ Q_UNUSED(lineNumber);
+ qDebug() << "FIXME: ScriptEngine::runToLineExec()";
+}
+
+void ScriptEngine::runToFunctionExec(const QString &functionName)
+{
+ Q_UNUSED(functionName);
+ qDebug() << "FIXME: ScriptEngine::runToFunctionExec()";
+}
+
+void ScriptEngine::jumpToLineExec(const QString &fileName, int lineNumber)
+{
+ Q_UNUSED(fileName);
+ Q_UNUSED(lineNumber);
+ qDebug() << "FIXME: ScriptEngine::jumpToLineExec()";
+}
+
+void ScriptEngine::activateFrame(int index)
+{
+ Q_UNUSED(index);
+}
+
+void ScriptEngine::selectThread(int index)
+{
+ Q_UNUSED(index);
+}
+
+void ScriptEngine::attemptBreakpointSynchronization()
+{
+ BreakHandler *handler = qq->breakHandler();
+ bool updateNeeded = false;
+ for (int index = 0; index != handler->size(); ++index) {
+ BreakpointData *data = handler->at(index);
+ if (data->pending) {
+ data->pending = false; // FIXME
+ updateNeeded = true;
+ }
+ if (data->bpNumber.isEmpty()) {
+ data->bpNumber = QString::number(index + 1);
+ updateNeeded = true;
+ }
+ if (!data->fileName.isEmpty() && data->markerFileName.isEmpty()) {
+ data->markerFileName = data->fileName;
+ data->markerLineNumber = data->lineNumber.toInt();
+ updateNeeded = true;
+ }
+ }
+ if (updateNeeded)
+ handler->updateMarkers();
+}
+
+void ScriptEngine::reloadDisassembler()
+{
+}
+
+void ScriptEngine::loadSymbols(const QString &moduleName)
+{
+ Q_UNUSED(moduleName);
+}
+
+void ScriptEngine::loadAllSymbols()
+{
+}
+
+void ScriptEngine::reloadModules()
+{
+}
+
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Tooltip specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+static WatchData m_toolTip;
+static QPoint m_toolTipPos;
+static QHash<QString, WatchData> m_toolTipCache;
+
+static bool hasLetterOrNumber(const QString &exp)
+{
+ for (int i = exp.size(); --i >= 0; )
+ if (exp[i].isLetterOrNumber())
+ return true;
+ return false;
+}
+
+static bool hasSideEffects(const QString &exp)
+{
+ // FIXME: complete?
+ return exp.contains("-=")
+ || exp.contains("+=")
+ || exp.contains("/=")
+ || exp.contains("*=")
+ || exp.contains("&=")
+ || exp.contains("|=")
+ || exp.contains("^=")
+ || exp.contains("--")
+ || exp.contains("++");
+}
+
+void ScriptEngine::setToolTipExpression(const QPoint &pos, const QString &exp0)
+{
+ Q_UNUSED(pos);
+ Q_UNUSED(exp0);
+
+ if (q->status() != DebuggerInferiorStopped) {
+ //qDebug() << "SUPPRESSING DEBUGGER TOOLTIP, INFERIOR NOT STOPPED";
+ return;
+ }
+
+ //m_toolTipPos = pos;
+ QString exp = exp0;
+
+/*
+ if (m_toolTipCache.contains(exp)) {
+ const WatchData & data = m_toolTipCache[exp];
+ q->watchHandler()->removeChildren(data.iname);
+ insertData(data);
+ return;
+ }
+*/
+
+ QToolTip::hideText();
+ if (exp.isEmpty() || exp.startsWith("#")) {
+ QToolTip::hideText();
+ return;
+ }
+
+ if (!hasLetterOrNumber(exp)) {
+ QToolTip::showText(m_toolTipPos,
+ "'" + exp + "' contains no identifier");
+ return;
+ }
+
+ if (exp.startsWith('"') && exp.endsWith('"')) {
+ QToolTip::showText(m_toolTipPos, "String literal " + exp);
+ return;
+ }
+
+ if (exp.startsWith("++") || exp.startsWith("--"))
+ exp = exp.mid(2);
+
+ if (exp.endsWith("++") || exp.endsWith("--"))
+ exp = exp.mid(2);
+
+ if (exp.startsWith("<") || exp.startsWith("["))
+ return;
+
+ if (hasSideEffects(exp)) {
+ QToolTip::showText(m_toolTipPos,
+ "Cowardly refusing to evaluate expression '" + exp
+ + "' with potential side effects");
+ return;
+ }
+
+#if 0
+ //if (m_manager->status() != DebuggerInferiorStopped)
+ // return;
+
+ // FIXME: 'exp' can contain illegal characters
+ m_toolTip = WatchData();
+ m_toolTip.exp = exp;
+ m_toolTip.name = exp;
+ m_toolTip.iname = tooltipIName;
+ insertData(m_toolTip);
+#endif
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Watch specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+void ScriptEngine::assignValueInDebugger(const QString &expression,
+ const QString &value)
+{
+ Q_UNUSED(expression);
+ Q_UNUSED(value);
+}
+
+void ScriptEngine::maybeBreakNow(bool byFunction)
+{
+ QScriptContext *context = m_scriptEngine->currentContext();
+ QScriptContextInfo info(context);
+
+ //
+ // Update breakpoints
+ //
+ QString functionName = info.functionName();
+ QString fileName = info.fileName();
+ int lineNumber = info.lineNumber();
+ if (byFunction)
+ lineNumber = info.functionStartLineNumber();
+
+ BreakHandler *handler = qq->breakHandler();
+
+ if (m_stopOnNextLine) {
+ m_stopOnNextLine = false;
+ } else {
+ int index = 0;
+ for (; index != handler->size(); ++index) {
+ BreakpointData *data = handler->at(index);
+ if (byFunction) {
+ if (!functionName.isEmpty() && data->funcName == functionName)
+ break;
+ } else {
+ if (info.lineNumber() == data->lineNumber.toInt()
+ && fileName == data->fileName)
+ break;
+ }
+ }
+
+ if (index == handler->size())
+ return;
+
+ // we just run into a breakpoint
+ //qDebug() << "RESOLVING BREAKPOINT AT " << fileName << lineNumber;
+ BreakpointData *data = handler->at(index);
+ data->bpLineNumber = QString::number(lineNumber);
+ data->bpFileName = fileName;
+ data->bpFuncName = functionName;
+ data->markerLineNumber = lineNumber;
+ data->markerFileName = fileName;
+ data->pending = false;
+ data->updateMarker();
+ }
+
+ qq->notifyInferiorStopped();
+ q->gotoLocation(fileName, lineNumber, true);
+
+ qq->watchHandler()->reinitializeWatchers();
+ //qDebug() << "UPDATE LOCALS";
+
+ //
+ // Build stack
+ //
+ QList<StackFrame> stackFrames;
+ int i = 0;
+ for (QScriptContext *c = context; c; c = c->parentContext(), ++i) {
+ QScriptContextInfo info(c);
+ StackFrame frame;
+ frame.level = i;
+ frame.file = info.fileName();
+ frame.function = info.functionName();
+ frame.from = QString::number(info.functionStartLineNumber());
+ frame.to = QString::number(info.functionEndLineNumber());
+ frame.line = info.lineNumber();
+
+ if (frame.function.isEmpty())
+ frame.function = "<global scope>";
+ //frame.address = ...;
+ stackFrames.append(frame);
+ }
+ qq->stackHandler()->setFrames(stackFrames);
+
+ //
+ // Build locals
+ //
+ WatchData data;
+ data.iname = "local";
+ data.name = "local";
+ data.scriptValue = context->activationObject();
+ qq->watchHandler()->insertData(data);
+ updateWatchModel();
+
+ // FIXME: Use an extra thread. This here is evil
+ m_stopped = true;
+ while (m_stopped) {
+ //qDebug() << "LOOPING";
+ QApplication::processEvents();
+ }
+ //qDebug() << "RUNNING AGAIN";
+}
+
+void ScriptEngine::updateWatchModel()
+{
+ while (true) {
+ QList<WatchData> list = qq->watchHandler()->takeCurrentIncompletes();
+ if (list.isEmpty())
+ break;
+ foreach (const WatchData &data, list)
+ updateSubItem(data);
+ }
+ qq->watchHandler()->rebuildModel();
+ q->showStatusMessage(tr("Stopped."), 5000);
+}
+
+void ScriptEngine::updateSubItem(const WatchData &data0)
+{
+ WatchData data = data0;
+ //qDebug() << "\nUPDATE SUBITEM: " << data.toString();
+ QWB_ASSERT(data.isValid(), return);
+
+ if (data.isTypeNeeded() || data.isValueNeeded()) {
+ QScriptValue ob = data.scriptValue;
+ if (ob.isArray()) {
+ data.setType("Array");
+ data.setValue(" ");
+ } else if (ob.isBool()) {
+ data.setType("Bool");
+ data.setValue(ob.toBool() ? "true" : "false");
+ data.setChildCount(0);
+ } else if (ob.isDate()) {
+ data.setType("Date");
+ data.setValue(ob.toDateTime().toString().toUtf8());
+ data.setChildCount(0);
+ } else if (ob.isError()) {
+ data.setType("Error");
+ data.setValue(" ");
+ } else if (ob.isFunction()) {
+ data.setType("Function");
+ data.setValue(" ");
+ } else if (ob.isNull()) {
+ data.setType("<null>");
+ data.setValue("<null>");
+ } else if (ob.isNumber()) {
+ data.setType("Number");
+ data.setValue(QString::number(ob.toNumber()).toUtf8());
+ data.setChildCount(0);
+ } else if (ob.isObject()) {
+ data.setType("Object");
+ data.setValue(" ");
+ } else if (ob.isQMetaObject()) {
+ data.setType("QMetaObject");
+ data.setValue(" ");
+ } else if (ob.isQObject()) {
+ data.setType("QObject");
+ data.setValue(" ");
+ } else if (ob.isRegExp()) {
+ data.setType("RegExp");
+ data.setValue(ob.toRegExp().pattern().toUtf8());
+ } else if (ob.isString()) {
+ data.setType("String");
+ data.setValue(ob.toString().toUtf8());
+ } else if (ob.isVariant()) {
+ data.setType("Variant");
+ data.setValue(" ");
+ } else if (ob.isUndefined()) {
+ data.setType("<undefined>");
+ data.setValue("<unknown>");
+ } else {
+ data.setType("<unknown>");
+ data.setValue("<unknown>");
+ }
+ qq->watchHandler()->insertData(data);
+ return;
+ }
+
+ if (data.isChildrenNeeded()) {
+ int numChild = 0;
+ QScriptValueIterator it(data.scriptValue);
+ while (it.hasNext()) {
+ it.next();
+ WatchData data1;
+ data1.iname = data.iname + "." + it.name();
+ data1.name = it.name();
+ data1.scriptValue = it.value();
+ if (qq->watchHandler()->isExpandedIName(data1.iname))
+ data1.setChildrenNeeded();
+ else
+ data1.setChildrenUnneeded();
+ qq->watchHandler()->insertData(data1);
+ ++numChild;
+ }
+ //qDebug() << " ... CHILDREN: " << numChild;
+ data.setChildCount(numChild);
+ data.setChildrenUnneeded();
+ qq->watchHandler()->insertData(data);
+ return;
+ }
+
+ if (data.isChildCountNeeded()) {
+ int numChild = 0;
+ QScriptValueIterator it(data.scriptValue);
+ while (it.hasNext()) {
+ it.next();
+ ++numChild;
+ }
+ data.setChildCount(numChild);
+ //qDebug() << " ... CHILDCOUNT: " << numChild;
+ qq->watchHandler()->insertData(data);
+ return;
+ }
+
+ QWB_ASSERT(false, return);
+}
+
+IDebuggerEngine *createScriptEngine(DebuggerManager *parent)
+{
+ return new ScriptEngine(parent);
+}
+
diff --git a/src/plugins/debugger/scriptengine.h b/src/plugins/debugger/scriptengine.h
new file mode 100644
index 0000000000..69cb440570
--- /dev/null
+++ b/src/plugins/debugger/scriptengine.h
@@ -0,0 +1,134 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_SCRIPTENGINE_H
+#define DEBUGGER_SCRIPTENGINE_H
+
+#include <QtCore/QByteArray>
+#include <QtCore/QHash>
+#include <QtCore/QMap>
+#include <QtCore/QObject>
+#include <QtCore/QProcess>
+#include <QtCore/QPoint>
+#include <QtCore/QSet>
+#include <QtCore/QVariant>
+
+#include <QtNetwork/QLocalSocket>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QAbstractItemModel;
+class QSplitter;
+class QToolBar;
+class QScriptEngine;
+class QScriptValue;
+QT_END_NAMESPACE
+
+#include "idebuggerengine.h"
+
+namespace Debugger {
+namespace Internal {
+
+class DebuggerManager;
+class IDebuggerManagerAccessForEngines;
+class ScriptAgent;
+class WatchData;
+
+class ScriptEngine : public IDebuggerEngine
+{
+ Q_OBJECT
+
+public:
+ ScriptEngine(DebuggerManager *parent);
+ ~ScriptEngine();
+
+private:
+ // IDebuggerEngine implementation
+ void stepExec();
+ void stepOutExec();
+ void nextExec();
+ void stepIExec();
+ void nextIExec();
+
+ void shutdown();
+ void setToolTipExpression(const QPoint &pos, const QString &exp);
+ bool startDebugger();
+ void exitDebugger();
+
+ void continueInferior();
+ void runInferior();
+ void interruptInferior();
+
+ void runToLineExec(const QString &fileName, int lineNumber);
+ void runToFunctionExec(const QString &functionName);
+ void jumpToLineExec(const QString &fileName, int lineNumber);
+
+ void activateFrame(int index);
+ void selectThread(int index);
+
+ void attemptBreakpointSynchronization();
+
+ void loadSessionData() {}
+ void saveSessionData() {}
+
+ void assignValueInDebugger(const QString &expr, const QString &value);
+ void executeDebuggerCommand(const QString & command);
+
+ void loadSymbols(const QString &moduleName);
+ void loadAllSymbols();
+ void reloadDisassembler();
+ void reloadModules();
+ void reloadRegisters() {}
+
+ bool supportsThreads() const { return true; }
+ void maybeBreakNow(bool byFunction);
+ void updateWatchModel();
+ void updateSubItem(const WatchData &data0);
+
+private:
+ friend class ScriptAgent;
+ DebuggerManager *q;
+ IDebuggerManagerAccessForEngines *qq;
+
+ QScriptEngine *m_scriptEngine;
+ QString m_scriptContents;
+ QString m_scriptFileName;
+ ScriptAgent *m_scriptAgent;
+
+ bool m_stopped;
+ bool m_stopOnNextLine;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_SCRIPTENGINE_H
diff --git a/src/plugins/debugger/stackhandler.cpp b/src/plugins/debugger/stackhandler.cpp
new file mode 100644
index 0000000000..d05e259c09
--- /dev/null
+++ b/src/plugins/debugger/stackhandler.cpp
@@ -0,0 +1,271 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "stackhandler.h"
+
+#include "assert.h"
+
+#include <QtCore/QAbstractTableModel>
+#include <QtCore/QDebug>
+#include <QtCore/QFileInfo>
+
+using namespace Debugger::Internal;
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// StackHandler
+//
+////////////////////////////////////////////////////////////////////////
+
+StackHandler::StackHandler(QObject *parent)
+ : QAbstractTableModel(parent), m_currentIndex(0)
+{
+ m_emptyIcon = QIcon(":/gdbdebugger/images/empty.svg");
+ m_positionIcon = QIcon(":/gdbdebugger/images/location.svg");
+}
+
+int StackHandler::rowCount(const QModelIndex &parent) const
+{
+ // Since the stack is not a tree, row count is 0 for any valid parent
+ return parent.isValid() ? 0 : m_stackFrames.size();
+}
+
+int StackHandler::columnCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : 4;
+}
+
+QVariant StackHandler::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || index.row() >= m_stackFrames.size())
+ return QVariant();
+
+ const StackFrame &frame = m_stackFrames.at(index.row());
+
+ if (role == Qt::DisplayRole) {
+ switch (index.column()) {
+ case 0: // Stack frame level
+ return QString::number(frame.level);
+ case 1: // Function name
+ return frame.function;
+ case 2: // File name
+ return frame.file.isEmpty() ? frame.from : QFileInfo(frame.file).fileName();
+ case 3: // Line number
+ return frame.line;
+ case 4: // Address
+ return frame.address;
+ }
+ } else if (role == Qt::ToolTipRole) {
+ return "<table><tr><td>Address:</td><td>" + frame.address + "</td></tr>"
+ + "<tr><td>Function: </td><td>" + frame.function + "</td></tr>"
+ + "<tr><td>File: </td><td>" + frame.file + "</td></tr>"
+ + "<tr><td>Line: </td><td>" + QString::number(frame.line) + "</td></tr>"
+ + "<tr><td>From: </td><td>" + frame.from + "</td></tr></table>"
+ + "<tr><td>To: </td><td>" + frame.to + "</td></tr></table>";
+ } else if (role == Qt::DecorationRole && index.column() == 0) {
+ // Return icon that indicates whether this is the active stack frame
+ return (index.row() == m_currentIndex) ? m_positionIcon : m_emptyIcon;
+ }
+
+ return QVariant();
+}
+
+QVariant StackHandler::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ static const char * const headers[] = {
+ QT_TR_NOOP("Level"),
+ QT_TR_NOOP("Function"),
+ QT_TR_NOOP("File"),
+ QT_TR_NOOP("Line"),
+ QT_TR_NOOP("Address")
+ };
+ if (section < 5)
+ return tr(headers[section]);
+ }
+ return QVariant();
+}
+
+Qt::ItemFlags StackHandler::flags(const QModelIndex &index) const
+{
+ if (index.row() >= m_stackFrames.size())
+ return 0;
+ const StackFrame &frame = m_stackFrames.at(index.row());
+ const bool isValid = !frame.file.isEmpty() && !frame.function.isEmpty();
+ return isValid ? QAbstractTableModel::flags(index) : Qt::ItemFlags(0);
+}
+
+StackFrame StackHandler::currentFrame() const
+{
+ QWB_ASSERT(m_currentIndex >= 0, return StackFrame());
+ QWB_ASSERT(m_currentIndex < m_stackFrames.size(), return StackFrame());
+ return m_stackFrames.at(m_currentIndex);
+}
+
+void StackHandler::setCurrentIndex(int level)
+{
+ if (level == m_currentIndex)
+ return;
+
+ // Emit changed for previous frame
+ QModelIndex i = index(m_currentIndex, 0);
+ emit dataChanged(i, i);
+
+ m_currentIndex = level;
+
+ // Emit changed for new frame
+ i = index(m_currentIndex, 0);
+ emit dataChanged(i, i);
+}
+
+void StackHandler::removeAll()
+{
+ m_stackFrames.clear();
+ m_currentIndex = 0;
+ reset();
+}
+
+void StackHandler::setFrames(const QList<StackFrame> &frames)
+{
+ m_stackFrames = frames;
+ if (m_currentIndex >= m_stackFrames.size())
+ m_currentIndex = m_stackFrames.size() - 1;
+ reset();
+}
+
+QList<StackFrame> StackHandler::frames() const
+{
+ return m_stackFrames;
+}
+
+bool StackHandler::isDebuggingDumpers() const
+{
+ for (int i = m_stackFrames.size(); --i >= 0; )
+ if (m_stackFrames.at(i).function.startsWith("qDumpObjectData"))
+ return true;
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// ThreadsHandler
+//
+////////////////////////////////////////////////////////////////////////
+
+ThreadsHandler::ThreadsHandler(QObject *parent)
+ : QAbstractTableModel(parent), m_currentIndex(0)
+{
+ m_emptyIcon = QIcon(":/gdbdebugger/images/empty.svg");
+ m_positionIcon = QIcon(":/gdbdebugger/images/location.svg");
+}
+
+int ThreadsHandler::rowCount(const QModelIndex &parent) const
+{
+ // Since the stack is not a tree, row count is 0 for any valid parent
+ return parent.isValid() ? 0 : m_threads.size();
+}
+
+int ThreadsHandler::columnCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : 1;
+}
+
+QVariant ThreadsHandler::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || index.row() >= m_threads.size())
+ return QVariant();
+
+ if (role == Qt::DisplayRole) {
+ switch (index.column()) {
+ case 0: // Thread ID
+ return m_threads.at(index.row()).id;
+ case 1: // Function name
+ return "???";
+ }
+ } else if (role == Qt::ToolTipRole) {
+ return "Thread: " + QString::number(m_threads.at(index.row()).id);
+ } else if (role == Qt::DecorationRole && index.column() == 0) {
+ // Return icon that indicates whether this is the active stack frame
+ return (index.row() == m_currentIndex) ? m_positionIcon : m_emptyIcon;
+ }
+
+ return QVariant();
+}
+
+QVariant ThreadsHandler::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ static const char * const headers[] = {
+ QT_TR_NOOP("Thread ID"),
+ };
+ if (section < 1)
+ return tr(headers[section]);
+ }
+ return QVariant();
+}
+
+void ThreadsHandler::setCurrentThread(int index)
+{
+ if (index == m_currentIndex)
+ return;
+
+ // Emit changed for previous frame
+ QModelIndex i = ThreadsHandler::index(m_currentIndex, 0);
+ emit dataChanged(i, i);
+
+ m_currentIndex = index;
+
+ // Emit changed for new frame
+ i = ThreadsHandler::index(m_currentIndex, 0);
+ emit dataChanged(i, i);
+}
+
+void ThreadsHandler::setThreads(const QList<ThreadData> &threads)
+{
+ m_threads = threads;
+ if (m_currentIndex >= m_threads.size())
+ m_currentIndex = m_threads.size() - 1;
+ reset();
+}
+
+QList<ThreadData> ThreadsHandler::threads() const
+{
+ return m_threads;
+}
+
+void ThreadsHandler::removeAll()
+{
+ m_threads.clear();
+ m_currentIndex = 0;
+ reset();
+}
diff --git a/src/plugins/debugger/stackhandler.h b/src/plugins/debugger/stackhandler.h
new file mode 100644
index 0000000000..81a4686f68
--- /dev/null
+++ b/src/plugins/debugger/stackhandler.h
@@ -0,0 +1,136 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_STACKHANDLER_H
+#define DEBUGGER_STACKHANDLER_H
+
+#include <QtCore/QAbstractTableModel>
+#include <QtCore/QObject>
+
+#include <QtGui/QIcon>
+
+namespace Debugger {
+namespace Internal {
+
+////////////////////////////////////////////////////////////////////////
+//
+// StackModel
+//
+////////////////////////////////////////////////////////////////////////
+
+struct StackFrame
+{
+ int level;
+ QString function;
+ QString file; // we try to put an absolute file name in there
+ QString from;
+ QString to;
+ int line;
+ QString address;
+};
+
+/*! A model to represent the stack in a QTreeView. */
+class StackHandler : public QAbstractTableModel
+{
+public:
+ StackHandler(QObject *parent = 0);
+
+ void setFrames(const QList<StackFrame> &frames);
+ QList<StackFrame> frames() const;
+ void setCurrentIndex(int index);
+ int currentIndex() const { return m_currentIndex; }
+ StackFrame currentFrame() const;
+ int stackSize() const { return m_stackFrames.size(); }
+
+ // Called from StackHandler after a new stack list has been received
+ void removeAll();
+ QAbstractItemModel *stackModel() { return this; }
+ bool isDebuggingDumpers() const;
+
+private:
+ // QAbstractTableModel
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ QList<StackFrame> m_stackFrames;
+ int m_currentIndex;
+ QIcon m_positionIcon;
+ QIcon m_emptyIcon;
+};
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// ThreadsHandler
+//
+////////////////////////////////////////////////////////////////////////
+
+struct ThreadData
+{
+ int id;
+};
+
+/*! A model to represent the running threads in a QTreeView or ComboBox */
+class ThreadsHandler : public QAbstractTableModel
+{
+public:
+ ThreadsHandler(QObject *parent = 0);
+
+ void setCurrentThread(int index);
+ void selectThread(int index);
+ void setThreads(const QList<ThreadData> &threads);
+ void removeAll();
+ QList<ThreadData> threads() const;
+ QAbstractItemModel *threadsModel() { return this; }
+
+private:
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+
+private:
+ friend class StackHandler;
+ QList<ThreadData> m_threads;
+ int m_currentIndex;
+ QIcon m_positionIcon;
+ QIcon m_emptyIcon;
+};
+
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_STACKHANDLER_H
diff --git a/src/plugins/debugger/stackwindow.cpp b/src/plugins/debugger/stackwindow.cpp
new file mode 100644
index 0000000000..3dc1209a00
--- /dev/null
+++ b/src/plugins/debugger/stackwindow.cpp
@@ -0,0 +1,117 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "stackwindow.h"
+
+#include "assert.h"
+#include "stackhandler.h"
+
+#include <QAction>
+#include <QComboBox>
+#include <QDebug>
+#include <QHeaderView>
+#include <QMenu>
+#include <QResizeEvent>
+#include <QTreeView>
+#include <QVBoxLayout>
+
+using Debugger::Internal::StackWindow;
+
+StackWindow::StackWindow(QWidget *parent)
+ : QTreeView(parent), m_alwaysResizeColumnsToContents(false)
+{
+ setWindowTitle(tr("Stack"));
+
+ setAlternatingRowColors(true);
+ setRootIsDecorated(false);
+ setIconSize(QSize(10, 10));
+
+ header()->setDefaultAlignment(Qt::AlignLeft);
+
+ connect(this, SIGNAL(activated(QModelIndex)),
+ this, SLOT(rowActivated(QModelIndex)));
+}
+
+void StackWindow::resizeEvent(QResizeEvent *event)
+{
+ QHeaderView *hv = header();
+ int totalSize = event->size().width() - 120;
+ if (totalSize > 10) {
+ hv->resizeSection(0, 45);
+ hv->resizeSection(1, totalSize / 2);
+ hv->resizeSection(2, totalSize / 2);
+ hv->resizeSection(3, 55);
+ }
+ QTreeView::resizeEvent(event);
+}
+
+void StackWindow::rowActivated(const QModelIndex &index)
+{
+ //qDebug() << "ACTIVATED: " << index.row() << index.column();
+ emit frameActivated(index.row());
+}
+
+void StackWindow::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QMenu menu;
+ QAction *act1 = new QAction(tr("Adjust column widths to contents"), &menu);
+ QAction *act2 = new QAction(tr("Always adjust column widths to contents"), &menu);
+ act2->setCheckable(true);
+ act2->setChecked(m_alwaysResizeColumnsToContents);
+
+ menu.addAction(act1);
+ menu.addAction(act2);
+
+ QAction *act = menu.exec(ev->globalPos());
+
+ if (act == act1)
+ resizeColumnsToContents();
+ else if (act == act2)
+ setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
+}
+
+void StackWindow::resizeColumnsToContents()
+{
+ resizeColumnToContents(0);
+ resizeColumnToContents(1);
+ resizeColumnToContents(2);
+}
+
+void StackWindow::setAlwaysResizeColumnsToContents(bool on)
+{
+ m_alwaysResizeColumnsToContents = on;
+ QHeaderView::ResizeMode mode = on
+ ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
+ header()->setResizeMode(0, mode);
+ header()->setResizeMode(1, mode);
+ header()->setResizeMode(2, mode);
+}
diff --git a/src/plugins/debugger/stackwindow.h b/src/plugins/debugger/stackwindow.h
new file mode 100644
index 0000000000..5edfeb3fb6
--- /dev/null
+++ b/src/plugins/debugger/stackwindow.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_STACKWINDOW_H
+#define DEBUGGER_STACKWINDOW_H
+
+#include <QtGui/QTreeView>
+#include <QtGui/QWidget>
+
+QT_BEGIN_NAMESPACE
+class QComboBox;
+class QModelIndex;
+QT_END_NAMESPACE
+
+namespace Debugger {
+namespace Internal {
+
+class StackWindow : public QTreeView
+{
+ Q_OBJECT
+
+public:
+ StackWindow(QWidget *parent = 0);
+
+signals:
+ void frameActivated(int);
+
+public slots:
+ void resizeColumnsToContents();
+ void setAlwaysResizeColumnsToContents(bool on);
+
+private slots:
+ void rowActivated(const QModelIndex &index);
+
+private:
+ void resizeEvent(QResizeEvent *ev);
+ void contextMenuEvent(QContextMenuEvent *ev);
+
+ bool m_alwaysResizeColumnsToContents;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_STACKWINDOW_H
+
diff --git a/src/plugins/debugger/startexternaldialog.cpp b/src/plugins/debugger/startexternaldialog.cpp
new file mode 100644
index 0000000000..c87982ab1d
--- /dev/null
+++ b/src/plugins/debugger/startexternaldialog.cpp
@@ -0,0 +1,124 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "startexternaldialog.h"
+
+#include <QtGui/QFileDialog>
+#include <QtGui/QPushButton>
+
+using namespace Debugger::Internal;
+
+StartExternalDialog::StartExternalDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ setupUi(this);
+ buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
+
+ //execLabel->setHidden(false);
+ //execEdit->setHidden(false);
+ //browseButton->setHidden(false);
+
+ execLabel->setText(tr("Executable:"));
+ argLabel->setText(tr("Arguments:"));
+
+ connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
+ connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+
+ connect(browseButton, SIGNAL(clicked()),
+ this, SLOT(onBrowseButton()));
+}
+
+void StartExternalDialog::setExecutableFile(const QString &str)
+{
+ execEdit->setText(str);
+}
+
+void StartExternalDialog::setExecutableArguments(const QString &str)
+{
+ argsEdit->setText(str);
+}
+
+QString StartExternalDialog::executableFile() const
+{
+ return execEdit->text();
+}
+
+QString StartExternalDialog::executableArguments() const
+{
+ return argsEdit->text();
+ /*
+ bool inQuotes = false;
+ QString args = argsEdit->text();
+ QChar current;
+ QChar last;
+ QString arg;
+
+ QStringList result;
+ if (!args.isEmpty())
+ result << QLatin1String("--args");
+ result << execEdit->text();
+
+ for(int i=0; i<args.length(); ++i) {
+ current = args.at(i);
+
+ if (current == QLatin1Char('\"') && last != QLatin1Char('\\')) {
+ if (inQuotes && !arg.isEmpty()) {
+ result << arg;
+ arg.clear();
+ }
+ inQuotes = !inQuotes;
+ } else if (!inQuotes && current == QLatin1Char(' ')) {
+ arg = arg.trimmed();
+ if (!arg.isEmpty()) {
+ result << arg;
+ arg.clear();
+ }
+ } else {
+ arg += current;
+ }
+
+ last = current;
+ }
+
+ if (!arg.isEmpty())
+ result << arg;
+
+ return result;
+ */
+}
+
+void StartExternalDialog::onBrowseButton()
+{
+ QString fileName = QFileDialog::getOpenFileName(this, tr("Select Executable"),
+ execEdit->text());
+ execEdit->setText(fileName);
+}
diff --git a/src/plugins/debugger/startexternaldialog.h b/src/plugins/debugger/startexternaldialog.h
new file mode 100644
index 0000000000..10a43670db
--- /dev/null
+++ b/src/plugins/debugger/startexternaldialog.h
@@ -0,0 +1,63 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_STARTEXTERNALDIALOG_H
+#define DEBUGGER_STARTEXTERNALDIALOG_H
+
+#include <QtGui/QDialog>
+
+#include "ui_startexternaldialog.h"
+
+namespace Debugger {
+namespace Internal {
+
+class StartExternalDialog : public QDialog, Ui::StartExternalDialog
+{
+ Q_OBJECT
+
+public:
+ StartExternalDialog(QWidget *parent);
+
+ void setExecutableFile(const QString &executable);
+ void setExecutableArguments(const QString &args);
+
+ QString executableFile() const;
+ QString executableArguments() const;
+
+private slots:
+ void onBrowseButton();
+};
+
+} // namespace Debugger
+} // namespace Internal
+
+#endif // DEBUGGER_STARTEXTERNALDIALOG_H
diff --git a/src/plugins/debugger/startexternaldialog.ui b/src/plugins/debugger/startexternaldialog.ui
new file mode 100644
index 0000000000..7888db2a3e
--- /dev/null
+++ b/src/plugins/debugger/startexternaldialog.ui
@@ -0,0 +1,93 @@
+<ui version="4.0" >
+ <class>StartExternalDialog</class>
+ <widget class="QDialog" name="StartExternalDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>425</width>
+ <height>127</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Start Debugger</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <item>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="1" column="1" >
+ <widget class="QLineEdit" name="argsEdit" />
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLineEdit" name="execEdit" />
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="execLabel" >
+ <property name="text" >
+ <string>Executable:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" >
+ <widget class="QToolButton" name="browseButton" >
+ <property name="text" >
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="argLabel" >
+ <property name="text" >
+ <string>Arguments:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>407</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="Line" name="line" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/debugger/threadswindow.cpp b/src/plugins/debugger/threadswindow.cpp
new file mode 100644
index 0000000000..beffda84cb
--- /dev/null
+++ b/src/plugins/debugger/threadswindow.cpp
@@ -0,0 +1,112 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "threadswindow.h"
+
+#include "assert.h"
+#include "stackhandler.h"
+
+#include <QAction>
+#include <QComboBox>
+#include <QDebug>
+#include <QHeaderView>
+#include <QMenu>
+#include <QResizeEvent>
+#include <QTreeView>
+#include <QVBoxLayout>
+
+using Debugger::Internal::ThreadsWindow;
+
+ThreadsWindow::ThreadsWindow(QWidget *parent)
+ : QTreeView(parent), m_alwaysResizeColumnsToContents(false)
+{
+ setWindowTitle(tr("Thread"));
+
+ setAlternatingRowColors(true);
+ setRootIsDecorated(false);
+ setIconSize(QSize(10, 10));
+
+ header()->setDefaultAlignment(Qt::AlignLeft);
+
+ connect(this, SIGNAL(activated(const QModelIndex &)),
+ this, SLOT(rowActivated(const QModelIndex &)));
+}
+
+void ThreadsWindow::resizeEvent(QResizeEvent *event)
+{
+ //QHeaderView *hv = header();
+ //int totalSize = event->size().width() - 120;
+ //hv->resizeSection(0, 45);
+ //hv->resizeSection(1, totalSize);
+ //hv->resizeSection(2, 55);
+ QTreeView::resizeEvent(event);
+}
+
+void ThreadsWindow::rowActivated(const QModelIndex &index)
+{
+ //qDebug() << "ACTIVATED: " << index.row() << index.column();
+ emit threadSelected(index.row());
+}
+
+void ThreadsWindow::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QMenu menu;
+ QAction *act1 = new QAction(tr("Adjust column widths to contents"), &menu);
+ QAction *act2 = new QAction(tr("Always adjust column widths to contents"), &menu);
+ act2->setCheckable(true);
+ act2->setChecked(m_alwaysResizeColumnsToContents);
+ menu.addAction(act1);
+ menu.addAction(act2);
+
+ QAction *act = menu.exec(ev->globalPos());
+
+ if (act == act1)
+ resizeColumnsToContents();
+ else if (act == act2)
+ setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
+}
+
+void ThreadsWindow::resizeColumnsToContents()
+{
+ resizeColumnToContents(0);
+ //resizeColumnToContents(1);
+}
+
+void ThreadsWindow::setAlwaysResizeColumnsToContents(bool on)
+{
+ m_alwaysResizeColumnsToContents = on;
+ QHeaderView::ResizeMode mode = on
+ ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
+ header()->setResizeMode(0, mode);
+ //header()->setResizeMode(1, mode);
+}
+
diff --git a/src/plugins/debugger/threadswindow.h b/src/plugins/debugger/threadswindow.h
new file mode 100644
index 0000000000..05b94dc531
--- /dev/null
+++ b/src/plugins/debugger/threadswindow.h
@@ -0,0 +1,68 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_THREADWINDOW_H
+#define DEBUGGER_THREADWINDOW_H
+
+#include <QtGui/QTreeView>
+
+namespace Debugger {
+namespace Internal {
+
+class ThreadsWindow : public QTreeView
+{
+ Q_OBJECT
+
+public:
+ ThreadsWindow(QWidget *parent = 0);
+
+signals:
+ void threadSelected(int);
+
+public slots:
+ void resizeColumnsToContents();
+ void setAlwaysResizeColumnsToContents(bool on);
+
+private slots:
+ void rowActivated(const QModelIndex &index);
+
+private:
+ void resizeEvent(QResizeEvent *ev);
+ void contextMenuEvent(QContextMenuEvent *ev);
+
+ bool m_alwaysResizeColumnsToContents;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_THREADWINDOW_H
diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp
new file mode 100644
index 0000000000..9147aa308e
--- /dev/null
+++ b/src/plugins/debugger/watchhandler.cpp
@@ -0,0 +1,1137 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "watchhandler.h"
+
+#if USE_MODEL_TEST
+#include "modeltest.h"
+#endif
+
+#include "assert.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QEvent>
+
+#include <QtGui/QApplication>
+#include <QtGui/QLabel>
+#include <QtGui/QToolTip>
+#include <QtGui/QTextEdit>
+
+#include <ctype.h>
+
+// creates debug output regarding pending watch data results
+//#define DEBUG_PENDING 1
+// creates debug output for accesses to the itemmodel
+//#define DEBUG_MODEL 1
+
+#if DEBUG_MODEL
+# define MODEL_DEBUG(s) qDebug() << s
+#else
+# define MODEL_DEBUG(s)
+#endif
+#define MODEL_DEBUGX(s) qDebug() << s
+
+using namespace Debugger::Internal;
+
+static const QString strNotInScope = QLatin1String("<not in scope>");
+
+static bool isIntOrFloatType(const QString &type)
+{
+ static const QStringList types = QStringList()
+ << "char" << "int" << "short" << "float" << "double" << "long"
+ << "bool" << "signed char" << "unsigned" << "unsigned char"
+ << "unsigned int" << "unsigned long" << "long long";
+ return types.contains(type);
+}
+
+static bool isPointerType(const QString &type)
+{
+ return type.endsWith("*") || type.endsWith("* const");
+}
+
+static QString htmlQuote(const QString &str0)
+{
+ QString str = str0;
+ str.replace('&', "&amp;");
+ str.replace('<', "&lt;");
+ str.replace('>', "&gt;");
+ return str;
+}
+
+////////////////////////////////////////////////////////////////////
+//
+// WatchData
+//
+////////////////////////////////////////////////////////////////////
+
+WatchData::WatchData()
+{
+ valuedisabled = false;
+ state = InitialState;
+ childCount = -1;
+ parentIndex = -1;
+ row = -1;
+ level = -1;
+ changed = false;
+}
+
+void WatchData::setError(const QString &msg)
+{
+ setAllUnneeded();
+ value = msg;
+ setChildCount(0);
+ valuedisabled = true;
+}
+
+static QByteArray quoteUnprintable(const QByteArray &ba)
+{
+ QByteArray res;
+ char buf[10];
+ for (int i = 0, n = ba.size(); i != n; ++i) {
+ char c = ba.at(i);
+ if (isprint(c)) {
+ res += c;
+ } else {
+ qsnprintf(buf, sizeof(buf) - 1, "\\%x", int(c));
+ res += buf;
+ }
+ }
+ return res;
+}
+
+void WatchData::setValue(const QByteArray &value0)
+{
+ value = quoteUnprintable(value0);
+ if (value == "{...}") {
+ value.clear();
+ childCount = 1; // at least one...
+ }
+
+ // avoid duplicated information
+ if (value.startsWith("(") && value.contains(") 0x"))
+ value = value.mid(value.lastIndexOf(") 0x") + 2);
+
+ // doubles are sometimes displayed as "@0x6141378: 1.2".
+ // I don't want that.
+ if (/*isIntOrFloatType(type) && */ value.startsWith("@0x")
+ && value.contains(':')) {
+ value = value.mid(value.indexOf(':') + 2);
+ setChildCount(0);
+ }
+
+ // "numchild" is sometimes lying
+ //MODEL_DEBUG("\n\n\nPOINTER: " << type << value);
+ if (isPointerType(type))
+ setChildCount(value != "0x0" && value != "<null>");
+
+ // pointer type information is available in the 'type'
+ // column. No need to duplicate it here.
+ if (value.startsWith("(" + type + ") 0x"))
+ value = value.section(" ", -1, -1);
+
+ setValueUnneeded();
+}
+
+void WatchData::setValueToolTip(const QString &tooltip)
+{
+ valuetooltip = tooltip;
+}
+
+void WatchData::setType(const QString &str)
+{
+ type = str.trimmed();
+ bool changed = true;
+ while (changed) {
+ if (type.endsWith("const"))
+ type.chop(5);
+ else if (type.endsWith(" "))
+ type.chop(1);
+ else if (type.endsWith("&"))
+ type.chop(1);
+ else if (type.startsWith("const "))
+ type = type.mid(6);
+ else if (type.startsWith("volatile "))
+ type = type.mid(9);
+ else if (type.startsWith("class "))
+ type = type.mid(6);
+ else if (type.startsWith("struct "))
+ type = type.mid(6);
+ else if (type.startsWith(" "))
+ type = type.mid(1);
+ else
+ changed = false;
+ }
+ setTypeUnneeded();
+ if (isIntOrFloatType(type))
+ setChildCount(0);
+}
+
+void WatchData::setAddress(const QString & str)
+{
+ addr = str;
+}
+
+QString WatchData::toString() const
+{
+ QString res = "{";
+
+ res += "level=\"" + QString::number(level) + "\",";
+ res += "parent=\"" + QString::number(parentIndex) + "\",";
+ res += "row=\"" + QString::number(row) + "\",";
+ res += "child=\"";
+ foreach (int index, childIndex)
+ res += QString::number(index) + ",";
+ if (res.endsWith(','))
+ res[res.size() - 1] = '"';
+ else
+ res += '"';
+ res += ",";
+
+
+ if (!iname.isEmpty())
+ res += "iname=\"" + iname + "\",";
+ if (!exp.isEmpty())
+ res += "exp=\"" + exp + "\",";
+
+ if (!variable.isEmpty())
+ res += "variable=\"" + variable + "\",";
+
+ if (isValueNeeded())
+ res += "value=<needed>,";
+ if (isValueKnown() && !value.isEmpty())
+ res += "value=\"" + value + "\",";
+
+ if (!editvalue.isEmpty())
+ res += "editvalue=\"" + editvalue + "\",";
+
+ if (isTypeNeeded())
+ res += "type=<needed>,";
+ if (isTypeKnown() && !type.isEmpty())
+ res += "type=\"" + type + "\",";
+
+ if (isChildCountNeeded())
+ res += "numchild=<needed>,";
+ if (isChildCountKnown() && childCount == -1)
+ res += "numchild=\"" + QString::number(childCount) + "\",";
+
+ if (isChildrenNeeded())
+ res += "children=<needed>,";
+
+ if (res.endsWith(','))
+ res[res.size() - 1] = '}';
+ else
+ res += '}';
+
+ return res;
+}
+
+
+static bool iNameSorter(const WatchData &d1, const WatchData &d2)
+{
+ if (d1.level != d2.level)
+ return d1.level < d2.level;
+
+ for (int level = 0; level != d1.level; ++level) {
+ QString name1 = d1.iname.section('.', level, level);
+ QString name2 = d2.iname.section('.', level, level);
+ //MODEL_DEBUG(" SORT: " << name1 << name2 << (name1 < name2));
+
+ if (name1 != name2) {
+ // This formerly used inames. in this case 'lastIndexOf' probably
+ // makes more sense.
+ if (name1.startsWith('[') && name2.startsWith('[')) {
+ return name1.mid(1, name1.indexOf(']') - 1).toInt()
+ < name2.mid(1, name2.indexOf(']') - 1).toInt();
+ // numbers should be sorted according to their numerical value
+ //int pos = d1.name.lastIndexOf('.');
+ //if (pos != -1 && pos + 1 != d1.name.size() && d1.name.at(pos + 1).isDigit())
+ // return d1.name.size() < d2.name.size();
+ // fall through
+ }
+ return name1 < name2;
+ }
+ }
+ return false;
+}
+
+static QString parentName(const QString &iname)
+{
+ int pos = iname.lastIndexOf(".");
+ if (pos == -1)
+ return QString();
+ return iname.left(pos);
+}
+
+
+static void insertDataHelper(QList<WatchData> &list, const WatchData &data)
+{
+ // FIXME: Quadratic algorithm
+ for (int i = list.size(); --i >= 0; ) {
+ if (list.at(i).iname == data.iname) {
+ list[i] = data;
+ return;
+ }
+ }
+ list.append(data);
+}
+
+static WatchData take(const QString &iname, QList<WatchData> *list)
+{
+ for (int i = list->size(); --i >= 0;) {
+ if (list->at(i).iname == iname) {
+ WatchData res = list->at(i);
+ (*list)[i] = list->back();
+ (void) list->takeLast();
+ return res;
+ //return list->takeAt(i);
+ }
+ }
+ return WatchData();
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// WatchHandler
+//
+///////////////////////////////////////////////////////////////////////
+
+WatchHandler::WatchHandler()
+{
+ m_expandPointers = true;
+ m_inFetchMore = false;
+ m_inChange = false;
+
+ cleanModel();
+ m_displaySet = m_completeSet;
+}
+
+bool WatchHandler::setData(const QModelIndex &idx,
+ const QVariant &value, int role)
+{
+/*
+ Q_UNUSED(idx);
+ Q_UNUSED(value);
+ Q_UNUSED(role);
+ if (role == VisualRole) {
+ QString iname = inameFromIndex(index);
+ setDisplayedIName(iname, value.toBool());
+ return true;
+ }
+ return true;
+*/
+ return QAbstractItemModel::setData(idx, value, role);
+}
+
+static QString niceType(QString type)
+{
+ if (type.contains("std::")) {
+ static QRegExp re("std::vector<(.*)\\s*,std::allocator<(.*)>\\s*>");
+ re.setMinimal(true);
+
+ type.replace("std::basic_string<char, std::char_traits<char>, "
+ "std::allocator<char> >", "std::string");
+ type.replace("std::basic_string<wchar_t, std::char_traits<wchar_t>, "
+ "std::allocator<wchar_t> >", "std::wstring");
+
+ for (int i = 0; i != 10; ++i) {
+ if (re.indexIn(type) == -1 || re.cap(1) != re.cap(2))
+ break;
+ type.replace(re.cap(0), "std::vector<" + re.cap(1) + ">");
+ }
+
+ type.replace(" >", ">");
+ }
+ return type;
+}
+
+QVariant WatchHandler::data(const QModelIndex &idx, int role) const
+{
+ int node = idx.internalId();
+ if (node < 0)
+ return QVariant();
+
+ const WatchData &data = m_displaySet.at(node);
+
+ switch (role) {
+ case Qt::DisplayRole: {
+ switch (idx.column()) {
+ case 0: return data.name;
+ case 1: return data.value;
+ case 2: return niceType(data.type);
+ default: break;
+ }
+ break;
+ }
+
+ case Qt::ToolTipRole: {
+ QString val = data.value;
+ if (val.size() > 1000)
+ val = val.left(1000) + " ... <cut off>";
+
+ QString tt = "<table>";
+ //tt += "<tr><td>internal name</td><td> : </td><td>";
+ //tt += htmlQuote(iname) + "</td></tr>";
+ tt += "<tr><td>expression</td><td> : </td><td>";
+ tt += htmlQuote(data.exp) + "</td></tr>";
+ tt += "<tr><td>type</td><td> : </td><td>";
+ tt += htmlQuote(data.type) + "</td></tr>";
+ //if (!valuetooltip.isEmpty())
+ // tt += valuetooltip;
+ //else
+ tt += "<tr><td>value</td><td> : </td><td>";
+ tt += htmlQuote(data.value) + "</td></tr>";
+ tt += "<tr><td>addr</td><td> : </td><td>";
+ tt += htmlQuote(data.addr) + "</td></tr>";
+ tt += "<tr><td>iname</td><td> : </td><td>";
+ tt += htmlQuote(data.iname) + "</td></tr>";
+ tt += "</table>";
+ tt.replace("@value@", htmlQuote(data.value));
+
+ if (tt.size() > 10000)
+ tt = tt.left(10000) + " ... <cut off>";
+ return tt;
+ }
+
+ case Qt::ForegroundRole: {
+ static const QVariant red(QColor(200, 0, 0));
+ static const QVariant black(QColor(0, 0, 0));
+ static const QVariant gray(QColor(140, 140, 140));
+ switch (idx.column()) {
+ case 0: return black;
+ case 1: return data.valuedisabled ? gray : data.changed ? red : black;
+ case 2: return black;
+ }
+ break;
+ }
+
+ case INameRole:
+ return data.iname;
+
+ case VisualRole:
+ return m_displayedINames.contains(data.iname);
+
+ default:
+ break;
+ }
+ return QVariant();
+}
+
+Qt::ItemFlags WatchHandler::flags(const QModelIndex &idx) const
+{
+ using namespace Qt;
+
+ if (!idx.isValid())
+ return ItemFlags();
+
+ int node = idx.internalId();
+ if (node < 0)
+ return ItemFlags();
+
+ // enabled, editable, selectable, checkable, and can be used both as the
+ // source of a drag and drop operation and as a drop target.
+
+ static const ItemFlags DefaultNotEditable =
+ ItemIsSelectable
+ | ItemIsDragEnabled
+ | ItemIsDropEnabled
+ // | ItemIsUserCheckable
+ // | ItemIsTristate
+ | ItemIsEnabled;
+
+ static const ItemFlags DefaultEditable =
+ DefaultNotEditable | ItemIsEditable;
+
+ const WatchData &data = m_displaySet.at(node);
+ return idx.column() == 1 &&
+ data.isWatcher() ? DefaultEditable : DefaultNotEditable;
+}
+
+QVariant WatchHandler::headerData(int section, Qt::Orientation orientation,
+ int role) const
+{
+ if (orientation == Qt::Vertical)
+ return QVariant();
+ if (role == Qt::DisplayRole) {
+ switch (section) {
+ case 0: return tr("Name") + " ";
+ case 1: return tr("Value") + " ";
+ case 2: return tr("Type") + " ";
+ }
+ }
+ return QVariant();
+}
+
+QString WatchHandler::toString() const
+{
+ QString res;
+ res += "\nIncomplete:\n";
+ for (int i = 0, n = m_incompleteSet.size(); i != n; ++i) {
+ res += QString("%1: ").arg(i);
+ res += m_incompleteSet.at(i).toString();
+ res += '\n';
+ }
+ res += "\nComplete:\n";
+ for (int i = 0, n = m_completeSet.size(); i != n; ++i) {
+ res += QString("%1: ").arg(i);
+ res += m_completeSet.at(i).toString();
+ res += '\n';
+ }
+ res += "\nDisplay:\n";
+ for (int i = 0, n = m_displaySet.size(); i != n; ++i) {
+ res += QString("%1: ").arg(i);
+ res += m_displaySet.at(i).toString();
+ res += '\n';
+ }
+#if 0
+ res += "\nOld:\n";
+ for (int i = 0, n = m_oldSet.size(); i != n; ++i) {
+ res += m_oldSet.at(i).toString();
+ res += '\n';
+ }
+#endif
+ return res;
+}
+
+WatchData *WatchHandler::findData(const QString &iname)
+{
+ for (int i = m_completeSet.size(); --i >= 0; )
+ if (m_completeSet.at(i).iname == iname)
+ return &m_completeSet[i];
+ return 0;
+}
+
+WatchData WatchHandler::takeData(const QString &iname)
+{
+ WatchData data = take(iname, &m_incompleteSet);
+ if (data.isValid())
+ return data;
+ return take(iname, &m_completeSet);
+}
+
+QList<WatchData> WatchHandler::takeCurrentIncompletes()
+{
+ QList<WatchData> res = m_incompleteSet;
+ //MODEL_DEBUG("TAKING INCOMPLETES" << toString());
+ m_incompleteSet.clear();
+ return res;
+}
+
+void WatchHandler::rebuildModel()
+{
+ if (m_inChange) {
+ MODEL_DEBUG("RECREATE MODEL IGNORED, CURRENT SET:\n" << toString());
+ return;
+ }
+
+ #ifdef DEBUG_PENDING
+ MODEL_DEBUG("RECREATE MODEL, CURRENT SET:\n" << toString());
+ #endif
+
+ QHash<QString, QString> oldValues;
+ for (int i = 0, n = m_oldSet.size(); i != n; ++i) {
+ WatchData &data = m_oldSet[i];
+ oldValues[data.iname] = data.value;
+ }
+ #ifdef DEBUG_PENDING
+ MODEL_DEBUG("OLD VALUES: " << oldValues);
+ #endif
+
+ for (int i = m_completeSet.size(); --i >= 0; ) {
+ WatchData &data = m_completeSet[i];
+ data.level = data.iname.isEmpty() ? 0 : data.iname.count('.') + 1;
+ data.childIndex.clear();
+ }
+
+ qSort(m_completeSet.begin(), m_completeSet.end(), &iNameSorter);
+
+ QHash<QString, int> iname2idx;
+
+ for (int i = m_completeSet.size(); --i > 0; ) {
+ WatchData &data = m_completeSet[i];
+ data.parentIndex = 0;
+ data.childIndex.clear();
+ iname2idx[data.iname] = i;
+ }
+
+ for (int i = 1; i < m_completeSet.size(); ++i) {
+ WatchData &data = m_completeSet[i];
+ QString parentIName = parentName(data.iname);
+ data.parentIndex = iname2idx.value(parentIName, 0);
+ WatchData &parent = m_completeSet[data.parentIndex];
+ data.row = parent.childIndex.size();
+ parent.childIndex.append(i);
+ }
+
+ m_oldSet = m_completeSet;
+ m_oldSet += m_incompleteSet;
+
+ for (int i = 0, n = m_completeSet.size(); i != n; ++i) {
+ WatchData &data = m_completeSet[i];
+ data.changed = !data.value.isEmpty()
+ && data.value != oldValues[data.iname]
+ && data.value != strNotInScope;
+ }
+
+ //emit layoutAboutToBeChanged();
+
+ m_displaySet = m_completeSet;
+
+ #ifdef DEBUG_PENDING
+ MODEL_DEBUG("SET " << toString());
+ #endif
+
+#if 1
+ // Append dummy item to get the [+] effect
+ for (int i = 0, n = m_displaySet.size(); i != n; ++i) {
+ WatchData &data = m_displaySet[i];
+ if (data.childCount > 0 && data.childIndex.size() == 0) {
+ WatchData dummy;
+ dummy.state = 0;
+ dummy.row = 0;
+ dummy.iname = data.iname + ".dummy";
+ //dummy.name = data.iname + ".dummy";
+ //dummy.name = "<loading>";
+ dummy.level = data.level + 1;
+ dummy.parentIndex = i;
+ dummy.childCount = 0;
+ data.childIndex.append(m_displaySet.size());
+ m_displaySet.append(dummy);
+ }
+ }
+#endif
+
+ // Possibly append dummy items to prevent empty views
+ bool ok = true;
+ QWB_ASSERT(m_displaySet.size() >= 2, ok = false);
+ QWB_ASSERT(m_displaySet.at(1).iname == "local", ok = false);
+ QWB_ASSERT(m_displaySet.at(2).iname == "tooltip", ok = false);
+ QWB_ASSERT(m_displaySet.at(3).iname == "watch", ok = false);
+ if (ok) {
+ for (int i = 1; i <= 3; ++i) {
+ WatchData &data = m_displaySet[i];
+ if (data.childIndex.size() == 0) {
+ WatchData dummy;
+ dummy.state = 0;
+ dummy.row = 0;
+ if (i == 1) {
+ dummy.iname = "local.dummy";
+ dummy.name = "<No Locals>";
+ } else if (i == 2) {
+ dummy.iname = "tooltip.dummy";
+ dummy.name = "<No Tooltip>";
+ } else {
+ dummy.iname = "watch.dummy";
+ dummy.name = "<No Watchers>";
+ }
+ dummy.level = 2;
+ dummy.parentIndex = i;
+ dummy.childCount = 0;
+ data.childIndex.append(m_displaySet.size());
+ m_displaySet.append(dummy);
+ }
+ }
+ }
+
+ m_inChange = true;
+ //qDebug() << "WATCHHANDLER: RESET ABOUT TO EMIT";
+ emit reset();
+ //qDebug() << "WATCHHANDLER: RESET EMITTED";
+ m_inChange = false;
+ //emit layoutChanged();
+ //QSet<QString> einames = m_expandedINames;
+ //einames.insert("local");
+ //einames.insert("watch");
+ //emit expandedItems(einames);
+
+ #if DEBUG_MODEL
+ #if USE_MODEL_TEST
+ //(void) new ModelTest(this, this);
+ #endif
+ #endif
+
+ #ifdef DEBUG_PENDING
+ MODEL_DEBUG("SORTED: " << toString());
+ MODEL_DEBUG("EXPANDED INAMES: " << m_expandedINames);
+ #endif
+}
+
+void WatchHandler::cleanup()
+{
+ m_oldSet.clear();
+ m_expandedINames.clear();
+ m_displayedINames.clear();
+ cleanModel();
+ m_displaySet = m_completeSet;
+#if 0
+ for (EditWindows::ConstIterator it = m_editWindows.begin();
+ it != m_editWindows.end(); ++it) {
+ if (!it.value().isNull())
+ delete it.value();
+ }
+ m_editWindows.clear();
+#endif
+ emit reset();
+}
+
+void WatchHandler::collapseChildren(const QModelIndex &idx)
+{
+ if (m_inChange || m_completeSet.isEmpty()) {
+ //qDebug() << "WATCHHANDLER: COLLAPSE IGNORED" << idx;
+ return;
+ }
+ QWB_ASSERT(checkIndex(idx.internalId()), return);
+#if 0
+ QString iname0 = m_displaySet.at(idx.internalId()).iname;
+ MODEL_DEBUG("COLLAPSE NODE" << iname0);
+ QString iname1 = iname0 + '.';
+ for (int i = m_completeSet.size(); --i >= 0; ) {
+ QString iname = m_completeSet.at(i).iname;
+ if (iname.startsWith(iname1)) {
+ // Better leave it in in case the user re-enters the branch?
+ (void) m_completeSet.takeAt(i);
+ MODEL_DEBUG(" REMOVING " << iname);
+ m_expandedINames.remove(iname);
+ }
+ }
+ m_expandedINames.remove(iname0);
+ //MODEL_DEBUG(toString());
+ //rebuildModel();
+#endif
+}
+
+void WatchHandler::expandChildren(const QModelIndex &idx)
+{
+ if (m_inChange || m_completeSet.isEmpty()) {
+ //qDebug() << "WATCHHANDLER: EXPAND IGNORED" << idx;
+ return;
+ }
+ int index = idx.internalId();
+ if (index == 0)
+ return;
+ QWB_ASSERT(index >= 0, qDebug() << toString() << index; return);
+ QWB_ASSERT(index < m_completeSet.size(), qDebug() << toString() << index; return);
+ const WatchData &display = m_displaySet.at(index);
+ QWB_ASSERT(index >= 0, qDebug() << toString() << index; return);
+ QWB_ASSERT(index < m_completeSet.size(), qDebug() << toString() << index; return);
+ const WatchData &complete = m_completeSet.at(index);
+ MODEL_DEBUG("\n\nEXPAND" << display.iname);
+ if (display.iname.isEmpty()) {
+ // This should not happen but the view seems to send spurious
+ // "expand()" signals folr the root item from time to time.
+ // Try to handle that gracfully.
+ //MODEL_DEBUG(toString());
+ qDebug() << "FIXME: expandChildren, no data " << display.iname << "found"
+ << idx;
+ //rebuildModel();
+ return;
+ }
+
+ //qDebug() << " ... NODE: " << display.toString()
+ // << complete.childIndex.size() << complete.childCount;
+
+ if (m_expandedINames.contains(display.iname))
+ return;
+
+ // This is a performance hack and not strictly necessary.
+ // Remove it if there are troubles when expanding nodes.
+ if (0 && complete.childCount > 0 && complete.childIndex.size() > 0) {
+ MODEL_DEBUG("SKIP FETCHING CHILDREN");
+ return;
+ }
+
+ WatchData data = takeData(display.iname); // remove previous data
+ m_expandedINames.insert(data.iname);
+ if (data.iname.contains('.')) // not for top-level items
+ data.setChildrenNeeded();
+ insertData(data);
+ emit watchModelUpdateRequested();
+}
+
+void WatchHandler::insertData(const WatchData &data)
+{
+ //MODEL_DEBUG("INSERTDATA: " << data.toString());
+ QWB_ASSERT(data.isValid(), return);
+ if (data.isSomethingNeeded())
+ insertDataHelper(m_incompleteSet, data);
+ else
+ insertDataHelper(m_completeSet, data);
+ //MODEL_DEBUG("INSERT RESULT" << toString());
+}
+
+void WatchHandler::watchExpression(const QString &exp)
+{
+ // FIXME: 'exp' can contain illegal characters
+ //MODEL_DEBUG("WATCH: " << exp);
+ WatchData data;
+ data.exp = exp;
+ data.name = exp;
+ data.iname = "watch." + exp;
+ insertData(data);
+}
+
+
+void WatchHandler::setDisplayedIName(const QString &iname, bool on)
+{
+ WatchData *d = findData(iname);
+ if (!on || !d) {
+ delete m_editWindows.take(iname);
+ m_displayedINames.remove(iname);
+ return;
+ }
+ if (d->exp.isEmpty()) {
+ //emit statusMessageRequested(tr("Sorry. Cannot visualize objects without known address."), 5000);
+ return;
+ }
+ d->setValueNeeded();
+ m_displayedINames.insert(iname);
+ insertData(*d);
+}
+
+void WatchHandler::showEditValue(const WatchData &data)
+{
+ // editvalue is always base64 encoded
+ QByteArray ba = QByteArray::fromBase64(data.editvalue);
+ //QByteArray ba = data.editvalue;
+ QWidget *w = m_editWindows.value(data.iname);
+ qDebug() << "SHOW_EDIT_VALUE " << data.toString() << data.type
+ << data.iname << w;
+ if (data.type == "QImage") {
+ if (!w) {
+ w = new QLabel;
+ m_editWindows[data.iname] = w;
+ }
+ QDataStream ds(&ba, QIODevice::ReadOnly);
+ QVariant v;
+ ds >> v;
+ QString type = QString::fromAscii(v.typeName());
+ QImage im = v.value<QImage>();
+ if (QLabel *l = qobject_cast<QLabel *>(w))
+ l->setPixmap(QPixmap::fromImage(im));
+ } else if (data.type == "QPixmap") {
+ if (!w) {
+ w = new QLabel;
+ m_editWindows[data.iname] = w;
+ }
+ QDataStream ds(&ba, QIODevice::ReadOnly);
+ QVariant v;
+ ds >> v;
+ QString type = QString::fromAscii(v.typeName());
+ QPixmap im = v.value<QPixmap>();
+ if (QLabel *l = qobject_cast<QLabel *>(w))
+ l->setPixmap(im);
+ } else if (data.type == "QString") {
+ if (!w) {
+ w = new QTextEdit;
+ m_editWindows[data.iname] = w;
+ }
+#if 0
+ QDataStream ds(&ba, QIODevice::ReadOnly);
+ QVariant v;
+ ds >> v;
+ QString type = QString::fromAscii(v.typeName());
+ QString str = v.value<QString>();
+#else
+ MODEL_DEBUG("DATA: " << ba);
+ QString str = QString::fromUtf16((ushort *)ba.constData(), ba.size()/2);
+#endif
+ if (QTextEdit *t = qobject_cast<QTextEdit *>(w))
+ t->setText(str);
+ }
+ if (w)
+ w->show();
+}
+
+void WatchHandler::removeWatchExpression(const QString &iname)
+{
+ MODEL_DEBUG("REMOVE WATCH: " << iname);
+ (void) takeData(iname);
+ emit watchModelUpdateRequested();
+}
+
+void WatchHandler::cleanModel()
+{
+ // This uses data stored in m_oldSet to re-create a new set
+ // one-by-one
+ m_completeSet.clear();
+ m_incompleteSet.clear();
+
+ WatchData root;
+ root.state = 0;
+ root.level = 0;
+ root.row = 0;
+ root.name = "Root";
+ root.parentIndex = -1;
+ root.childIndex.append(1);
+ root.childIndex.append(2);
+ root.childIndex.append(3);
+ m_completeSet.append(root);
+
+ WatchData local;
+ local.iname = "local";
+ local.name = "Locals";
+ local.state = 0;
+ local.level = 1;
+ local.row = 0;
+ local.parentIndex = 0;
+ m_completeSet.append(local);
+
+ WatchData tooltip;
+ tooltip.iname = "tooltip";
+ tooltip.name = "Tooltip";
+ tooltip.state = 0;
+ tooltip.level = 1;
+ tooltip.row = 1;
+ tooltip.parentIndex = 0;
+ m_completeSet.append(tooltip);
+
+ WatchData watch;
+ watch.iname = "watch";
+ watch.name = "Watchers";
+ watch.state = 0;
+ watch.level = 1;
+ watch.row = 2;
+ watch.parentIndex = 0;
+ m_completeSet.append(watch);
+}
+
+
+void WatchHandler::reinitializeWatchers()
+{
+ cleanModel();
+
+ // copy over all watchers and mark all watchers as incomplete
+ for (int i = 0, n = m_oldSet.size(); i < n; ++i) {
+ WatchData data = m_oldSet.at(i);
+ if (data.isWatcher()) {
+ data.level = -1;
+ data.row = -1;
+ data.parentIndex = -1;
+ data.variable.clear();
+ data.setAllNeeded();
+ data.valuedisabled = false;
+ insertData(data); // properly handles "neededChildren"
+ }
+ }
+}
+
+bool WatchHandler::canFetchMore(const QModelIndex &parent) const
+{
+ MODEL_DEBUG("CAN FETCH MORE: " << parent << "false");
+#if 1
+ Q_UNUSED(parent);
+ return false;
+#else
+ // FIXME: not robust enough. Problem is that fetchMore
+ // needs to be made synchronous to be useful. Busy loop is no good.
+ if (!parent.isValid())
+ return false;
+ QWB_ASSERT(checkIndex(parent.internalId()), return false);
+ const WatchData &data = m_displaySet.at(parent.internalId());
+ MODEL_DEBUG("CAN FETCH MORE: " << parent << " children: " << data.childCount
+ << data.iname);
+ return data.childCount > 0;
+#endif
+}
+
+void WatchHandler::fetchMore(const QModelIndex &parent)
+{
+ MODEL_DEBUG("FETCH MORE: " << parent);
+ return;
+
+ QWB_ASSERT(checkIndex(parent.internalId()), return);
+ QString iname = m_displaySet.at(parent.internalId()).iname;
+
+ if (m_inFetchMore) {
+ MODEL_DEBUG("LOOP IN FETCH MORE" << iname);
+ return;
+ }
+ m_inFetchMore = true;
+
+ WatchData data = takeData(iname);
+ MODEL_DEBUG("FETCH MORE: " << parent << ":" << iname << data.name);
+
+ if (!data.isValid()) {
+ MODEL_DEBUG("FIXME: FETCH MORE, no data " << iname << "found");
+ return;
+ }
+
+ m_expandedINames.insert(data.iname);
+ if (data.iname.contains('.')) // not for top-level items
+ data.setChildrenNeeded();
+
+ MODEL_DEBUG("FETCH MORE: data:" << data.toString());
+ insertData(data);
+ //emit watchUpdateRequested();
+
+ while (m_inFetchMore) {
+ QApplication::processEvents();
+ }
+ m_inFetchMore = false;
+ MODEL_DEBUG("BUSY LOOP FINISHED, data:" << data.toString());
+}
+
+QModelIndex WatchHandler::index(int row, int col, const QModelIndex &parent) const
+{
+ #ifdef DEBUG_MODEL
+ MODEL_DEBUG("INDEX " << row << col << parent);
+ #endif
+ //if (col != 0) {
+ // MODEL_DEBUG(" -> " << QModelIndex() << " (3) ");
+ // return QModelIndex();
+ //}
+ if (row < 0) {
+ MODEL_DEBUG(" -> " << QModelIndex() << " (4) ");
+ return QModelIndex();
+ }
+ if (!parent.isValid()) {
+ if (row == 0 && col >= 0 && col < 3 && parent.row() == -1) {
+ MODEL_DEBUG(" -> " << createIndex(0, 0, 0) << " (B) ");
+ return createIndex(0, col, 0);
+ }
+ MODEL_DEBUG(" -> " << QModelIndex() << " (1) ");
+ return QModelIndex();
+ }
+ int parentIndex = parent.internalId();
+ if (parentIndex < 0) {
+ //MODEL_DEBUG("INDEX " << row << col << parentIndex << "INVALID");
+ MODEL_DEBUG(" -> " << QModelIndex() << " (2) ");
+ return QModelIndex();
+ }
+ QWB_ASSERT(checkIndex(parentIndex), return QModelIndex());
+ const WatchData &data = m_displaySet.at(parentIndex);
+ QWB_ASSERT(row >= 0, qDebug() << "ROW: " << row << "PARENT: " << parent
+ << data.toString() << toString(); return QModelIndex());
+ QWB_ASSERT(row < data.childIndex.size(),
+ MODEL_DEBUG("ROW: " << row << data.toString() << toString());
+ return QModelIndex());
+ QModelIndex idx = createIndex(row, col, data.childIndex.at(row));
+ QWB_ASSERT(idx.row() == m_displaySet.at(idx.internalId()).row,
+ return QModelIndex());
+ MODEL_DEBUG(" -> " << idx << " (A) ");
+ return idx;
+}
+
+QModelIndex WatchHandler::parent(const QModelIndex &idx) const
+{
+ if (!idx.isValid()) {
+ MODEL_DEBUG(" -> " << QModelIndex() << " (1) ");
+ return QModelIndex();
+ }
+ MODEL_DEBUG("PARENT " << idx);
+ int currentIndex = idx.internalId();
+ QWB_ASSERT(checkIndex(currentIndex), return QModelIndex());
+ QWB_ASSERT(idx.row() == m_displaySet.at(currentIndex).row,
+ MODEL_DEBUG("IDX: " << idx << toString(); return QModelIndex()));
+ int parentIndex = m_displaySet.at(currentIndex).parentIndex;
+ if (parentIndex < 0) {
+ MODEL_DEBUG(" -> " << QModelIndex() << " (2) ");
+ return QModelIndex();
+ }
+ QWB_ASSERT(checkIndex(parentIndex), return QModelIndex());
+ QModelIndex parent =
+ createIndex(m_displaySet.at(parentIndex).row, 0, parentIndex);
+ MODEL_DEBUG(" -> " << parent);
+ return parent;
+}
+
+int WatchHandler::rowCount(const QModelIndex &idx) const
+{
+ MODEL_DEBUG("ROW COUNT " << idx);
+ if (idx.column() > 0) {
+ MODEL_DEBUG(" -> " << 0 << " (A) ");
+ return 0;
+ }
+ int thisIndex = idx.internalId();
+ QWB_ASSERT(checkIndex(thisIndex), return 0);
+ if (idx.row() == -1 && idx.column() == -1) {
+ MODEL_DEBUG(" -> " << 3 << " (B) ");
+ return 1;
+ }
+ if (thisIndex < 0) {
+ MODEL_DEBUG(" -> " << 0 << " (C) ");
+ return 0;
+ }
+ if (thisIndex == 0) {
+ MODEL_DEBUG(" -> " << 3 << " (D) ");
+ return 3;
+ }
+ const WatchData &data = m_displaySet.at(thisIndex);
+ int rows = data.childIndex.size();
+ MODEL_DEBUG(" -> " << rows << " (E) ");
+ return rows;
+ // Try lazy evaluation
+ //if (rows > 0)
+ // return rows;
+ //return data.childCount;
+}
+
+int WatchHandler::columnCount(const QModelIndex &idx) const
+{
+ MODEL_DEBUG("COLUMN COUNT " << idx);
+ if (idx == QModelIndex()) {
+ MODEL_DEBUG(" -> " << 3 << " (C) ");
+ return 3;
+ }
+ if (idx.column() != 0) {
+ MODEL_DEBUG(" -> " << 0 << " (A) ");
+ return 0;
+ }
+ MODEL_DEBUG(" -> " << 3 << " (B) ");
+ QWB_ASSERT(checkIndex(idx.internalId()), return 3);
+ return 3;
+}
+
+bool WatchHandler::hasChildren(const QModelIndex &idx) const
+{
+ // that's the base implementation:
+ bool base = rowCount(idx) > 0 && columnCount(idx) > 0;
+ MODEL_DEBUG("HAS CHILDREN: " << idx << base);
+ return base;
+ QWB_ASSERT(checkIndex(idx.internalId()), return false);
+ const WatchData &data = m_displaySet.at(idx.internalId());
+ MODEL_DEBUG("HAS CHILDREN: " << idx << data.toString());
+ return data.childCount > 0; // || data.childIndex.size() > 0;
+}
+
+bool WatchHandler::checkIndex(int id) const
+{
+ if (id < 0) {
+ MODEL_DEBUG("CHECK INDEX FAILED" << id);
+ return false;
+ }
+ if (id >= m_displaySet.size()) {
+ MODEL_DEBUG("CHECK INDEX FAILED" << id << toString());
+ return false;
+ }
+ return true;
+}
diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h
new file mode 100644
index 0000000000..b3db114be8
--- /dev/null
+++ b/src/plugins/debugger/watchhandler.h
@@ -0,0 +1,219 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_WATCHHANDLER_H
+#define DEBUGGER_WATCHHANDLER_H
+
+#include <QtCore/QPointer>
+#include <QtCore/QObject>
+#include <QtCore/QHash>
+#include <QtCore/QSet>
+#include <QtGui/QStandardItem>
+#include <QtGui/QStandardItemModel>
+#include <QtGui/QTreeView>
+#include <QtScript/QScriptValue>
+
+namespace Debugger {
+namespace Internal {
+
+class WatchData
+{
+public:
+ WatchData();
+
+ enum State
+ {
+ Complete = 0,
+ ChildCountNeeded = 1,
+ ValueNeeded = 2,
+ TypeNeeded = 4,
+ ChildrenNeeded = 8,
+
+ NeededMask = ValueNeeded
+ | TypeNeeded
+ | ChildrenNeeded
+ | ChildCountNeeded,
+
+ InitialState = ValueNeeded
+ | TypeNeeded
+ | ChildrenNeeded
+ | ChildCountNeeded
+ };
+
+ void setValue(const QByteArray &);
+ void setType(const QString &);
+ void setValueToolTip(const QString &);
+ void setError(const QString &);
+ void setAddress(const QString &address);
+
+ bool isSomethingNeeded() const { return state & NeededMask; }
+ void setAllNeeded() { state = NeededMask; }
+ void setAllUnneeded() { state = State(0); }
+
+ bool isTypeNeeded() const { return state & TypeNeeded; }
+ bool isTypeKnown() const { return !(state & TypeNeeded); }
+ void setTypeNeeded() { state = State(state | TypeNeeded); }
+ void setTypeUnneeded() { state = State(state & ~TypeNeeded); }
+
+ bool isValueNeeded() const { return state & ValueNeeded; }
+ bool isValueKnown() const { return !(state & ValueNeeded); }
+ void setValueNeeded() { state = State(state | ValueNeeded); }
+ void setValueUnneeded() { state = State(state & ~ValueNeeded); }
+
+ bool isChildrenNeeded() const { return state & ChildrenNeeded; }
+ bool isChildrenKnown() const { return !(state & ChildrenNeeded); }
+ void setChildrenNeeded() { state = State(state | ChildrenNeeded); }
+ void setChildrenUnneeded() { state = State(state & ~ChildrenNeeded); }
+
+ bool isChildCountNeeded() const { return state & ChildCountNeeded; }
+ bool isChildCountKnown() const { return !(state & ChildCountNeeded); }
+ void setChildCountNeeded() { state = State(state | ChildCountNeeded); }
+ void setChildCountUnneeded() { state = State(state & ~ChildCountNeeded); }
+ void setChildCount(int n) { childCount = n; setChildCountUnneeded();
+ if (n == 0) setChildrenUnneeded(); }
+
+ QString toString() const;
+ bool isLocal() const { return iname.startsWith(QLatin1String("local.")); }
+ bool isWatcher() const { return iname.startsWith(QLatin1String("watch.")); };
+ bool isValid() const { return !iname.isEmpty(); }
+
+public:
+ QString iname; // internal name sth like 'local.baz.public.a'
+ QString exp; // the expression
+ QString name; // displayed name
+ QString value; // displayed value
+ QByteArray editvalue; // displayed value
+ QString valuetooltip; // tooltip in value column
+ QString type; // displayed type
+ QString variable; // name of internal Gdb variable if created
+ QString addr; // displayed adress
+ QString framekey; // key for type cache
+ QScriptValue scriptValue; // if needed...
+ int childCount;
+ bool valuedisabled; // value will be greyed out
+
+private:
+
+public:
+ int state;
+
+ // Model
+ int parentIndex;
+ int row;
+ int level;
+ QList<int> childIndex;
+ bool changed;
+};
+
+enum { INameRole = Qt::UserRole, VisualRole };
+
+
+class WatchHandler : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ WatchHandler();
+ QAbstractItemModel *model() { return this; }
+
+ //
+ // QAbstractItemModel
+ //
+ bool setData(const QModelIndex &index, const QVariant &value, int role);
+ QVariant data(const QModelIndex &index, int role) const;
+ QModelIndex index(int, int, const QModelIndex &idx) const;
+ QModelIndex parent(const QModelIndex &idx) const;
+ int rowCount(const QModelIndex &idx) const;
+ int columnCount(const QModelIndex &idx) const;
+ bool hasChildren(const QModelIndex &idx) const;
+ Qt::ItemFlags flags(const QModelIndex &idx) const;
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+ bool checkIndex(int id) const;
+
+//public slots:
+ void cleanup();
+ void watchExpression(const QString &exp);
+ void removeWatchExpression(const QString &exp);
+ void reinitializeWatchers();
+
+ void collapseChildren(const QModelIndex &idx);
+ void expandChildren(const QModelIndex &idx);
+
+ void rebuildModel(); // unconditionally version of above
+ void showEditValue(const WatchData &data);
+
+ bool isDisplayedIName(const QString &iname) const
+ { return m_displayedINames.contains(iname); }
+ bool isExpandedIName(const QString &iname) const
+ { return m_expandedINames.contains(iname); }
+
+ void insertData(const WatchData &data);
+ QList<WatchData> takeCurrentIncompletes();
+
+ bool canFetchMore(const QModelIndex &parent) const;
+ void fetchMore(const QModelIndex &parent);
+
+ WatchData *findData(const QString &iname);
+
+signals:
+ void watchModelUpdateRequested();
+
+private:
+ WatchData takeData(const QString &iname);
+ QString toString() const;
+ void cleanModel();
+
+ bool m_expandPointers;
+ bool m_inChange;
+
+ typedef QMap<QString, QPointer<QWidget> > EditWindows;
+ EditWindows m_editWindows;
+
+ QList<WatchData> m_incompleteSet;
+ QList<WatchData> m_completeSet;
+ QList<WatchData> m_oldSet;
+ QList<WatchData> m_displaySet;
+
+ void setDisplayedIName(const QString &iname, bool on);
+ QSet<QString> m_expandedINames; // those expanded in the treeview
+ QSet<QString> m_displayedINames; // those with "external" viewers
+
+ bool m_inFetchMore;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+Q_DECLARE_METATYPE(Debugger::Internal::WatchData);
+
+#endif // DEBUGGER_WATCHHANDLER_H
diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp
new file mode 100644
index 0000000000..6f153d2de5
--- /dev/null
+++ b/src/plugins/debugger/watchwindow.cpp
@@ -0,0 +1,253 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#include "watchwindow.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QTimer>
+
+#include <QtGui/QAction>
+#include <QtGui/QContextMenuEvent>
+#include <QtGui/QHeaderView>
+#include <QtGui/QLineEdit>
+#include <QtGui/QMenu>
+#include <QtGui/QResizeEvent>
+#include <QtGui/QSplitter>
+
+using namespace Debugger::Internal;
+
+enum { INameRole = Qt::UserRole, VisualRole };
+
+/////////////////////////////////////////////////////////////////////
+//
+// WatchWindow
+//
+/////////////////////////////////////////////////////////////////////
+
+WatchWindow::WatchWindow(Type type, QWidget *parent)
+ : QTreeView(parent), m_type(type)
+{
+ m_blocked = false;
+ setWindowTitle(tr("Locals and Watchers"));
+ setAlternatingRowColors(true);
+ setIndentation(indentation() * 9/10);
+ setUniformRowHeights(true);
+
+ connect(itemDelegate(), SIGNAL(commitData(QWidget *)),
+ this, SLOT(handleChangedItem(QWidget *)));
+ connect(this, SIGNAL(expanded(QModelIndex)),
+ this, SLOT(expandNode(QModelIndex)));
+ connect(this, SIGNAL(collapsed(QModelIndex)),
+ this, SLOT(collapseNode(QModelIndex)));
+}
+
+void WatchWindow::expandNode(const QModelIndex &idx)
+{
+ //QModelIndex mi0 = idx.sibling(idx.row(), 0);
+ //QString iname = model()->data(mi0, INameRole).toString();
+ //QString name = model()->data(mi0, Qt::DisplayRole).toString();
+ //qDebug() << "\n\nEXPAND NODE " // << iname << name
+ // << idx << (m_blocked ? "blocked" : "passed");
+ //if (isExpanded(idx))
+ // return;
+ //if (m_blocked)
+ // return;
+ emit requestExpandChildren(idx);
+}
+
+void WatchWindow::collapseNode(const QModelIndex &idx)
+{
+ //QModelIndex mi0 = idx.sibling(idx.row(), 0);
+ //QString iname = model()->data(mi0, INameRole).toString();
+ //QString name = model()->data(mi0, Qt::DisplayRole).toString();
+ //qDebug() << "COLLAPSE NODE " << idx;
+ if (m_blocked)
+ return;
+ emit requestCollapseChildren(idx);
+}
+
+void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QMenu menu;
+ QAction *act1 = new QAction("Adjust column widths to contents", &menu);
+ QAction *act2 = new QAction("Always adjust column widths to contents", &menu);
+ act2->setCheckable(true);
+ act2->setChecked(m_alwaysResizeColumnsToContents);
+
+ menu.addAction(act1);
+ menu.addAction(act2);
+
+ QAction *act3 = 0;
+ QAction *act4 = 0;
+ QModelIndex idx = indexAt(ev->pos());
+ QModelIndex mi0 = idx.sibling(idx.row(), 0);
+ QString exp = model()->data(mi0).toString();
+ QString iname = model()->data(mi0, INameRole).toString();
+ QModelIndex mi1 = idx.sibling(idx.row(), 0);
+ QString value = model()->data(mi1).toString();
+ bool visual = false;
+ if (idx.isValid()) {
+ menu.addSeparator();
+ if (m_type == LocalsType)
+ act3 = new QAction("Watch expression '" + exp + "'", &menu);
+ else
+ act3 = new QAction("Remove expression '" + exp + "'", &menu);
+ menu.addAction(act3);
+
+ visual = model()->data(mi0, VisualRole).toBool();
+ act4 = new QAction("Watch expression '" + exp + "' in separate widget", &menu);
+ act4->setCheckable(true);
+ act4->setChecked(visual);
+ // FIXME: menu.addAction(act4);
+ }
+
+ QAction *act = menu.exec(ev->globalPos());
+
+ if (!act)
+ ;
+ else if (act == act1)
+ resizeColumnsToContents();
+ else if (act == act2)
+ setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
+ else if (act == act3)
+ if (m_type == LocalsType)
+ emit requestWatchExpression(exp);
+ else
+ emit requestRemoveWatchExpression(iname);
+ else if (act == act4)
+ model()->setData(mi0, !visual, VisualRole);
+}
+
+void WatchWindow::resizeColumnsToContents()
+{
+ resizeColumnToContents(0);
+ resizeColumnToContents(1);
+}
+
+void WatchWindow::setAlwaysResizeColumnsToContents(bool on)
+{
+ if (!header())
+ return;
+ m_alwaysResizeColumnsToContents = on;
+ QHeaderView::ResizeMode mode = on
+ ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
+ header()->setResizeMode(0, mode);
+ header()->setResizeMode(1, mode);
+}
+
+void WatchWindow::editItem(const QModelIndex &idx)
+{
+ Q_UNUSED(idx); // FIXME
+}
+
+void WatchWindow::reset()
+{
+ int row = 0;
+ if (m_type == TooltipType)
+ row = 1;
+ else if (m_type == WatchersType)
+ row = 2;
+ //qDebug() << "WATCHWINDOW::RESET" << row;
+ QTreeView::reset();
+ setRootIndex(model()->index(row, 0, model()->index(0, 0)));
+ //setRootIndex(model()->index(0, 0));
+}
+
+void WatchWindow::setModel(QAbstractItemModel *model)
+{
+ QTreeView::setModel(model);
+
+ setRootIsDecorated(true);
+ header()->setDefaultAlignment(Qt::AlignLeft);
+ header()->setResizeMode(QHeaderView::ResizeToContents);
+ if (m_type != LocalsType)
+ header()->hide();
+
+ connect(model, SIGNAL(modelAboutToBeReset()),
+ this, SLOT(modelAboutToBeReset()));
+ connect(model, SIGNAL(modelReset()),
+ this, SLOT(modelReset()));
+}
+
+void WatchWindow::modelAboutToBeReset()
+{
+ m_blocked = true;
+ //qDebug() << "Model about to be reset";
+ m_expandedItems.clear();
+ m_expandedItems.insert("local");
+ m_expandedItems.insert("watch");
+ modelAboutToBeResetHelper(model()->index(0, 0));
+ //qDebug() << " expanded: " << m_expandedItems;
+}
+
+void WatchWindow::modelAboutToBeResetHelper(const QModelIndex &idx)
+{
+ QString iname = model()->data(idx, INameRole).toString();
+ //qDebug() << "Model about to be reset helper" << iname << idx
+ // << isExpanded(idx);
+ if (isExpanded(idx))
+ m_expandedItems.insert(iname);
+ for (int i = 0, n = model()->rowCount(idx); i != n; ++i) {
+ QModelIndex idx1 = model()->index(i, 0, idx);
+ modelAboutToBeResetHelper(idx1);
+ }
+}
+
+void WatchWindow::modelReset()
+{
+ //qDebug() << "Model reset";
+ expand(model()->index(0, 0));
+ modelResetHelper(model()->index(0, 0));
+ m_blocked = false;
+}
+
+void WatchWindow::modelResetHelper(const QModelIndex &idx)
+{
+ QString name = model()->data(idx, Qt::DisplayRole).toString();
+ QString iname = model()->data(idx, INameRole).toString();
+ //qDebug() << "Model reset helper" << iname << name;
+ if (m_expandedItems.contains(iname)) {
+ expand(idx);
+ for (int i = 0, n = model()->rowCount(idx); i != n; ++i) {
+ QModelIndex idx1 = model()->index(i, 0, idx);
+ modelResetHelper(idx1);
+ }
+ }
+}
+
+void WatchWindow::handleChangedItem(QWidget *widget)
+{
+ QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
+ if (lineEdit)
+ requestAssignValue("foo", lineEdit->text());
+}
+
diff --git a/src/plugins/debugger/watchwindow.h b/src/plugins/debugger/watchwindow.h
new file mode 100644
index 0000000000..da0ee9ee8f
--- /dev/null
+++ b/src/plugins/debugger/watchwindow.h
@@ -0,0 +1,95 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception version
+** 1.2, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+#ifndef DEBUGGER_WATCHWINDOW_H
+#define DEBUGGER_WATCHWINDOW_H
+
+#include <QtGui/QTreeView>
+
+namespace Debugger {
+namespace Internal {
+
+/////////////////////////////////////////////////////////////////////
+//
+// WatchWindow
+//
+/////////////////////////////////////////////////////////////////////
+
+class WatchWindow : public QTreeView
+{
+ Q_OBJECT
+
+public:
+ enum Type { LocalsType, TooltipType, WatchersType };
+
+ WatchWindow(Type type, QWidget *parent = 0);
+ void setType(Type type) { m_type = type; }
+ Type type() const { return m_type; }
+
+public slots:
+ void resizeColumnsToContents();
+ void setAlwaysResizeColumnsToContents(bool on = true);
+ void setModel(QAbstractItemModel *model);
+
+signals:
+ void requestWatchExpression(const QString &exp);
+ void requestRemoveWatchExpression(const QString &iname);
+ void requestAssignValue(const QString &exp, const QString &value);
+ void requestExpandChildren(const QModelIndex &idx);
+ void requestCollapseChildren(const QModelIndex &idx);
+
+private slots:
+ void handleChangedItem(QWidget *);
+ void expandNode(const QModelIndex &index);
+ void collapseNode(const QModelIndex &index);
+ void modelAboutToBeReset();
+ void modelReset();
+
+private:
+ void contextMenuEvent(QContextMenuEvent *ev);
+ void editItem(const QModelIndex &idx);
+ void reset(); /* reimpl */
+
+ void modelAboutToBeResetHelper(const QModelIndex &idx);
+ void modelResetHelper(const QModelIndex &idx);
+
+ bool m_alwaysResizeColumnsToContents;
+ Type m_type;
+ bool m_blocked;
+ QSet<QString> m_expandedItems;
+};
+
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_WATCHWINDOW_H