Clean up #includes.
[kdbg.git] / kdbg / dbgmainwnd.cpp
blob083afaa94e72090ba0c4baa7ce9d59e1a61067e2
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 <k3process.h>
21 #include <kshortcutsdialog.h>
22 #include <kanimatedbutton.h>
23 #include <kwindowsystem.h>
24 #include <ktoolbar.h>
25 #include <kurl.h>
26 #include <kxmlguifactory.h>
27 #include <Q3ListBox>
28 #include <QFile>
29 #include <QFileInfo>
30 #include <Q3TabDialog>
31 #include <Q3ValueList>
32 #include <Q3PopupMenu>
33 #include <QDockWidget>
34 #include "dbgmainwnd.h"
35 #include "debugger.h"
36 #include "commandids.h"
37 #include "winstack.h"
38 #include "brkpt.h"
39 #include "threadlist.h"
40 #include "memwindow.h"
41 #include "ttywnd.h"
42 #include "watchwindow.h"
43 #include "procattach.h"
44 #include "prefdebugger.h"
45 #include "prefmisc.h"
46 #include "gdbdriver.h"
47 #include "xsldbgdriver.h"
48 #include "mydebug.h"
49 #include <sys/stat.h> /* mknod(2) */
50 #include <unistd.h> /* getpid */
53 static const char defaultTermCmdStr[] = "xterm -name kdbgio -title %T -e sh -c %C";
54 static const char defaultSourceFilter[] = "*.c *.cc *.cpp *.c++ *.C *.CC";
55 static const char defaultHeaderFilter[] = "*.h *.hh *.hpp *.h++";
57 DebuggerMainWnd::DebuggerMainWnd() :
58 KXmlGuiWindow(),
59 m_debugger(0),
60 #ifdef GDB_TRANSCRIPT
61 m_transcriptFile(GDB_TRANSCRIPT),
62 #endif
63 m_outputTermCmdStr(defaultTermCmdStr),
64 m_outputTermProc(0),
65 m_ttyLevel(-1), /* no tty yet */
66 m_popForeground(false),
67 m_backTimeout(1000),
68 m_tabWidth(0),
69 m_sourceFilter(defaultSourceFilter),
70 m_headerFilter(defaultHeaderFilter),
71 m_animation(0),
72 m_statusActive(i18n("active"))
74 m_filesWindow = new WinStack(this);
75 setCentralWidget(m_filesWindow);
77 QDockWidget* dw1 = createDockWidget("Stack", i18n("Stack"));
78 m_btWindow = new Q3ListBox(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 initToolbar(); // kind of obsolete?
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 connect(m_localVariables, SIGNAL(contextMenuRequested(Q3ListViewItem*, const QPoint&, int)),
175 this, SLOT(slotLocalsPopup(Q3ListViewItem*, const QPoint&)));
177 restoreSettings(KGlobal::config());
179 updateUI();
180 m_bpTable->updateUI();
183 DebuggerMainWnd::~DebuggerMainWnd()
185 saveSettings(KGlobal::config());
186 // must delete m_debugger early since it references our windows
187 delete m_debugger;
188 m_debugger = 0;
190 delete m_memoryWindow;
191 delete m_threads;
192 delete m_ttyWindow;
193 delete m_bpTable;
194 delete m_registers;
195 delete m_watches;
196 delete m_localVariables;
197 delete m_btWindow;
198 delete m_filesWindow;
200 // if the output window is open, close it
201 if (m_outputTermProc != 0) {
202 m_outputTermProc->disconnect(); /* ignore signals */
203 m_outputTermProc->kill();
204 shutdownTermWindow();
208 QDockWidget* DebuggerMainWnd::createDockWidget(const char* name, const QString& title)
210 QDockWidget* w = new QDockWidget(title, this);
211 w->setObjectName(name);
212 // view menu changes when docking state changes
213 connect(w, SIGNAL(visibilityChanged(bool)), SLOT(updateUI()));
214 return w;
217 QAction* DebuggerMainWnd::createAction(const QString& text, const char* icon,
218 int shortcut, const QObject* receiver,
219 const char* slot, const char* name)
221 KAction* a = actionCollection()->addAction(name);
222 a->setText(text);
223 a->setIcon(KIcon(icon));
224 if (shortcut)
225 a->setShortcut(KShortcut(shortcut));
226 connect(a, SIGNAL(triggered()), receiver, slot);
227 return a;
230 QAction* DebuggerMainWnd::createAction(const QString& text,
231 int shortcut, const QObject* receiver,
232 const char* slot, const char* name)
234 KAction* a = actionCollection()->addAction(name);
235 a->setText(text);
236 if (shortcut)
237 a->setShortcut(KShortcut(shortcut));
238 connect(a, SIGNAL(triggered()), receiver, slot);
239 return a;
243 void DebuggerMainWnd::initKAction()
245 // file menu
246 KAction* open = KStandardAction::open(this, SLOT(slotFileOpen()),
247 actionCollection());
248 open->setText(i18n("&Open Source..."));
249 m_closeAction = KStandardAction::close(m_filesWindow, SLOT(slotClose()), actionCollection());
250 m_reloadAction = createAction(i18n("&Reload Source"), "reload", 0,
251 m_filesWindow, SLOT(slotFileReload()), "file_reload");
252 m_fileExecAction = createAction(i18n("&Executable..."), "execopen", 0,
253 this, SLOT(slotFileExe()), "file_executable");
254 m_recentExecAction = new KRecentFilesAction(i18n("Recent E&xecutables"),
255 actionCollection());
256 m_recentExecAction->setObjectName("file_executable_recent");
257 connect(m_recentExecAction, SIGNAL(urlSelected(const KUrl&)),
258 this, SLOT(slotRecentExec(const KUrl&)));
259 m_coreDumpAction = createAction(i18n("&Core dump..."), 0,
260 this, SLOT(slotFileCore()), "file_core_dump");
261 KStandardAction::quit(kapp, SLOT(closeAllWindows()), actionCollection());
263 // settings menu
264 m_settingsAction = createAction(i18n("This &Program..."), 0,
265 this, SLOT(slotFileProgSettings()), "settings_program");
266 createAction(i18n("&Global Options..."), 0,
267 this, SLOT(slotFileGlobalSettings()), "settings_global");
268 KStandardAction::keyBindings(this, SLOT(slotConfigureKeys()), actionCollection());
269 KStandardAction::showStatusbar(this, SLOT(slotViewStatusbar()), actionCollection());
271 // view menu
272 m_findAction = KStandardAction::find(m_filesWindow, SLOT(slotViewFind()), actionCollection());
273 KStandardAction::findNext(m_filesWindow, SLOT(slotFindForward()), actionCollection());
274 KStandardAction::findPrev(m_filesWindow, SLOT(slotFindBackward()), actionCollection());
276 i18n("Source &code");
277 struct { QString text; QWidget* w; QString id; QAction** act; } dw[] = {
278 { i18n("Stac&k"), m_btWindow, "view_stack", &m_btWindowAction },
279 { i18n("&Locals"), m_localVariables, "view_locals", &m_localVariablesAction },
280 { i18n("&Watched expressions"), m_watches, "view_watched_expressions", &m_watchesAction },
281 { i18n("&Registers"), m_registers, "view_registers", &m_registersAction },
282 { i18n("&Breakpoints"), m_bpTable, "view_breakpoints", &m_bpTableAction },
283 { i18n("T&hreads"), m_threads, "view_threads", &m_threadsAction },
284 { i18n("&Output"), m_ttyWindow, "view_output", &m_ttyWindowAction },
285 { i18n("&Memory"), m_memoryWindow, "view_memory", &m_memoryWindowAction }
287 for (unsigned i = 0; i < sizeof(dw)/sizeof(dw[0]); i++) {
288 QDockWidget* d = dockParent(dw[i].w);
289 *dw[i].act = new KToggleAction(dw[i].text, actionCollection());
290 actionCollection()->addAction(dw[i].id, *dw[i].act);
291 connect(*dw[i].act, SIGNAL(triggered()), d, SLOT(show()));
294 // execution menu
295 m_runAction = createAction(i18n("&Run"), "pgmrun", Qt::Key_F5,
296 m_debugger, SLOT(programRun()), "exec_run");
297 connect(m_runAction, SIGNAL(activated()), this, SLOT(intoBackground()));
298 m_stepIntoAction = createAction(i18n("Step &into"), "pgmstep", Qt::Key_F8,
299 m_debugger, SLOT(programStep()), "exec_step_into");
300 connect(m_stepIntoAction, SIGNAL(activated()), this, SLOT(intoBackground()));
301 m_stepOverAction = createAction(i18n("Step &over"), "pgmnext", Qt::Key_F10,
302 m_debugger, SLOT(programNext()), "exec_step_over");
303 connect(m_stepOverAction, SIGNAL(activated()), this, SLOT(intoBackground()));
304 m_stepOutAction = createAction(i18n("Step o&ut"), "pgmfinish", Qt::Key_F6,
305 m_debugger, SLOT(programFinish()), "exec_step_out");
306 connect(m_stepOutAction, SIGNAL(activated()), this, SLOT(intoBackground()));
307 m_toCursorAction = createAction(i18n("Run to &cursor"), Qt::Key_F7,
308 this, SLOT(slotExecUntil()), "exec_run_to_cursor");
309 connect(m_toCursorAction, SIGNAL(activated()), this, SLOT(intoBackground()));
310 m_stepIntoIAction = createAction(i18n("Step i&nto by instruction"), "pgmstepi", Qt::SHIFT+Qt::Key_F8,
311 m_debugger, SLOT(programStepi()), "exec_step_into_by_insn");
312 connect(m_stepIntoIAction, SIGNAL(activated()), this, SLOT(intoBackground()));
313 m_stepOverIAction = createAction(i18n("Step o&ver by instruction"), "pgmnexti", Qt::SHIFT+Qt::Key_F10,
314 m_debugger, SLOT(programNexti()), "exec_step_over_by_insn");
315 connect(m_stepOverIAction, SIGNAL(activated()), this, SLOT(intoBackground()));
316 m_execMovePCAction = createAction(i18n("&Program counter to current line"), 0,
317 m_filesWindow, SLOT(slotMoveProgramCounter()), "exec_movepc");
318 m_breakAction = createAction(i18n("&Break"), 0,
319 m_debugger, SLOT(programBreak()), "exec_break");
320 m_killAction = createAction(i18n("&Kill"), 0,
321 m_debugger, SLOT(programKill()), "exec_kill");
322 m_restartAction = createAction(i18n("Re&start"), 0,
323 m_debugger, SLOT(programRunAgain()), "exec_restart");
324 m_attachAction = createAction(i18n("A&ttach..."), 0,
325 this, SLOT(slotExecAttach()), "exec_attach");
326 m_argumentsAction = createAction(i18n("&Arguments..."), 0,
327 this, SLOT(slotExecArgs()), "exec_arguments");
329 // breakpoint menu
330 m_bpSetAction = createAction(i18n("Set/Clear &breakpoint"), "brkpt", Qt::Key_F9,
331 m_filesWindow, SLOT(slotBrkptSet()), "breakpoint_set");
332 m_bpSetTempAction = createAction(i18n("Set &temporary breakpoint"), Qt::SHIFT+Qt::Key_F9,
333 m_filesWindow, SLOT(slotBrkptSetTemp()), "breakpoint_set_temporary");
334 m_bpEnableAction = createAction(i18n("&Enable/Disable breakpoint"), Qt::CTRL+Qt::Key_F9,
335 m_filesWindow, SLOT(slotBrkptEnable()), "breakpoint_enable");
337 // only in popup menus
338 createAction(i18n("Watch Expression"), 0,
339 this, SLOT(slotLocalsToWatch()), "watch_expression");
340 m_editValueAction = createAction(i18n("Edit Value"), Qt::Key_F2,
341 this, SLOT(slotEditValue()), "edit_value");
343 // all actions force an UI update
344 Q3ValueList<QAction*> actions = actionCollection()->actions();
345 Q3ValueList<QAction*>::Iterator it = actions.begin();
346 for (; it != actions.end(); ++it) {
347 connect(*it, SIGNAL(activated()), this, SLOT(updateUI()));
350 createGUI("kdbgui.rc");
353 void DebuggerMainWnd::initToolbar()
355 KToolBar* toolbar = toolBar("mainToolBar");
356 m_animation = new KAnimatedButton(toolbar);
357 toolbar->addWidget(m_animation);
358 m_animation->setIcons("pulse");
359 connect(m_animation, SIGNAL(triggered(QAction*)), m_debugger, SLOT(programBreak()));
360 m_animRunning = false;
362 KStatusBar* statusbar = statusBar();
363 statusbar->insertItem(m_statusActive, ID_STATUS_ACTIVE);
364 m_lastActiveStatusText = m_statusActive;
365 statusbar->insertItem("", ID_STATUS_MSG); /* message pane */
367 // reserve some translations
368 i18n("Restart");
369 i18n("Core dump");
372 bool DebuggerMainWnd::queryClose()
374 if (m_debugger != 0) {
375 m_debugger->shutdown();
377 return true;
381 // instance properties
382 void DebuggerMainWnd::saveProperties(KConfigGroup& cg)
384 // session management
385 QString executable = "";
386 if (m_debugger != 0) {
387 executable = m_debugger->executable();
389 cg.writeEntry("executable", executable);
392 void DebuggerMainWnd::readProperties(const KConfigGroup& cg)
394 // session management
395 QString execName = cg.readEntry("executable");
397 TRACE("readProperties: executable=" + execName);
398 if (!execName.isEmpty()) {
399 debugProgram(execName, "");
403 static const char WindowGroup[] = "Windows";
404 static const char ToolbarGroup[] = "ToolbarSettings";
405 static const char RecentExecutables[] = "RecentExecutables";
406 static const char LastSession[] = "LastSession";
407 static const char OutputWindowGroup[] = "OutputWindow";
408 static const char TermCmdStr[] = "TermCmdStr";
409 static const char KeepScript[] = "KeepScript";
410 static const char DebuggerGroup[] = "Debugger";
411 static const char DebuggerCmdStr[] = "DebuggerCmdStr";
412 static const char PreferencesGroup[] = "Preferences";
413 static const char PopForeground[] = "PopForeground";
414 static const char BackTimeout[] = "BackTimeout";
415 static const char TabWidth[] = "TabWidth";
416 static const char SourceFileFilter[] = "SourceFileFilter";
417 static const char HeaderFileFilter[] = "HeaderFileFilter";
419 void DebuggerMainWnd::saveSettings(KSharedConfigPtr config)
421 KConfigGroup g = config->group(WindowGroup);
423 QByteArray layout = saveState();
424 g.writeEntry("Layout", layout);
425 g.writeEntry("Geometry", geometry());
427 KConfigGroup tg = config->group(ToolbarGroup);
428 toolBar("mainToolBar")->saveSettings(tg);
430 m_recentExecAction->saveEntries(config->group(RecentExecutables));
432 KConfigGroup lg = config->group(LastSession);
433 lg.writeEntry("Width0Locals", m_localVariables->columnWidth(0));
434 lg.writeEntry("Width0Watches", m_watches->columnWidth(0));
436 if (m_debugger != 0) {
437 m_debugger->saveSettings(config.data());
440 config->group(OutputWindowGroup).writeEntry(TermCmdStr, m_outputTermCmdStr);
441 config->group(DebuggerGroup).writeEntry(DebuggerCmdStr, m_debuggerCmdStr);
443 KConfigGroup pg(config->group(PreferencesGroup));
444 pg.writeEntry(PopForeground, m_popForeground);
445 pg.writeEntry(BackTimeout, m_backTimeout);
446 pg.writeEntry(TabWidth, m_tabWidth);
447 pg.writeEntry(SourceFileFilter, m_sourceFilter);
448 pg.writeEntry(HeaderFileFilter, m_headerFilter);
451 void DebuggerMainWnd::restoreSettings(KSharedConfigPtr config)
453 KConfigGroup g = config->group(WindowGroup);
455 QByteArray layout = g.readEntry("Layout", QByteArray());
456 if (!restoreState(layout))
457 makeDefaultLayout();
458 QRect r = g.readEntry("Geometry", QRect());
459 if (!r.isEmpty())
460 setGeometry(r);
462 toolBar("mainToolBar")->applySettings(config->group(ToolbarGroup));
464 m_recentExecAction->loadEntries(config->group(RecentExecutables));
466 KConfigGroup lg = config->group(LastSession);
467 int w;
468 w = lg.readEntry("Width0Locals", -1);
469 if (w >= 0 && w < 30000)
470 m_localVariables->setColumnWidth(0, w);
471 w = lg.readEntry("Width0Watches", -1);
472 if (w >= 0 && w < 30000)
473 m_watches->setColumnWidth(0, w);
475 if (m_debugger != 0) {
476 m_debugger->restoreSettings(config.data());
479 KConfigGroup og(config->group(OutputWindowGroup));
481 * For debugging and emergency purposes, let the config file override
482 * the shell script that is used to keep the output window open. This
483 * string must have EXACTLY 1 %s sequence in it.
485 setTerminalCmd(og.readEntry(TermCmdStr, defaultTermCmdStr));
486 m_outputTermKeepScript = og.readEntry(KeepScript);
488 setDebuggerCmdStr(config->group(DebuggerGroup).readEntry(DebuggerCmdStr));
490 KConfigGroup pg(config->group(PreferencesGroup));
491 m_popForeground = pg.readEntry(PopForeground, false);
492 m_backTimeout = pg.readEntry(BackTimeout, 1000);
493 m_tabWidth = pg.readEntry(TabWidth, 0);
494 m_sourceFilter = pg.readEntry(SourceFileFilter, m_sourceFilter);
495 m_headerFilter = pg.readEntry(HeaderFileFilter, m_headerFilter);
497 emit setTabWidth(m_tabWidth);
500 void DebuggerMainWnd::updateUI()
502 m_findAction->setChecked(m_filesWindow->m_findDlg.isVisible());
503 m_findAction->setEnabled(m_filesWindow->hasWindows());
504 m_bpSetAction->setEnabled(m_debugger->canChangeBreakpoints());
505 m_bpSetTempAction->setEnabled(m_debugger->canChangeBreakpoints());
506 m_bpEnableAction->setEnabled(m_debugger->canChangeBreakpoints());
507 m_bpTableAction->setChecked(isDockVisible(m_bpTable));
508 m_btWindowAction->setChecked(isDockVisible(m_btWindow));
509 m_localVariablesAction->setChecked(isDockVisible(m_localVariables));
510 m_watchesAction->setChecked(isDockVisible(m_watches));
511 m_registersAction->setChecked(isDockVisible(m_registers));
512 m_threadsAction->setChecked(isDockVisible(m_threads));
513 m_memoryWindowAction->setChecked(isDockVisible(m_memoryWindow));
514 m_ttyWindowAction->setChecked(isDockVisible(m_ttyWindow));
516 m_fileExecAction->setEnabled(m_debugger->isIdle());
517 m_settingsAction->setEnabled(m_debugger->haveExecutable());
518 m_coreDumpAction->setEnabled(m_debugger->canStart());
519 m_closeAction->setEnabled(m_filesWindow->hasWindows());
520 m_reloadAction->setEnabled(m_filesWindow->hasWindows());
521 m_stepIntoAction->setEnabled(m_debugger->canSingleStep());
522 m_stepIntoIAction->setEnabled(m_debugger->canSingleStep());
523 m_stepOverAction->setEnabled(m_debugger->canSingleStep());
524 m_stepOverIAction->setEnabled(m_debugger->canSingleStep());
525 m_stepOutAction->setEnabled(m_debugger->canSingleStep());
526 m_toCursorAction->setEnabled(m_debugger->canSingleStep());
527 m_execMovePCAction->setEnabled(m_debugger->canSingleStep());
528 m_restartAction->setEnabled(m_debugger->canSingleStep());
529 m_attachAction->setEnabled(m_debugger->isReady());
530 m_runAction->setEnabled(m_debugger->canStart() || m_debugger->canSingleStep());
531 m_killAction->setEnabled(m_debugger->haveExecutable() && m_debugger->isProgramActive());
532 m_breakAction->setEnabled(m_debugger->isProgramRunning());
533 m_argumentsAction->setEnabled(m_debugger->haveExecutable());
534 m_editValueAction->setEnabled(m_debugger->canSingleStep());
536 // animation
537 if (m_debugger->isIdle()) {
538 if (m_animRunning) {
539 m_animation->stop();
540 m_animRunning = false;
542 } else {
543 if (!m_animRunning) {
544 m_animation->start();
545 m_animRunning = true;
549 // update statusbar
550 QString newStatus;
551 if (m_debugger->isProgramActive())
552 newStatus = m_statusActive;
553 if (newStatus != m_lastActiveStatusText) {
554 statusBar()->changeItem(newStatus, ID_STATUS_ACTIVE);
555 m_lastActiveStatusText = newStatus;
559 void DebuggerMainWnd::updateLineItems()
561 m_filesWindow->updateLineItems(m_debugger);
564 void DebuggerMainWnd::slotAddWatch()
566 if (m_debugger != 0) {
567 QString t = m_watches->watchText();
568 m_debugger->addWatch(t);
572 void DebuggerMainWnd::slotAddWatch(const QString& text)
574 if (m_debugger != 0) {
575 m_debugger->addWatch(text);
579 void DebuggerMainWnd::slotNewFileLoaded()
581 // updates program counter in the new file
582 if (m_debugger != 0)
583 m_filesWindow->updateLineItems(m_debugger);
586 QDockWidget* DebuggerMainWnd::dockParent(QWidget* w)
588 while ((w = w->parentWidget()) != 0) {
589 if (w->isA("QDockWidget"))
590 return static_cast<QDockWidget*>(w);
592 return 0;
595 bool DebuggerMainWnd::isDockVisible(QWidget* w)
597 QDockWidget* d = dockParent(w);
598 return d != 0 && d->isVisible();
601 void DebuggerMainWnd::makeDefaultLayout()
603 // +---------------+---------+
604 // | Source | Locals |
605 // | | |
606 // |---------------+---------+
607 // |Stack, Brkpts, | Watches |
608 // |Output,... | |
609 // +---------------+---------+
611 addDockWidget(Qt::RightDockWidgetArea, dockParent(m_localVariables));
612 addDockWidget(Qt::BottomDockWidgetArea, dockParent(m_memoryWindow));
613 splitDockWidget(dockParent(m_memoryWindow), dockParent(m_threads), Qt::Horizontal);
614 tabifyDockWidget(dockParent(m_memoryWindow), dockParent(m_registers));
615 tabifyDockWidget(dockParent(m_registers), dockParent(m_bpTable));
616 tabifyDockWidget(dockParent(m_bpTable), dockParent(m_ttyWindow));
617 tabifyDockWidget(dockParent(m_ttyWindow), dockParent(m_btWindow));
618 tabifyDockWidget(dockParent(m_threads), dockParent(m_watches));
619 dockParent(m_localVariables)->setVisible(true);
620 dockParent(m_ttyWindow)->setVisible(true);
621 dockParent(m_watches)->setVisible(true);
622 dockParent(m_btWindow)->setVisible(true);
623 dockParent(m_bpTable)->setVisible(true);
626 bool DebuggerMainWnd::debugProgram(const QString& exe, const QString& lang)
628 // check the file name
629 QFileInfo fi(exe);
631 bool success = fi.isFile();
632 if (!success)
634 QString msg = i18n("`%1' is not a file or does not exist");
635 KMessageBox::sorry(this, msg.arg(exe));
637 else
639 success = startDriver(fi.absFilePath(), lang);
642 if (success)
644 m_recentExecAction->addUrl(KUrl(fi.absFilePath()));
646 // keep the directory
647 m_lastDirectory = fi.dirPath(true);
648 m_filesWindow->setExtraDirectory(m_lastDirectory);
650 // set caption to basename part of executable
651 QString caption = fi.fileName();
652 setCaption(caption);
654 else
656 m_recentExecAction->removeUrl(KUrl(fi.absFilePath()));
659 return success;
662 static const char GeneralGroup[] = "General";
664 bool DebuggerMainWnd::startDriver(const QString& executable, QString lang)
666 assert(m_debugger != 0);
668 TRACE(QString("trying language '%1'...").arg(lang));
669 DebuggerDriver* driver = driverFromLang(lang);
671 if (driver == 0)
673 // see if there is a language in the per-program config file
674 QString configName = m_debugger->getConfigForExe(executable);
675 if (QFile::exists(configName))
677 KConfig c(configName, KConfig::SimpleConfig);
679 // Using "GDB" as default here is for backwards compatibility:
680 // The config file exists but doesn't have an entry,
681 // so it must have been created by an old version of KDbg
682 // that had only the GDB driver.
683 lang = c.group(GeneralGroup)
684 .readEntry(KDebugger::DriverNameEntry, "GDB");
686 TRACE(QString("...bad, trying config driver %1...").arg(lang));
687 driver = driverFromLang(lang);
691 if (driver == 0)
693 QString name = driverNameFromFile(executable);
695 TRACE(QString("...no luck, trying %1 derived"
696 " from file contents").arg(name));
697 driver = driverFromLang(name);
699 if (driver == 0)
701 // oops
702 QString msg = i18n("Don't know how to debug language `%1'");
703 KMessageBox::sorry(this, msg.arg(lang));
704 return false;
707 driver->setLogFileName(m_transcriptFile);
709 bool success = m_debugger->debugProgram(executable, driver);
711 if (!success)
713 delete driver;
715 QString msg = i18n("Could not start the debugger process.\n"
716 "Please shut down KDbg and resolve the problem.");
717 KMessageBox::sorry(this, msg);
720 return success;
723 // derive driver from language
724 DebuggerDriver* DebuggerMainWnd::driverFromLang(QString lang)
726 // lang is needed in all lowercase
727 lang = lang.lower();
729 // The following table relates languages and debugger drivers
730 static const struct L {
731 const char* shortest; // abbreviated to this is still unique
732 const char* full; // full name of language
733 int driver;
734 } langs[] = {
735 { "c", "c++", 1 },
736 { "f", "fortran", 1 },
737 { "p", "python", 3 },
738 { "x", "xslt", 2 },
739 // the following are actually driver names
740 { "gdb", "gdb", 1 },
741 { "xsldbg", "xsldbg", 2 },
743 const int N = sizeof(langs)/sizeof(langs[0]);
745 // lookup the language name
746 int driverID = 0;
747 for (int i = 0; i < N; i++)
749 const L& l = langs[i];
751 // shortest must match
752 if (!lang.startsWith(l.shortest))
753 continue;
755 // lang must not be longer than the full name, and it must match
756 if (QString(l.full).startsWith(lang))
758 driverID = l.driver;
759 break;
762 DebuggerDriver* driver = 0;
763 switch (driverID) {
764 case 1:
766 GdbDriver* gdb = new GdbDriver;
767 gdb->setDefaultInvocation(m_debuggerCmdStr);
768 driver = gdb;
770 break;
771 case 2:
772 driver = new XsldbgDriver;
773 break;
774 default:
775 // unknown language
776 break;
778 return driver;
782 * Try to guess the language to use from the contents of the file.
784 QString DebuggerMainWnd::driverNameFromFile(const QString& exe)
786 /* Inprecise but simple test to see if file is in XSLT language */
787 if (exe.right(4).lower() == ".xsl")
788 return "XSLT";
790 return "GDB";
793 void DebuggerMainWnd::setCoreFile(const QString& corefile)
795 assert(m_debugger != 0);
796 m_debugger->useCoreFile(corefile, true);
799 void DebuggerMainWnd::setRemoteDevice(const QString& remoteDevice)
801 if (m_debugger != 0) {
802 m_debugger->setRemoteDevice(remoteDevice);
806 void DebuggerMainWnd::overrideProgramArguments(const QString& args)
808 assert(m_debugger != 0);
809 m_debugger->overrideProgramArguments(args);
812 void DebuggerMainWnd::setTranscript(const QString& name)
814 m_transcriptFile = name;
815 if (m_debugger != 0 && m_debugger->driver() != 0)
816 m_debugger->driver()->setLogFileName(m_transcriptFile);
819 void DebuggerMainWnd::setAttachPid(const QString& pid)
821 assert(m_debugger != 0);
822 m_debugger->setAttachPid(pid);
825 void DebuggerMainWnd::slotNewStatusMsg()
827 QString msg = m_debugger->statusMessage();
828 statusBar()->changeItem(msg, ID_STATUS_MSG);
831 void DebuggerMainWnd::slotFileGlobalSettings()
833 int oldTabWidth = m_tabWidth;
835 Q3TabDialog dlg(this, "global_options", true);
836 QString title = KGlobal::caption();
837 title += i18n(": Global options");
838 dlg.setCaption(title);
839 dlg.setCancelButton(i18n("Cancel"));
840 dlg.setOKButton(i18n("OK"));
842 PrefDebugger prefDebugger(&dlg);
843 prefDebugger.setDebuggerCmd(m_debuggerCmdStr.isEmpty() ?
844 GdbDriver::defaultGdb() : m_debuggerCmdStr);
845 prefDebugger.setTerminal(m_outputTermCmdStr);
847 PrefMisc prefMisc(&dlg);
848 prefMisc.setPopIntoForeground(m_popForeground);
849 prefMisc.setBackTimeout(m_backTimeout);
850 prefMisc.setTabWidth(m_tabWidth);
851 prefMisc.setSourceFilter(m_sourceFilter);
852 prefMisc.setHeaderFilter(m_headerFilter);
854 dlg.addTab(&prefDebugger, i18n("&Debugger"));
855 dlg.addTab(&prefMisc, i18n("&Miscellaneous"));
856 if (dlg.exec() == QDialog::Accepted)
858 setDebuggerCmdStr(prefDebugger.debuggerCmd());
859 setTerminalCmd(prefDebugger.terminal());
860 m_popForeground = prefMisc.popIntoForeground();
861 m_backTimeout = prefMisc.backTimeout();
862 m_tabWidth = prefMisc.tabWidth();
863 m_sourceFilter = prefMisc.sourceFilter();
864 if (m_sourceFilter.isEmpty())
865 m_sourceFilter = defaultSourceFilter;
866 m_headerFilter = prefMisc.headerFilter();
867 if (m_headerFilter.isEmpty())
868 m_headerFilter = defaultHeaderFilter;
871 if (m_tabWidth != oldTabWidth) {
872 emit setTabWidth(m_tabWidth);
876 void DebuggerMainWnd::setTerminalCmd(const QString& cmd)
878 m_outputTermCmdStr = cmd;
879 // revert to default if empty
880 if (m_outputTermCmdStr.isEmpty()) {
881 m_outputTermCmdStr = defaultTermCmdStr;
885 void DebuggerMainWnd::setDebuggerCmdStr(const QString& cmd)
887 m_debuggerCmdStr = cmd;
888 // make empty if it is the default
889 if (m_debuggerCmdStr == GdbDriver::defaultGdb()) {
890 m_debuggerCmdStr = QString();
894 void DebuggerMainWnd::slotDebuggerStarting()
896 if (m_debugger == 0) /* paranoia check */
897 return;
899 if (m_ttyLevel == m_debugger->ttyLevel())
900 return;
902 // shut down terminal emulations we will not need
903 switch (m_ttyLevel) {
904 case KDebugger::ttySimpleOutputOnly:
905 m_ttyWindow->deactivate();
906 break;
907 case KDebugger::ttyFull:
908 if (m_outputTermProc != 0) {
909 m_outputTermProc->kill();
910 // will be deleted in slot
912 break;
913 default: break;
916 m_ttyLevel = m_debugger->ttyLevel();
918 QString ttyName;
919 switch (m_ttyLevel) {
920 case KDebugger::ttySimpleOutputOnly:
921 ttyName = m_ttyWindow->activate();
922 break;
923 case KDebugger::ttyFull:
924 if (m_outputTermProc == 0) {
925 // create an output window
926 ttyName = createOutputWindow();
927 TRACE(ttyName.isEmpty() ?
928 "createOuputWindow failed" : "successfully created output window");
930 break;
931 default: break;
934 m_debugger->setTerminal(ttyName);
937 void DebuggerMainWnd::slotToggleBreak(const QString& fileName, int lineNo,
938 const DbgAddr& address, bool temp)
940 // lineNo is zero-based
941 if (m_debugger != 0) {
942 m_debugger->setBreakpoint(fileName, lineNo, address, temp);
946 void DebuggerMainWnd::slotEnaDisBreak(const QString& fileName, int lineNo,
947 const DbgAddr& address)
949 // lineNo is zero-based
950 if (m_debugger != 0) {
951 m_debugger->enableDisableBreakpoint(fileName, lineNo, address);
955 QString DebuggerMainWnd::createOutputWindow()
957 // create a name for a fifo
958 QString fifoName;
959 fifoName.sprintf("/tmp/kdbgttywin%05d", ::getpid());
961 // create a fifo that will pass in the tty name
962 QFile::remove(fifoName); // remove remnants
963 #ifdef HAVE_MKFIFO
964 if (::mkfifo(fifoName.local8Bit(), S_IRUSR|S_IWUSR) < 0) {
965 // failed
966 TRACE("mkfifo " + fifoName + " failed");
967 return QString();
969 #else
970 if (::mknod(fifoName.local8Bit(), S_IFIFO | S_IRUSR|S_IWUSR, 0) < 0) {
971 // failed
972 TRACE("mknod " + fifoName + " failed");
973 return QString();
975 #endif
977 m_outputTermProc = new K3Process;
980 * Spawn an xterm that in turn runs a shell script that passes us
981 * back the terminal name and then only sits and waits.
983 static const char shellScriptFmt[] =
984 "tty>%s;"
985 "trap \"\" INT QUIT TSTP;" /* ignore various signals */
986 "exec<&-;exec>&-;" /* close stdin and stdout */
987 "while :;do sleep 3600;done";
988 // let config file override this script
989 QString shellScript;
990 if (!m_outputTermKeepScript.isEmpty()) {
991 shellScript = m_outputTermKeepScript;
992 } else {
993 shellScript = shellScriptFmt;
996 shellScript.replace("%s", fifoName);
997 TRACE("output window script is " + shellScript);
999 QString title = KGlobal::caption();
1000 title += i18n(": Program output");
1002 // parse the command line specified in the preferences
1003 QStringList cmdParts = QStringList::split(' ', m_outputTermCmdStr);
1006 * Build the argv array. Thereby substitute special sequences:
1008 struct {
1009 char seq[4];
1010 QString replace;
1011 } substitute[] = {
1012 { "%T", title },
1013 { "%C", shellScript }
1016 for (QStringList::iterator i = cmdParts.begin(); i != cmdParts.end(); ++i)
1018 QString& str = *i;
1019 for (int j = sizeof(substitute)/sizeof(substitute[0])-1; j >= 0; j--) {
1020 int pos = str.find(substitute[j].seq);
1021 if (pos >= 0) {
1022 str.replace(pos, 2, substitute[j].replace);
1023 break; /* substitute only one sequence */
1026 *m_outputTermProc << str;
1029 QString tty;
1031 if (m_outputTermProc->start())
1033 // read the ttyname from the fifo
1034 QFile f(fifoName);
1035 if (f.open(QIODevice::ReadOnly))
1037 QByteArray t = f.readAll();
1038 tty = QString::fromLocal8Bit(t, t.size());
1039 f.close();
1041 f.remove();
1043 // remove whitespace
1044 tty = tty.stripWhiteSpace();
1045 TRACE("tty=" + tty);
1047 connect(m_outputTermProc, SIGNAL(processExited(K3Process*)),
1048 SLOT(slotTermEmuExited()));
1050 else
1052 // error, could not start xterm
1053 TRACE("fork failed for fifo " + fifoName);
1054 QFile::remove(fifoName);
1055 shutdownTermWindow();
1058 return tty;
1061 void DebuggerMainWnd::slotTermEmuExited()
1063 shutdownTermWindow();
1066 void DebuggerMainWnd::shutdownTermWindow()
1068 delete m_outputTermProc;
1069 m_outputTermProc = 0;
1072 void DebuggerMainWnd::slotProgramStopped()
1074 // when the program stopped, move the window to the foreground
1075 if (m_popForeground) {
1076 // unfortunately, this requires quite some force to work :-(
1077 KWindowSystem::raiseWindow(winId());
1078 KWindowSystem::forceActiveWindow(winId());
1080 m_backTimer.stop();
1083 void DebuggerMainWnd::intoBackground()
1085 if (m_popForeground) {
1086 m_backTimer.start(m_backTimeout, true); /* single-shot */
1090 void DebuggerMainWnd::slotBackTimer()
1092 lower();
1095 void DebuggerMainWnd::slotRecentExec(const KUrl& url)
1097 QString exe = url.path();
1098 debugProgram(exe, "");
1101 QString DebuggerMainWnd::makeSourceFilter()
1103 QString f;
1104 f = m_sourceFilter + " " + m_headerFilter + i18n("|All source files\n");
1105 f += m_sourceFilter + i18n("|Source files\n");
1106 f += m_headerFilter + i18n("|Header files\n");
1107 f += i18n("*|All files");
1108 return f;
1112 * Pop up the context menu in the locals window
1114 void DebuggerMainWnd::slotLocalsPopup(Q3ListViewItem*, const QPoint& pt)
1116 Q3PopupMenu* popup =
1117 static_cast<Q3PopupMenu*>(factory()->container("popup_locals", this));
1118 if (popup == 0) {
1119 return;
1121 if (popup->isVisible()) {
1122 popup->hide();
1123 } else {
1124 popup->popup(pt);
1129 * Copies the currently selected item to the watch window.
1131 void DebuggerMainWnd::slotLocalsToWatch()
1133 VarTree* item = m_localVariables->selectedItem();
1135 if (item != 0 && m_debugger != 0) {
1136 QString text = item->computeExpr();
1137 m_debugger->addWatch(text);
1142 * Starts editing a value in a value display
1144 void DebuggerMainWnd::slotEditValue()
1146 // does one of the value trees have the focus
1147 QWidget* f = kapp->focusWidget();
1148 ExprWnd* wnd;
1149 if (f == m_localVariables) {
1150 wnd = m_localVariables;
1151 } else if (f == m_watches->watchVariables()) {
1152 wnd = m_watches->watchVariables();
1153 } else {
1154 return;
1157 if (m_localVariables->isEditing() ||
1158 m_watches->watchVariables()->isEditing())
1160 return; /* don't edit twice */
1163 VarTree* expr = wnd->currentItem();
1164 if (expr != 0 && m_debugger != 0 && m_debugger->canSingleStep())
1166 TRACE("edit value");
1167 // determine the text to edit
1168 QString text = m_debugger->driver()->editableValue(expr);
1169 wnd->editValue(expr, text);
1173 // helper that gets a file name (it only differs in the caption of the dialog)
1174 static QString myGetFileName(QString caption,
1175 QString dir, QString filter,
1176 QWidget* parent)
1178 QString filename;
1179 KFileDialog dlg(dir, filter, parent);
1181 dlg.setCaption(caption);
1183 if (dlg.exec() == QDialog::Accepted)
1184 filename = dlg.selectedFile();
1186 return filename;
1189 void DebuggerMainWnd::slotFileOpen()
1191 // start browsing in the active file's directory
1192 // fall back to last used directory (executable)
1193 QString dir = m_lastDirectory;
1194 QString fileName = m_filesWindow->activeFileName();
1195 if (!fileName.isEmpty()) {
1196 QFileInfo fi(fileName);
1197 dir = fi.dirPath();
1200 fileName = myGetFileName(i18n("Open"),
1201 dir,
1202 makeSourceFilter(), this);
1204 if (!fileName.isEmpty())
1206 QFileInfo fi(fileName);
1207 m_lastDirectory = fi.dirPath();
1208 m_filesWindow->setExtraDirectory(m_lastDirectory);
1209 m_filesWindow->activateFile(fileName);
1213 void DebuggerMainWnd::slotFileExe()
1215 if (m_debugger->isIdle())
1217 // open a new executable
1218 QString executable = myGetFileName(i18n("Select the executable to debug"),
1219 m_lastDirectory, 0, this);
1220 if (executable.isEmpty())
1221 return;
1223 debugProgram(executable, "");
1227 void DebuggerMainWnd::slotFileCore()
1229 if (m_debugger->canStart())
1231 QString corefile = myGetFileName(i18n("Select core dump"),
1232 m_lastDirectory, 0, this);
1233 if (!corefile.isEmpty()) {
1234 m_debugger->useCoreFile(corefile, false);
1239 void DebuggerMainWnd::slotFileProgSettings()
1241 if (m_debugger != 0) {
1242 m_debugger->programSettings(this);
1246 void DebuggerMainWnd::slotViewStatusbar()
1248 if (statusBar()->isVisible())
1249 statusBar()->hide();
1250 else
1251 statusBar()->show();
1252 setSettingsDirty();
1255 void DebuggerMainWnd::slotExecUntil()
1257 if (m_debugger != 0)
1259 QString file;
1260 int lineNo;
1261 if (m_filesWindow->activeLine(file, lineNo))
1262 m_debugger->runUntil(file, lineNo);
1266 void DebuggerMainWnd::slotExecAttach()
1268 #ifdef PS_COMMAND
1269 ProcAttachPS dlg(this);
1270 // seed filter with executable name
1271 QFileInfo fi = m_debugger->executable();
1272 dlg.setFilterText(fi.fileName());
1273 #else
1274 ProcAttach dlg(this);
1275 dlg.setText(m_debugger->attachedPid());
1276 #endif
1277 if (dlg.exec()) {
1278 m_debugger->attachProgram(dlg.text());
1282 void DebuggerMainWnd::slotExecArgs()
1284 if (m_debugger != 0) {
1285 m_debugger->programArgs(this);
1289 void DebuggerMainWnd::slotConfigureKeys()
1291 KShortcutsDialog::configure(actionCollection());
1294 #include "dbgmainwnd.moc"