2 * Copyright Johannes Sixt
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
7 #include <kapplication.h>
8 #include <klocale.h> /* i18n */
9 #include <kmessagebox.h>
11 #include <kstatusbar.h>
12 #include <kiconloader.h>
13 #include <kstdaccel.h>
14 #include <kstdaction.h>
16 #include <kpopupmenu.h>
17 #include <kfiledialog.h>
19 #include <kkeydialog.h>
20 #include <kanimwidget.h>
22 #include <q3listbox.h>
24 #include <qfileinfo.h>
25 #include <q3tabdialog.h>
26 #include <q3textstream.h>
27 #include <Q3ValueList>
28 #include <Q3PopupMenu>
29 #include "dbgmainwnd.h"
31 #include "commandids.h"
34 #include "threadlist.h"
35 #include "memwindow.h"
37 #include "watchwindow.h"
38 #include "procattach.h"
39 #include "prefdebugger.h"
41 #include "gdbdriver.h"
42 #include "xsldbgdriver.h"
44 #include <sys/stat.h> /* mknod(2) */
45 #include <unistd.h> /* getpid */
48 static const char defaultTermCmdStr
[] = "xterm -name kdbgio -title %T -e sh -c %C";
49 static const char defaultSourceFilter
[] = "*.c *.cc *.cpp *.c++ *.C *.CC";
50 static const char defaultHeaderFilter
[] = "*.h *.hh *.hpp *.h++";
52 DebuggerMainWnd::DebuggerMainWnd(const char* name
) :
56 m_transcriptFile(GDB_TRANSCRIPT
),
58 m_outputTermCmdStr(defaultTermCmdStr
),
60 m_ttyLevel(-1), /* no tty yet */
61 m_popForeground(false),
64 m_sourceFilter(defaultSourceFilter
),
65 m_headerFilter(defaultHeaderFilter
),
66 m_statusActive(i18n("active"))
68 m_filesWindow
= new WinStack(this);
69 setCentralWidget(m_filesWindow
);
71 QDockWidget
* dw1
= createDockWidget("Stack", i18n("Stack"));
72 m_btWindow
= new Q3ListBox(dw1
);
73 dw1
->setWidget(m_btWindow
);
74 QDockWidget
* dw2
= createDockWidget("Locals", i18n("Locals"));
75 m_localVariables
= new ExprWnd(dw2
, i18n("Variable"));
76 dw2
->setWidget(m_localVariables
);
77 QDockWidget
* dw3
= createDockWidget("Watches", i18n("Watches"));
78 m_watches
= new WatchWindow(dw3
);
79 dw3
->setWidget(m_watches
);
80 QDockWidget
* dw4
= createDockWidget("Registers", i18n("Registers"));
81 m_registers
= new RegisterView(dw4
);
82 dw4
->setWidget(m_registers
);
83 QDockWidget
* dw5
= createDockWidget("Breakpoints", i18n("Breakpoints"));
84 m_bpTable
= new BreakpointTable(dw5
);
85 dw5
->setWidget(m_bpTable
);
86 QDockWidget
* dw6
= createDockWidget("Output", i18n("Output"));
87 m_ttyWindow
= new TTYWindow(dw6
);
88 dw6
->setWidget(m_ttyWindow
);
89 QDockWidget
* dw7
= createDockWidget("Threads", i18n("Threads"));
90 m_threads
= new ThreadList(dw7
);
91 dw7
->setWidget(m_threads
);
92 QDockWidget
* dw8
= createDockWidget("Memory", i18n("Memory"));
93 m_memoryWindow
= new MemoryWindow(dw8
);
94 dw8
->setWidget(m_memoryWindow
);
96 m_debugger
= new KDebugger(this, m_localVariables
, m_watches
->watchVariables(), m_btWindow
);
98 connect(m_debugger
, SIGNAL(updateStatusMessage()), SLOT(slotNewStatusMsg()));
99 connect(m_debugger
, SIGNAL(updateUI()), SLOT(updateUI()));
100 connect(m_debugger
, SIGNAL(breakpointsChanged()), SLOT(updateLineItems()));
101 connect(m_debugger
, SIGNAL(debuggerStarting()), SLOT(slotDebuggerStarting()));
102 m_bpTable
->setDebugger(m_debugger
);
103 m_memoryWindow
->setDebugger(m_debugger
);
105 setStandardToolBarMenuEnabled(true);
107 initToolbar(); // kind of obsolete?
109 connect(m_watches
, SIGNAL(addWatch()), SLOT(slotAddWatch()));
110 connect(m_watches
, SIGNAL(deleteWatch()), m_debugger
, SLOT(slotDeleteWatch()));
111 connect(m_watches
, SIGNAL(textDropped(const QString
&)), SLOT(slotAddWatch(const QString
&)));
113 connect(&m_filesWindow
->m_findDlg
, SIGNAL(closed()), SLOT(updateUI()));
114 connect(m_filesWindow
, SIGNAL(newFileLoaded()),
115 SLOT(slotNewFileLoaded()));
116 connect(m_filesWindow
, SIGNAL(toggleBreak(const QString
&,int,const DbgAddr
&,bool)),
117 this, SLOT(slotToggleBreak(const QString
&,int,const DbgAddr
&,bool)));
118 connect(m_filesWindow
, SIGNAL(enadisBreak(const QString
&,int,const DbgAddr
&)),
119 this, SLOT(slotEnaDisBreak(const QString
&,int,const DbgAddr
&)));
120 connect(m_debugger
, SIGNAL(activateFileLine(const QString
&,int,const DbgAddr
&)),
121 m_filesWindow
, SLOT(activate(const QString
&,int,const DbgAddr
&)));
122 connect(m_debugger
, SIGNAL(executableUpdated()),
123 m_filesWindow
, SLOT(reloadAllFiles()));
124 connect(m_debugger
, SIGNAL(updatePC(const QString
&,int,const DbgAddr
&,int)),
125 m_filesWindow
, SLOT(updatePC(const QString
&,int,const DbgAddr
&,int)));
126 // value popup communication
127 connect(m_filesWindow
, SIGNAL(initiateValuePopup(const QString
&)),
128 m_debugger
, SLOT(slotValuePopup(const QString
&)));
129 connect(m_debugger
, SIGNAL(valuePopup(const QString
&)),
130 m_filesWindow
, SLOT(slotShowValueTip(const QString
&)));
132 connect(m_filesWindow
, SIGNAL(disassemble(const QString
&, int)),
133 m_debugger
, SLOT(slotDisassemble(const QString
&, int)));
134 connect(m_debugger
, SIGNAL(disassembled(const QString
&,int,const std::list
<DisassembledCode
>&)),
135 m_filesWindow
, SLOT(slotDisassembled(const QString
&,int,const std::list
<DisassembledCode
>&)));
136 connect(m_filesWindow
, SIGNAL(moveProgramCounter(const QString
&,int,const DbgAddr
&)),
137 m_debugger
, SLOT(setProgramCounter(const QString
&,int,const DbgAddr
&)));
139 connect(m_debugger
, SIGNAL(programStopped()), SLOT(slotProgramStopped()));
140 connect(&m_backTimer
, SIGNAL(timeout()), SLOT(slotBackTimer()));
142 connect(this, SIGNAL(setTabWidth(int)), m_filesWindow
, SIGNAL(setTabWidth(int)));
144 // connect breakpoint table
145 connect(m_bpTable
, SIGNAL(activateFileLine(const QString
&,int,const DbgAddr
&)),
146 m_filesWindow
, SLOT(activate(const QString
&,int,const DbgAddr
&)));
147 connect(m_debugger
, SIGNAL(updateUI()), m_bpTable
, SLOT(updateUI()));
148 connect(m_debugger
, SIGNAL(breakpointsChanged()), m_bpTable
, SLOT(updateBreakList()));
149 connect(m_debugger
, SIGNAL(breakpointsChanged()), m_bpTable
, SLOT(updateUI()));
151 connect(m_debugger
, SIGNAL(registersChanged(const std::list
<RegisterInfo
>&)),
152 m_registers
, SLOT(updateRegisters(const std::list
<RegisterInfo
>&)));
154 connect(m_debugger
, SIGNAL(memoryDumpChanged(const QString
&, const std::list
<MemoryDump
>&)),
155 m_memoryWindow
, SLOT(slotNewMemoryDump(const QString
&, const std::list
<MemoryDump
>&)));
156 connect(m_debugger
, SIGNAL(saveProgramSpecific(KConfigBase
*)),
157 m_memoryWindow
, SLOT(saveProgramSpecific(KConfigBase
*)));
158 connect(m_debugger
, SIGNAL(restoreProgramSpecific(KConfigBase
*)),
159 m_memoryWindow
, SLOT(restoreProgramSpecific(KConfigBase
*)));
162 connect(m_debugger
, SIGNAL(threadsChanged(const std::list
<ThreadInfo
>&)),
163 m_threads
, SLOT(updateThreads(const std::list
<ThreadInfo
>&)));
164 connect(m_threads
, SIGNAL(setThread(int)),
165 m_debugger
, SLOT(setThread(int)));
167 // popup menu of the local variables window
168 connect(m_localVariables
, SIGNAL(contextMenuRequested(Q3ListViewItem
*, const QPoint
&, int)),
169 this, SLOT(slotLocalsPopup(Q3ListViewItem
*, const QPoint
&)));
171 restoreSettings(kapp
->config());
174 m_bpTable
->updateUI();
177 DebuggerMainWnd::~DebuggerMainWnd()
179 saveSettings(kapp
->config());
180 // must delete m_debugger early since it references our windows
184 delete m_memoryWindow
;
190 delete m_localVariables
;
192 delete m_filesWindow
;
194 // if the output window is open, close it
195 if (m_outputTermProc
!= 0) {
196 m_outputTermProc
->disconnect(); /* ignore signals */
197 m_outputTermProc
->kill();
198 shutdownTermWindow();
202 QDockWidget
* DebuggerMainWnd::createDockWidget(const char* name
, const QString
& title
)
204 QDockWidget
* w
= new QDockWidget(this, name
);
205 w
->setCaption(title
);
206 // view menu changes when docking state changes
207 connect(w
, SIGNAL(visibilityChanged(bool)), SLOT(updateUI()));
211 KAction
* DebuggerMainWnd::createAction(const QString
& text
, const char* icon
,
212 int shortcut
, const QObject
* receiver
,
213 const char* slot
, const char* name
)
215 KAction
* a
= new KAction(text
, icon
, shortcut
, receiver
, slot
,
216 actionCollection(), name
);
220 KAction
* DebuggerMainWnd::createAction(const QString
& text
,
221 int shortcut
, const QObject
* receiver
,
222 const char* slot
, const char* name
)
224 KAction
* a
= new KAction(text
, shortcut
, receiver
, slot
,
225 actionCollection(), name
);
230 void DebuggerMainWnd::initKAction()
233 KAction
* open
= KStdAction::open(this, SLOT(slotFileOpen()),
235 open
->setText(i18n("&Open Source..."));
236 m_closeAction
= KStdAction::close(m_filesWindow
, SLOT(slotClose()), actionCollection());
237 m_reloadAction
= createAction(i18n("&Reload Source"), "reload", 0,
238 m_filesWindow
, SLOT(slotFileReload()), "file_reload");
239 m_fileExecAction
= createAction(i18n("&Executable..."), "execopen", 0,
240 this, SLOT(slotFileExe()), "file_executable");
241 m_recentExecAction
= new KRecentFilesAction(i18n("Recent E&xecutables"), 0,
242 this, SLOT(slotRecentExec(const KURL
&)),
243 actionCollection(), "file_executable_recent");
244 m_coreDumpAction
= createAction(i18n("&Core dump..."), 0,
245 this, SLOT(slotFileCore()), "file_core_dump");
246 KStdAction::quit(kapp
, SLOT(closeAllWindows()), actionCollection());
249 m_settingsAction
= createAction(i18n("This &Program..."), 0,
250 this, SLOT(slotFileProgSettings()), "settings_program");
251 createAction(i18n("&Global Options..."), 0,
252 this, SLOT(slotFileGlobalSettings()), "settings_global");
253 KStdAction::keyBindings(this, SLOT(slotConfigureKeys()), actionCollection());
254 KStdAction::showStatusbar(this, SLOT(slotViewStatusbar()), actionCollection());
257 m_findAction
= new KToggleAction(i18n("&Find"), "find", CTRL
+Key_F
, m_filesWindow
,
258 SLOT(slotViewFind()), actionCollection(),
260 (void)KStdAction::findNext(m_filesWindow
, SLOT(slotFindForward()), actionCollection(), "view_findnext");
261 (void)KStdAction::findPrev(m_filesWindow
, SLOT(slotFindBackward()), actionCollection(), "view_findprev");
263 i18n("Source &code");
264 struct { QString text
; QWidget
* w
; QString id
; KToggleAction
** act
; } dw
[] = {
265 { i18n("Stac&k"), m_btWindow
, "view_stack", &m_btWindowAction
},
266 { i18n("&Locals"), m_localVariables
, "view_locals", &m_localVariablesAction
},
267 { i18n("&Watched expressions"), m_watches
, "view_watched_expressions", &m_watchesAction
},
268 { i18n("&Registers"), m_registers
, "view_registers", &m_registersAction
},
269 { i18n("&Breakpoints"), m_bpTable
, "view_breakpoints", &m_bpTableAction
},
270 { i18n("T&hreads"), m_threads
, "view_threads", &m_threadsAction
},
271 { i18n("&Output"), m_ttyWindow
, "view_output", &m_ttyWindowAction
},
272 { i18n("&Memory"), m_memoryWindow
, "view_memory", &m_memoryWindowAction
}
274 for (unsigned i
= 0; i
< sizeof(dw
)/sizeof(dw
[0]); i
++) {
275 QDockWidget
* d
= dockParent(dw
[i
].w
);
276 *dw
[i
].act
= new KToggleAction(dw
[i
].text
, 0, d
, SLOT(show()),
277 actionCollection(), dw
[i
].id
);
281 m_runAction
= createAction(i18n("&Run"), "pgmrun", Qt::Key_F5
,
282 m_debugger
, SLOT(programRun()), "exec_run");
283 connect(m_runAction
, SIGNAL(activated()), this, SLOT(intoBackground()));
284 m_stepIntoAction
= createAction(i18n("Step &into"), "pgmstep", Qt::Key_F8
,
285 m_debugger
, SLOT(programStep()), "exec_step_into");
286 connect(m_stepIntoAction
, SIGNAL(activated()), this, SLOT(intoBackground()));
287 m_stepOverAction
= createAction(i18n("Step &over"), "pgmnext", Qt::Key_F10
,
288 m_debugger
, SLOT(programNext()), "exec_step_over");
289 connect(m_stepOverAction
, SIGNAL(activated()), this, SLOT(intoBackground()));
290 m_stepOutAction
= createAction(i18n("Step o&ut"), "pgmfinish", Qt::Key_F6
,
291 m_debugger
, SLOT(programFinish()), "exec_step_out");
292 connect(m_stepOutAction
, SIGNAL(activated()), this, SLOT(intoBackground()));
293 m_toCursorAction
= createAction(i18n("Run to &cursor"), Qt::Key_F7
,
294 this, SLOT(slotExecUntil()), "exec_run_to_cursor");
295 connect(m_toCursorAction
, SIGNAL(activated()), this, SLOT(intoBackground()));
296 m_stepIntoIAction
= createAction(i18n("Step i&nto by instruction"), "pgmstepi", Qt::SHIFT
+Qt::Key_F8
,
297 m_debugger
, SLOT(programStepi()), "exec_step_into_by_insn");
298 connect(m_stepIntoIAction
, SIGNAL(activated()), this, SLOT(intoBackground()));
299 m_stepOverIAction
= createAction(i18n("Step o&ver by instruction"), "pgmnexti", Qt::SHIFT
+Qt::Key_F10
,
300 m_debugger
, SLOT(programNexti()), "exec_step_over_by_insn");
301 connect(m_stepOverIAction
, SIGNAL(activated()), this, SLOT(intoBackground()));
302 m_execMovePCAction
= createAction(i18n("&Program counter to current line"), 0,
303 m_filesWindow
, SLOT(slotMoveProgramCounter()), "exec_movepc");
304 m_breakAction
= createAction(i18n("&Break"), 0,
305 m_debugger
, SLOT(programBreak()), "exec_break");
306 m_killAction
= createAction(i18n("&Kill"), 0,
307 m_debugger
, SLOT(programKill()), "exec_kill");
308 m_restartAction
= createAction(i18n("Re&start"), 0,
309 m_debugger
, SLOT(programRunAgain()), "exec_restart");
310 m_attachAction
= createAction(i18n("A&ttach..."), 0,
311 this, SLOT(slotExecAttach()), "exec_attach");
312 m_argumentsAction
= createAction(i18n("&Arguments..."), 0,
313 this, SLOT(slotExecArgs()), "exec_arguments");
316 m_bpSetAction
= createAction(i18n("Set/Clear &breakpoint"), "brkpt", Qt::Key_F9
,
317 m_filesWindow
, SLOT(slotBrkptSet()), "breakpoint_set");
318 m_bpSetTempAction
= createAction(i18n("Set &temporary breakpoint"), Qt::SHIFT
+Qt::Key_F9
,
319 m_filesWindow
, SLOT(slotBrkptSetTemp()), "breakpoint_set_temporary");
320 m_bpEnableAction
= createAction(i18n("&Enable/Disable breakpoint"), Qt::CTRL
+Qt::Key_F9
,
321 m_filesWindow
, SLOT(slotBrkptEnable()), "breakpoint_enable");
323 // only in popup menus
324 createAction(i18n("Watch Expression"), 0,
325 this, SLOT(slotLocalsToWatch()), "watch_expression");
326 m_editValueAction
= createAction(i18n("Edit Value"), Qt::Key_F2
,
327 this, SLOT(slotEditValue()), "edit_value");
329 // all actions force an UI update
330 Q3ValueList
<KAction
*> actions
= actionCollection()->actions();
331 Q3ValueList
<KAction
*>::Iterator it
= actions
.begin();
332 for (; it
!= actions
.end(); ++it
) {
333 connect(*it
, SIGNAL(activated()), this, SLOT(updateUI()));
336 createGUI("kdbgui.rc");
339 void DebuggerMainWnd::initToolbar()
341 KToolBar
* toolbar
= toolBar("mainToolBar");
342 toolbar
->insertAnimatedWidget(ID_STATUS_BUSY
,
343 m_breakAction
, SLOT(activate()), "pulse", -1);
344 toolbar
->alignItemRight(ID_STATUS_BUSY
, true);
345 m_animRunning
= false;
347 KStatusBar
* statusbar
= statusBar();
348 statusbar
->insertItem(m_statusActive
, ID_STATUS_ACTIVE
);
349 m_lastActiveStatusText
= m_statusActive
;
350 statusbar
->insertItem("", ID_STATUS_MSG
); /* message pane */
352 // reserve some translations
357 bool DebuggerMainWnd::queryClose()
359 if (m_debugger
!= 0) {
360 m_debugger
->shutdown();
366 // instance properties
367 void DebuggerMainWnd::saveProperties(KConfig
* config
)
369 // session management
370 QString executable
= "";
371 if (m_debugger
!= 0) {
372 executable
= m_debugger
->executable();
374 config
->writeEntry("executable", executable
);
377 void DebuggerMainWnd::readProperties(KConfig
* config
)
379 // session management
380 QString execName
= config
->readEntry("executable");
382 TRACE("readProperties: executable=" + execName
);
383 if (!execName
.isEmpty()) {
384 debugProgram(execName
, "");
388 static const char WindowGroup
[] = "Windows";
389 static const char RecentExecutables
[] = "RecentExecutables";
390 static const char LastSession
[] = "LastSession";
391 static const char OutputWindowGroup
[] = "OutputWindow";
392 static const char TermCmdStr
[] = "TermCmdStr";
393 static const char KeepScript
[] = "KeepScript";
394 static const char DebuggerGroup
[] = "Debugger";
395 static const char DebuggerCmdStr
[] = "DebuggerCmdStr";
396 static const char PreferencesGroup
[] = "Preferences";
397 static const char PopForeground
[] = "PopForeground";
398 static const char BackTimeout
[] = "BackTimeout";
399 static const char TabWidth
[] = "TabWidth";
400 static const char SourceFileFilter
[] = "SourceFileFilter";
401 static const char HeaderFileFilter
[] = "HeaderFileFilter";
403 void DebuggerMainWnd::saveSettings(KConfig
* config
)
405 KConfigGroupSaver
g(config
, WindowGroup
);
409 Q3TextStream
stream(&layout
, QIODevice::WriteOnly
);
412 config
->writeEntry("Layout", layout
);
414 m_recentExecAction
->saveEntries(config
, RecentExecutables
);
416 KConfigGroupSaver
g2(config
, LastSession
);
417 config
->writeEntry("Width0Locals", m_localVariables
->columnWidth(0));
418 config
->writeEntry("Width0Watches", m_watches
->columnWidth(0));
420 if (m_debugger
!= 0) {
421 m_debugger
->saveSettings(config
);
424 KConfigGroupSaver
g3(config
, OutputWindowGroup
);
425 config
->writeEntry(TermCmdStr
, m_outputTermCmdStr
);
427 config
->setGroup(DebuggerGroup
);
428 config
->writeEntry(DebuggerCmdStr
, m_debuggerCmdStr
);
430 config
->setGroup(PreferencesGroup
);
431 config
->writeEntry(PopForeground
, m_popForeground
);
432 config
->writeEntry(BackTimeout
, m_backTimeout
);
433 config
->writeEntry(TabWidth
, m_tabWidth
);
434 config
->writeEntry(SourceFileFilter
, m_sourceFilter
);
435 config
->writeEntry(HeaderFileFilter
, m_headerFilter
);
438 void DebuggerMainWnd::restoreSettings(KConfig
* config
)
440 KConfigGroupSaver
g(config
, WindowGroup
);
442 QString layout
= config
->readEntry("Layout");
444 Q3TextStream
stream(&layout
, QIODevice::ReadOnly
);
448 m_recentExecAction
->loadEntries(config
, RecentExecutables
);
450 KConfigGroupSaver
g2(config
, LastSession
);
452 w
= config
->readNumEntry("Width0Locals", -1);
453 if (w
>= 0 && w
< 30000)
454 m_localVariables
->setColumnWidth(0, w
);
455 w
= config
->readNumEntry("Width0Watches", -1);
456 if (w
>= 0 && w
< 30000)
457 m_watches
->setColumnWidth(0, w
);
459 if (m_debugger
!= 0) {
460 m_debugger
->restoreSettings(config
);
463 KConfigGroupSaver
g3(config
, OutputWindowGroup
);
465 * For debugging and emergency purposes, let the config file override
466 * the shell script that is used to keep the output window open. This
467 * string must have EXACTLY 1 %s sequence in it.
469 setTerminalCmd(config
->readEntry(TermCmdStr
, defaultTermCmdStr
));
470 m_outputTermKeepScript
= config
->readEntry(KeepScript
);
472 config
->setGroup(DebuggerGroup
);
473 setDebuggerCmdStr(config
->readEntry(DebuggerCmdStr
));
475 config
->setGroup(PreferencesGroup
);
476 m_popForeground
= config
->readBoolEntry(PopForeground
, false);
477 m_backTimeout
= config
->readNumEntry(BackTimeout
, 1000);
478 m_tabWidth
= config
->readNumEntry(TabWidth
, 0);
479 m_sourceFilter
= config
->readEntry(SourceFileFilter
, m_sourceFilter
);
480 m_headerFilter
= config
->readEntry(HeaderFileFilter
, m_headerFilter
);
482 emit
setTabWidth(m_tabWidth
);
485 void DebuggerMainWnd::updateUI()
487 m_findAction
->setChecked(m_filesWindow
->m_findDlg
.isVisible());
488 m_findAction
->setEnabled(m_filesWindow
->hasWindows());
489 m_bpSetAction
->setEnabled(m_debugger
->canChangeBreakpoints());
490 m_bpSetTempAction
->setEnabled(m_debugger
->canChangeBreakpoints());
491 m_bpEnableAction
->setEnabled(m_debugger
->canChangeBreakpoints());
492 m_bpTableAction
->setChecked(isDockVisible(m_bpTable
));
493 m_btWindowAction
->setChecked(isDockVisible(m_btWindow
));
494 m_localVariablesAction
->setChecked(isDockVisible(m_localVariables
));
495 m_watchesAction
->setChecked(isDockVisible(m_watches
));
496 m_registersAction
->setChecked(isDockVisible(m_registers
));
497 m_threadsAction
->setChecked(isDockVisible(m_threads
));
498 m_memoryWindowAction
->setChecked(isDockVisible(m_memoryWindow
));
499 m_ttyWindowAction
->setChecked(isDockVisible(m_ttyWindow
));
501 m_fileExecAction
->setEnabled(m_debugger
->isIdle());
502 m_settingsAction
->setEnabled(m_debugger
->haveExecutable());
503 m_coreDumpAction
->setEnabled(m_debugger
->canStart());
504 m_closeAction
->setEnabled(m_filesWindow
->hasWindows());
505 m_reloadAction
->setEnabled(m_filesWindow
->hasWindows());
506 m_stepIntoAction
->setEnabled(m_debugger
->canSingleStep());
507 m_stepIntoIAction
->setEnabled(m_debugger
->canSingleStep());
508 m_stepOverAction
->setEnabled(m_debugger
->canSingleStep());
509 m_stepOverIAction
->setEnabled(m_debugger
->canSingleStep());
510 m_stepOutAction
->setEnabled(m_debugger
->canSingleStep());
511 m_toCursorAction
->setEnabled(m_debugger
->canSingleStep());
512 m_execMovePCAction
->setEnabled(m_debugger
->canSingleStep());
513 m_restartAction
->setEnabled(m_debugger
->canSingleStep());
514 m_attachAction
->setEnabled(m_debugger
->isReady());
515 m_runAction
->setEnabled(m_debugger
->canStart() || m_debugger
->canSingleStep());
516 m_killAction
->setEnabled(m_debugger
->haveExecutable() && m_debugger
->isProgramActive());
517 m_breakAction
->setEnabled(m_debugger
->isProgramRunning());
518 m_argumentsAction
->setEnabled(m_debugger
->haveExecutable());
519 m_editValueAction
->setEnabled(m_debugger
->canSingleStep());
522 KAnimWidget
* w
= toolBar("mainToolBar")->animatedWidget(ID_STATUS_BUSY
);
523 if (m_debugger
->isIdle()) {
526 m_animRunning
= false;
529 if (!m_animRunning
) {
531 m_animRunning
= true;
537 if (m_debugger
->isProgramActive())
538 newStatus
= m_statusActive
;
539 if (newStatus
!= m_lastActiveStatusText
) {
540 statusBar()->changeItem(newStatus
, ID_STATUS_ACTIVE
);
541 m_lastActiveStatusText
= newStatus
;
545 void DebuggerMainWnd::updateLineItems()
547 m_filesWindow
->updateLineItems(m_debugger
);
550 void DebuggerMainWnd::slotAddWatch()
552 if (m_debugger
!= 0) {
553 QString t
= m_watches
->watchText();
554 m_debugger
->addWatch(t
);
558 void DebuggerMainWnd::slotAddWatch(const QString
& text
)
560 if (m_debugger
!= 0) {
561 m_debugger
->addWatch(text
);
565 void DebuggerMainWnd::slotNewFileLoaded()
567 // updates program counter in the new file
569 m_filesWindow
->updateLineItems(m_debugger
);
572 QDockWidget
* DebuggerMainWnd::dockParent(QWidget
* w
)
574 while ((w
= w
->parentWidget()) != 0) {
575 if (w
->isA("QDockWindow"))
576 return static_cast<QDockWidget
*>(w
);
581 bool DebuggerMainWnd::isDockVisible(QWidget
* w
)
583 QDockWidget
* d
= dockParent(w
);
584 return d
!= 0 && d
->isVisible();
587 bool DebuggerMainWnd::debugProgram(const QString
& exe
, const QString
& lang
)
589 // check the file name
592 bool success
= fi
.isFile();
595 QString msg
= i18n("`%1' is not a file or does not exist");
596 KMessageBox::sorry(this, msg
.arg(exe
));
600 success
= startDriver(fi
.absFilePath(), lang
);
605 m_recentExecAction
->addURL(KURL(fi
.absFilePath()));
607 // keep the directory
608 m_lastDirectory
= fi
.dirPath(true);
609 m_filesWindow
->setExtraDirectory(m_lastDirectory
);
611 // set caption to basename part of executable
612 QString caption
= fi
.fileName();
617 m_recentExecAction
->removeURL(KURL(fi
.absFilePath()));
623 static const char GeneralGroup
[] = "General";
625 bool DebuggerMainWnd::startDriver(const QString
& executable
, QString lang
)
627 assert(m_debugger
!= 0);
629 TRACE(QString("trying language '%1'...").arg(lang
));
630 DebuggerDriver
* driver
= driverFromLang(lang
);
634 // see if there is a language in the per-program config file
635 QString configName
= m_debugger
->getConfigForExe(executable
);
636 if (QFile::exists(configName
))
638 KSimpleConfig
c(configName
, true); // read-only
639 c
.setGroup(GeneralGroup
);
641 // Using "GDB" as default here is for backwards compatibility:
642 // The config file exists but doesn't have an entry,
643 // so it must have been created by an old version of KDbg
644 // that had only the GDB driver.
645 lang
= c
.readEntry(KDebugger::DriverNameEntry
, "GDB");
647 TRACE(QString("...bad, trying config driver %1...").arg(lang
));
648 driver
= driverFromLang(lang
);
654 QString name
= driverNameFromFile(executable
);
656 TRACE(QString("...no luck, trying %1 derived"
657 " from file contents").arg(name
));
658 driver
= driverFromLang(name
);
663 QString msg
= i18n("Don't know how to debug language `%1'");
664 KMessageBox::sorry(this, msg
.arg(lang
));
668 driver
->setLogFileName(m_transcriptFile
);
670 bool success
= m_debugger
->debugProgram(executable
, driver
);
676 QString msg
= i18n("Could not start the debugger process.\n"
677 "Please shut down KDbg and resolve the problem.");
678 KMessageBox::sorry(this, msg
);
684 // derive driver from language
685 DebuggerDriver
* DebuggerMainWnd::driverFromLang(QString lang
)
687 // lang is needed in all lowercase
690 // The following table relates languages and debugger drivers
691 static const struct L
{
692 const char* shortest
; // abbreviated to this is still unique
693 const char* full
; // full name of language
697 { "f", "fortran", 1 },
698 { "p", "python", 3 },
700 // the following are actually driver names
702 { "xsldbg", "xsldbg", 2 },
704 const int N
= sizeof(langs
)/sizeof(langs
[0]);
706 // lookup the language name
708 for (int i
= 0; i
< N
; i
++)
710 const L
& l
= langs
[i
];
712 // shortest must match
713 if (!lang
.startsWith(l
.shortest
))
716 // lang must not be longer than the full name, and it must match
717 if (QString(l
.full
).startsWith(lang
))
723 DebuggerDriver
* driver
= 0;
727 GdbDriver
* gdb
= new GdbDriver
;
728 gdb
->setDefaultInvocation(m_debuggerCmdStr
);
733 driver
= new XsldbgDriver
;
743 * Try to guess the language to use from the contents of the file.
745 QString
DebuggerMainWnd::driverNameFromFile(const QString
& exe
)
747 /* Inprecise but simple test to see if file is in XSLT language */
748 if (exe
.right(4).lower() == ".xsl")
754 void DebuggerMainWnd::setCoreFile(const QString
& corefile
)
756 assert(m_debugger
!= 0);
757 m_debugger
->useCoreFile(corefile
, true);
760 void DebuggerMainWnd::setRemoteDevice(const QString
& remoteDevice
)
762 if (m_debugger
!= 0) {
763 m_debugger
->setRemoteDevice(remoteDevice
);
767 void DebuggerMainWnd::overrideProgramArguments(const QString
& args
)
769 assert(m_debugger
!= 0);
770 m_debugger
->overrideProgramArguments(args
);
773 void DebuggerMainWnd::setTranscript(const QString
& name
)
775 m_transcriptFile
= name
;
776 if (m_debugger
!= 0 && m_debugger
->driver() != 0)
777 m_debugger
->driver()->setLogFileName(m_transcriptFile
);
780 void DebuggerMainWnd::setAttachPid(const QString
& pid
)
782 assert(m_debugger
!= 0);
783 m_debugger
->setAttachPid(pid
);
786 void DebuggerMainWnd::slotNewStatusMsg()
788 QString msg
= m_debugger
->statusMessage();
789 statusBar()->changeItem(msg
, ID_STATUS_MSG
);
792 void DebuggerMainWnd::slotFileGlobalSettings()
794 int oldTabWidth
= m_tabWidth
;
796 Q3TabDialog
dlg(this, "global_options", true);
797 QString title
= kapp
->caption();
798 title
+= i18n(": Global options");
799 dlg
.setCaption(title
);
800 dlg
.setCancelButton(i18n("Cancel"));
801 dlg
.setOKButton(i18n("OK"));
803 PrefDebugger
prefDebugger(&dlg
);
804 prefDebugger
.setDebuggerCmd(m_debuggerCmdStr
.isEmpty() ?
805 GdbDriver::defaultGdb() : m_debuggerCmdStr
);
806 prefDebugger
.setTerminal(m_outputTermCmdStr
);
808 PrefMisc
prefMisc(&dlg
);
809 prefMisc
.setPopIntoForeground(m_popForeground
);
810 prefMisc
.setBackTimeout(m_backTimeout
);
811 prefMisc
.setTabWidth(m_tabWidth
);
812 prefMisc
.setSourceFilter(m_sourceFilter
);
813 prefMisc
.setHeaderFilter(m_headerFilter
);
815 dlg
.addTab(&prefDebugger
, i18n("&Debugger"));
816 dlg
.addTab(&prefMisc
, i18n("&Miscellaneous"));
817 if (dlg
.exec() == QDialog::Accepted
)
819 setDebuggerCmdStr(prefDebugger
.debuggerCmd());
820 setTerminalCmd(prefDebugger
.terminal());
821 m_popForeground
= prefMisc
.popIntoForeground();
822 m_backTimeout
= prefMisc
.backTimeout();
823 m_tabWidth
= prefMisc
.tabWidth();
824 m_sourceFilter
= prefMisc
.sourceFilter();
825 if (m_sourceFilter
.isEmpty())
826 m_sourceFilter
= defaultSourceFilter
;
827 m_headerFilter
= prefMisc
.headerFilter();
828 if (m_headerFilter
.isEmpty())
829 m_headerFilter
= defaultHeaderFilter
;
832 if (m_tabWidth
!= oldTabWidth
) {
833 emit
setTabWidth(m_tabWidth
);
837 void DebuggerMainWnd::setTerminalCmd(const QString
& cmd
)
839 m_outputTermCmdStr
= cmd
;
840 // revert to default if empty
841 if (m_outputTermCmdStr
.isEmpty()) {
842 m_outputTermCmdStr
= defaultTermCmdStr
;
846 void DebuggerMainWnd::setDebuggerCmdStr(const QString
& cmd
)
848 m_debuggerCmdStr
= cmd
;
849 // make empty if it is the default
850 if (m_debuggerCmdStr
== GdbDriver::defaultGdb()) {
851 m_debuggerCmdStr
= QString();
855 void DebuggerMainWnd::slotDebuggerStarting()
857 if (m_debugger
== 0) /* paranoia check */
860 if (m_ttyLevel
== m_debugger
->ttyLevel())
863 // shut down terminal emulations we will not need
864 switch (m_ttyLevel
) {
865 case KDebugger::ttySimpleOutputOnly
:
866 m_ttyWindow
->deactivate();
868 case KDebugger::ttyFull
:
869 if (m_outputTermProc
!= 0) {
870 m_outputTermProc
->kill();
871 // will be deleted in slot
877 m_ttyLevel
= m_debugger
->ttyLevel();
880 switch (m_ttyLevel
) {
881 case KDebugger::ttySimpleOutputOnly
:
882 ttyName
= m_ttyWindow
->activate();
884 case KDebugger::ttyFull
:
885 if (m_outputTermProc
== 0) {
886 // create an output window
887 ttyName
= createOutputWindow();
888 TRACE(ttyName
.isEmpty() ?
889 "createOuputWindow failed" : "successfully created output window");
895 m_debugger
->setTerminal(ttyName
);
898 void DebuggerMainWnd::slotToggleBreak(const QString
& fileName
, int lineNo
,
899 const DbgAddr
& address
, bool temp
)
901 // lineNo is zero-based
902 if (m_debugger
!= 0) {
903 m_debugger
->setBreakpoint(fileName
, lineNo
, address
, temp
);
907 void DebuggerMainWnd::slotEnaDisBreak(const QString
& fileName
, int lineNo
,
908 const DbgAddr
& address
)
910 // lineNo is zero-based
911 if (m_debugger
!= 0) {
912 m_debugger
->enableDisableBreakpoint(fileName
, lineNo
, address
);
916 QString
DebuggerMainWnd::createOutputWindow()
918 // create a name for a fifo
920 fifoName
.sprintf("/tmp/kdbgttywin%05d", ::getpid());
922 // create a fifo that will pass in the tty name
923 QFile::remove(fifoName
); // remove remnants
925 if (::mkfifo(fifoName
.local8Bit(), S_IRUSR
|S_IWUSR
) < 0) {
927 TRACE("mkfifo " + fifoName
+ " failed");
931 if (::mknod(fifoName
.local8Bit(), S_IFIFO
| S_IRUSR
|S_IWUSR
, 0) < 0) {
933 TRACE("mknod " + fifoName
+ " failed");
938 m_outputTermProc
= new KProcess
;
941 * Spawn an xterm that in turn runs a shell script that passes us
942 * back the terminal name and then only sits and waits.
944 static const char shellScriptFmt
[] =
946 "trap \"\" INT QUIT TSTP;" /* ignore various signals */
947 "exec<&-;exec>&-;" /* close stdin and stdout */
948 "while :;do sleep 3600;done";
949 // let config file override this script
951 if (!m_outputTermKeepScript
.isEmpty()) {
952 shellScript
= m_outputTermKeepScript
;
954 shellScript
= shellScriptFmt
;
957 shellScript
.replace("%s", fifoName
);
958 TRACE("output window script is " + shellScript
);
960 QString title
= kapp
->caption();
961 title
+= i18n(": Program output");
963 // parse the command line specified in the preferences
964 QStringList cmdParts
= QStringList::split(' ', m_outputTermCmdStr
);
967 * Build the argv array. Thereby substitute special sequences:
974 { "%C", shellScript
}
977 for (QStringList::iterator i
= cmdParts
.begin(); i
!= cmdParts
.end(); ++i
)
980 for (int j
= sizeof(substitute
)/sizeof(substitute
[0])-1; j
>= 0; j
--) {
981 int pos
= str
.find(substitute
[j
].seq
);
983 str
.replace(pos
, 2, substitute
[j
].replace
);
984 break; /* substitute only one sequence */
987 *m_outputTermProc
<< str
;
992 if (m_outputTermProc
->start())
994 // read the ttyname from the fifo
996 if (f
.open(QIODevice::ReadOnly
))
998 QByteArray t
= f
.readAll();
999 tty
= QString::fromLocal8Bit(t
, t
.size());
1004 // remove whitespace
1005 tty
= tty
.stripWhiteSpace();
1006 TRACE("tty=" + tty
);
1008 connect(m_outputTermProc
, SIGNAL(processExited(KProcess
*)),
1009 SLOT(slotTermEmuExited()));
1013 // error, could not start xterm
1014 TRACE("fork failed for fifo " + fifoName
);
1015 QFile::remove(fifoName
);
1016 shutdownTermWindow();
1022 void DebuggerMainWnd::slotTermEmuExited()
1024 shutdownTermWindow();
1027 void DebuggerMainWnd::shutdownTermWindow()
1029 delete m_outputTermProc
;
1030 m_outputTermProc
= 0;
1033 void DebuggerMainWnd::slotProgramStopped()
1035 // when the program stopped, move the window to the foreground
1036 if (m_popForeground
) {
1037 // unfortunately, this requires quite some force to work :-(
1038 KWin::raiseWindow(winId());
1039 KWin::forceActiveWindow(winId());
1044 void DebuggerMainWnd::intoBackground()
1046 if (m_popForeground
) {
1047 m_backTimer
.start(m_backTimeout
, true); /* single-shot */
1051 void DebuggerMainWnd::slotBackTimer()
1056 void DebuggerMainWnd::slotRecentExec(const KURL
& url
)
1058 QString exe
= url
.path();
1059 debugProgram(exe
, "");
1062 QString
DebuggerMainWnd::makeSourceFilter()
1065 f
= m_sourceFilter
+ " " + m_headerFilter
+ i18n("|All source files\n");
1066 f
+= m_sourceFilter
+ i18n("|Source files\n");
1067 f
+= m_headerFilter
+ i18n("|Header files\n");
1068 f
+= i18n("*|All files");
1073 * Pop up the context menu in the locals window
1075 void DebuggerMainWnd::slotLocalsPopup(Q3ListViewItem
*, const QPoint
& pt
)
1077 Q3PopupMenu
* popup
=
1078 static_cast<Q3PopupMenu
*>(factory()->container("popup_locals", this));
1082 if (popup
->isVisible()) {
1090 * Copies the currently selected item to the watch window.
1092 void DebuggerMainWnd::slotLocalsToWatch()
1094 VarTree
* item
= m_localVariables
->selectedItem();
1096 if (item
!= 0 && m_debugger
!= 0) {
1097 QString text
= item
->computeExpr();
1098 m_debugger
->addWatch(text
);
1103 * Starts editing a value in a value display
1105 void DebuggerMainWnd::slotEditValue()
1107 // does one of the value trees have the focus
1108 QWidget
* f
= kapp
->focusWidget();
1110 if (f
== m_localVariables
) {
1111 wnd
= m_localVariables
;
1112 } else if (f
== m_watches
->watchVariables()) {
1113 wnd
= m_watches
->watchVariables();
1118 if (m_localVariables
->isEditing() ||
1119 m_watches
->watchVariables()->isEditing())
1121 return; /* don't edit twice */
1124 VarTree
* expr
= wnd
->currentItem();
1125 if (expr
!= 0 && m_debugger
!= 0 && m_debugger
->canSingleStep())
1127 TRACE("edit value");
1128 // determine the text to edit
1129 QString text
= m_debugger
->driver()->editableValue(expr
);
1130 wnd
->editValue(expr
, text
);
1134 // helper that gets a file name (it only differs in the caption of the dialog)
1135 static QString
myGetFileName(QString caption
,
1136 QString dir
, QString filter
,
1140 KFileDialog
dlg(dir
, filter
, parent
, "filedialog", true);
1142 dlg
.setCaption(caption
);
1144 if (dlg
.exec() == QDialog::Accepted
)
1145 filename
= dlg
.selectedFile();
1150 void DebuggerMainWnd::slotFileOpen()
1152 // start browsing in the active file's directory
1153 // fall back to last used directory (executable)
1154 QString dir
= m_lastDirectory
;
1155 QString fileName
= m_filesWindow
->activeFileName();
1156 if (!fileName
.isEmpty()) {
1157 QFileInfo
fi(fileName
);
1161 fileName
= myGetFileName(i18n("Open"),
1163 makeSourceFilter(), this);
1165 if (!fileName
.isEmpty())
1167 QFileInfo
fi(fileName
);
1168 m_lastDirectory
= fi
.dirPath();
1169 m_filesWindow
->setExtraDirectory(m_lastDirectory
);
1170 m_filesWindow
->activateFile(fileName
);
1174 void DebuggerMainWnd::slotFileExe()
1176 if (m_debugger
->isIdle())
1178 // open a new executable
1179 QString executable
= myGetFileName(i18n("Select the executable to debug"),
1180 m_lastDirectory
, 0, this);
1181 if (executable
.isEmpty())
1184 debugProgram(executable
, "");
1188 void DebuggerMainWnd::slotFileCore()
1190 if (m_debugger
->canStart())
1192 QString corefile
= myGetFileName(i18n("Select core dump"),
1193 m_lastDirectory
, 0, this);
1194 if (!corefile
.isEmpty()) {
1195 m_debugger
->useCoreFile(corefile
, false);
1200 void DebuggerMainWnd::slotFileProgSettings()
1202 if (m_debugger
!= 0) {
1203 m_debugger
->programSettings(this);
1207 void DebuggerMainWnd::slotViewStatusbar()
1209 if (statusBar()->isVisible())
1210 statusBar()->hide();
1212 statusBar()->show();
1216 void DebuggerMainWnd::slotExecUntil()
1218 if (m_debugger
!= 0)
1222 if (m_filesWindow
->activeLine(file
, lineNo
))
1223 m_debugger
->runUntil(file
, lineNo
);
1227 void DebuggerMainWnd::slotExecAttach()
1230 ProcAttachPS
dlg(this);
1231 // seed filter with executable name
1232 QFileInfo fi
= m_debugger
->executable();
1233 dlg
.filterEdit
->setText(fi
.fileName());
1235 ProcAttach
dlg(this);
1236 dlg
.setText(m_debugger
->attachedPid());
1239 m_debugger
->attachProgram(dlg
.text());
1243 void DebuggerMainWnd::slotExecArgs()
1245 if (m_debugger
!= 0) {
1246 m_debugger
->programArgs(this);
1250 void DebuggerMainWnd::slotConfigureKeys()
1252 KKeyDialog::configure(actionCollection(), this);
1255 #include "dbgmainwnd.moc"