Update version number and build instructions in the spec file.
[kdbg.git] / kdbg / dbgmainwnd.cpp
blobe3d6232ae6de73eecf8c9ef8f70d3a78a6786c15
1 /*
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.
5 */
7 #include <kapplication.h>
8 #include <klocale.h> /* i18n */
9 #include <kmessagebox.h>
10 #include <kconfig.h>
11 #include <kstatusbar.h>
12 #include <kicon.h>
13 #include <kstandardaction.h>
14 #include <kstandardshortcut.h>
15 #include <kaction.h>
16 #include <kactioncollection.h>
17 #include <krecentfilesaction.h>
18 #include <ktoggleaction.h>
19 #include <kfiledialog.h>
20 #include <kshortcutsdialog.h>
21 #include <kanimatedbutton.h>
22 #include <kwindowsystem.h>
23 #include <ktoolbar.h>
24 #include <kurl.h>
25 #include <kxmlguifactory.h>
26 #include <KPageDialog>
27 #include <QListWidget>
28 #include <QFile>
29 #include <QFileInfo>
30 #include <QList>
31 #include <QDockWidget>
32 #include <QProcess>
33 #include "dbgmainwnd.h"
34 #include "debugger.h"
35 #include "commandids.h"
36 #include "winstack.h"
37 #include "brkpt.h"
38 #include "threadlist.h"
39 #include "memwindow.h"
40 #include "ttywnd.h"
41 #include "watchwindow.h"
42 #include "procattach.h"
43 #include "prefdebugger.h"
44 #include "prefmisc.h"
45 #include "gdbdriver.h"
46 #include "xsldbgdriver.h"
47 #include "mydebug.h"
48 #include <sys/stat.h> /* mknod(2) */
49 #include <unistd.h> /* getpid */
52 static const char defaultTermCmdStr[] = "xterm -name kdbgio -title %T -e sh -c %C";
53 static const char defaultSourceFilter[] = "*.c *.cc *.cpp *.c++ *.C *.CC";
54 static const char defaultHeaderFilter[] = "*.h *.hh *.hpp *.h++";
56 DebuggerMainWnd::DebuggerMainWnd() :
57 KXmlGuiWindow(),
58 m_debugger(0),
59 #ifdef GDB_TRANSCRIPT
60 m_transcriptFile(GDB_TRANSCRIPT),
61 #endif
62 m_outputTermCmdStr(defaultTermCmdStr),
63 m_outputTermProc(new QProcess),
64 m_ttyLevel(-1), /* no tty yet */
65 m_popForeground(false),
66 m_backTimeout(1000),
67 m_tabWidth(0),
68 m_sourceFilter(defaultSourceFilter),
69 m_headerFilter(defaultHeaderFilter),
70 m_statusActive(i18n("active"))
72 setDockNestingEnabled(true);
74 m_filesWindow = new WinStack(this);
75 setCentralWidget(m_filesWindow);
77 QDockWidget* dw1 = createDockWidget("Stack", i18n("Stack"));
78 m_btWindow = new QListWidget(dw1);
79 dw1->setWidget(m_btWindow);
80 QDockWidget* dw2 = createDockWidget("Locals", i18n("Locals"));
81 m_localVariables = new ExprWnd(dw2, i18n("Variable"));
82 dw2->setWidget(m_localVariables);
83 QDockWidget* dw3 = createDockWidget("Watches", i18n("Watches"));
84 m_watches = new WatchWindow(dw3);
85 dw3->setWidget(m_watches);
86 QDockWidget* dw4 = createDockWidget("Registers", i18n("Registers"));
87 m_registers = new RegisterView(dw4);
88 dw4->setWidget(m_registers);
89 QDockWidget* dw5 = createDockWidget("Breakpoints", i18n("Breakpoints"));
90 m_bpTable = new BreakpointTable(dw5);
91 dw5->setWidget(m_bpTable);
92 QDockWidget* dw6 = createDockWidget("Output", i18n("Output"));
93 m_ttyWindow = new TTYWindow(dw6);
94 dw6->setWidget(m_ttyWindow);
95 QDockWidget* dw7 = createDockWidget("Threads", i18n("Threads"));
96 m_threads = new ThreadList(dw7);
97 dw7->setWidget(m_threads);
98 QDockWidget* dw8 = createDockWidget("Memory", i18n("Memory"));
99 m_memoryWindow = new MemoryWindow(dw8);
100 dw8->setWidget(m_memoryWindow);
102 m_debugger = new KDebugger(this, m_localVariables, m_watches->watchVariables(), m_btWindow);
104 connect(m_debugger, SIGNAL(updateStatusMessage()), SLOT(slotNewStatusMsg()));
105 connect(m_debugger, SIGNAL(updateUI()), SLOT(updateUI()));
106 connect(m_debugger, SIGNAL(breakpointsChanged()), SLOT(updateLineItems()));
107 connect(m_debugger, SIGNAL(debuggerStarting()), SLOT(slotDebuggerStarting()));
108 m_bpTable->setDebugger(m_debugger);
109 m_memoryWindow->setDebugger(m_debugger);
111 setStandardToolBarMenuEnabled(true);
112 initKAction();
113 initStatusBar();
115 connect(m_watches, SIGNAL(addWatch()), SLOT(slotAddWatch()));
116 connect(m_watches, SIGNAL(deleteWatch()), m_debugger, SLOT(slotDeleteWatch()));
117 connect(m_watches, SIGNAL(textDropped(const QString&)), SLOT(slotAddWatch(const QString&)));
119 connect(&m_filesWindow->m_findDlg, SIGNAL(closed()), SLOT(updateUI()));
120 connect(m_filesWindow, SIGNAL(newFileLoaded()),
121 SLOT(slotNewFileLoaded()));
122 connect(m_filesWindow, SIGNAL(toggleBreak(const QString&,int,const DbgAddr&,bool)),
123 this, SLOT(slotToggleBreak(const QString&,int,const DbgAddr&,bool)));
124 connect(m_filesWindow, SIGNAL(enadisBreak(const QString&,int,const DbgAddr&)),
125 this, SLOT(slotEnaDisBreak(const QString&,int,const DbgAddr&)));
126 connect(m_debugger, SIGNAL(activateFileLine(const QString&,int,const DbgAddr&)),
127 m_filesWindow, SLOT(activate(const QString&,int,const DbgAddr&)));
128 connect(m_debugger, SIGNAL(executableUpdated()),
129 m_filesWindow, SLOT(reloadAllFiles()));
130 connect(m_debugger, SIGNAL(updatePC(const QString&,int,const DbgAddr&,int)),
131 m_filesWindow, SLOT(updatePC(const QString&,int,const DbgAddr&,int)));
132 // value popup communication
133 connect(m_filesWindow, SIGNAL(initiateValuePopup(const QString&)),
134 m_debugger, SLOT(slotValuePopup(const QString&)));
135 connect(m_debugger, SIGNAL(valuePopup(const QString&)),
136 m_filesWindow, SLOT(slotShowValueTip(const QString&)));
137 // disassembling
138 connect(m_filesWindow, SIGNAL(disassemble(const QString&, int)),
139 m_debugger, SLOT(slotDisassemble(const QString&, int)));
140 connect(m_debugger, SIGNAL(disassembled(const QString&,int,const std::list<DisassembledCode>&)),
141 m_filesWindow, SLOT(slotDisassembled(const QString&,int,const std::list<DisassembledCode>&)));
142 connect(m_filesWindow, SIGNAL(moveProgramCounter(const QString&,int,const DbgAddr&)),
143 m_debugger, SLOT(setProgramCounter(const QString&,int,const DbgAddr&)));
144 // program stopped
145 connect(m_debugger, SIGNAL(programStopped()), SLOT(slotProgramStopped()));
146 connect(&m_backTimer, SIGNAL(timeout()), SLOT(slotBackTimer()));
147 // tab width
148 connect(this, SIGNAL(setTabWidth(int)), m_filesWindow, SIGNAL(setTabWidth(int)));
150 // connect breakpoint table
151 connect(m_bpTable, SIGNAL(activateFileLine(const QString&,int,const DbgAddr&)),
152 m_filesWindow, SLOT(activate(const QString&,int,const DbgAddr&)));
153 connect(m_debugger, SIGNAL(updateUI()), m_bpTable, SLOT(updateUI()));
154 connect(m_debugger, SIGNAL(breakpointsChanged()), m_bpTable, SLOT(updateBreakList()));
155 connect(m_debugger, SIGNAL(breakpointsChanged()), m_bpTable, SLOT(updateUI()));
157 connect(m_debugger, SIGNAL(registersChanged(const std::list<RegisterInfo>&)),
158 m_registers, SLOT(updateRegisters(const std::list<RegisterInfo>&)));
160 connect(m_debugger, SIGNAL(memoryDumpChanged(const QString&, const std::list<MemoryDump>&)),
161 m_memoryWindow, SLOT(slotNewMemoryDump(const QString&, const std::list<MemoryDump>&)));
162 connect(m_debugger, SIGNAL(saveProgramSpecific(KConfigBase*)),
163 m_memoryWindow, SLOT(saveProgramSpecific(KConfigBase*)));
164 connect(m_debugger, SIGNAL(restoreProgramSpecific(KConfigBase*)),
165 m_memoryWindow, SLOT(restoreProgramSpecific(KConfigBase*)));
167 // thread window
168 connect(m_debugger, SIGNAL(threadsChanged(const std::list<ThreadInfo>&)),
169 m_threads, SLOT(updateThreads(const std::list<ThreadInfo>&)));
170 connect(m_threads, SIGNAL(setThread(int)),
171 m_debugger, SLOT(setThread(int)));
173 // popup menu of the local variables window
174 m_localVariables->setContextMenuPolicy(Qt::CustomContextMenu);
175 connect(m_localVariables, SIGNAL(customContextMenuRequested(const QPoint&)),
176 this, SLOT(slotLocalsPopup(const QPoint&)));
178 makeDefaultLayout();
179 setupGUI(KXmlGuiWindow::Default, "kdbgui.rc");
180 restoreSettings(KGlobal::config());
182 // The animation button is not part of the restored window state.
183 // We must create it after the toolbar was loaded.
184 initAnimation();
186 updateUI();
187 m_bpTable->updateUI();
190 DebuggerMainWnd::~DebuggerMainWnd()
192 saveSettings(KGlobal::config());
193 // must delete m_debugger early since it references our windows
194 delete m_debugger;
195 m_debugger = 0;
197 delete m_memoryWindow;
198 delete m_threads;
199 delete m_ttyWindow;
200 delete m_bpTable;
201 delete m_registers;
202 delete m_watches;
203 delete m_localVariables;
204 delete m_btWindow;
205 delete m_filesWindow;
207 delete m_outputTermProc;
210 QDockWidget* DebuggerMainWnd::createDockWidget(const char* name, const QString& title)
212 QDockWidget* w = new QDockWidget(title, this);
213 w->setObjectName(name);
214 // view menu changes when docking state changes
215 connect(w, SIGNAL(visibilityChanged(bool)), SLOT(updateUI()));
216 return w;
219 QAction* DebuggerMainWnd::createAction(const QString& text, const char* icon,
220 int shortcut, const QObject* receiver,
221 const char* slot, const char* name)
223 KAction* a = actionCollection()->addAction(name);
224 a->setText(text);
225 a->setIcon(KIcon(icon));
226 if (shortcut)
227 a->setShortcut(KShortcut(shortcut));
228 connect(a, SIGNAL(triggered()), receiver, slot);
229 return a;
232 QAction* DebuggerMainWnd::createAction(const QString& text,
233 int shortcut, const QObject* receiver,
234 const char* slot, const char* name)
236 KAction* a = actionCollection()->addAction(name);
237 a->setText(text);
238 if (shortcut)
239 a->setShortcut(KShortcut(shortcut));
240 connect(a, SIGNAL(triggered()), receiver, slot);
241 return a;
245 void DebuggerMainWnd::initKAction()
247 // file menu
248 KAction* open = KStandardAction::open(this, SLOT(slotFileOpen()),
249 actionCollection());
250 open->setText(i18n("&Open Source..."));
251 m_closeAction = KStandardAction::close(m_filesWindow, SLOT(slotClose()), actionCollection());
252 m_reloadAction = createAction(i18n("&Reload Source"), "view-refresh", 0,
253 m_filesWindow, SLOT(slotFileReload()), "file_reload");
254 m_fileExecAction = createAction(i18n("&Executable..."),
255 "document-open-executable", 0,
256 this, SLOT(slotFileExe()), "file_executable");
257 m_recentExecAction = KStandardAction::openRecent(this, SLOT(slotRecentExec(const KUrl&)),
258 actionCollection());
259 m_recentExecAction->setObjectName("file_executable_recent");
260 m_recentExecAction->setText(i18n("Recent E&xecutables"));
261 m_coreDumpAction = createAction(i18n("&Core dump..."), 0,
262 this, SLOT(slotFileCore()), "file_core_dump");
263 KStandardAction::quit(kapp, SLOT(closeAllWindows()), actionCollection());
265 // settings menu
266 m_settingsAction = createAction(i18n("This &Program..."), 0,
267 this, SLOT(slotFileProgSettings()), "settings_program");
268 createAction(i18n("&Global Options..."), 0,
269 this, SLOT(slotFileGlobalSettings()), "settings_global");
270 KStandardAction::keyBindings(this, SLOT(slotConfigureKeys()), actionCollection());
271 KStandardAction::showStatusbar(this, SLOT(slotViewStatusbar()), actionCollection());
273 // view menu
274 m_findAction = KStandardAction::find(m_filesWindow, SLOT(slotViewFind()), actionCollection());
275 KStandardAction::findNext(m_filesWindow, SLOT(slotFindForward()), actionCollection());
276 KStandardAction::findPrev(m_filesWindow, SLOT(slotFindBackward()), actionCollection());
278 i18n("Source &code");
279 struct { QString text; QWidget* w; QString id; QAction** act; } dw[] = {
280 { i18n("Stac&k"), m_btWindow, "view_stack", &m_btWindowAction },
281 { i18n("&Locals"), m_localVariables, "view_locals", &m_localVariablesAction },
282 { i18n("&Watched expressions"), m_watches, "view_watched_expressions", &m_watchesAction },
283 { i18n("&Registers"), m_registers, "view_registers", &m_registersAction },
284 { i18n("&Breakpoints"), m_bpTable, "view_breakpoints", &m_bpTableAction },
285 { i18n("T&hreads"), m_threads, "view_threads", &m_threadsAction },
286 { i18n("&Output"), m_ttyWindow, "view_output", &m_ttyWindowAction },
287 { i18n("&Memory"), m_memoryWindow, "view_memory", &m_memoryWindowAction }
289 for (unsigned i = 0; i < sizeof(dw)/sizeof(dw[0]); i++) {
290 QDockWidget* d = dockParent(dw[i].w);
291 *dw[i].act = new KToggleAction(dw[i].text, actionCollection());
292 actionCollection()->addAction(dw[i].id, *dw[i].act);
293 connect(*dw[i].act, SIGNAL(triggered()), d, SLOT(show()));
296 // execution menu
297 m_runAction = createAction(i18n("&Run"),
298 "debug-run", Qt::Key_F5,
299 m_debugger, SLOT(programRun()), "exec_run");
300 connect(m_runAction, SIGNAL(activated()), this, SLOT(intoBackground()));
301 m_stepIntoAction = createAction(i18n("Step &into"),
302 "debug-step-into", Qt::Key_F8,
303 m_debugger, SLOT(programStep()), "exec_step_into");
304 connect(m_stepIntoAction, SIGNAL(activated()), this, SLOT(intoBackground()));
305 m_stepOverAction = createAction(i18n("Step &over"),
306 "debug-step-over", Qt::Key_F10,
307 m_debugger, SLOT(programNext()), "exec_step_over");
308 connect(m_stepOverAction, SIGNAL(activated()), this, SLOT(intoBackground()));
309 m_stepOutAction = createAction(i18n("Step o&ut"),
310 "debug-step-out", Qt::Key_F6,
311 m_debugger, SLOT(programFinish()), "exec_step_out");
312 connect(m_stepOutAction, SIGNAL(activated()), this, SLOT(intoBackground()));
313 m_toCursorAction = createAction(i18n("Run to &cursor"),
314 "debug-execute-to-cursor", Qt::Key_F7,
315 this, SLOT(slotExecUntil()), "exec_run_to_cursor");
316 connect(m_toCursorAction, SIGNAL(activated()), this, SLOT(intoBackground()));
317 m_stepIntoIAction = createAction(i18n("Step i&nto by instruction"),
318 "debug-step-into-instruction", Qt::SHIFT+Qt::Key_F8,
319 m_debugger, SLOT(programStepi()), "exec_step_into_by_insn");
320 connect(m_stepIntoIAction, SIGNAL(activated()), this, SLOT(intoBackground()));
321 m_stepOverIAction = createAction(i18n("Step o&ver by instruction"),
322 "debug-step-instruction", Qt::SHIFT+Qt::Key_F10,
323 m_debugger, SLOT(programNexti()), "exec_step_over_by_insn");
324 connect(m_stepOverIAction, SIGNAL(activated()), this, SLOT(intoBackground()));
325 m_execMovePCAction = createAction(i18n("&Program counter to current line"),
326 "debug-run-cursor", 0,
327 m_filesWindow, SLOT(slotMoveProgramCounter()), "exec_movepc");
328 m_breakAction = createAction(i18n("&Break"), 0,
329 m_debugger, SLOT(programBreak()), "exec_break");
330 m_killAction = createAction(i18n("&Kill"), 0,
331 m_debugger, SLOT(programKill()), "exec_kill");
332 m_restartAction = createAction(i18n("Re&start"), 0,
333 m_debugger, SLOT(programRunAgain()), "exec_restart");
334 m_attachAction = createAction(i18n("A&ttach..."), 0,
335 this, SLOT(slotExecAttach()), "exec_attach");
336 m_argumentsAction = createAction(i18n("&Arguments..."), 0,
337 this, SLOT(slotExecArgs()), "exec_arguments");
339 // breakpoint menu
340 m_bpSetAction = createAction(i18n("Set/Clear &breakpoint"), "brkpt", Qt::Key_F9,
341 m_filesWindow, SLOT(slotBrkptSet()), "breakpoint_set");
342 m_bpSetTempAction = createAction(i18n("Set &temporary breakpoint"), Qt::SHIFT+Qt::Key_F9,
343 m_filesWindow, SLOT(slotBrkptSetTemp()), "breakpoint_set_temporary");
344 m_bpEnableAction = createAction(i18n("&Enable/Disable breakpoint"), Qt::CTRL+Qt::Key_F9,
345 m_filesWindow, SLOT(slotBrkptEnable()), "breakpoint_enable");
347 // only in popup menus
348 createAction(i18n("Watch Expression"), 0,
349 this, SLOT(slotLocalsToWatch()), "watch_expression");
350 m_editValueAction = createAction(i18n("Edit Value"), Qt::Key_F2,
351 this, SLOT(slotEditValue()), "edit_value");
353 // all actions force an UI update
354 QList<QAction*> actions = actionCollection()->actions();
355 foreach(QAction* action, actions) {
356 connect(action, SIGNAL(activated()), this, SLOT(updateUI()));
360 void DebuggerMainWnd::initAnimation()
362 KToolBar* toolbar = toolBar("mainToolBar");
363 m_animation = new KAnimatedButton(toolbar);
364 toolbar->addWidget(m_animation);
365 m_animation->setIcons("pulse");
366 connect(m_animation, SIGNAL(clicked(bool)), m_debugger, SLOT(programBreak()));
367 m_animRunning = false;
370 void DebuggerMainWnd::initStatusBar()
372 KStatusBar* statusbar = statusBar();
373 statusbar->insertItem(m_statusActive, ID_STATUS_ACTIVE);
374 m_lastActiveStatusText = m_statusActive;
375 statusbar->insertItem("", ID_STATUS_MSG); /* message pane */
377 // reserve some translations
378 i18n("Restart");
379 i18n("Core dump");
382 bool DebuggerMainWnd::queryClose()
384 if (m_debugger != 0) {
385 m_debugger->shutdown();
387 return true;
391 // instance properties
392 void DebuggerMainWnd::saveProperties(KConfigGroup& cg)
394 // session management
395 QString executable = "";
396 if (m_debugger != 0) {
397 executable = m_debugger->executable();
399 cg.writeEntry("executable", executable);
402 void DebuggerMainWnd::readProperties(const KConfigGroup& cg)
404 // session management
405 QString execName = cg.readEntry("executable");
407 TRACE("readProperties: executable=" + execName);
408 if (!execName.isEmpty()) {
409 debugProgram(execName, "");
413 static const char RecentExecutables[] = "RecentExecutables";
414 static const char LastSession[] = "LastSession";
415 static const char OutputWindowGroup[] = "OutputWindow";
416 static const char TermCmdStr[] = "TermCmdStr";
417 static const char KeepScript[] = "KeepScript";
418 static const char DebuggerGroup[] = "Debugger";
419 static const char DebuggerCmdStr[] = "DebuggerCmdStr";
420 static const char PreferencesGroup[] = "Preferences";
421 static const char PopForeground[] = "PopForeground";
422 static const char BackTimeout[] = "BackTimeout";
423 static const char TabWidth[] = "TabWidth";
424 static const char SourceFileFilter[] = "SourceFileFilter";
425 static const char HeaderFileFilter[] = "HeaderFileFilter";
427 void DebuggerMainWnd::saveSettings(KSharedConfigPtr config)
429 m_recentExecAction->saveEntries(config->group(RecentExecutables));
431 KConfigGroup lg = config->group(LastSession);
432 lg.writeEntry("Width0Locals", m_localVariables->columnWidth(0));
433 lg.writeEntry("Width0Watches", m_watches->columnWidth(0));
435 if (m_debugger != 0) {
436 m_debugger->saveSettings(config.data());
439 config->group(OutputWindowGroup).writeEntry(TermCmdStr, m_outputTermCmdStr);
440 config->group(DebuggerGroup).writeEntry(DebuggerCmdStr, m_debuggerCmdStr);
442 KConfigGroup pg(config->group(PreferencesGroup));
443 pg.writeEntry(PopForeground, m_popForeground);
444 pg.writeEntry(BackTimeout, m_backTimeout);
445 pg.writeEntry(TabWidth, m_tabWidth);
446 pg.writeEntry(SourceFileFilter, m_sourceFilter);
447 pg.writeEntry(HeaderFileFilter, m_headerFilter);
450 void DebuggerMainWnd::restoreSettings(KSharedConfigPtr config)
452 m_recentExecAction->loadEntries(config->group(RecentExecutables));
454 KConfigGroup lg = config->group(LastSession);
455 int w;
456 w = lg.readEntry("Width0Locals", -1);
457 if (w >= 0 && w < 30000)
458 m_localVariables->setColumnWidth(0, w);
459 w = lg.readEntry("Width0Watches", -1);
460 if (w >= 0 && w < 30000)
461 m_watches->setColumnWidth(0, w);
463 if (m_debugger != 0) {
464 m_debugger->restoreSettings(config.data());
467 KConfigGroup og(config->group(OutputWindowGroup));
469 * For debugging and emergency purposes, let the config file override
470 * the shell script that is used to keep the output window open. This
471 * string must have EXACTLY 1 %s sequence in it.
473 setTerminalCmd(og.readEntry(TermCmdStr, defaultTermCmdStr));
474 m_outputTermKeepScript = og.readEntry(KeepScript);
476 setDebuggerCmdStr(config->group(DebuggerGroup).readEntry(DebuggerCmdStr));
478 KConfigGroup pg(config->group(PreferencesGroup));
479 m_popForeground = pg.readEntry(PopForeground, false);
480 m_backTimeout = pg.readEntry(BackTimeout, 1000);
481 m_tabWidth = pg.readEntry(TabWidth, 0);
482 m_sourceFilter = pg.readEntry(SourceFileFilter, m_sourceFilter);
483 m_headerFilter = pg.readEntry(HeaderFileFilter, m_headerFilter);
485 emit setTabWidth(m_tabWidth);
488 void DebuggerMainWnd::updateUI()
490 m_findAction->setChecked(m_filesWindow->m_findDlg.isVisible());
491 m_findAction->setEnabled(m_filesWindow->hasWindows());
492 m_bpSetAction->setEnabled(m_debugger->canChangeBreakpoints());
493 m_bpSetTempAction->setEnabled(m_debugger->canChangeBreakpoints());
494 m_bpEnableAction->setEnabled(m_debugger->canChangeBreakpoints());
495 m_bpTableAction->setChecked(isDockVisible(m_bpTable));
496 m_btWindowAction->setChecked(isDockVisible(m_btWindow));
497 m_localVariablesAction->setChecked(isDockVisible(m_localVariables));
498 m_watchesAction->setChecked(isDockVisible(m_watches));
499 m_registersAction->setChecked(isDockVisible(m_registers));
500 m_threadsAction->setChecked(isDockVisible(m_threads));
501 m_memoryWindowAction->setChecked(isDockVisible(m_memoryWindow));
502 m_ttyWindowAction->setChecked(isDockVisible(m_ttyWindow));
504 m_fileExecAction->setEnabled(m_debugger->isIdle());
505 m_settingsAction->setEnabled(m_debugger->haveExecutable());
506 m_coreDumpAction->setEnabled(m_debugger->canStart());
507 m_closeAction->setEnabled(m_filesWindow->hasWindows());
508 m_reloadAction->setEnabled(m_filesWindow->hasWindows());
509 m_stepIntoAction->setEnabled(m_debugger->canSingleStep());
510 m_stepIntoIAction->setEnabled(m_debugger->canSingleStep());
511 m_stepOverAction->setEnabled(m_debugger->canSingleStep());
512 m_stepOverIAction->setEnabled(m_debugger->canSingleStep());
513 m_stepOutAction->setEnabled(m_debugger->canSingleStep());
514 m_toCursorAction->setEnabled(m_debugger->canSingleStep());
515 m_execMovePCAction->setEnabled(m_debugger->canSingleStep());
516 m_restartAction->setEnabled(m_debugger->canSingleStep());
517 m_attachAction->setEnabled(m_debugger->isReady());
518 m_runAction->setEnabled(m_debugger->canStart() || m_debugger->canSingleStep());
519 m_killAction->setEnabled(m_debugger->haveExecutable() && m_debugger->isProgramActive());
520 m_breakAction->setEnabled(m_debugger->isProgramRunning());
521 m_argumentsAction->setEnabled(m_debugger->haveExecutable());
522 m_editValueAction->setEnabled(m_debugger->canSingleStep());
524 // animation
525 if (m_debugger->isIdle()) {
526 if (m_animRunning && m_animation) {
527 m_animation->stop();
528 m_animRunning = false;
530 } else {
531 if (!m_animRunning && m_animation) {
532 m_animation->start();
533 m_animRunning = true;
537 // update statusbar
538 QString newStatus;
539 if (m_debugger->isProgramActive())
540 newStatus = m_statusActive;
541 if (newStatus != m_lastActiveStatusText) {
542 statusBar()->changeItem(newStatus, ID_STATUS_ACTIVE);
543 m_lastActiveStatusText = newStatus;
547 void DebuggerMainWnd::updateLineItems()
549 m_filesWindow->updateLineItems(m_debugger);
552 void DebuggerMainWnd::slotAddWatch()
554 if (m_debugger != 0) {
555 QString t = m_watches->watchText();
556 m_debugger->addWatch(t);
560 void DebuggerMainWnd::slotAddWatch(const QString& text)
562 if (m_debugger != 0) {
563 m_debugger->addWatch(text);
567 void DebuggerMainWnd::slotNewFileLoaded()
569 // updates program counter in the new file
570 if (m_debugger != 0)
571 m_filesWindow->updateLineItems(m_debugger);
574 QDockWidget* DebuggerMainWnd::dockParent(QWidget* w)
576 while ((w = w->parentWidget()) != 0) {
577 if (QDockWidget* dock = qobject_cast<QDockWidget*>(w))
578 return dock;
580 return 0;
583 bool DebuggerMainWnd::isDockVisible(QWidget* w)
585 QDockWidget* d = dockParent(w);
586 return d != 0 && d->isVisible();
589 void DebuggerMainWnd::makeDefaultLayout()
591 // +---------------+---------+
592 // | Source | Locals |
593 // | | |
594 // |---------------+---------+
595 // |Stack, Brkpts, | Watches |
596 // |Output,... | |
597 // +---------------+---------+
599 addDockWidget(Qt::RightDockWidgetArea, dockParent(m_localVariables));
600 addDockWidget(Qt::BottomDockWidgetArea, dockParent(m_memoryWindow));
601 splitDockWidget(dockParent(m_memoryWindow), dockParent(m_threads), Qt::Horizontal);
602 tabifyDockWidget(dockParent(m_memoryWindow), dockParent(m_registers));
603 tabifyDockWidget(dockParent(m_registers), dockParent(m_bpTable));
604 tabifyDockWidget(dockParent(m_bpTable), dockParent(m_ttyWindow));
605 tabifyDockWidget(dockParent(m_ttyWindow), dockParent(m_btWindow));
606 tabifyDockWidget(dockParent(m_threads), dockParent(m_watches));
607 dockParent(m_localVariables)->setVisible(true);
608 dockParent(m_ttyWindow)->setVisible(true);
609 dockParent(m_watches)->setVisible(true);
610 dockParent(m_btWindow)->setVisible(true);
611 dockParent(m_bpTable)->setVisible(true);
614 bool DebuggerMainWnd::debugProgram(const QString& exe, const QString& lang)
616 // check the file name
617 QFileInfo fi(exe);
619 bool success = fi.isFile();
620 if (!success)
622 QString msg = i18n("`%1' is not a file or does not exist");
623 KMessageBox::sorry(this, msg.arg(exe));
625 else
627 success = startDriver(fi.absoluteFilePath(), lang);
630 if (success)
632 m_recentExecAction->addUrl(KUrl(fi.absoluteFilePath()));
634 // keep the directory
635 m_lastDirectory = fi.absolutePath();
636 m_filesWindow->setExtraDirectory(m_lastDirectory);
638 // set caption to basename part of executable
639 QString caption = fi.fileName();
640 setCaption(caption);
642 else
644 m_recentExecAction->removeUrl(KUrl(fi.absoluteFilePath()));
647 return success;
650 static const char GeneralGroup[] = "General";
652 bool DebuggerMainWnd::startDriver(const QString& executable, QString lang)
654 assert(m_debugger != 0);
656 TRACE(QString("trying language '%1'...").arg(lang));
657 DebuggerDriver* driver = driverFromLang(lang);
659 if (driver == 0)
661 // see if there is a language in the per-program config file
662 QString configName = m_debugger->getConfigForExe(executable);
663 if (QFile::exists(configName))
665 KConfig c(configName, KConfig::SimpleConfig);
667 // Using "GDB" as default here is for backwards compatibility:
668 // The config file exists but doesn't have an entry,
669 // so it must have been created by an old version of KDbg
670 // that had only the GDB driver.
671 lang = c.group(GeneralGroup)
672 .readEntry(KDebugger::DriverNameEntry, "GDB");
674 TRACE(QString("...bad, trying config driver %1...").arg(lang));
675 driver = driverFromLang(lang);
679 if (driver == 0)
681 QString name = driverNameFromFile(executable);
683 TRACE(QString("...no luck, trying %1 derived"
684 " from file contents").arg(name));
685 driver = driverFromLang(name);
687 if (driver == 0)
689 // oops
690 QString msg = i18n("Don't know how to debug language `%1'");
691 KMessageBox::sorry(this, msg.arg(lang));
692 return false;
695 driver->setLogFileName(m_transcriptFile);
697 bool success = m_debugger->debugProgram(executable, driver);
699 if (!success)
701 delete driver;
703 QString msg = i18n("Could not start the debugger process.\n"
704 "Please shut down KDbg and resolve the problem.");
705 KMessageBox::sorry(this, msg);
708 return success;
711 // derive driver from language
712 DebuggerDriver* DebuggerMainWnd::driverFromLang(QString lang)
714 // lang is needed in all lowercase
715 lang = lang.toLower();
717 // The following table relates languages and debugger drivers
718 static const struct L {
719 const char* shortest; // abbreviated to this is still unique
720 const char* full; // full name of language
721 int driver;
722 } langs[] = {
723 { "c", "c++", 1 },
724 { "f", "fortran", 1 },
725 { "p", "python", 3 },
726 { "x", "xslt", 2 },
727 // the following are actually driver names
728 { "gdb", "gdb", 1 },
729 { "xsldbg", "xsldbg", 2 },
731 const int N = sizeof(langs)/sizeof(langs[0]);
733 // lookup the language name
734 int driverID = 0;
735 for (int i = 0; i < N; i++)
737 const L& l = langs[i];
739 // shortest must match
740 if (!lang.startsWith(l.shortest))
741 continue;
743 // lang must not be longer than the full name, and it must match
744 if (QString(l.full).startsWith(lang))
746 driverID = l.driver;
747 break;
750 DebuggerDriver* driver = 0;
751 switch (driverID) {
752 case 1:
754 GdbDriver* gdb = new GdbDriver;
755 gdb->setDefaultInvocation(m_debuggerCmdStr);
756 driver = gdb;
758 break;
759 case 2:
760 driver = new XsldbgDriver;
761 break;
762 default:
763 // unknown language
764 break;
766 return driver;
770 * Try to guess the language to use from the contents of the file.
772 QString DebuggerMainWnd::driverNameFromFile(const QString& exe)
774 /* Inprecise but simple test to see if file is in XSLT language */
775 if (exe.right(4).toLower() == ".xsl")
776 return "XSLT";
778 return "GDB";
781 void DebuggerMainWnd::setCoreFile(const QString& corefile)
783 assert(m_debugger != 0);
784 m_debugger->useCoreFile(corefile, true);
787 void DebuggerMainWnd::setRemoteDevice(const QString& remoteDevice)
789 if (m_debugger != 0) {
790 m_debugger->setRemoteDevice(remoteDevice);
794 void DebuggerMainWnd::overrideProgramArguments(const QString& args)
796 assert(m_debugger != 0);
797 m_debugger->overrideProgramArguments(args);
800 void DebuggerMainWnd::setTranscript(const QString& name)
802 m_transcriptFile = name;
803 if (m_debugger != 0 && m_debugger->driver() != 0)
804 m_debugger->driver()->setLogFileName(m_transcriptFile);
807 void DebuggerMainWnd::setAttachPid(const QString& pid)
809 assert(m_debugger != 0);
810 m_debugger->setAttachPid(pid);
813 void DebuggerMainWnd::slotNewStatusMsg()
815 QString msg = m_debugger->statusMessage();
816 statusBar()->changeItem(msg, ID_STATUS_MSG);
819 void DebuggerMainWnd::slotFileGlobalSettings()
821 int oldTabWidth = m_tabWidth;
823 KPageDialog dlg(this);
824 QString title = KGlobal::caption();
825 title += i18n(": Global options");
826 dlg.setCaption(title);
828 PrefDebugger prefDebugger(&dlg);
829 prefDebugger.setDebuggerCmd(m_debuggerCmdStr.isEmpty() ?
830 GdbDriver::defaultGdb() : m_debuggerCmdStr);
831 prefDebugger.setTerminal(m_outputTermCmdStr);
833 PrefMisc prefMisc(&dlg);
834 prefMisc.setPopIntoForeground(m_popForeground);
835 prefMisc.setBackTimeout(m_backTimeout);
836 prefMisc.setTabWidth(m_tabWidth);
837 prefMisc.setSourceFilter(m_sourceFilter);
838 prefMisc.setHeaderFilter(m_headerFilter);
840 dlg.addPage(&prefDebugger, i18n("Debugger"));
841 dlg.addPage(&prefMisc, i18n("Miscellaneous"));
842 if (dlg.exec() == QDialog::Accepted)
844 setDebuggerCmdStr(prefDebugger.debuggerCmd());
845 setTerminalCmd(prefDebugger.terminal());
846 m_popForeground = prefMisc.popIntoForeground();
847 m_backTimeout = prefMisc.backTimeout();
848 m_tabWidth = prefMisc.tabWidth();
849 m_sourceFilter = prefMisc.sourceFilter();
850 if (m_sourceFilter.isEmpty())
851 m_sourceFilter = defaultSourceFilter;
852 m_headerFilter = prefMisc.headerFilter();
853 if (m_headerFilter.isEmpty())
854 m_headerFilter = defaultHeaderFilter;
857 if (m_tabWidth != oldTabWidth) {
858 emit setTabWidth(m_tabWidth);
862 void DebuggerMainWnd::setTerminalCmd(const QString& cmd)
864 m_outputTermCmdStr = cmd;
865 // revert to default if empty
866 if (m_outputTermCmdStr.isEmpty()) {
867 m_outputTermCmdStr = defaultTermCmdStr;
871 void DebuggerMainWnd::setDebuggerCmdStr(const QString& cmd)
873 m_debuggerCmdStr = cmd;
874 // make empty if it is the default
875 if (m_debuggerCmdStr == GdbDriver::defaultGdb()) {
876 m_debuggerCmdStr = QString();
880 void DebuggerMainWnd::slotDebuggerStarting()
882 if (m_debugger == 0) /* paranoia check */
883 return;
885 if (m_ttyLevel == m_debugger->ttyLevel())
886 return;
888 // shut down terminal emulations we will not need
889 switch (m_ttyLevel) {
890 case KDebugger::ttySimpleOutputOnly:
891 m_ttyWindow->deactivate();
892 break;
893 case KDebugger::ttyFull:
894 m_outputTermProc->kill();
895 break;
896 default: break;
899 m_ttyLevel = m_debugger->ttyLevel();
901 QString ttyName;
902 switch (m_ttyLevel) {
903 case KDebugger::ttySimpleOutputOnly:
904 ttyName = m_ttyWindow->activate();
905 break;
906 case KDebugger::ttyFull:
907 // create an output window
908 ttyName = createOutputWindow();
909 TRACE(ttyName.isEmpty() ?
910 "createOuputWindow failed" : "successfully created output window");
911 break;
912 default: break;
915 m_debugger->setTerminal(ttyName);
918 void DebuggerMainWnd::slotToggleBreak(const QString& fileName, int lineNo,
919 const DbgAddr& address, bool temp)
921 // lineNo is zero-based
922 if (m_debugger != 0) {
923 m_debugger->setBreakpoint(fileName, lineNo, address, temp);
927 void DebuggerMainWnd::slotEnaDisBreak(const QString& fileName, int lineNo,
928 const DbgAddr& address)
930 // lineNo is zero-based
931 if (m_debugger != 0) {
932 m_debugger->enableDisableBreakpoint(fileName, lineNo, address);
936 QString DebuggerMainWnd::createOutputWindow()
938 // create a name for a fifo
939 QString fifoName;
940 fifoName.sprintf("/tmp/kdbgttywin%05d", ::getpid());
942 // create a fifo that will pass in the tty name
943 QFile::remove(fifoName); // remove remnants
944 #ifdef HAVE_MKFIFO
945 if (::mkfifo(fifoName.toLocal8Bit(), S_IRUSR|S_IWUSR) < 0) {
946 // failed
947 TRACE("mkfifo " + fifoName + " failed");
948 return QString();
950 #else
951 if (::mknod(fifoName.toLocal8Bit(), S_IFIFO | S_IRUSR|S_IWUSR, 0) < 0) {
952 // failed
953 TRACE("mknod " + fifoName + " failed");
954 return QString();
956 #endif
959 * Spawn an xterm that in turn runs a shell script that passes us
960 * back the terminal name and then only sits and waits.
962 static const char shellScriptFmt[] =
963 "tty>%s;"
964 "trap \"\" INT QUIT TSTP;" /* ignore various signals */
965 "exec<&-;exec>&-;" /* close stdin and stdout */
966 "while :;do sleep 3600;done";
967 // let config file override this script
968 QString shellScript;
969 if (!m_outputTermKeepScript.isEmpty()) {
970 shellScript = m_outputTermKeepScript;
971 } else {
972 shellScript = shellScriptFmt;
975 shellScript.replace("%s", fifoName);
976 TRACE("output window script is " + shellScript);
978 QString title = KGlobal::caption();
979 title += i18n(": Program output");
981 // parse the command line specified in the preferences
982 QStringList cmdParts = m_outputTermCmdStr.split(' ');
985 * Build the argv array. Thereby substitute special sequences:
987 struct {
988 char seq[4];
989 QString replace;
990 } substitute[] = {
991 { "%T", title },
992 { "%C", shellScript }
995 for (QStringList::iterator i = cmdParts.begin(); i != cmdParts.end(); ++i)
997 QString& str = *i;
998 for (int j = sizeof(substitute)/sizeof(substitute[0])-1; j >= 0; j--) {
999 int pos = str.indexOf(substitute[j].seq);
1000 if (pos >= 0) {
1001 str.replace(pos, 2, substitute[j].replace);
1002 break; /* substitute only one sequence */
1007 QString tty, pgm = cmdParts.takeFirst();
1009 m_outputTermProc->start(pgm, cmdParts);
1010 if (m_outputTermProc->waitForStarted())
1012 // read the ttyname from the fifo
1013 QFile f(fifoName);
1014 if (f.open(QIODevice::ReadOnly))
1016 QByteArray t = f.readAll();
1017 tty = QString::fromLocal8Bit(t, t.size());
1018 f.close();
1020 f.remove();
1022 // remove whitespace
1023 tty = tty.trimmed();
1024 TRACE("tty=" + tty);
1026 else
1028 // error, could not start xterm
1029 TRACE("fork failed for fifo " + fifoName);
1030 QFile::remove(fifoName);
1033 return tty;
1036 void DebuggerMainWnd::slotProgramStopped()
1038 // when the program stopped, move the window to the foreground
1039 if (m_popForeground) {
1040 // unfortunately, this requires quite some force to work :-(
1041 KWindowSystem::raiseWindow(winId());
1042 KWindowSystem::forceActiveWindow(winId());
1044 m_backTimer.stop();
1047 void DebuggerMainWnd::intoBackground()
1049 if (m_popForeground) {
1050 m_backTimer.setSingleShot(true);
1051 m_backTimer.start(m_backTimeout);
1055 void DebuggerMainWnd::slotBackTimer()
1057 lower();
1060 void DebuggerMainWnd::slotRecentExec(const KUrl& url)
1062 QString exe = url.path();
1063 debugProgram(exe, "");
1066 QString DebuggerMainWnd::makeSourceFilter()
1068 QString f;
1069 f = m_sourceFilter + " " + m_headerFilter + i18n("|All source files\n");
1070 f += m_sourceFilter + i18n("|Source files\n");
1071 f += m_headerFilter + i18n("|Header files\n");
1072 f += i18n("*|All files");
1073 return f;
1077 * Pop up the context menu in the locals window
1079 void DebuggerMainWnd::slotLocalsPopup(const QPoint& pt)
1081 QMenu* popup = static_cast<QMenu*>(factory()->container("popup_locals", this));
1082 if (popup == 0) {
1083 return;
1085 if (popup->isVisible()) {
1086 popup->hide();
1087 } else {
1088 popup->popup(m_localVariables->viewport()->mapToGlobal(pt));
1093 * Copies the currently selected item to the watch window.
1095 void DebuggerMainWnd::slotLocalsToWatch()
1097 VarTree* item = m_localVariables->selectedItem();
1099 if (item != 0 && m_debugger != 0) {
1100 QString text = item->computeExpr();
1101 m_debugger->addWatch(text);
1106 * Starts editing a value in a value display
1108 void DebuggerMainWnd::slotEditValue()
1110 // does one of the value trees have the focus
1111 QWidget* f = kapp->focusWidget();
1112 ExprWnd* wnd;
1113 if (f == m_localVariables) {
1114 wnd = m_localVariables;
1115 } else if (f == m_watches->watchVariables()) {
1116 wnd = m_watches->watchVariables();
1117 } else {
1118 return;
1121 if (m_localVariables->isEditing() ||
1122 m_watches->watchVariables()->isEditing())
1124 return; /* don't edit twice */
1127 VarTree* expr = wnd->selectedItem();
1128 if (expr != 0 && m_debugger != 0 && m_debugger->canSingleStep())
1130 TRACE("edit value");
1131 // determine the text to edit
1132 QString text = m_debugger->driver()->editableValue(expr);
1133 wnd->editValue(expr, text);
1137 // helper that gets a file name (it only differs in the caption of the dialog)
1138 static QString myGetFileName(QString caption,
1139 QString dir, QString filter,
1140 QWidget* parent)
1142 QString filename;
1143 KFileDialog dlg(dir, filter, parent);
1145 dlg.setCaption(caption);
1147 if (dlg.exec() == QDialog::Accepted)
1148 filename = dlg.selectedFile();
1150 return filename;
1153 void DebuggerMainWnd::slotFileOpen()
1155 // start browsing in the active file's directory
1156 // fall back to last used directory (executable)
1157 QString dir = m_lastDirectory;
1158 QString fileName = m_filesWindow->activeFileName();
1159 if (!fileName.isEmpty()) {
1160 QFileInfo fi(fileName);
1161 dir = fi.path();
1164 fileName = myGetFileName(i18n("Open"),
1165 dir,
1166 makeSourceFilter(), this);
1168 if (!fileName.isEmpty())
1170 QFileInfo fi(fileName);
1171 m_lastDirectory = fi.path();
1172 m_filesWindow->setExtraDirectory(m_lastDirectory);
1173 m_filesWindow->activateFile(fileName);
1177 void DebuggerMainWnd::slotFileExe()
1179 if (m_debugger->isIdle())
1181 // open a new executable
1182 QString executable = myGetFileName(i18n("Select the executable to debug"),
1183 m_lastDirectory, 0, this);
1184 if (executable.isEmpty())
1185 return;
1187 debugProgram(executable, "");
1191 void DebuggerMainWnd::slotFileCore()
1193 if (m_debugger->canStart())
1195 QString corefile = myGetFileName(i18n("Select core dump"),
1196 m_lastDirectory, 0, this);
1197 if (!corefile.isEmpty()) {
1198 m_debugger->useCoreFile(corefile, false);
1203 void DebuggerMainWnd::slotFileProgSettings()
1205 if (m_debugger != 0) {
1206 m_debugger->programSettings(this);
1210 void DebuggerMainWnd::slotViewStatusbar()
1212 if (statusBar()->isVisible())
1213 statusBar()->hide();
1214 else
1215 statusBar()->show();
1216 setSettingsDirty();
1219 void DebuggerMainWnd::slotExecUntil()
1221 if (m_debugger != 0)
1223 QString file;
1224 int lineNo;
1225 if (m_filesWindow->activeLine(file, lineNo))
1226 m_debugger->runUntil(file, lineNo);
1230 void DebuggerMainWnd::slotExecAttach()
1232 #ifdef PS_COMMAND
1233 ProcAttachPS dlg(this);
1234 // seed filter with executable name
1235 QFileInfo fi = m_debugger->executable();
1236 dlg.setFilterText(fi.fileName());
1237 #else
1238 ProcAttach dlg(this);
1239 dlg.setText(m_debugger->attachedPid());
1240 #endif
1241 if (dlg.exec()) {
1242 m_debugger->attachProgram(dlg.text());
1246 void DebuggerMainWnd::slotExecArgs()
1248 if (m_debugger != 0) {
1249 m_debugger->programArgs(this);
1253 void DebuggerMainWnd::slotConfigureKeys()
1255 KShortcutsDialog::configure(actionCollection());
1258 #include "dbgmainwnd.moc"