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  */
6 
7 #include <klocalizedstring.h>		/* i18n */
8 #include <kmessagebox.h>
9 #include <kconfig.h>
10 #include <kconfiggroup.h>
11 #include <kiconengine.h>
12 #include <kiconloader.h>
13 #include <kstandardaction.h>
14 #include <kstandardshortcut.h>
15 #include <kactioncollection.h>
16 #include <krecentfilesaction.h>
17 #include <kshortcutsdialog.h>
18 #include <kanimatedbutton.h>
19 #include <kwindowsystem.h>
20 #include <ksqueezedtextlabel.h>
21 #include <ktoolbar.h>
22 #include <kxmlguifactory.h>
23 #include <KPageDialog>
24 #include <QListWidget>
25 #include <QFile>
26 #include <QFileDialog>
27 #include <QFileInfo>
28 #include <QGuiApplication>
29 #include <QIcon>
30 #include <QList>
31 #include <QDockWidget>
32 #include <QProcess>
33 #include <QStatusBar>
34 #include <QUrl>
35 #include "dbgmainwnd.h"
36 #include "debugger.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 <typeinfo>
50 #include <sys/stat.h>			/* mknod(2) */
51 #include <unistd.h>			/* getpid */
52 
53 
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++";
57 
DebuggerMainWnd()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(new QProcess),
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_statusActive(i18n("active"))
73 {
74     setDockNestingEnabled(true);
75 
76     m_filesWindow = new WinStack(this);
77     setCentralWidget(m_filesWindow);
78 
79     QDockWidget* dw1 = createDockWidget("Stack", i18n("Stack"));
80     m_btWindow = new QListWidget(dw1);
81     dw1->setWidget(m_btWindow);
82     QDockWidget* dw2 = createDockWidget("Locals", i18n("Locals"));
83     m_localVariables = new ExprWnd(dw2, i18n("Variable"));
84     dw2->setWidget(m_localVariables);
85     QDockWidget* dw3 = createDockWidget("Watches", i18n("Watched Expressions"));
86     m_watches = new WatchWindow(dw3);
87     dw3->setWidget(m_watches);
88     QDockWidget* dw4 = createDockWidget("Registers", i18n("Registers"));
89     m_registers = new RegisterView(dw4);
90     dw4->setWidget(m_registers);
91     QDockWidget* dw5 = createDockWidget("Breakpoints", i18n("Breakpoints"));
92     m_bpTable = new BreakpointTable(dw5);
93     dw5->setWidget(m_bpTable);
94     QDockWidget* dw6 = createDockWidget("Output", i18n("Output"));
95     m_ttyWindow = new TTYWindow(dw6);
96     dw6->setWidget(m_ttyWindow);
97     QDockWidget* dw7 = createDockWidget("Threads", i18n("Threads"));
98     m_threads = new ThreadList(dw7);
99     dw7->setWidget(m_threads);
100     QDockWidget* dw8 = createDockWidget("Memory", i18n("Memory"));
101     m_memoryWindow = new MemoryWindow(dw8);
102     dw8->setWidget(m_memoryWindow);
103 
104     m_debugger = new KDebugger(this, m_localVariables, m_watches->watchVariables(), m_btWindow);
105 
106     connect(m_debugger, SIGNAL(updateStatusMessage()), SLOT(slotNewStatusMsg()));
107     connect(m_debugger, SIGNAL(updateUI()), SLOT(updateUI()));
108     connect(m_debugger, SIGNAL(breakpointsChanged()), SLOT(updateLineItems()));
109     connect(m_debugger, SIGNAL(debuggerStarting()), SLOT(slotDebuggerStarting()));
110     m_bpTable->setDebugger(m_debugger);
111     m_memoryWindow->setDebugger(m_debugger);
112 
113     setStandardToolBarMenuEnabled(true);
114     initKAction();
115     initStatusBar();
116 
117     connect(m_watches, SIGNAL(addWatch()), SLOT(slotAddWatch()));
118     connect(m_watches, SIGNAL(deleteWatch()), m_debugger, SLOT(slotDeleteWatch()));
119     connect(m_watches, SIGNAL(textDropped(const QString&)), SLOT(slotAddWatch(const QString&)));
120 
121     connect(&m_filesWindow->m_findDlg, SIGNAL(closed()), SLOT(updateUI()));
122     connect(m_filesWindow, SIGNAL(newFileLoaded()),
123 	    SLOT(slotNewFileLoaded()));
124     connect(m_filesWindow, SIGNAL(toggleBreak(const QString&,int,const DbgAddr&,bool)),
125 	    this, SLOT(slotToggleBreak(const QString&,int,const DbgAddr&,bool)));
126     connect(m_filesWindow, SIGNAL(enadisBreak(const QString&,int,const DbgAddr&)),
127 	    this, SLOT(slotEnaDisBreak(const QString&,int,const DbgAddr&)));
128     connect(m_debugger, SIGNAL(activateFileLine(const QString&,int,const DbgAddr&)),
129 	    m_filesWindow, SLOT(activate(const QString&,int,const DbgAddr&)));
130     connect(m_debugger, SIGNAL(executableUpdated()),
131 	    m_filesWindow, SLOT(reloadAllFiles()));
132     connect(m_debugger, SIGNAL(updatePC(const QString&,int,const DbgAddr&,int)),
133 	    m_filesWindow, SLOT(updatePC(const QString&,int,const DbgAddr&,int)));
134     // value popup communication
135     connect(m_filesWindow, SIGNAL(initiateValuePopup(const QString&)),
136 	    m_debugger, SLOT(slotValuePopup(const QString&)));
137     connect(m_debugger, SIGNAL(valuePopup(const QString&)),
138 	    m_filesWindow, SLOT(slotShowValueTip(const QString&)));
139     // disassembling
140     connect(m_filesWindow, SIGNAL(disassemble(const QString&, int)),
141 	    m_debugger, SLOT(slotDisassemble(const QString&, int)));
142     connect(m_debugger, SIGNAL(disassembled(const QString&,int,const std::list<DisassembledCode>&)),
143 	    m_filesWindow, SLOT(slotDisassembled(const QString&,int,const std::list<DisassembledCode>&)));
144     connect(m_filesWindow, SIGNAL(moveProgramCounter(const QString&,int,const DbgAddr&)),
145 	    m_debugger, SLOT(setProgramCounter(const QString&,int,const DbgAddr&)));
146     // program stopped
147     connect(m_debugger, SIGNAL(programStopped()), SLOT(slotProgramStopped()));
148     connect(&m_backTimer, SIGNAL(timeout()), SLOT(slotBackTimer()));
149     // tab width
150     connect(this, SIGNAL(setTabWidth(int)), m_filesWindow, SIGNAL(setTabWidth(int)));
151 
152     // connect breakpoint table
153     connect(m_bpTable, SIGNAL(activateFileLine(const QString&,int,const DbgAddr&)),
154 	    m_filesWindow, SLOT(activate(const QString&,int,const DbgAddr&)));
155     connect(m_debugger, SIGNAL(updateUI()), m_bpTable, SLOT(updateUI()));
156     connect(m_debugger, SIGNAL(breakpointsChanged()), m_bpTable, SLOT(updateBreakList()));
157     connect(m_debugger, SIGNAL(breakpointsChanged()), m_bpTable, SLOT(updateUI()));
158 
159     connect(m_debugger, SIGNAL(registersChanged(const std::list<RegisterInfo>&)),
160 	    m_registers, SLOT(updateRegisters(const std::list<RegisterInfo>&)));
161 
162     connect(m_debugger, SIGNAL(memoryDumpChanged(const QString&, const std::list<MemoryDump>&)),
163 	    m_memoryWindow, SLOT(slotNewMemoryDump(const QString&, const std::list<MemoryDump>&)));
164     connect(m_debugger, SIGNAL(saveProgramSpecific(KConfigBase*)),
165 	    m_memoryWindow, SLOT(saveProgramSpecific(KConfigBase*)));
166     connect(m_debugger, SIGNAL(restoreProgramSpecific(KConfigBase*)),
167 	    m_memoryWindow, SLOT(restoreProgramSpecific(KConfigBase*)));
168 
169     // thread window
170     connect(m_debugger, SIGNAL(threadsChanged(const std::list<ThreadInfo>&)),
171 	    m_threads, SLOT(updateThreads(const std::list<ThreadInfo>&)));
172     connect(m_threads, SIGNAL(setThread(int)),
173 	    m_debugger, SLOT(setThread(int)));
174 
175     // popup menu of the local variables window
176     m_localVariables->setContextMenuPolicy(Qt::CustomContextMenu);
177     connect(m_localVariables, SIGNAL(customContextMenuRequested(const QPoint&)),
178 	    this, SLOT(slotLocalsPopup(const QPoint&)));
179 
180     makeDefaultLayout();
181     setupGUI(KXmlGuiWindow::Default, "kdbgui.rc");
182     restoreSettings(KSharedConfig::openConfig());
183 
184     // The animation button is not part of the restored window state.
185     // We must create it after the toolbar was loaded.
186     initAnimation();
187 
188     updateUI();
189     m_bpTable->updateUI();
190 }
191 
~DebuggerMainWnd()192 DebuggerMainWnd::~DebuggerMainWnd()
193 {
194     saveSettings(KSharedConfig::openConfig());
195     // must delete m_debugger early since it references our windows
196     delete m_debugger;
197     m_debugger = 0;
198 
199     delete m_memoryWindow;
200     delete m_threads;
201     delete m_ttyWindow;
202     delete m_bpTable;
203     delete m_registers;
204     delete m_watches;
205     delete m_localVariables;
206     delete m_btWindow;
207     delete m_filesWindow;
208 
209     delete m_outputTermProc;
210 }
211 
createDockWidget(const char * name,const QString & title)212 QDockWidget* DebuggerMainWnd::createDockWidget(const char* name, const QString& title)
213 {
214     QDockWidget* w = new QDockWidget(title, this);
215     w->setObjectName(name);
216     // view menu changes when docking state changes
217     connect(w, SIGNAL(visibilityChanged(bool)), SLOT(updateUI()));
218     return w;
219 }
220 
createAction(const QString & text,const char * icon,int shortcut,const QObject * receiver,const char * slot,const char * name)221 QAction* DebuggerMainWnd::createAction(const QString& text, const char* icon,
222 			int shortcut, const QObject* receiver,
223 			const char* slot, const char* name)
224 {
225     QAction* a = actionCollection()->addAction(name);
226     a->setText(text);
227     a->setIcon(QIcon(new KIconEngine(icon, KIconLoader::global())));
228     if (shortcut)
229 	actionCollection()->setDefaultShortcut(a, QKeySequence(shortcut));
230     connect(a, SIGNAL(triggered()), receiver, slot);
231     return a;
232 }
233 
createAction(const QString & text,int shortcut,const QObject * receiver,const char * slot,const char * name)234 QAction* DebuggerMainWnd::createAction(const QString& text,
235 			int shortcut, const QObject* receiver,
236 			const char* slot, const char* name)
237 {
238     QAction* a = actionCollection()->addAction(name);
239     a->setText(text);
240     if (shortcut)
241 	actionCollection()->setDefaultShortcut(a, QKeySequence(shortcut));
242     connect(a, SIGNAL(triggered()), receiver, slot);
243     return a;
244 }
245 
246 
initKAction()247 void DebuggerMainWnd::initKAction()
248 {
249     // file menu
250     QAction* open = KStandardAction::open(this, SLOT(slotFileOpen()),
251                       actionCollection());
252     open->setText(i18n("&Open Source Code..."));
253     m_closeAction = KStandardAction::close(m_filesWindow, SLOT(slotClose()), actionCollection());
254     m_reloadAction = createAction(i18n("&Reload Source Code"), "view-refresh", 0,
255 			m_filesWindow, SLOT(slotFileReload()), "file_reload");
256     m_fileExecAction = createAction(i18n("&Load Executable..."),
257 			"document-open-executable", 0,
258 			this, SLOT(slotFileExe()), "file_executable");
259     m_recentExecAction = KStandardAction::openRecent(this, SLOT(slotRecentExec(const QUrl&)),
260 		      actionCollection());
261     m_recentExecAction->setObjectName("file_executable_recent");
262     m_recentExecAction->setText(i18n("Recent E&xecutables"));
263     m_coreDumpAction = createAction(i18n("&Core Dump..."), 0,
264 			this, SLOT(slotFileCore()), "file_core_dump");
265     KStandardAction::quit(this, SLOT(close()), actionCollection());
266 
267     // settings menu
268     m_settingsAction = createAction(i18n("This &Program..."), 0,
269 			this, SLOT(slotFileProgSettings()), "settings_program");
270     createAction(i18n("&Global Options..."), 0,
271 			this, SLOT(slotFileGlobalSettings()), "settings_global");
272     KStandardAction::keyBindings(this, SLOT(slotConfigureKeys()), actionCollection());
273     KStandardAction::showStatusbar(this, SLOT(slotViewStatusbar()), actionCollection());
274 
275     // view menu
276     m_findAction = KStandardAction::find(m_filesWindow, SLOT(slotViewFind()), actionCollection());
277     KStandardAction::findNext(m_filesWindow, SLOT(slotFindForward()), actionCollection());
278     KStandardAction::findPrev(m_filesWindow, SLOT(slotFindBackward()), actionCollection());
279 
280     struct { QWidget* w; QString id; QAction** act; } dw[] = {
281 	{ m_btWindow, "view_stack", &m_btWindowAction },
282 	{ m_localVariables, "view_locals", &m_localVariablesAction },
283 	{ m_watches, "view_watched_expressions", &m_watchesAction },
284 	{ m_registers, "view_registers", &m_registersAction },
285 	{ m_bpTable, "view_breakpoints", &m_bpTableAction },
286 	{ m_threads, "view_threads", &m_threadsAction },
287 	{ m_ttyWindow, "view_output", &m_ttyWindowAction },
288 	{ m_memoryWindow, "view_memory", &m_memoryWindowAction }
289     };
290     for (unsigned i = 0; i < sizeof(dw)/sizeof(dw[0]); i++) {
291 	QDockWidget* d = dockParent(dw[i].w);
292 	QAction* action = d->toggleViewAction();
293 	actionCollection()->addAction(dw[i].id, action);
294 	*dw[i].act = action;
295     }
296 
297     // execution menu
298     m_runAction = createAction(i18n("&Run"),
299 			"debug-run", Qt::Key_F5,
300 			m_debugger, SLOT(programRun()), "exec_run");
301     connect(m_runAction, SIGNAL(triggered()), this, SLOT(intoBackground()));
302     m_stepIntoAction = createAction(i18n("Step &into"),
303 			"debug-step-into", Qt::Key_F8,
304 			m_debugger, SLOT(programStep()), "exec_step_into");
305     connect(m_stepIntoAction, SIGNAL(triggered()), this, SLOT(intoBackground()));
306     m_stepOverAction = createAction(i18n("Step &over"),
307 			"debug-step-over", Qt::Key_F10,
308 			m_debugger, SLOT(programNext()), "exec_step_over");
309     connect(m_stepOverAction, SIGNAL(triggered()), this, SLOT(intoBackground()));
310     m_stepOutAction = createAction(i18n("Step o&ut"),
311 			"debug-step-out", Qt::Key_F6,
312 			m_debugger, SLOT(programFinish()), "exec_step_out");
313     connect(m_stepOutAction, SIGNAL(triggered()), this, SLOT(intoBackground()));
314     m_toCursorAction = createAction(i18n("Run to &cursor"),
315 			"debug-execute-to-cursor", Qt::Key_F7,
316 			this, SLOT(slotExecUntil()), "exec_run_to_cursor");
317     connect(m_toCursorAction, SIGNAL(triggered()), this, SLOT(intoBackground()));
318     m_stepIntoIAction = createAction(i18n("Step i&nto by instruction"),
319 			"debug-step-into-instruction", Qt::SHIFT+Qt::Key_F8,
320 			m_debugger, SLOT(programStepi()), "exec_step_into_by_insn");
321     connect(m_stepIntoIAction, SIGNAL(triggered()), this, SLOT(intoBackground()));
322     m_stepOverIAction = createAction(i18n("Step o&ver by instruction"),
323 			"debug-step-instruction", Qt::SHIFT+Qt::Key_F10,
324 			m_debugger, SLOT(programNexti()), "exec_step_over_by_insn");
325     connect(m_stepOverIAction, SIGNAL(triggered()), this, SLOT(intoBackground()));
326     m_execMovePCAction = createAction(i18n("&Program counter to current line"),
327 			"debug-run-cursor", 0,
328 			m_filesWindow, SLOT(slotMoveProgramCounter()), "exec_movepc");
329     m_breakAction = createAction(i18n("&Break"), 0,
330 			m_debugger, SLOT(programBreak()), "exec_break");
331     m_killAction = createAction(i18n("&Kill"), 0,
332 			m_debugger, SLOT(programKill()), "exec_kill");
333     m_restartAction = createAction(i18n("Re&start"), 0,
334 			m_debugger, SLOT(programRunAgain()), "exec_restart");
335     m_attachAction = createAction(i18n("A&ttach..."), 0,
336 			this, SLOT(slotExecAttach()), "exec_attach");
337     m_detachAction = createAction(i18n("&Detach"), 0,
338 			m_debugger, SLOT(programDetach()), "exec_detach");
339     m_argumentsAction = createAction(i18n("&Arguments..."), 0,
340 			this, SLOT(slotExecArgs()), "exec_arguments");
341 
342     // breakpoint menu
343     m_bpSetAction = createAction(i18n("Set/Clear &breakpoint"), "brkpt", Qt::Key_F9,
344 			m_filesWindow, SLOT(slotBrkptSet()), "breakpoint_set");
345     m_bpSetTempAction = createAction(i18n("Set &temporary breakpoint"), Qt::SHIFT+Qt::Key_F9,
346 			m_filesWindow, SLOT(slotBrkptSetTemp()), "breakpoint_set_temporary");
347     m_bpEnableAction = createAction(i18n("&Enable/Disable breakpoint"), Qt::CTRL+Qt::Key_F9,
348 			m_filesWindow, SLOT(slotBrkptEnable()), "breakpoint_enable");
349 
350     // only in popup menus
351     createAction(i18n("Watch Expression"), 0,
352 			this, SLOT(slotLocalsToWatch()), "watch_expression");
353     m_editValueAction = createAction(i18n("Edit Value"), Qt::Key_F2,
354 			this, SLOT(slotEditValue()), "edit_value");
355 
356     // all actions force an UI update
357     QList<QAction*> actions = actionCollection()->actions();
358     foreach(QAction* action, actions) {
359 	connect(action, SIGNAL(triggered()), this, SLOT(updateUI()));
360     }
361 }
362 
initAnimation()363 void DebuggerMainWnd::initAnimation()
364 {
365     KToolBar* toolbar = toolBar("mainToolBar");
366     m_animation = new KAnimatedButton(toolbar);
367     toolbar->addWidget(m_animation);
368     m_animation->setAnimationPath(KIconLoader::global()->moviePath("pulse", KIconLoader::Toolbar));
369     connect(m_animation, SIGNAL(clicked(bool)), m_debugger, SLOT(programBreak()));
370     m_animRunning = false;
371 }
372 
initStatusBar()373 void DebuggerMainWnd::initStatusBar()
374 {
375     QStatusBar* statusbar = statusBar();
376     m_statusActiveLabel = new KSqueezedTextLabel(statusbar);
377     m_statusActiveLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
378     statusbar->addPermanentWidget(m_statusActiveLabel);
379     m_statusActiveLabel->show();
380     m_lastActiveStatusText = m_statusActive;
381 
382     /* message pane */
383     m_statusMsgLabel = new KSqueezedTextLabel(statusbar);
384     m_statusMsgLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
385     statusbar->addPermanentWidget(m_statusMsgLabel);
386     m_statusMsgLabel->show();
387 
388     // reserve some translations
389     i18n("Restart");
390     i18n("Core dump");
391 }
392 
queryClose()393 bool DebuggerMainWnd::queryClose()
394 {
395     if (m_debugger != 0) {
396 	m_debugger->shutdown();
397     }
398     return true;
399 }
400 
401 
402 // instance properties
saveProperties(KConfigGroup & cg)403 void DebuggerMainWnd::saveProperties(KConfigGroup& cg)
404 {
405     // session management
406     QString executable = "";
407     if (m_debugger != 0) {
408 	executable = m_debugger->executable();
409     }
410     cg.writeEntry("executable", executable);
411 }
412 
readProperties(const KConfigGroup & cg)413 void DebuggerMainWnd::readProperties(const KConfigGroup& cg)
414 {
415     // session management
416     QString execName = cg.readEntry("executable");
417 
418     TRACE("readProperties: executable=" + execName);
419     if (!execName.isEmpty()) {
420 	debugProgram(execName, "");
421     }
422 }
423 
424 static const char RecentExecutables[] = "RecentExecutables";
425 static const char LastSession[] = "LastSession";
426 static const char OutputWindowGroup[] = "OutputWindow";
427 static const char TermCmdStr[] = "TermCmdStr";
428 static const char KeepScript[] = "KeepScript";
429 static const char DebuggerGroup[] = "Debugger";
430 static const char DebuggerCmdStr[] = "DebuggerCmdStr";
431 static const char PreferencesGroup[] = "Preferences";
432 static const char PopForeground[] = "PopForeground";
433 static const char BackTimeout[] = "BackTimeout";
434 static const char TabWidth[] = "TabWidth";
435 static const char SourceFileFilter[] = "SourceFileFilter";
436 static const char HeaderFileFilter[] = "HeaderFileFilter";
437 
saveSettings(KSharedConfigPtr config)438 void DebuggerMainWnd::saveSettings(KSharedConfigPtr config)
439 {
440     m_recentExecAction->saveEntries(config->group(RecentExecutables));
441 
442     KConfigGroup lg = config->group(LastSession);
443     lg.writeEntry("Width0Locals", m_localVariables->columnWidth(0));
444     lg.writeEntry("Width0Watches", m_watches->columnWidth(0));
445 
446     if (m_debugger != 0) {
447 	m_debugger->saveSettings(config.data());
448     }
449 
450     config->group(OutputWindowGroup).writeEntry(TermCmdStr, m_outputTermCmdStr);
451     config->group(DebuggerGroup).writeEntry(DebuggerCmdStr, m_debuggerCmdStr);
452 
453     KConfigGroup pg(config->group(PreferencesGroup));
454     pg.writeEntry(PopForeground, m_popForeground);
455     pg.writeEntry(BackTimeout, m_backTimeout);
456     pg.writeEntry(TabWidth, m_tabWidth);
457     pg.writeEntry(SourceFileFilter, m_sourceFilter);
458     pg.writeEntry(HeaderFileFilter, m_headerFilter);
459 }
460 
restoreSettings(KSharedConfigPtr config)461 void DebuggerMainWnd::restoreSettings(KSharedConfigPtr config)
462 {
463     m_recentExecAction->loadEntries(config->group(RecentExecutables));
464 
465     KConfigGroup lg = config->group(LastSession);
466     int w;
467     w = lg.readEntry("Width0Locals", -1);
468     if (w >= 0 && w < 30000)
469 	m_localVariables->setColumnWidth(0, w);
470     w = lg.readEntry("Width0Watches", -1);
471     if (w >= 0 && w < 30000)
472 	m_watches->setColumnWidth(0, w);
473 
474     if (m_debugger != 0) {
475 	m_debugger->restoreSettings(config.data());
476     }
477 
478     KConfigGroup og(config->group(OutputWindowGroup));
479     /*
480      * For debugging and emergency purposes, let the config file override
481      * the shell script that is used to keep the output window open. This
482      * string must have EXACTLY 1 %s sequence in it.
483      */
484     setTerminalCmd(og.readEntry(TermCmdStr, defaultTermCmdStr));
485     m_outputTermKeepScript = og.readEntry(KeepScript);
486 
487     setDebuggerCmdStr(config->group(DebuggerGroup).readEntry(DebuggerCmdStr));
488 
489     KConfigGroup pg(config->group(PreferencesGroup));
490     m_popForeground = pg.readEntry(PopForeground, false);
491     m_backTimeout = pg.readEntry(BackTimeout, 1000);
492     m_tabWidth = pg.readEntry(TabWidth, 0);
493     m_sourceFilter = pg.readEntry(SourceFileFilter, m_sourceFilter);
494     m_headerFilter = pg.readEntry(HeaderFileFilter, m_headerFilter);
495 
496     emit setTabWidth(m_tabWidth);
497 }
498 
updateUI()499 void DebuggerMainWnd::updateUI()
500 {
501     m_findAction->setChecked(m_filesWindow->m_findDlg.isVisible());
502     m_findAction->setEnabled(m_filesWindow->hasWindows());
503     m_bpSetAction->setEnabled(m_debugger->canChangeBreakpoints());
504     m_bpSetTempAction->setEnabled(m_debugger->canChangeBreakpoints());
505     m_bpEnableAction->setEnabled(m_debugger->canChangeBreakpoints());
506 
507     m_fileExecAction->setEnabled(m_debugger->isIdle());
508     m_settingsAction->setEnabled(m_debugger->haveExecutable());
509     m_coreDumpAction->setEnabled(m_debugger->canStart());
510     m_closeAction->setEnabled(m_filesWindow->hasWindows());
511     m_reloadAction->setEnabled(m_filesWindow->hasWindows());
512     m_stepIntoAction->setEnabled(m_debugger->canSingleStep());
513     m_stepIntoIAction->setEnabled(m_debugger->canSingleStep());
514     m_stepOverAction->setEnabled(m_debugger->canSingleStep());
515     m_stepOverIAction->setEnabled(m_debugger->canSingleStep());
516     m_stepOutAction->setEnabled(m_debugger->canSingleStep());
517     m_toCursorAction->setEnabled(m_debugger->canSingleStep());
518     m_execMovePCAction->setEnabled(m_debugger->canSingleStep());
519     m_restartAction->setEnabled(m_debugger->canSingleStep());
520     m_attachAction->setEnabled(m_debugger->isReady());
521     m_detachAction->setEnabled(m_debugger->canSingleStep());
522     m_runAction->setEnabled(m_debugger->canStart() || m_debugger->canSingleStep());
523     m_killAction->setEnabled(m_debugger->haveExecutable() && m_debugger->isProgramActive());
524     m_breakAction->setEnabled(m_debugger->isProgramRunning());
525     m_argumentsAction->setEnabled(m_debugger->haveExecutable());
526     m_editValueAction->setEnabled(m_debugger->canSingleStep());
527 
528     // animation
529     if (m_debugger->isIdle()) {
530 	if (m_animRunning && m_animation) {
531 	    m_animation->stop();
532 	    m_animRunning = false;
533 	}
534     } else {
535 	if (!m_animRunning && m_animation) {
536 	    m_animation->start();
537 	    m_animRunning = true;
538 	}
539     }
540 
541     // update statusbar
542     QString newStatus;
543     if (m_debugger->isProgramActive())
544 	newStatus = m_statusActive;
545     if (newStatus != m_lastActiveStatusText) {
546 	m_statusActiveLabel->setText(newStatus);
547 	m_lastActiveStatusText = newStatus;
548     }
549 }
550 
updateLineItems()551 void DebuggerMainWnd::updateLineItems()
552 {
553     m_filesWindow->updateLineItems(m_debugger);
554 }
555 
slotAddWatch()556 void DebuggerMainWnd::slotAddWatch()
557 {
558     if (m_debugger != 0) {
559 	QString t = m_watches->watchText();
560 	m_debugger->addWatch(t);
561     }
562 }
563 
slotAddWatch(const QString & text)564 void DebuggerMainWnd::slotAddWatch(const QString& text)
565 {
566     if (m_debugger != 0) {
567 	m_debugger->addWatch(text);
568     }
569 }
570 
slotNewFileLoaded()571 void DebuggerMainWnd::slotNewFileLoaded()
572 {
573     // updates program counter in the new file
574     if (m_debugger != 0)
575 	m_filesWindow->updateLineItems(m_debugger);
576 }
577 
dockParent(QWidget * w)578 QDockWidget* DebuggerMainWnd::dockParent(QWidget* w)
579 {
580     while ((w = w->parentWidget()) != 0) {
581 	if (QDockWidget* dock = qobject_cast<QDockWidget*>(w))
582 	    return dock;
583     }
584     return 0;
585 }
586 
makeDefaultLayout()587 void DebuggerMainWnd::makeDefaultLayout()
588 {
589     // +---------------+---------+
590     // | Source        | Locals  |
591     // |               |         |
592     // |---------------+---------+
593     // |Stack, Brkpts, | Watches |
594     // |Output,...     |         |
595     // +---------------+---------+
596 
597     addDockWidget(Qt::RightDockWidgetArea, dockParent(m_localVariables));
598     addDockWidget(Qt::BottomDockWidgetArea, dockParent(m_memoryWindow));
599     splitDockWidget(dockParent(m_memoryWindow), dockParent(m_threads), Qt::Horizontal);
600     tabifyDockWidget(dockParent(m_memoryWindow), dockParent(m_registers));
601     tabifyDockWidget(dockParent(m_registers), dockParent(m_bpTable));
602     tabifyDockWidget(dockParent(m_bpTable), dockParent(m_ttyWindow));
603     tabifyDockWidget(dockParent(m_ttyWindow), dockParent(m_btWindow));
604     tabifyDockWidget(dockParent(m_threads), dockParent(m_watches));
605     dockParent(m_localVariables)->setVisible(true);
606     dockParent(m_ttyWindow)->setVisible(true);
607     dockParent(m_watches)->setVisible(true);
608     dockParent(m_btWindow)->setVisible(true);
609     dockParent(m_bpTable)->setVisible(true);
610 }
611 
debugProgram(const QString & exe,const QString & lang)612 bool DebuggerMainWnd::debugProgram(const QString& exe, const QString& lang)
613 {
614     // check the file name
615     QFileInfo fi(exe);
616 
617     bool success = fi.isFile();
618     if (!success)
619     {
620 	QString msg = i18n("`%1' is not a file or does not exist");
621 	KMessageBox::sorry(this, msg.arg(exe));
622     }
623     else
624     {
625 	success = startDriver(fi.absoluteFilePath(), lang);
626     }
627 
628     if (success)
629     {
630 	m_recentExecAction->addUrl(QUrl::fromLocalFile(fi.absoluteFilePath()));
631 
632 	// keep the directory
633 	m_lastDirectory = fi.absolutePath();
634 	m_filesWindow->setExtraDirectory(m_lastDirectory);
635 
636 	// set caption to basename part of executable
637 	QString caption = fi.fileName();
638 	setCaption(caption);
639     }
640     else
641     {
642 	m_recentExecAction->removeUrl(QUrl::fromLocalFile(fi.absoluteFilePath()));
643     }
644 
645     return success;
646 }
647 
648 static const char GeneralGroup[] = "General";
649 
startDriver(const QString & executable,QString lang)650 bool DebuggerMainWnd::startDriver(const QString& executable, QString lang)
651 {
652     assert(m_debugger != 0);
653 
654     TRACE(QString("trying language '%1'...").arg(lang));
655     DebuggerDriver* driver = driverFromLang(lang);
656 
657     if (driver == 0)
658     {
659 	// see if there is a language in the per-program config file
660 	QString configName = m_debugger->getConfigForExe(executable);
661 	if (QFile::exists(configName))
662 	{
663 	    KConfig c(configName, KConfig::SimpleConfig);
664 
665 	    // Using "GDB" as default here is for backwards compatibility:
666 	    // The config file exists but doesn't have an entry,
667 	    // so it must have been created by an old version of KDbg
668 	    // that had only the GDB driver.
669 	    lang = c.group(GeneralGroup)
670 		    .readEntry(KDebugger::DriverNameEntry, "GDB");
671 
672 	    TRACE(QString("...bad, trying config driver %1...").arg(lang));
673 	    driver = driverFromLang(lang);
674 	}
675 
676     }
677     if (driver == 0)
678     {
679 	QString name = driverNameFromFile(executable);
680 
681 	TRACE(QString("...no luck, trying %1 derived"
682 		" from file contents").arg(name));
683 	driver = driverFromLang(name);
684     }
685     if (driver == 0)
686     {
687 	// oops
688 	QString msg = i18n("Don't know how to debug language `%1'");
689 	KMessageBox::sorry(this, msg.arg(lang));
690 	return false;
691     }
692     if (typeid(*driver) == typeid(XsldbgDriver)) {
693 	KMessageBox::information(this, i18n("XSL debugging is no longer supported and will be removed in a future version of KDbg"));
694     }
695 
696     driver->setLogFileName(m_transcriptFile);
697 
698     bool success = m_debugger->debugProgram(executable, driver);
699 
700     if (!success)
701     {
702 	delete driver;
703 
704 	QString msg = i18n("Could not start the debugger process.\n"
705 			   "Please shut down KDbg and resolve the problem.");
706 	KMessageBox::sorry(this, msg);
707     }
708 
709     return success;
710 }
711 
712 // derive driver from language
driverFromLang(QString lang)713 DebuggerDriver* DebuggerMainWnd::driverFromLang(QString lang)
714 {
715     // lang is needed in all lowercase
716     lang = lang.toLower();
717 
718     // The following table relates languages and debugger drivers
719     static const struct L {
720 	const char* shortest;	// abbreviated to this is still unique
721 	const char* full;	// full name of language
722 	int driver;
723     } langs[] = {
724 	{ "c",       "c++",     1 },
725 	{ "f",       "fortran", 1 },
726 	{ "p",       "python",  3 },
727 	{ "x",       "xslt",    2 },
728 	// the following are actually driver names
729 	{ "gdb",     "gdb",     1 },
730 	{ "xsldbg",  "xsldbg",  2 },
731     };
732     const int N = sizeof(langs)/sizeof(langs[0]);
733 
734     // lookup the language name
735     int driverID = 0;
736     for (int i = 0; i < N; i++)
737     {
738 	const L& l = langs[i];
739 
740 	// shortest must match
741 	if (!lang.startsWith(l.shortest))
742 	    continue;
743 
744 	// lang must not be longer than the full name, and it must match
745 	if (QString(l.full).startsWith(lang))
746 	{
747 	    driverID = l.driver;
748 	    break;
749 	}
750     }
751     DebuggerDriver* driver = 0;
752     switch (driverID) {
753     case 1:
754 	{
755 	    GdbDriver* gdb = new GdbDriver;
756 	    gdb->setDefaultInvocation(m_debuggerCmdStr);
757 	    driver = gdb;
758 	}
759 	break;
760     case 2:
761 	driver = new XsldbgDriver;
762 	break;
763     default:
764 	// unknown language
765 	break;
766     }
767     return driver;
768 }
769 
770 /**
771  * Try to guess the language to use from the contents of the file.
772  */
driverNameFromFile(const QString & exe)773 QString DebuggerMainWnd::driverNameFromFile(const QString& exe)
774 {
775     /* Inprecise but simple test to see if file is in XSLT language */
776     if (exe.right(4).toLower() == ".xsl")
777 	return "XSLT";
778 
779     return "GDB";
780 }
781 
setCoreFile(const QString & corefile)782 void DebuggerMainWnd::setCoreFile(const QString& corefile)
783 {
784     assert(m_debugger != 0);
785     m_debugger->useCoreFile(corefile, true);
786 }
787 
setRemoteDevice(const QString & remoteDevice)788 void DebuggerMainWnd::setRemoteDevice(const QString& remoteDevice)
789 {
790     if (m_debugger != 0) {
791 	m_debugger->setRemoteDevice(remoteDevice);
792     }
793 }
794 
overrideProgramArguments(const QString & args)795 void DebuggerMainWnd::overrideProgramArguments(const QString& args)
796 {
797     assert(m_debugger != 0);
798     m_debugger->overrideProgramArguments(args);
799 }
800 
setTranscript(const QString & name)801 void DebuggerMainWnd::setTranscript(const QString& name)
802 {
803     m_transcriptFile = name;
804     if (m_debugger != 0 && m_debugger->driver() != 0)
805 	m_debugger->driver()->setLogFileName(m_transcriptFile);
806 }
807 
setAttachPid(const QString & pid)808 void DebuggerMainWnd::setAttachPid(const QString& pid)
809 {
810     assert(m_debugger != 0);
811     m_debugger->setAttachPid(pid);
812 }
813 
slotNewStatusMsg()814 void DebuggerMainWnd::slotNewStatusMsg()
815 {
816     QString msg = m_debugger->statusMessage();
817     m_statusMsgLabel->setText(msg.trimmed());
818 }
819 
slotFileGlobalSettings()820 void DebuggerMainWnd::slotFileGlobalSettings()
821 {
822     int oldTabWidth = m_tabWidth;
823 
824     KPageDialog dlg(this);
825     dlg.setWindowTitle(i18n("Global Options"));
826 
827     PrefDebugger prefDebugger(&dlg);
828     prefDebugger.setDebuggerCmd(m_debuggerCmdStr.isEmpty()  ?
829 				GdbDriver::defaultGdb()  :  m_debuggerCmdStr);
830     prefDebugger.setTerminal(m_outputTermCmdStr);
831 
832     PrefMisc prefMisc(&dlg);
833     prefMisc.setPopIntoForeground(m_popForeground);
834     prefMisc.setBackTimeout(m_backTimeout);
835     prefMisc.setTabWidth(m_tabWidth);
836     prefMisc.setSourceFilter(m_sourceFilter);
837     prefMisc.setHeaderFilter(m_headerFilter);
838 
839     dlg.addPage(&prefDebugger, i18n("Debugger"));
840     dlg.addPage(&prefMisc, i18n("Miscellaneous"));
841     if (dlg.exec() == QDialog::Accepted)
842     {
843 	setDebuggerCmdStr(prefDebugger.debuggerCmd());
844 	setTerminalCmd(prefDebugger.terminal());
845 	m_popForeground = prefMisc.popIntoForeground();
846 	m_backTimeout = prefMisc.backTimeout();
847 	m_tabWidth = prefMisc.tabWidth();
848 	m_sourceFilter = prefMisc.sourceFilter();
849 	if (m_sourceFilter.isEmpty())
850 	    m_sourceFilter = defaultSourceFilter;
851 	m_headerFilter = prefMisc.headerFilter();
852 	if (m_headerFilter.isEmpty())
853 	    m_headerFilter = defaultHeaderFilter;
854     }
855 
856     if (m_tabWidth != oldTabWidth) {
857 	emit setTabWidth(m_tabWidth);
858     }
859 }
860 
setTerminalCmd(const QString & cmd)861 void DebuggerMainWnd::setTerminalCmd(const QString& cmd)
862 {
863     m_outputTermCmdStr = cmd;
864     // revert to default if empty
865     if (m_outputTermCmdStr.isEmpty()) {
866 	m_outputTermCmdStr = defaultTermCmdStr;
867     }
868 }
869 
setDebuggerCmdStr(const QString & cmd)870 void DebuggerMainWnd::setDebuggerCmdStr(const QString& cmd)
871 {
872     m_debuggerCmdStr = cmd;
873     // make empty if it is the default
874     if (m_debuggerCmdStr == GdbDriver::defaultGdb()) {
875 	m_debuggerCmdStr = QString();
876     }
877 }
878 
slotDebuggerStarting()879 void DebuggerMainWnd::slotDebuggerStarting()
880 {
881     if (m_debugger == 0)		/* paranoia check */
882 	return;
883 
884     if (m_ttyLevel == m_debugger->ttyLevel())
885 	return;
886 
887     // shut down terminal emulations we will not need
888     switch (m_ttyLevel) {
889     case KDebugger::ttySimpleOutputOnly:
890 	m_ttyWindow->deactivate();
891 	break;
892     case KDebugger::ttyFull:
893 	m_outputTermProc->kill();
894 	break;
895     default: break;
896     }
897 
898     m_ttyLevel = m_debugger->ttyLevel();
899 
900     QString ttyName;
901     switch (m_ttyLevel) {
902     case KDebugger::ttySimpleOutputOnly:
903 	ttyName = m_ttyWindow->activate();
904 	break;
905     case KDebugger::ttyFull:
906 	// create an output window
907 	ttyName = createOutputWindow();
908 	TRACE(ttyName.isEmpty() ?
909 	      "createOuputWindow failed" : "successfully created output window");
910 	break;
911     default: break;
912     }
913 
914     m_debugger->setTerminal(ttyName);
915 }
916 
slotToggleBreak(const QString & fileName,int lineNo,const DbgAddr & address,bool temp)917 void DebuggerMainWnd::slotToggleBreak(const QString& fileName, int lineNo,
918 				      const DbgAddr& address, bool temp)
919 {
920     // lineNo is zero-based
921     if (m_debugger != 0) {
922 	m_debugger->setBreakpoint(fileName, lineNo, address, temp);
923     }
924 }
925 
slotEnaDisBreak(const QString & fileName,int lineNo,const DbgAddr & address)926 void DebuggerMainWnd::slotEnaDisBreak(const QString& fileName, int lineNo,
927 				      const DbgAddr& address)
928 {
929     // lineNo is zero-based
930     if (m_debugger != 0) {
931 	m_debugger->enableDisableBreakpoint(fileName, lineNo, address);
932     }
933 }
934 
createOutputWindow()935 QString DebuggerMainWnd::createOutputWindow()
936 {
937     // create a name for a fifo
938     QString fifoName;
939     fifoName.sprintf("/tmp/kdbgttywin%05d", ::getpid());
940 
941     // create a fifo that will pass in the tty name
942     QFile::remove(fifoName);		// remove remnants
943 #ifdef HAVE_MKFIFO
944     if (::mkfifo(fifoName.toLocal8Bit(), S_IRUSR|S_IWUSR) < 0) {
945 	// failed
946 	TRACE("mkfifo " + fifoName + " failed");
947 	return QString();
948     }
949 #else
950     if (::mknod(fifoName.toLocal8Bit(), S_IFIFO | S_IRUSR|S_IWUSR, 0) < 0) {
951 	// failed
952 	TRACE("mknod " + fifoName + " failed");
953 	return QString();
954     }
955 #endif
956 
957     /*
958      * Spawn an xterm that in turn runs a shell script that passes us
959      * back the terminal name and then only sits and waits.
960      */
961     static const char shellScriptFmt[] =
962 	"tty>%s;"
963 	"trap \"\" INT QUIT TSTP;"	/* ignore various signals */
964 	"exec<&-;exec>&-;"		/* close stdin and stdout */
965 	"while :;do sleep 3600;done";
966     // let config file override this script
967     QString shellScript;
968     if (!m_outputTermKeepScript.isEmpty()) {
969 	shellScript = m_outputTermKeepScript;
970     } else {
971 	shellScript = shellScriptFmt;
972     }
973 
974     shellScript.replace("%s", fifoName);
975     TRACE("output window script is " + shellScript);
976 
977     QString title = QGuiApplication::applicationDisplayName();
978     title += i18n(": Program output");
979 
980     // parse the command line specified in the preferences
981     QStringList cmdParts = m_outputTermCmdStr.split(' ');
982 
983     /*
984      * Build the argv array. Thereby substitute special sequences:
985      */
986     struct {
987 	char seq[4];
988 	QString replace;
989     } substitute[] = {
990 	{ "%T", title },
991 	{ "%C", shellScript }
992     };
993 
994     for (QStringList::iterator i = cmdParts.begin(); i != cmdParts.end(); ++i)
995     {
996 	QString& str = *i;
997 	for (int j = sizeof(substitute)/sizeof(substitute[0])-1; j >= 0; j--) {
998 	    int pos = str.indexOf(substitute[j].seq);
999 	    if (pos >= 0) {
1000 		str.replace(pos, 2, substitute[j].replace);
1001 		break;		/* substitute only one sequence */
1002 	    }
1003 	}
1004     }
1005 
1006     QString tty, pgm = cmdParts.takeFirst();
1007 
1008     m_outputTermProc->start(pgm, cmdParts);
1009     if (m_outputTermProc->waitForStarted())
1010     {
1011 	// read the ttyname from the fifo
1012 	QFile f(fifoName);
1013 	if (f.open(QIODevice::ReadOnly))
1014 	{
1015 	    QByteArray t = f.readAll();
1016 	    tty = QString::fromLocal8Bit(t, t.size());
1017 	    f.close();
1018 	}
1019 	f.remove();
1020 
1021 	// remove whitespace
1022 	tty = tty.trimmed();
1023 	TRACE("tty=" + tty);
1024     }
1025     else
1026     {
1027 	// error, could not start xterm
1028 	TRACE("fork failed for fifo " + fifoName);
1029 	QFile::remove(fifoName);
1030     }
1031 
1032     return tty;
1033 }
1034 
slotProgramStopped()1035 void DebuggerMainWnd::slotProgramStopped()
1036 {
1037     // when the program stopped, move the window to the foreground
1038     if (m_popForeground) {
1039 	// unfortunately, this requires quite some force to work :-(
1040 	KWindowSystem::raiseWindow(winId());
1041 	KWindowSystem::forceActiveWindow(winId());
1042     }
1043     m_backTimer.stop();
1044 }
1045 
intoBackground()1046 void DebuggerMainWnd::intoBackground()
1047 {
1048     if (m_popForeground) {
1049 	m_backTimer.setSingleShot(true);
1050 	m_backTimer.start(m_backTimeout);
1051     }
1052 }
1053 
slotBackTimer()1054 void DebuggerMainWnd::slotBackTimer()
1055 {
1056     lower();
1057 }
1058 
slotRecentExec(const QUrl & url)1059 void DebuggerMainWnd::slotRecentExec(const QUrl& url)
1060 {
1061     QString exe = url.toLocalFile();
1062     debugProgram(exe, "");
1063 }
1064 
makeSourceFilter()1065 QString DebuggerMainWnd::makeSourceFilter()
1066 {
1067     QString f;
1068     f = i18n("All source files") + " (" + m_sourceFilter + " " + m_headerFilter + ")";
1069     f += ";;" + i18n("Source files") + " (" + m_sourceFilter + ")";
1070     f += ";;" + i18n("Header files") + " (" + m_headerFilter + ")";
1071     f += ";;" + i18n("All files") + " (*)";
1072     return f;
1073 }
1074 
1075 /*
1076  * Pop up the context menu in the locals window
1077  */
slotLocalsPopup(const QPoint & pt)1078 void DebuggerMainWnd::slotLocalsPopup(const QPoint& pt)
1079 {
1080     QMenu* popup = static_cast<QMenu*>(factory()->container("popup_locals", this));
1081     if (popup == 0) {
1082         return;
1083     }
1084     if (popup->isVisible()) {
1085 	popup->hide();
1086     } else {
1087 	popup->popup(m_localVariables->viewport()->mapToGlobal(pt));
1088     }
1089 }
1090 
1091 /*
1092  * Copies the currently selected item to the watch window.
1093  */
slotLocalsToWatch()1094 void DebuggerMainWnd::slotLocalsToWatch()
1095 {
1096     VarTree* item = m_localVariables->selectedItem();
1097 
1098     if (item != 0 && m_debugger != 0) {
1099 	QString text = item->computeExpr();
1100 	m_debugger->addWatch(text);
1101     }
1102 }
1103 
1104 /*
1105  * Starts editing a value in a value display
1106  */
slotEditValue()1107 void DebuggerMainWnd::slotEditValue()
1108 {
1109     // does one of the value trees have the focus
1110     QWidget* f = QApplication::focusWidget();
1111     ExprWnd* wnd;
1112     if (f == m_localVariables) {
1113 	wnd = m_localVariables;
1114     } else if (f == m_watches->watchVariables()) {
1115 	wnd = m_watches->watchVariables();
1116     } else {
1117 	return;
1118     }
1119 
1120     if (m_localVariables->isEditing() ||
1121 	m_watches->watchVariables()->isEditing())
1122     {
1123 	return;				/* don't edit twice */
1124     }
1125 
1126     VarTree* expr = wnd->selectedItem();
1127     if (expr != 0 && m_debugger != 0 && m_debugger->canSingleStep())
1128     {
1129 	TRACE("edit value");
1130 	// determine the text to edit
1131 	QString text = m_debugger->driver()->editableValue(expr);
1132 	wnd->editValue(expr, text);
1133     }
1134 }
1135 
slotFileOpen()1136 void DebuggerMainWnd::slotFileOpen()
1137 {
1138     // start browsing in the active file's directory
1139     // fall back to last used directory (executable)
1140     QString dir = m_lastDirectory;
1141     QString fileName = m_filesWindow->activeFileName();
1142     if (!fileName.isEmpty()) {
1143 	QFileInfo fi(fileName);
1144 	dir = fi.path();
1145     }
1146 
1147     fileName = QFileDialog::getOpenFileName(this,
1148 			i18n("Open Source Code"), dir, makeSourceFilter());
1149 
1150     if (!fileName.isEmpty())
1151     {
1152 	QFileInfo fi(fileName);
1153 	m_lastDirectory = fi.path();
1154 	m_filesWindow->setExtraDirectory(m_lastDirectory);
1155 	m_filesWindow->activateFile(fileName);
1156     }
1157 }
1158 
slotFileExe()1159 void DebuggerMainWnd::slotFileExe()
1160 {
1161     if (m_debugger->isIdle())
1162     {
1163 	// open a new executable
1164 	QString executable = QFileDialog::getOpenFileName(this,
1165 				i18n("Select the Executable to Debug"),
1166 				m_lastDirectory);
1167 	if (executable.isEmpty())
1168 	    return;
1169 
1170 	debugProgram(executable, "");
1171     }
1172 }
1173 
slotFileCore()1174 void DebuggerMainWnd::slotFileCore()
1175 {
1176     if (m_debugger->canStart())
1177     {
1178 	QString corefile = QFileDialog::getOpenFileName(this,
1179 				i18n("Select Core Dump"),
1180 				m_lastDirectory);
1181 	if (!corefile.isEmpty()) {
1182 	    m_debugger->useCoreFile(corefile, false);
1183 	}
1184     }
1185 }
1186 
slotFileProgSettings()1187 void DebuggerMainWnd::slotFileProgSettings()
1188 {
1189     if (m_debugger != 0) {
1190 	m_debugger->programSettings(this);
1191     }
1192 }
1193 
slotViewStatusbar()1194 void DebuggerMainWnd::slotViewStatusbar()
1195 {
1196     if (statusBar()->isVisible())
1197 	statusBar()->hide();
1198     else
1199 	statusBar()->show();
1200     setSettingsDirty();
1201 }
1202 
slotExecUntil()1203 void DebuggerMainWnd::slotExecUntil()
1204 {
1205     if (m_debugger != 0)
1206     {
1207 	QString file;
1208 	int lineNo;
1209 	if (m_filesWindow->activeLine(file, lineNo))
1210 	    m_debugger->runUntil(file, lineNo);
1211     }
1212 }
1213 
slotExecAttach()1214 void DebuggerMainWnd::slotExecAttach()
1215 {
1216 #ifdef PS_COMMAND
1217     ProcAttachPS dlg(this);
1218     // seed filter with executable name
1219     QFileInfo fi = m_debugger->executable();
1220     dlg.setFilterText(fi.fileName());
1221 #else
1222     ProcAttach dlg(this);
1223     dlg.setText(m_debugger->attachedPid());
1224 #endif
1225     if (dlg.exec()) {
1226 	m_debugger->attachProgram(dlg.text());
1227     }
1228 }
1229 
slotExecArgs()1230 void DebuggerMainWnd::slotExecArgs()
1231 {
1232     if (m_debugger != 0) {
1233 	m_debugger->programArgs(this);
1234     }
1235 }
1236 
slotConfigureKeys()1237 void DebuggerMainWnd::slotConfigureKeys()
1238 {
1239     KShortcutsDialog::configure(actionCollection());
1240 }
1241 
1242 #include "dbgmainwnd.moc"
1243