Store the main window geometry across sessions.
[kdbg.git] / kdbg / dbgmainwnd.cpp
blob0e28e3de5757f0a6f1dfd42e41355bcf1e71d75e
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.h>
28 #include <qfile.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"
36 #include "debugger.h"
37 #include "commandids.h"
38 #include "winstack.h"
39 #include "brkpt.h"
40 #include "threadlist.h"
41 #include "memwindow.h"
42 #include "ttywnd.h"
43 #include "watchwindow.h"
44 #include "procattach.h"
45 #include "prefdebugger.h"
46 #include "prefmisc.h"
47 #include "gdbdriver.h"
48 #include "xsldbgdriver.h"
49 #include "mydebug.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() :
59 KXmlGuiWindow(),
60 m_debugger(0),
61 #ifdef GDB_TRANSCRIPT
62 m_transcriptFile(GDB_TRANSCRIPT),
63 #endif
64 m_outputTermCmdStr(defaultTermCmdStr),
65 m_outputTermProc(0),
66 m_ttyLevel(-1), /* no tty yet */
67 m_popForeground(false),
68 m_backTimeout(1000),
69 m_tabWidth(0),
70 m_sourceFilter(defaultSourceFilter),
71 m_headerFilter(defaultHeaderFilter),
72 m_animation(0),
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);
113 initKAction();
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&)));
138 // disassembling
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&)));
145 // program stopped
146 connect(m_debugger, SIGNAL(programStopped()), SLOT(slotProgramStopped()));
147 connect(&m_backTimer, SIGNAL(timeout()), SLOT(slotBackTimer()));
148 // tab width
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*)));
168 // thread window
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());
180 updateUI();
181 m_bpTable->updateUI();
184 DebuggerMainWnd::~DebuggerMainWnd()
186 saveSettings(KGlobal::config());
187 // must delete m_debugger early since it references our windows
188 delete m_debugger;
189 m_debugger = 0;
191 delete m_memoryWindow;
192 delete m_threads;
193 delete m_ttyWindow;
194 delete m_bpTable;
195 delete m_registers;
196 delete m_watches;
197 delete m_localVariables;
198 delete m_btWindow;
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()));
215 return w;
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);
223 a->setText(text);
224 a->setIcon(KIcon(icon));
225 if (shortcut)
226 a->setShortcut(KShortcut(shortcut));
227 connect(a, SIGNAL(triggered()), receiver, slot);
228 return a;
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);
236 a->setText(text);
237 if (shortcut)
238 a->setShortcut(KShortcut(shortcut));
239 connect(a, SIGNAL(triggered()), receiver, slot);
240 return a;
244 void DebuggerMainWnd::initKAction()
246 // file menu
247 KAction* open = KStandardAction::open(this, SLOT(slotFileOpen()),
248 actionCollection());
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"),
256 actionCollection());
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());
264 // settings menu
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());
272 // view menu
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()));
295 // execution menu
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");
330 // breakpoint menu
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
369 i18n("Restart");
370 i18n("Core dump");
373 bool DebuggerMainWnd::queryClose()
375 if (m_debugger != 0) {
376 m_debugger->shutdown();
378 return true;
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))
458 makeDefaultLayout();
459 QRect r = g.readEntry("Geometry", QRect());
460 if (!r.isEmpty())
461 setGeometry(r);
463 toolBar("mainToolBar")->applySettings(config->group(ToolbarGroup));
465 m_recentExecAction->loadEntries(config->group(RecentExecutables));
467 KConfigGroup lg = config->group(LastSession);
468 int w;
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());
537 // animation
538 if (m_debugger->isIdle()) {
539 if (m_animRunning) {
540 m_animation->stop();
541 m_animRunning = false;
543 } else {
544 if (!m_animRunning) {
545 m_animation->start();
546 m_animRunning = true;
550 // update statusbar
551 QString newStatus;
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
583 if (m_debugger != 0)
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);
593 return 0;
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 |
606 // | | |
607 // |---------------+---------+
608 // |Stack, Brkpts, | Watches |
609 // |Output,... | |
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
630 QFileInfo fi(exe);
632 bool success = fi.isFile();
633 if (!success)
635 QString msg = i18n("`%1' is not a file or does not exist");
636 KMessageBox::sorry(this, msg.arg(exe));
638 else
640 success = startDriver(fi.absFilePath(), lang);
643 if (success)
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();
653 setCaption(caption);
655 else
657 m_recentExecAction->removeUrl(KUrl(fi.absFilePath()));
660 return success;
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);
672 if (driver == 0)
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);
692 if (driver == 0)
694 QString name = driverNameFromFile(executable);
696 TRACE(QString("...no luck, trying %1 derived"
697 " from file contents").arg(name));
698 driver = driverFromLang(name);
700 if (driver == 0)
702 // oops
703 QString msg = i18n("Don't know how to debug language `%1'");
704 KMessageBox::sorry(this, msg.arg(lang));
705 return false;
708 driver->setLogFileName(m_transcriptFile);
710 bool success = m_debugger->debugProgram(executable, driver);
712 if (!success)
714 delete 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);
721 return success;
724 // derive driver from language
725 DebuggerDriver* DebuggerMainWnd::driverFromLang(QString lang)
727 // lang is needed in all lowercase
728 lang = lang.lower();
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
734 int driver;
735 } langs[] = {
736 { "c", "c++", 1 },
737 { "f", "fortran", 1 },
738 { "p", "python", 3 },
739 { "x", "xslt", 2 },
740 // the following are actually driver names
741 { "gdb", "gdb", 1 },
742 { "xsldbg", "xsldbg", 2 },
744 const int N = sizeof(langs)/sizeof(langs[0]);
746 // lookup the language name
747 int driverID = 0;
748 for (int i = 0; i < N; i++)
750 const L& l = langs[i];
752 // shortest must match
753 if (!lang.startsWith(l.shortest))
754 continue;
756 // lang must not be longer than the full name, and it must match
757 if (QString(l.full).startsWith(lang))
759 driverID = l.driver;
760 break;
763 DebuggerDriver* driver = 0;
764 switch (driverID) {
765 case 1:
767 GdbDriver* gdb = new GdbDriver;
768 gdb->setDefaultInvocation(m_debuggerCmdStr);
769 driver = gdb;
771 break;
772 case 2:
773 driver = new XsldbgDriver;
774 break;
775 default:
776 // unknown language
777 break;
779 return driver;
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")
789 return "XSLT";
791 return "GDB";
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 */
898 return;
900 if (m_ttyLevel == m_debugger->ttyLevel())
901 return;
903 // shut down terminal emulations we will not need
904 switch (m_ttyLevel) {
905 case KDebugger::ttySimpleOutputOnly:
906 m_ttyWindow->deactivate();
907 break;
908 case KDebugger::ttyFull:
909 if (m_outputTermProc != 0) {
910 m_outputTermProc->kill();
911 // will be deleted in slot
913 break;
914 default: break;
917 m_ttyLevel = m_debugger->ttyLevel();
919 QString ttyName;
920 switch (m_ttyLevel) {
921 case KDebugger::ttySimpleOutputOnly:
922 ttyName = m_ttyWindow->activate();
923 break;
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");
931 break;
932 default: break;
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
959 QString fifoName;
960 fifoName.sprintf("/tmp/kdbgttywin%05d", ::getpid());
962 // create a fifo that will pass in the tty name
963 QFile::remove(fifoName); // remove remnants
964 #ifdef HAVE_MKFIFO
965 if (::mkfifo(fifoName.local8Bit(), S_IRUSR|S_IWUSR) < 0) {
966 // failed
967 TRACE("mkfifo " + fifoName + " failed");
968 return QString();
970 #else
971 if (::mknod(fifoName.local8Bit(), S_IFIFO | S_IRUSR|S_IWUSR, 0) < 0) {
972 // failed
973 TRACE("mknod " + fifoName + " failed");
974 return QString();
976 #endif
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[] =
985 "tty>%s;"
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
990 QString shellScript;
991 if (!m_outputTermKeepScript.isEmpty()) {
992 shellScript = m_outputTermKeepScript;
993 } else {
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:
1009 struct {
1010 char seq[4];
1011 QString replace;
1012 } substitute[] = {
1013 { "%T", title },
1014 { "%C", shellScript }
1017 for (QStringList::iterator i = cmdParts.begin(); i != cmdParts.end(); ++i)
1019 QString& str = *i;
1020 for (int j = sizeof(substitute)/sizeof(substitute[0])-1; j >= 0; j--) {
1021 int pos = str.find(substitute[j].seq);
1022 if (pos >= 0) {
1023 str.replace(pos, 2, substitute[j].replace);
1024 break; /* substitute only one sequence */
1027 *m_outputTermProc << str;
1030 QString tty;
1032 if (m_outputTermProc->start())
1034 // read the ttyname from the fifo
1035 QFile f(fifoName);
1036 if (f.open(QIODevice::ReadOnly))
1038 QByteArray t = f.readAll();
1039 tty = QString::fromLocal8Bit(t, t.size());
1040 f.close();
1042 f.remove();
1044 // remove whitespace
1045 tty = tty.stripWhiteSpace();
1046 TRACE("tty=" + tty);
1048 connect(m_outputTermProc, SIGNAL(processExited(K3Process*)),
1049 SLOT(slotTermEmuExited()));
1051 else
1053 // error, could not start xterm
1054 TRACE("fork failed for fifo " + fifoName);
1055 QFile::remove(fifoName);
1056 shutdownTermWindow();
1059 return tty;
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());
1081 m_backTimer.stop();
1084 void DebuggerMainWnd::intoBackground()
1086 if (m_popForeground) {
1087 m_backTimer.start(m_backTimeout, true); /* single-shot */
1091 void DebuggerMainWnd::slotBackTimer()
1093 lower();
1096 void DebuggerMainWnd::slotRecentExec(const KUrl& url)
1098 QString exe = url.path();
1099 debugProgram(exe, "");
1102 QString DebuggerMainWnd::makeSourceFilter()
1104 QString f;
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");
1109 return f;
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));
1119 if (popup == 0) {
1120 return;
1122 if (popup->isVisible()) {
1123 popup->hide();
1124 } else {
1125 popup->popup(pt);
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();
1149 ExprWnd* wnd;
1150 if (f == m_localVariables) {
1151 wnd = m_localVariables;
1152 } else if (f == m_watches->watchVariables()) {
1153 wnd = m_watches->watchVariables();
1154 } else {
1155 return;
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,
1177 QWidget* parent)
1179 QString filename;
1180 KFileDialog dlg(dir, filter, parent);
1182 dlg.setCaption(caption);
1184 if (dlg.exec() == QDialog::Accepted)
1185 filename = dlg.selectedFile();
1187 return filename;
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);
1198 dir = fi.dirPath();
1201 fileName = myGetFileName(i18n("Open"),
1202 dir,
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())
1222 return;
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();
1251 else
1252 statusBar()->show();
1253 setSettingsDirty();
1256 void DebuggerMainWnd::slotExecUntil()
1258 if (m_debugger != 0)
1260 QString file;
1261 int lineNo;
1262 if (m_filesWindow->activeLine(file, lineNo))
1263 m_debugger->runUntil(file, lineNo);
1267 void DebuggerMainWnd::slotExecAttach()
1269 #ifdef PS_COMMAND
1270 ProcAttachPS dlg(this);
1271 // seed filter with executable name
1272 QFileInfo fi = m_debugger->executable();
1273 dlg.setFilterText(fi.fileName());
1274 #else
1275 ProcAttach dlg(this);
1276 dlg.setText(m_debugger->attachedPid());
1277 #endif
1278 if (dlg.exec()) {
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"