2 * Copyright Johannes Sixt
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
7 #include <kapplication.h>
8 #include <klocale.h> /* i18n */
9 #include <kmessagebox.h>
11 #include <kstatusbar.h>
13 #include <kstandardaction.h>
14 #include <kstandardshortcut.h>
16 #include <kactioncollection.h>
17 #include <krecentfilesaction.h>
18 #include <ktoggleaction.h>
19 #include <kfiledialog.h>
20 #include <k3process.h>
21 #include <kshortcutsdialog.h>
22 #include <kanimatedbutton.h>
23 #include <kwindowsystem.h>
26 #include <kxmlguifactory.h>
27 #include <q3listbox.h>
29 #include <qfileinfo.h>
30 #include <q3tabdialog.h>
31 #include <q3textstream.h>
32 #include <Q3ValueList>
33 #include <Q3PopupMenu>
34 #include <QDockWidget>
35 #include "dbgmainwnd.h"
37 #include "commandids.h"
40 #include "threadlist.h"
41 #include "memwindow.h"
43 #include "watchwindow.h"
44 #include "procattach.h"
45 #include "prefdebugger.h"
47 #include "gdbdriver.h"
48 #include "xsldbgdriver.h"
50 #include <sys/stat.h> /* mknod(2) */
51 #include <unistd.h> /* getpid */
54 static const char defaultTermCmdStr
[] = "xterm -name kdbgio -title %T -e sh -c %C";
55 static const char defaultSourceFilter
[] = "*.c *.cc *.cpp *.c++ *.C *.CC";
56 static const char defaultHeaderFilter
[] = "*.h *.hh *.hpp *.h++";
58 DebuggerMainWnd::DebuggerMainWnd() :
62 m_transcriptFile(GDB_TRANSCRIPT
),
64 m_outputTermCmdStr(defaultTermCmdStr
),
66 m_ttyLevel(-1), /* no tty yet */
67 m_popForeground(false),
70 m_sourceFilter(defaultSourceFilter
),
71 m_headerFilter(defaultHeaderFilter
),
73 m_statusActive(i18n("active"))
75 m_filesWindow
= new WinStack(this);
76 setCentralWidget(m_filesWindow
);
78 QDockWidget
* dw1
= createDockWidget("Stack", i18n("Stack"));
79 m_btWindow
= new Q3ListBox(dw1
);
80 dw1
->setWidget(m_btWindow
);
81 QDockWidget
* dw2
= createDockWidget("Locals", i18n("Locals"));
82 m_localVariables
= new ExprWnd(dw2
, i18n("Variable"));
83 dw2
->setWidget(m_localVariables
);
84 QDockWidget
* dw3
= createDockWidget("Watches", i18n("Watches"));
85 m_watches
= new WatchWindow(dw3
);
86 dw3
->setWidget(m_watches
);
87 QDockWidget
* dw4
= createDockWidget("Registers", i18n("Registers"));
88 m_registers
= new RegisterView(dw4
);
89 dw4
->setWidget(m_registers
);
90 QDockWidget
* dw5
= createDockWidget("Breakpoints", i18n("Breakpoints"));
91 m_bpTable
= new BreakpointTable(dw5
);
92 dw5
->setWidget(m_bpTable
);
93 QDockWidget
* dw6
= createDockWidget("Output", i18n("Output"));
94 m_ttyWindow
= new TTYWindow(dw6
);
95 dw6
->setWidget(m_ttyWindow
);
96 QDockWidget
* dw7
= createDockWidget("Threads", i18n("Threads"));
97 m_threads
= new ThreadList(dw7
);
98 dw7
->setWidget(m_threads
);
99 QDockWidget
* dw8
= createDockWidget("Memory", i18n("Memory"));
100 m_memoryWindow
= new MemoryWindow(dw8
);
101 dw8
->setWidget(m_memoryWindow
);
103 m_debugger
= new KDebugger(this, m_localVariables
, m_watches
->watchVariables(), m_btWindow
);
105 connect(m_debugger
, SIGNAL(updateStatusMessage()), SLOT(slotNewStatusMsg()));
106 connect(m_debugger
, SIGNAL(updateUI()), SLOT(updateUI()));
107 connect(m_debugger
, SIGNAL(breakpointsChanged()), SLOT(updateLineItems()));
108 connect(m_debugger
, SIGNAL(debuggerStarting()), SLOT(slotDebuggerStarting()));
109 m_bpTable
->setDebugger(m_debugger
);
110 m_memoryWindow
->setDebugger(m_debugger
);
112 setStandardToolBarMenuEnabled(true);
114 initToolbar(); // kind of obsolete?
116 connect(m_watches
, SIGNAL(addWatch()), SLOT(slotAddWatch()));
117 connect(m_watches
, SIGNAL(deleteWatch()), m_debugger
, SLOT(slotDeleteWatch()));
118 connect(m_watches
, SIGNAL(textDropped(const QString
&)), SLOT(slotAddWatch(const QString
&)));
120 connect(&m_filesWindow
->m_findDlg
, SIGNAL(closed()), SLOT(updateUI()));
121 connect(m_filesWindow
, SIGNAL(newFileLoaded()),
122 SLOT(slotNewFileLoaded()));
123 connect(m_filesWindow
, SIGNAL(toggleBreak(const QString
&,int,const DbgAddr
&,bool)),
124 this, SLOT(slotToggleBreak(const QString
&,int,const DbgAddr
&,bool)));
125 connect(m_filesWindow
, SIGNAL(enadisBreak(const QString
&,int,const DbgAddr
&)),
126 this, SLOT(slotEnaDisBreak(const QString
&,int,const DbgAddr
&)));
127 connect(m_debugger
, SIGNAL(activateFileLine(const QString
&,int,const DbgAddr
&)),
128 m_filesWindow
, SLOT(activate(const QString
&,int,const DbgAddr
&)));
129 connect(m_debugger
, SIGNAL(executableUpdated()),
130 m_filesWindow
, SLOT(reloadAllFiles()));
131 connect(m_debugger
, SIGNAL(updatePC(const QString
&,int,const DbgAddr
&,int)),
132 m_filesWindow
, SLOT(updatePC(const QString
&,int,const DbgAddr
&,int)));
133 // value popup communication
134 connect(m_filesWindow
, SIGNAL(initiateValuePopup(const QString
&)),
135 m_debugger
, SLOT(slotValuePopup(const QString
&)));
136 connect(m_debugger
, SIGNAL(valuePopup(const QString
&)),
137 m_filesWindow
, SLOT(slotShowValueTip(const QString
&)));
139 connect(m_filesWindow
, SIGNAL(disassemble(const QString
&, int)),
140 m_debugger
, SLOT(slotDisassemble(const QString
&, int)));
141 connect(m_debugger
, SIGNAL(disassembled(const QString
&,int,const std::list
<DisassembledCode
>&)),
142 m_filesWindow
, SLOT(slotDisassembled(const QString
&,int,const std::list
<DisassembledCode
>&)));
143 connect(m_filesWindow
, SIGNAL(moveProgramCounter(const QString
&,int,const DbgAddr
&)),
144 m_debugger
, SLOT(setProgramCounter(const QString
&,int,const DbgAddr
&)));
146 connect(m_debugger
, SIGNAL(programStopped()), SLOT(slotProgramStopped()));
147 connect(&m_backTimer
, SIGNAL(timeout()), SLOT(slotBackTimer()));
149 connect(this, SIGNAL(setTabWidth(int)), m_filesWindow
, SIGNAL(setTabWidth(int)));
151 // connect breakpoint table
152 connect(m_bpTable
, SIGNAL(activateFileLine(const QString
&,int,const DbgAddr
&)),
153 m_filesWindow
, SLOT(activate(const QString
&,int,const DbgAddr
&)));
154 connect(m_debugger
, SIGNAL(updateUI()), m_bpTable
, SLOT(updateUI()));
155 connect(m_debugger
, SIGNAL(breakpointsChanged()), m_bpTable
, SLOT(updateBreakList()));
156 connect(m_debugger
, SIGNAL(breakpointsChanged()), m_bpTable
, SLOT(updateUI()));
158 connect(m_debugger
, SIGNAL(registersChanged(const std::list
<RegisterInfo
>&)),
159 m_registers
, SLOT(updateRegisters(const std::list
<RegisterInfo
>&)));
161 connect(m_debugger
, SIGNAL(memoryDumpChanged(const QString
&, const std::list
<MemoryDump
>&)),
162 m_memoryWindow
, SLOT(slotNewMemoryDump(const QString
&, const std::list
<MemoryDump
>&)));
163 connect(m_debugger
, SIGNAL(saveProgramSpecific(KConfigBase
*)),
164 m_memoryWindow
, SLOT(saveProgramSpecific(KConfigBase
*)));
165 connect(m_debugger
, SIGNAL(restoreProgramSpecific(KConfigBase
*)),
166 m_memoryWindow
, SLOT(restoreProgramSpecific(KConfigBase
*)));
169 connect(m_debugger
, SIGNAL(threadsChanged(const std::list
<ThreadInfo
>&)),
170 m_threads
, SLOT(updateThreads(const std::list
<ThreadInfo
>&)));
171 connect(m_threads
, SIGNAL(setThread(int)),
172 m_debugger
, SLOT(setThread(int)));
174 // popup menu of the local variables window
175 connect(m_localVariables
, SIGNAL(contextMenuRequested(Q3ListViewItem
*, const QPoint
&, int)),
176 this, SLOT(slotLocalsPopup(Q3ListViewItem
*, const QPoint
&)));
178 restoreSettings(KGlobal::config());
181 m_bpTable
->updateUI();
184 DebuggerMainWnd::~DebuggerMainWnd()
186 saveSettings(KGlobal::config());
187 // must delete m_debugger early since it references our windows
191 delete m_memoryWindow
;
197 delete m_localVariables
;
199 delete m_filesWindow
;
201 // if the output window is open, close it
202 if (m_outputTermProc
!= 0) {
203 m_outputTermProc
->disconnect(); /* ignore signals */
204 m_outputTermProc
->kill();
205 shutdownTermWindow();
209 QDockWidget
* DebuggerMainWnd::createDockWidget(const char* name
, const QString
& title
)
211 QDockWidget
* w
= new QDockWidget(title
, this);
212 w
->setObjectName(name
);
213 // view menu changes when docking state changes
214 connect(w
, SIGNAL(visibilityChanged(bool)), SLOT(updateUI()));
218 QAction
* DebuggerMainWnd::createAction(const QString
& text
, const char* icon
,
219 int shortcut
, const QObject
* receiver
,
220 const char* slot
, const char* name
)
222 KAction
* a
= actionCollection()->addAction(name
);
224 a
->setIcon(KIcon(icon
));
226 a
->setShortcut(KShortcut(shortcut
));
227 connect(a
, SIGNAL(triggered()), receiver
, slot
);
231 QAction
* DebuggerMainWnd::createAction(const QString
& text
,
232 int shortcut
, const QObject
* receiver
,
233 const char* slot
, const char* name
)
235 KAction
* a
= actionCollection()->addAction(name
);
238 a
->setShortcut(KShortcut(shortcut
));
239 connect(a
, SIGNAL(triggered()), receiver
, slot
);
244 void DebuggerMainWnd::initKAction()
247 KAction
* open
= KStandardAction::open(this, SLOT(slotFileOpen()),
249 open
->setText(i18n("&Open Source..."));
250 m_closeAction
= KStandardAction::close(m_filesWindow
, SLOT(slotClose()), actionCollection());
251 m_reloadAction
= createAction(i18n("&Reload Source"), "reload", 0,
252 m_filesWindow
, SLOT(slotFileReload()), "file_reload");
253 m_fileExecAction
= createAction(i18n("&Executable..."), "execopen", 0,
254 this, SLOT(slotFileExe()), "file_executable");
255 m_recentExecAction
= new KRecentFilesAction(i18n("Recent E&xecutables"),
257 m_recentExecAction
->setObjectName("file_executable_recent");
258 connect(m_recentExecAction
, SIGNAL(urlSelected(const KUrl
&)),
259 this, SLOT(slotRecentExec(const KUrl
&)));
260 m_coreDumpAction
= createAction(i18n("&Core dump..."), 0,
261 this, SLOT(slotFileCore()), "file_core_dump");
262 KStandardAction::quit(kapp
, SLOT(closeAllWindows()), actionCollection());
265 m_settingsAction
= createAction(i18n("This &Program..."), 0,
266 this, SLOT(slotFileProgSettings()), "settings_program");
267 createAction(i18n("&Global Options..."), 0,
268 this, SLOT(slotFileGlobalSettings()), "settings_global");
269 KStandardAction::keyBindings(this, SLOT(slotConfigureKeys()), actionCollection());
270 KStandardAction::showStatusbar(this, SLOT(slotViewStatusbar()), actionCollection());
273 m_findAction
= KStandardAction::find(m_filesWindow
, SLOT(slotViewFind()), actionCollection());
274 KStandardAction::findNext(m_filesWindow
, SLOT(slotFindForward()), actionCollection());
275 KStandardAction::findPrev(m_filesWindow
, SLOT(slotFindBackward()), actionCollection());
277 i18n("Source &code");
278 struct { QString text
; QWidget
* w
; QString id
; QAction
** act
; } dw
[] = {
279 { i18n("Stac&k"), m_btWindow
, "view_stack", &m_btWindowAction
},
280 { i18n("&Locals"), m_localVariables
, "view_locals", &m_localVariablesAction
},
281 { i18n("&Watched expressions"), m_watches
, "view_watched_expressions", &m_watchesAction
},
282 { i18n("&Registers"), m_registers
, "view_registers", &m_registersAction
},
283 { i18n("&Breakpoints"), m_bpTable
, "view_breakpoints", &m_bpTableAction
},
284 { i18n("T&hreads"), m_threads
, "view_threads", &m_threadsAction
},
285 { i18n("&Output"), m_ttyWindow
, "view_output", &m_ttyWindowAction
},
286 { i18n("&Memory"), m_memoryWindow
, "view_memory", &m_memoryWindowAction
}
288 for (unsigned i
= 0; i
< sizeof(dw
)/sizeof(dw
[0]); i
++) {
289 QDockWidget
* d
= dockParent(dw
[i
].w
);
290 *dw
[i
].act
= new KToggleAction(dw
[i
].text
, actionCollection());
291 actionCollection()->addAction(dw
[i
].id
, *dw
[i
].act
);
292 connect(*dw
[i
].act
, SIGNAL(triggered()), d
, SLOT(show()));
296 m_runAction
= createAction(i18n("&Run"), "pgmrun", Qt::Key_F5
,
297 m_debugger
, SLOT(programRun()), "exec_run");
298 connect(m_runAction
, SIGNAL(activated()), this, SLOT(intoBackground()));
299 m_stepIntoAction
= createAction(i18n("Step &into"), "pgmstep", Qt::Key_F8
,
300 m_debugger
, SLOT(programStep()), "exec_step_into");
301 connect(m_stepIntoAction
, SIGNAL(activated()), this, SLOT(intoBackground()));
302 m_stepOverAction
= createAction(i18n("Step &over"), "pgmnext", Qt::Key_F10
,
303 m_debugger
, SLOT(programNext()), "exec_step_over");
304 connect(m_stepOverAction
, SIGNAL(activated()), this, SLOT(intoBackground()));
305 m_stepOutAction
= createAction(i18n("Step o&ut"), "pgmfinish", Qt::Key_F6
,
306 m_debugger
, SLOT(programFinish()), "exec_step_out");
307 connect(m_stepOutAction
, SIGNAL(activated()), this, SLOT(intoBackground()));
308 m_toCursorAction
= createAction(i18n("Run to &cursor"), Qt::Key_F7
,
309 this, SLOT(slotExecUntil()), "exec_run_to_cursor");
310 connect(m_toCursorAction
, SIGNAL(activated()), this, SLOT(intoBackground()));
311 m_stepIntoIAction
= createAction(i18n("Step i&nto by instruction"), "pgmstepi", Qt::SHIFT
+Qt::Key_F8
,
312 m_debugger
, SLOT(programStepi()), "exec_step_into_by_insn");
313 connect(m_stepIntoIAction
, SIGNAL(activated()), this, SLOT(intoBackground()));
314 m_stepOverIAction
= createAction(i18n("Step o&ver by instruction"), "pgmnexti", Qt::SHIFT
+Qt::Key_F10
,
315 m_debugger
, SLOT(programNexti()), "exec_step_over_by_insn");
316 connect(m_stepOverIAction
, SIGNAL(activated()), this, SLOT(intoBackground()));
317 m_execMovePCAction
= createAction(i18n("&Program counter to current line"), 0,
318 m_filesWindow
, SLOT(slotMoveProgramCounter()), "exec_movepc");
319 m_breakAction
= createAction(i18n("&Break"), 0,
320 m_debugger
, SLOT(programBreak()), "exec_break");
321 m_killAction
= createAction(i18n("&Kill"), 0,
322 m_debugger
, SLOT(programKill()), "exec_kill");
323 m_restartAction
= createAction(i18n("Re&start"), 0,
324 m_debugger
, SLOT(programRunAgain()), "exec_restart");
325 m_attachAction
= createAction(i18n("A&ttach..."), 0,
326 this, SLOT(slotExecAttach()), "exec_attach");
327 m_argumentsAction
= createAction(i18n("&Arguments..."), 0,
328 this, SLOT(slotExecArgs()), "exec_arguments");
331 m_bpSetAction
= createAction(i18n("Set/Clear &breakpoint"), "brkpt", Qt::Key_F9
,
332 m_filesWindow
, SLOT(slotBrkptSet()), "breakpoint_set");
333 m_bpSetTempAction
= createAction(i18n("Set &temporary breakpoint"), Qt::SHIFT
+Qt::Key_F9
,
334 m_filesWindow
, SLOT(slotBrkptSetTemp()), "breakpoint_set_temporary");
335 m_bpEnableAction
= createAction(i18n("&Enable/Disable breakpoint"), Qt::CTRL
+Qt::Key_F9
,
336 m_filesWindow
, SLOT(slotBrkptEnable()), "breakpoint_enable");
338 // only in popup menus
339 createAction(i18n("Watch Expression"), 0,
340 this, SLOT(slotLocalsToWatch()), "watch_expression");
341 m_editValueAction
= createAction(i18n("Edit Value"), Qt::Key_F2
,
342 this, SLOT(slotEditValue()), "edit_value");
344 // all actions force an UI update
345 Q3ValueList
<QAction
*> actions
= actionCollection()->actions();
346 Q3ValueList
<QAction
*>::Iterator it
= actions
.begin();
347 for (; it
!= actions
.end(); ++it
) {
348 connect(*it
, SIGNAL(activated()), this, SLOT(updateUI()));
351 createGUI("kdbgui.rc");
354 void DebuggerMainWnd::initToolbar()
356 KToolBar
* toolbar
= toolBar("mainToolBar");
357 m_animation
= new KAnimatedButton(toolbar
);
358 toolbar
->addWidget(m_animation
);
359 m_animation
->setIcons("pulse");
360 connect(m_animation
, SIGNAL(triggered(QAction
*)), m_debugger
, SLOT(programBreak()));
361 m_animRunning
= false;
363 KStatusBar
* statusbar
= statusBar();
364 statusbar
->insertItem(m_statusActive
, ID_STATUS_ACTIVE
);
365 m_lastActiveStatusText
= m_statusActive
;
366 statusbar
->insertItem("", ID_STATUS_MSG
); /* message pane */
368 // reserve some translations
373 bool DebuggerMainWnd::queryClose()
375 if (m_debugger
!= 0) {
376 m_debugger
->shutdown();
382 // instance properties
383 void DebuggerMainWnd::saveProperties(KConfigGroup
& cg
)
385 // session management
386 QString executable
= "";
387 if (m_debugger
!= 0) {
388 executable
= m_debugger
->executable();
390 cg
.writeEntry("executable", executable
);
393 void DebuggerMainWnd::readProperties(const KConfigGroup
& cg
)
395 // session management
396 QString execName
= cg
.readEntry("executable");
398 TRACE("readProperties: executable=" + execName
);
399 if (!execName
.isEmpty()) {
400 debugProgram(execName
, "");
404 static const char WindowGroup
[] = "Windows";
405 static const char ToolbarGroup
[] = "ToolbarSettings";
406 static const char RecentExecutables
[] = "RecentExecutables";
407 static const char LastSession
[] = "LastSession";
408 static const char OutputWindowGroup
[] = "OutputWindow";
409 static const char TermCmdStr
[] = "TermCmdStr";
410 static const char KeepScript
[] = "KeepScript";
411 static const char DebuggerGroup
[] = "Debugger";
412 static const char DebuggerCmdStr
[] = "DebuggerCmdStr";
413 static const char PreferencesGroup
[] = "Preferences";
414 static const char PopForeground
[] = "PopForeground";
415 static const char BackTimeout
[] = "BackTimeout";
416 static const char TabWidth
[] = "TabWidth";
417 static const char SourceFileFilter
[] = "SourceFileFilter";
418 static const char HeaderFileFilter
[] = "HeaderFileFilter";
420 void DebuggerMainWnd::saveSettings(KSharedConfigPtr config
)
422 KConfigGroup g
= config
->group(WindowGroup
);
424 QByteArray layout
= saveState();
425 g
.writeEntry("Layout", layout
);
426 g
.writeEntry("Geometry", geometry());
428 KConfigGroup tg
= config
->group(ToolbarGroup
);
429 toolBar("mainToolBar")->saveSettings(tg
);
431 m_recentExecAction
->saveEntries(config
->group(RecentExecutables
));
433 KConfigGroup lg
= config
->group(LastSession
);
434 lg
.writeEntry("Width0Locals", m_localVariables
->columnWidth(0));
435 lg
.writeEntry("Width0Watches", m_watches
->columnWidth(0));
437 if (m_debugger
!= 0) {
438 m_debugger
->saveSettings(config
.data());
441 config
->group(OutputWindowGroup
).writeEntry(TermCmdStr
, m_outputTermCmdStr
);
442 config
->group(DebuggerGroup
).writeEntry(DebuggerCmdStr
, m_debuggerCmdStr
);
444 KConfigGroup
pg(config
->group(PreferencesGroup
));
445 pg
.writeEntry(PopForeground
, m_popForeground
);
446 pg
.writeEntry(BackTimeout
, m_backTimeout
);
447 pg
.writeEntry(TabWidth
, m_tabWidth
);
448 pg
.writeEntry(SourceFileFilter
, m_sourceFilter
);
449 pg
.writeEntry(HeaderFileFilter
, m_headerFilter
);
452 void DebuggerMainWnd::restoreSettings(KSharedConfigPtr config
)
454 KConfigGroup g
= config
->group(WindowGroup
);
456 QByteArray layout
= g
.readEntry("Layout", QByteArray());
457 if (!restoreState(layout
))
459 QRect r
= g
.readEntry("Geometry", QRect());
463 toolBar("mainToolBar")->applySettings(config
->group(ToolbarGroup
));
465 m_recentExecAction
->loadEntries(config
->group(RecentExecutables
));
467 KConfigGroup lg
= config
->group(LastSession
);
469 w
= lg
.readEntry("Width0Locals", -1);
470 if (w
>= 0 && w
< 30000)
471 m_localVariables
->setColumnWidth(0, w
);
472 w
= lg
.readEntry("Width0Watches", -1);
473 if (w
>= 0 && w
< 30000)
474 m_watches
->setColumnWidth(0, w
);
476 if (m_debugger
!= 0) {
477 m_debugger
->restoreSettings(config
.data());
480 KConfigGroup
og(config
->group(OutputWindowGroup
));
482 * For debugging and emergency purposes, let the config file override
483 * the shell script that is used to keep the output window open. This
484 * string must have EXACTLY 1 %s sequence in it.
486 setTerminalCmd(og
.readEntry(TermCmdStr
, defaultTermCmdStr
));
487 m_outputTermKeepScript
= og
.readEntry(KeepScript
);
489 setDebuggerCmdStr(config
->group(DebuggerGroup
).readEntry(DebuggerCmdStr
));
491 KConfigGroup
pg(config
->group(PreferencesGroup
));
492 m_popForeground
= pg
.readEntry(PopForeground
, false);
493 m_backTimeout
= pg
.readEntry(BackTimeout
, 1000);
494 m_tabWidth
= pg
.readEntry(TabWidth
, 0);
495 m_sourceFilter
= pg
.readEntry(SourceFileFilter
, m_sourceFilter
);
496 m_headerFilter
= pg
.readEntry(HeaderFileFilter
, m_headerFilter
);
498 emit
setTabWidth(m_tabWidth
);
501 void DebuggerMainWnd::updateUI()
503 m_findAction
->setChecked(m_filesWindow
->m_findDlg
.isVisible());
504 m_findAction
->setEnabled(m_filesWindow
->hasWindows());
505 m_bpSetAction
->setEnabled(m_debugger
->canChangeBreakpoints());
506 m_bpSetTempAction
->setEnabled(m_debugger
->canChangeBreakpoints());
507 m_bpEnableAction
->setEnabled(m_debugger
->canChangeBreakpoints());
508 m_bpTableAction
->setChecked(isDockVisible(m_bpTable
));
509 m_btWindowAction
->setChecked(isDockVisible(m_btWindow
));
510 m_localVariablesAction
->setChecked(isDockVisible(m_localVariables
));
511 m_watchesAction
->setChecked(isDockVisible(m_watches
));
512 m_registersAction
->setChecked(isDockVisible(m_registers
));
513 m_threadsAction
->setChecked(isDockVisible(m_threads
));
514 m_memoryWindowAction
->setChecked(isDockVisible(m_memoryWindow
));
515 m_ttyWindowAction
->setChecked(isDockVisible(m_ttyWindow
));
517 m_fileExecAction
->setEnabled(m_debugger
->isIdle());
518 m_settingsAction
->setEnabled(m_debugger
->haveExecutable());
519 m_coreDumpAction
->setEnabled(m_debugger
->canStart());
520 m_closeAction
->setEnabled(m_filesWindow
->hasWindows());
521 m_reloadAction
->setEnabled(m_filesWindow
->hasWindows());
522 m_stepIntoAction
->setEnabled(m_debugger
->canSingleStep());
523 m_stepIntoIAction
->setEnabled(m_debugger
->canSingleStep());
524 m_stepOverAction
->setEnabled(m_debugger
->canSingleStep());
525 m_stepOverIAction
->setEnabled(m_debugger
->canSingleStep());
526 m_stepOutAction
->setEnabled(m_debugger
->canSingleStep());
527 m_toCursorAction
->setEnabled(m_debugger
->canSingleStep());
528 m_execMovePCAction
->setEnabled(m_debugger
->canSingleStep());
529 m_restartAction
->setEnabled(m_debugger
->canSingleStep());
530 m_attachAction
->setEnabled(m_debugger
->isReady());
531 m_runAction
->setEnabled(m_debugger
->canStart() || m_debugger
->canSingleStep());
532 m_killAction
->setEnabled(m_debugger
->haveExecutable() && m_debugger
->isProgramActive());
533 m_breakAction
->setEnabled(m_debugger
->isProgramRunning());
534 m_argumentsAction
->setEnabled(m_debugger
->haveExecutable());
535 m_editValueAction
->setEnabled(m_debugger
->canSingleStep());
538 if (m_debugger
->isIdle()) {
541 m_animRunning
= false;
544 if (!m_animRunning
) {
545 m_animation
->start();
546 m_animRunning
= true;
552 if (m_debugger
->isProgramActive())
553 newStatus
= m_statusActive
;
554 if (newStatus
!= m_lastActiveStatusText
) {
555 statusBar()->changeItem(newStatus
, ID_STATUS_ACTIVE
);
556 m_lastActiveStatusText
= newStatus
;
560 void DebuggerMainWnd::updateLineItems()
562 m_filesWindow
->updateLineItems(m_debugger
);
565 void DebuggerMainWnd::slotAddWatch()
567 if (m_debugger
!= 0) {
568 QString t
= m_watches
->watchText();
569 m_debugger
->addWatch(t
);
573 void DebuggerMainWnd::slotAddWatch(const QString
& text
)
575 if (m_debugger
!= 0) {
576 m_debugger
->addWatch(text
);
580 void DebuggerMainWnd::slotNewFileLoaded()
582 // updates program counter in the new file
584 m_filesWindow
->updateLineItems(m_debugger
);
587 QDockWidget
* DebuggerMainWnd::dockParent(QWidget
* w
)
589 while ((w
= w
->parentWidget()) != 0) {
590 if (w
->isA("QDockWidget"))
591 return static_cast<QDockWidget
*>(w
);
596 bool DebuggerMainWnd::isDockVisible(QWidget
* w
)
598 QDockWidget
* d
= dockParent(w
);
599 return d
!= 0 && d
->isVisible();
602 void DebuggerMainWnd::makeDefaultLayout()
604 // +---------------+---------+
605 // | Source | Locals |
607 // |---------------+---------+
608 // |Stack, Brkpts, | Watches |
610 // +---------------+---------+
612 addDockWidget(Qt::RightDockWidgetArea
, dockParent(m_localVariables
));
613 addDockWidget(Qt::BottomDockWidgetArea
, dockParent(m_memoryWindow
));
614 splitDockWidget(dockParent(m_memoryWindow
), dockParent(m_threads
), Qt::Horizontal
);
615 tabifyDockWidget(dockParent(m_memoryWindow
), dockParent(m_registers
));
616 tabifyDockWidget(dockParent(m_registers
), dockParent(m_bpTable
));
617 tabifyDockWidget(dockParent(m_bpTable
), dockParent(m_ttyWindow
));
618 tabifyDockWidget(dockParent(m_ttyWindow
), dockParent(m_btWindow
));
619 tabifyDockWidget(dockParent(m_threads
), dockParent(m_watches
));
620 dockParent(m_localVariables
)->setVisible(true);
621 dockParent(m_ttyWindow
)->setVisible(true);
622 dockParent(m_watches
)->setVisible(true);
623 dockParent(m_btWindow
)->setVisible(true);
624 dockParent(m_bpTable
)->setVisible(true);
627 bool DebuggerMainWnd::debugProgram(const QString
& exe
, const QString
& lang
)
629 // check the file name
632 bool success
= fi
.isFile();
635 QString msg
= i18n("`%1' is not a file or does not exist");
636 KMessageBox::sorry(this, msg
.arg(exe
));
640 success
= startDriver(fi
.absFilePath(), lang
);
645 m_recentExecAction
->addUrl(KUrl(fi
.absFilePath()));
647 // keep the directory
648 m_lastDirectory
= fi
.dirPath(true);
649 m_filesWindow
->setExtraDirectory(m_lastDirectory
);
651 // set caption to basename part of executable
652 QString caption
= fi
.fileName();
657 m_recentExecAction
->removeUrl(KUrl(fi
.absFilePath()));
663 static const char GeneralGroup
[] = "General";
665 bool DebuggerMainWnd::startDriver(const QString
& executable
, QString lang
)
667 assert(m_debugger
!= 0);
669 TRACE(QString("trying language '%1'...").arg(lang
));
670 DebuggerDriver
* driver
= driverFromLang(lang
);
674 // see if there is a language in the per-program config file
675 QString configName
= m_debugger
->getConfigForExe(executable
);
676 if (QFile::exists(configName
))
678 KConfig
c(configName
, KConfig::SimpleConfig
);
680 // Using "GDB" as default here is for backwards compatibility:
681 // The config file exists but doesn't have an entry,
682 // so it must have been created by an old version of KDbg
683 // that had only the GDB driver.
684 lang
= c
.group(GeneralGroup
)
685 .readEntry(KDebugger::DriverNameEntry
, "GDB");
687 TRACE(QString("...bad, trying config driver %1...").arg(lang
));
688 driver
= driverFromLang(lang
);
694 QString name
= driverNameFromFile(executable
);
696 TRACE(QString("...no luck, trying %1 derived"
697 " from file contents").arg(name
));
698 driver
= driverFromLang(name
);
703 QString msg
= i18n("Don't know how to debug language `%1'");
704 KMessageBox::sorry(this, msg
.arg(lang
));
708 driver
->setLogFileName(m_transcriptFile
);
710 bool success
= m_debugger
->debugProgram(executable
, driver
);
716 QString msg
= i18n("Could not start the debugger process.\n"
717 "Please shut down KDbg and resolve the problem.");
718 KMessageBox::sorry(this, msg
);
724 // derive driver from language
725 DebuggerDriver
* DebuggerMainWnd::driverFromLang(QString lang
)
727 // lang is needed in all lowercase
730 // The following table relates languages and debugger drivers
731 static const struct L
{
732 const char* shortest
; // abbreviated to this is still unique
733 const char* full
; // full name of language
737 { "f", "fortran", 1 },
738 { "p", "python", 3 },
740 // the following are actually driver names
742 { "xsldbg", "xsldbg", 2 },
744 const int N
= sizeof(langs
)/sizeof(langs
[0]);
746 // lookup the language name
748 for (int i
= 0; i
< N
; i
++)
750 const L
& l
= langs
[i
];
752 // shortest must match
753 if (!lang
.startsWith(l
.shortest
))
756 // lang must not be longer than the full name, and it must match
757 if (QString(l
.full
).startsWith(lang
))
763 DebuggerDriver
* driver
= 0;
767 GdbDriver
* gdb
= new GdbDriver
;
768 gdb
->setDefaultInvocation(m_debuggerCmdStr
);
773 driver
= new XsldbgDriver
;
783 * Try to guess the language to use from the contents of the file.
785 QString
DebuggerMainWnd::driverNameFromFile(const QString
& exe
)
787 /* Inprecise but simple test to see if file is in XSLT language */
788 if (exe
.right(4).lower() == ".xsl")
794 void DebuggerMainWnd::setCoreFile(const QString
& corefile
)
796 assert(m_debugger
!= 0);
797 m_debugger
->useCoreFile(corefile
, true);
800 void DebuggerMainWnd::setRemoteDevice(const QString
& remoteDevice
)
802 if (m_debugger
!= 0) {
803 m_debugger
->setRemoteDevice(remoteDevice
);
807 void DebuggerMainWnd::overrideProgramArguments(const QString
& args
)
809 assert(m_debugger
!= 0);
810 m_debugger
->overrideProgramArguments(args
);
813 void DebuggerMainWnd::setTranscript(const QString
& name
)
815 m_transcriptFile
= name
;
816 if (m_debugger
!= 0 && m_debugger
->driver() != 0)
817 m_debugger
->driver()->setLogFileName(m_transcriptFile
);
820 void DebuggerMainWnd::setAttachPid(const QString
& pid
)
822 assert(m_debugger
!= 0);
823 m_debugger
->setAttachPid(pid
);
826 void DebuggerMainWnd::slotNewStatusMsg()
828 QString msg
= m_debugger
->statusMessage();
829 statusBar()->changeItem(msg
, ID_STATUS_MSG
);
832 void DebuggerMainWnd::slotFileGlobalSettings()
834 int oldTabWidth
= m_tabWidth
;
836 Q3TabDialog
dlg(this, "global_options", true);
837 QString title
= KGlobal::caption();
838 title
+= i18n(": Global options");
839 dlg
.setCaption(title
);
840 dlg
.setCancelButton(i18n("Cancel"));
841 dlg
.setOKButton(i18n("OK"));
843 PrefDebugger
prefDebugger(&dlg
);
844 prefDebugger
.setDebuggerCmd(m_debuggerCmdStr
.isEmpty() ?
845 GdbDriver::defaultGdb() : m_debuggerCmdStr
);
846 prefDebugger
.setTerminal(m_outputTermCmdStr
);
848 PrefMisc
prefMisc(&dlg
);
849 prefMisc
.setPopIntoForeground(m_popForeground
);
850 prefMisc
.setBackTimeout(m_backTimeout
);
851 prefMisc
.setTabWidth(m_tabWidth
);
852 prefMisc
.setSourceFilter(m_sourceFilter
);
853 prefMisc
.setHeaderFilter(m_headerFilter
);
855 dlg
.addTab(&prefDebugger
, i18n("&Debugger"));
856 dlg
.addTab(&prefMisc
, i18n("&Miscellaneous"));
857 if (dlg
.exec() == QDialog::Accepted
)
859 setDebuggerCmdStr(prefDebugger
.debuggerCmd());
860 setTerminalCmd(prefDebugger
.terminal());
861 m_popForeground
= prefMisc
.popIntoForeground();
862 m_backTimeout
= prefMisc
.backTimeout();
863 m_tabWidth
= prefMisc
.tabWidth();
864 m_sourceFilter
= prefMisc
.sourceFilter();
865 if (m_sourceFilter
.isEmpty())
866 m_sourceFilter
= defaultSourceFilter
;
867 m_headerFilter
= prefMisc
.headerFilter();
868 if (m_headerFilter
.isEmpty())
869 m_headerFilter
= defaultHeaderFilter
;
872 if (m_tabWidth
!= oldTabWidth
) {
873 emit
setTabWidth(m_tabWidth
);
877 void DebuggerMainWnd::setTerminalCmd(const QString
& cmd
)
879 m_outputTermCmdStr
= cmd
;
880 // revert to default if empty
881 if (m_outputTermCmdStr
.isEmpty()) {
882 m_outputTermCmdStr
= defaultTermCmdStr
;
886 void DebuggerMainWnd::setDebuggerCmdStr(const QString
& cmd
)
888 m_debuggerCmdStr
= cmd
;
889 // make empty if it is the default
890 if (m_debuggerCmdStr
== GdbDriver::defaultGdb()) {
891 m_debuggerCmdStr
= QString();
895 void DebuggerMainWnd::slotDebuggerStarting()
897 if (m_debugger
== 0) /* paranoia check */
900 if (m_ttyLevel
== m_debugger
->ttyLevel())
903 // shut down terminal emulations we will not need
904 switch (m_ttyLevel
) {
905 case KDebugger::ttySimpleOutputOnly
:
906 m_ttyWindow
->deactivate();
908 case KDebugger::ttyFull
:
909 if (m_outputTermProc
!= 0) {
910 m_outputTermProc
->kill();
911 // will be deleted in slot
917 m_ttyLevel
= m_debugger
->ttyLevel();
920 switch (m_ttyLevel
) {
921 case KDebugger::ttySimpleOutputOnly
:
922 ttyName
= m_ttyWindow
->activate();
924 case KDebugger::ttyFull
:
925 if (m_outputTermProc
== 0) {
926 // create an output window
927 ttyName
= createOutputWindow();
928 TRACE(ttyName
.isEmpty() ?
929 "createOuputWindow failed" : "successfully created output window");
935 m_debugger
->setTerminal(ttyName
);
938 void DebuggerMainWnd::slotToggleBreak(const QString
& fileName
, int lineNo
,
939 const DbgAddr
& address
, bool temp
)
941 // lineNo is zero-based
942 if (m_debugger
!= 0) {
943 m_debugger
->setBreakpoint(fileName
, lineNo
, address
, temp
);
947 void DebuggerMainWnd::slotEnaDisBreak(const QString
& fileName
, int lineNo
,
948 const DbgAddr
& address
)
950 // lineNo is zero-based
951 if (m_debugger
!= 0) {
952 m_debugger
->enableDisableBreakpoint(fileName
, lineNo
, address
);
956 QString
DebuggerMainWnd::createOutputWindow()
958 // create a name for a fifo
960 fifoName
.sprintf("/tmp/kdbgttywin%05d", ::getpid());
962 // create a fifo that will pass in the tty name
963 QFile::remove(fifoName
); // remove remnants
965 if (::mkfifo(fifoName
.local8Bit(), S_IRUSR
|S_IWUSR
) < 0) {
967 TRACE("mkfifo " + fifoName
+ " failed");
971 if (::mknod(fifoName
.local8Bit(), S_IFIFO
| S_IRUSR
|S_IWUSR
, 0) < 0) {
973 TRACE("mknod " + fifoName
+ " failed");
978 m_outputTermProc
= new K3Process
;
981 * Spawn an xterm that in turn runs a shell script that passes us
982 * back the terminal name and then only sits and waits.
984 static const char shellScriptFmt
[] =
986 "trap \"\" INT QUIT TSTP;" /* ignore various signals */
987 "exec<&-;exec>&-;" /* close stdin and stdout */
988 "while :;do sleep 3600;done";
989 // let config file override this script
991 if (!m_outputTermKeepScript
.isEmpty()) {
992 shellScript
= m_outputTermKeepScript
;
994 shellScript
= shellScriptFmt
;
997 shellScript
.replace("%s", fifoName
);
998 TRACE("output window script is " + shellScript
);
1000 QString title
= KGlobal::caption();
1001 title
+= i18n(": Program output");
1003 // parse the command line specified in the preferences
1004 QStringList cmdParts
= QStringList::split(' ', m_outputTermCmdStr
);
1007 * Build the argv array. Thereby substitute special sequences:
1014 { "%C", shellScript
}
1017 for (QStringList::iterator i
= cmdParts
.begin(); i
!= cmdParts
.end(); ++i
)
1020 for (int j
= sizeof(substitute
)/sizeof(substitute
[0])-1; j
>= 0; j
--) {
1021 int pos
= str
.find(substitute
[j
].seq
);
1023 str
.replace(pos
, 2, substitute
[j
].replace
);
1024 break; /* substitute only one sequence */
1027 *m_outputTermProc
<< str
;
1032 if (m_outputTermProc
->start())
1034 // read the ttyname from the fifo
1036 if (f
.open(QIODevice::ReadOnly
))
1038 QByteArray t
= f
.readAll();
1039 tty
= QString::fromLocal8Bit(t
, t
.size());
1044 // remove whitespace
1045 tty
= tty
.stripWhiteSpace();
1046 TRACE("tty=" + tty
);
1048 connect(m_outputTermProc
, SIGNAL(processExited(K3Process
*)),
1049 SLOT(slotTermEmuExited()));
1053 // error, could not start xterm
1054 TRACE("fork failed for fifo " + fifoName
);
1055 QFile::remove(fifoName
);
1056 shutdownTermWindow();
1062 void DebuggerMainWnd::slotTermEmuExited()
1064 shutdownTermWindow();
1067 void DebuggerMainWnd::shutdownTermWindow()
1069 delete m_outputTermProc
;
1070 m_outputTermProc
= 0;
1073 void DebuggerMainWnd::slotProgramStopped()
1075 // when the program stopped, move the window to the foreground
1076 if (m_popForeground
) {
1077 // unfortunately, this requires quite some force to work :-(
1078 KWindowSystem::raiseWindow(winId());
1079 KWindowSystem::forceActiveWindow(winId());
1084 void DebuggerMainWnd::intoBackground()
1086 if (m_popForeground
) {
1087 m_backTimer
.start(m_backTimeout
, true); /* single-shot */
1091 void DebuggerMainWnd::slotBackTimer()
1096 void DebuggerMainWnd::slotRecentExec(const KUrl
& url
)
1098 QString exe
= url
.path();
1099 debugProgram(exe
, "");
1102 QString
DebuggerMainWnd::makeSourceFilter()
1105 f
= m_sourceFilter
+ " " + m_headerFilter
+ i18n("|All source files\n");
1106 f
+= m_sourceFilter
+ i18n("|Source files\n");
1107 f
+= m_headerFilter
+ i18n("|Header files\n");
1108 f
+= i18n("*|All files");
1113 * Pop up the context menu in the locals window
1115 void DebuggerMainWnd::slotLocalsPopup(Q3ListViewItem
*, const QPoint
& pt
)
1117 Q3PopupMenu
* popup
=
1118 static_cast<Q3PopupMenu
*>(factory()->container("popup_locals", this));
1122 if (popup
->isVisible()) {
1130 * Copies the currently selected item to the watch window.
1132 void DebuggerMainWnd::slotLocalsToWatch()
1134 VarTree
* item
= m_localVariables
->selectedItem();
1136 if (item
!= 0 && m_debugger
!= 0) {
1137 QString text
= item
->computeExpr();
1138 m_debugger
->addWatch(text
);
1143 * Starts editing a value in a value display
1145 void DebuggerMainWnd::slotEditValue()
1147 // does one of the value trees have the focus
1148 QWidget
* f
= kapp
->focusWidget();
1150 if (f
== m_localVariables
) {
1151 wnd
= m_localVariables
;
1152 } else if (f
== m_watches
->watchVariables()) {
1153 wnd
= m_watches
->watchVariables();
1158 if (m_localVariables
->isEditing() ||
1159 m_watches
->watchVariables()->isEditing())
1161 return; /* don't edit twice */
1164 VarTree
* expr
= wnd
->currentItem();
1165 if (expr
!= 0 && m_debugger
!= 0 && m_debugger
->canSingleStep())
1167 TRACE("edit value");
1168 // determine the text to edit
1169 QString text
= m_debugger
->driver()->editableValue(expr
);
1170 wnd
->editValue(expr
, text
);
1174 // helper that gets a file name (it only differs in the caption of the dialog)
1175 static QString
myGetFileName(QString caption
,
1176 QString dir
, QString filter
,
1180 KFileDialog
dlg(dir
, filter
, parent
);
1182 dlg
.setCaption(caption
);
1184 if (dlg
.exec() == QDialog::Accepted
)
1185 filename
= dlg
.selectedFile();
1190 void DebuggerMainWnd::slotFileOpen()
1192 // start browsing in the active file's directory
1193 // fall back to last used directory (executable)
1194 QString dir
= m_lastDirectory
;
1195 QString fileName
= m_filesWindow
->activeFileName();
1196 if (!fileName
.isEmpty()) {
1197 QFileInfo
fi(fileName
);
1201 fileName
= myGetFileName(i18n("Open"),
1203 makeSourceFilter(), this);
1205 if (!fileName
.isEmpty())
1207 QFileInfo
fi(fileName
);
1208 m_lastDirectory
= fi
.dirPath();
1209 m_filesWindow
->setExtraDirectory(m_lastDirectory
);
1210 m_filesWindow
->activateFile(fileName
);
1214 void DebuggerMainWnd::slotFileExe()
1216 if (m_debugger
->isIdle())
1218 // open a new executable
1219 QString executable
= myGetFileName(i18n("Select the executable to debug"),
1220 m_lastDirectory
, 0, this);
1221 if (executable
.isEmpty())
1224 debugProgram(executable
, "");
1228 void DebuggerMainWnd::slotFileCore()
1230 if (m_debugger
->canStart())
1232 QString corefile
= myGetFileName(i18n("Select core dump"),
1233 m_lastDirectory
, 0, this);
1234 if (!corefile
.isEmpty()) {
1235 m_debugger
->useCoreFile(corefile
, false);
1240 void DebuggerMainWnd::slotFileProgSettings()
1242 if (m_debugger
!= 0) {
1243 m_debugger
->programSettings(this);
1247 void DebuggerMainWnd::slotViewStatusbar()
1249 if (statusBar()->isVisible())
1250 statusBar()->hide();
1252 statusBar()->show();
1256 void DebuggerMainWnd::slotExecUntil()
1258 if (m_debugger
!= 0)
1262 if (m_filesWindow
->activeLine(file
, lineNo
))
1263 m_debugger
->runUntil(file
, lineNo
);
1267 void DebuggerMainWnd::slotExecAttach()
1270 ProcAttachPS
dlg(this);
1271 // seed filter with executable name
1272 QFileInfo fi
= m_debugger
->executable();
1273 dlg
.setFilterText(fi
.fileName());
1275 ProcAttach
dlg(this);
1276 dlg
.setText(m_debugger
->attachedPid());
1279 m_debugger
->attachProgram(dlg
.text());
1283 void DebuggerMainWnd::slotExecArgs()
1285 if (m_debugger
!= 0) {
1286 m_debugger
->programArgs(this);
1290 void DebuggerMainWnd::slotConfigureKeys()
1292 KShortcutsDialog::configure(actionCollection());
1295 #include "dbgmainwnd.moc"