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 <klocale.h> /* i18n */
8 #include <kmessagebox.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>
22 #include <kxmlguifactory.h>
23 #include <KPageDialog>
24 #include <QListWidget>
26 #include <QFileDialog>
28 #include <QGuiApplication>
31 #include <QDockWidget>
35 #include "dbgmainwnd.h"
39 #include "threadlist.h"
40 #include "memwindow.h"
42 #include "watchwindow.h"
43 #include "procattach.h"
44 #include "prefdebugger.h"
46 #include "gdbdriver.h"
47 #include "xsldbgdriver.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() :
62 m_transcriptFile(GDB_TRANSCRIPT
),
64 m_outputTermCmdStr(defaultTermCmdStr
),
65 m_outputTermProc(new QProcess
),
66 m_ttyLevel(-1), /* no tty yet */
67 m_popForeground(false),
70 m_sourceFilter(defaultSourceFilter
),
71 m_headerFilter(defaultHeaderFilter
),
72 m_statusActive(i18n("active"))
74 setDockNestingEnabled(true);
76 m_filesWindow
= new WinStack(this);
77 setCentralWidget(m_filesWindow
);
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
);
104 m_debugger
= new KDebugger(this, m_localVariables
, m_watches
->watchVariables(), m_btWindow
);
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
);
113 setStandardToolBarMenuEnabled(true);
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
&)));
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
&)));
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
&)));
147 connect(m_debugger
, SIGNAL(programStopped()), SLOT(slotProgramStopped()));
148 connect(&m_backTimer
, SIGNAL(timeout()), SLOT(slotBackTimer()));
150 connect(this, SIGNAL(setTabWidth(int)), m_filesWindow
, SIGNAL(setTabWidth(int)));
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()));
159 connect(m_debugger
, SIGNAL(registersChanged(const std::list
<RegisterInfo
>&)),
160 m_registers
, SLOT(updateRegisters(const std::list
<RegisterInfo
>&)));
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
*)));
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)));
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
&)));
181 setupGUI(KXmlGuiWindow::Default
, "kdbgui.rc");
182 restoreSettings(KSharedConfig::openConfig());
184 // The animation button is not part of the restored window state.
185 // We must create it after the toolbar was loaded.
189 m_bpTable
->updateUI();
192 DebuggerMainWnd::~DebuggerMainWnd()
194 saveSettings(KSharedConfig::openConfig());
195 // must delete m_debugger early since it references our windows
199 delete m_memoryWindow
;
205 delete m_localVariables
;
207 delete m_filesWindow
;
209 delete m_outputTermProc
;
212 QDockWidget
* DebuggerMainWnd::createDockWidget(const char* name
, const QString
& title
)
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()));
221 QAction
* DebuggerMainWnd::createAction(const QString
& text
, const char* icon
,
222 int shortcut
, const QObject
* receiver
,
223 const char* slot
, const char* name
)
225 QAction
* a
= actionCollection()->addAction(name
);
227 a
->setIcon(QIcon(new KIconEngine(icon
, KIconLoader::global())));
229 actionCollection()->setDefaultShortcut(a
, QKeySequence(shortcut
));
230 connect(a
, SIGNAL(triggered()), receiver
, slot
);
234 QAction
* DebuggerMainWnd::createAction(const QString
& text
,
235 int shortcut
, const QObject
* receiver
,
236 const char* slot
, const char* name
)
238 QAction
* a
= actionCollection()->addAction(name
);
241 actionCollection()->setDefaultShortcut(a
, QKeySequence(shortcut
));
242 connect(a
, SIGNAL(triggered()), receiver
, slot
);
247 void DebuggerMainWnd::initKAction()
250 QAction
* open
= KStandardAction::open(this, SLOT(slotFileOpen()),
252 open
->setText(i18n("&Open Source..."));
253 m_closeAction
= KStandardAction::close(m_filesWindow
, SLOT(slotClose()), actionCollection());
254 m_reloadAction
= createAction(i18n("&Reload Source"), "view-refresh", 0,
255 m_filesWindow
, SLOT(slotFileReload()), "file_reload");
256 m_fileExecAction
= createAction(i18n("&Executable..."),
257 "document-open-executable", 0,
258 this, SLOT(slotFileExe()), "file_executable");
259 m_recentExecAction
= KStandardAction::openRecent(this, SLOT(slotRecentExec(const QUrl
&)),
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());
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());
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());
280 i18n("Source &code");
281 struct { QWidget
* w
; QString id
; QAction
** act
; } dw
[] = {
282 { m_btWindow
, "view_stack", &m_btWindowAction
},
283 { m_localVariables
, "view_locals", &m_localVariablesAction
},
284 { m_watches
, "view_watched_expressions", &m_watchesAction
},
285 { m_registers
, "view_registers", &m_registersAction
},
286 { m_bpTable
, "view_breakpoints", &m_bpTableAction
},
287 { m_threads
, "view_threads", &m_threadsAction
},
288 { m_ttyWindow
, "view_output", &m_ttyWindowAction
},
289 { m_memoryWindow
, "view_memory", &m_memoryWindowAction
}
291 for (unsigned i
= 0; i
< sizeof(dw
)/sizeof(dw
[0]); i
++) {
292 QDockWidget
* d
= dockParent(dw
[i
].w
);
293 QAction
* action
= d
->toggleViewAction();
294 actionCollection()->addAction(dw
[i
].id
, action
);
299 m_runAction
= createAction(i18n("&Run"),
300 "debug-run", Qt::Key_F5
,
301 m_debugger
, SLOT(programRun()), "exec_run");
302 connect(m_runAction
, SIGNAL(triggered()), this, SLOT(intoBackground()));
303 m_stepIntoAction
= createAction(i18n("Step &into"),
304 "debug-step-into", Qt::Key_F8
,
305 m_debugger
, SLOT(programStep()), "exec_step_into");
306 connect(m_stepIntoAction
, SIGNAL(triggered()), this, SLOT(intoBackground()));
307 m_stepOverAction
= createAction(i18n("Step &over"),
308 "debug-step-over", Qt::Key_F10
,
309 m_debugger
, SLOT(programNext()), "exec_step_over");
310 connect(m_stepOverAction
, SIGNAL(triggered()), this, SLOT(intoBackground()));
311 m_stepOutAction
= createAction(i18n("Step o&ut"),
312 "debug-step-out", Qt::Key_F6
,
313 m_debugger
, SLOT(programFinish()), "exec_step_out");
314 connect(m_stepOutAction
, SIGNAL(triggered()), this, SLOT(intoBackground()));
315 m_toCursorAction
= createAction(i18n("Run to &cursor"),
316 "debug-execute-to-cursor", Qt::Key_F7
,
317 this, SLOT(slotExecUntil()), "exec_run_to_cursor");
318 connect(m_toCursorAction
, SIGNAL(triggered()), this, SLOT(intoBackground()));
319 m_stepIntoIAction
= createAction(i18n("Step i&nto by instruction"),
320 "debug-step-into-instruction", Qt::SHIFT
+Qt::Key_F8
,
321 m_debugger
, SLOT(programStepi()), "exec_step_into_by_insn");
322 connect(m_stepIntoIAction
, SIGNAL(triggered()), this, SLOT(intoBackground()));
323 m_stepOverIAction
= createAction(i18n("Step o&ver by instruction"),
324 "debug-step-instruction", Qt::SHIFT
+Qt::Key_F10
,
325 m_debugger
, SLOT(programNexti()), "exec_step_over_by_insn");
326 connect(m_stepOverIAction
, SIGNAL(triggered()), this, SLOT(intoBackground()));
327 m_execMovePCAction
= createAction(i18n("&Program counter to current line"),
328 "debug-run-cursor", 0,
329 m_filesWindow
, SLOT(slotMoveProgramCounter()), "exec_movepc");
330 m_breakAction
= createAction(i18n("&Break"), 0,
331 m_debugger
, SLOT(programBreak()), "exec_break");
332 m_killAction
= createAction(i18n("&Kill"), 0,
333 m_debugger
, SLOT(programKill()), "exec_kill");
334 m_restartAction
= createAction(i18n("Re&start"), 0,
335 m_debugger
, SLOT(programRunAgain()), "exec_restart");
336 m_attachAction
= createAction(i18n("A&ttach..."), 0,
337 this, SLOT(slotExecAttach()), "exec_attach");
338 m_detachAction
= createAction(i18n("&Detach"), 0,
339 m_debugger
, SLOT(programDetach()), "exec_detach");
340 m_argumentsAction
= createAction(i18n("&Arguments..."), 0,
341 this, SLOT(slotExecArgs()), "exec_arguments");
344 m_bpSetAction
= createAction(i18n("Set/Clear &breakpoint"), "brkpt", Qt::Key_F9
,
345 m_filesWindow
, SLOT(slotBrkptSet()), "breakpoint_set");
346 m_bpSetTempAction
= createAction(i18n("Set &temporary breakpoint"), Qt::SHIFT
+Qt::Key_F9
,
347 m_filesWindow
, SLOT(slotBrkptSetTemp()), "breakpoint_set_temporary");
348 m_bpEnableAction
= createAction(i18n("&Enable/Disable breakpoint"), Qt::CTRL
+Qt::Key_F9
,
349 m_filesWindow
, SLOT(slotBrkptEnable()), "breakpoint_enable");
351 // only in popup menus
352 createAction(i18n("Watch Expression"), 0,
353 this, SLOT(slotLocalsToWatch()), "watch_expression");
354 m_editValueAction
= createAction(i18n("Edit Value"), Qt::Key_F2
,
355 this, SLOT(slotEditValue()), "edit_value");
357 // all actions force an UI update
358 QList
<QAction
*> actions
= actionCollection()->actions();
359 foreach(QAction
* action
, actions
) {
360 connect(action
, SIGNAL(triggered()), this, SLOT(updateUI()));
364 void DebuggerMainWnd::initAnimation()
366 KToolBar
* toolbar
= toolBar("mainToolBar");
367 m_animation
= new KAnimatedButton(toolbar
);
368 toolbar
->addWidget(m_animation
);
369 m_animation
->setAnimationPath(KIconLoader::global()->moviePath("pulse", KIconLoader::Toolbar
));
370 connect(m_animation
, SIGNAL(clicked(bool)), m_debugger
, SLOT(programBreak()));
371 m_animRunning
= false;
374 void DebuggerMainWnd::initStatusBar()
376 QStatusBar
* statusbar
= statusBar();
377 m_statusActiveLabel
= new KSqueezedTextLabel(statusbar
);
378 m_statusActiveLabel
->setAlignment(Qt::AlignHCenter
| Qt::AlignVCenter
);
379 statusbar
->addPermanentWidget(m_statusActiveLabel
);
380 m_statusActiveLabel
->show();
381 m_lastActiveStatusText
= m_statusActive
;
384 m_statusMsgLabel
= new KSqueezedTextLabel(statusbar
);
385 m_statusMsgLabel
->setAlignment(Qt::AlignHCenter
| Qt::AlignVCenter
);
386 statusbar
->addPermanentWidget(m_statusMsgLabel
);
387 m_statusMsgLabel
->show();
389 // reserve some translations
394 bool DebuggerMainWnd::queryClose()
396 if (m_debugger
!= 0) {
397 m_debugger
->shutdown();
403 // instance properties
404 void DebuggerMainWnd::saveProperties(KConfigGroup
& cg
)
406 // session management
407 QString executable
= "";
408 if (m_debugger
!= 0) {
409 executable
= m_debugger
->executable();
411 cg
.writeEntry("executable", executable
);
414 void DebuggerMainWnd::readProperties(const KConfigGroup
& cg
)
416 // session management
417 QString execName
= cg
.readEntry("executable");
419 TRACE("readProperties: executable=" + execName
);
420 if (!execName
.isEmpty()) {
421 debugProgram(execName
, "");
425 static const char RecentExecutables
[] = "RecentExecutables";
426 static const char LastSession
[] = "LastSession";
427 static const char OutputWindowGroup
[] = "OutputWindow";
428 static const char TermCmdStr
[] = "TermCmdStr";
429 static const char KeepScript
[] = "KeepScript";
430 static const char DebuggerGroup
[] = "Debugger";
431 static const char DebuggerCmdStr
[] = "DebuggerCmdStr";
432 static const char PreferencesGroup
[] = "Preferences";
433 static const char PopForeground
[] = "PopForeground";
434 static const char BackTimeout
[] = "BackTimeout";
435 static const char TabWidth
[] = "TabWidth";
436 static const char SourceFileFilter
[] = "SourceFileFilter";
437 static const char HeaderFileFilter
[] = "HeaderFileFilter";
439 void DebuggerMainWnd::saveSettings(KSharedConfigPtr config
)
441 m_recentExecAction
->saveEntries(config
->group(RecentExecutables
));
443 KConfigGroup lg
= config
->group(LastSession
);
444 lg
.writeEntry("Width0Locals", m_localVariables
->columnWidth(0));
445 lg
.writeEntry("Width0Watches", m_watches
->columnWidth(0));
447 if (m_debugger
!= 0) {
448 m_debugger
->saveSettings(config
.data());
451 config
->group(OutputWindowGroup
).writeEntry(TermCmdStr
, m_outputTermCmdStr
);
452 config
->group(DebuggerGroup
).writeEntry(DebuggerCmdStr
, m_debuggerCmdStr
);
454 KConfigGroup
pg(config
->group(PreferencesGroup
));
455 pg
.writeEntry(PopForeground
, m_popForeground
);
456 pg
.writeEntry(BackTimeout
, m_backTimeout
);
457 pg
.writeEntry(TabWidth
, m_tabWidth
);
458 pg
.writeEntry(SourceFileFilter
, m_sourceFilter
);
459 pg
.writeEntry(HeaderFileFilter
, m_headerFilter
);
462 void DebuggerMainWnd::restoreSettings(KSharedConfigPtr config
)
464 m_recentExecAction
->loadEntries(config
->group(RecentExecutables
));
466 KConfigGroup lg
= config
->group(LastSession
);
468 w
= lg
.readEntry("Width0Locals", -1);
469 if (w
>= 0 && w
< 30000)
470 m_localVariables
->setColumnWidth(0, w
);
471 w
= lg
.readEntry("Width0Watches", -1);
472 if (w
>= 0 && w
< 30000)
473 m_watches
->setColumnWidth(0, w
);
475 if (m_debugger
!= 0) {
476 m_debugger
->restoreSettings(config
.data());
479 KConfigGroup
og(config
->group(OutputWindowGroup
));
481 * For debugging and emergency purposes, let the config file override
482 * the shell script that is used to keep the output window open. This
483 * string must have EXACTLY 1 %s sequence in it.
485 setTerminalCmd(og
.readEntry(TermCmdStr
, defaultTermCmdStr
));
486 m_outputTermKeepScript
= og
.readEntry(KeepScript
);
488 setDebuggerCmdStr(config
->group(DebuggerGroup
).readEntry(DebuggerCmdStr
));
490 KConfigGroup
pg(config
->group(PreferencesGroup
));
491 m_popForeground
= pg
.readEntry(PopForeground
, false);
492 m_backTimeout
= pg
.readEntry(BackTimeout
, 1000);
493 m_tabWidth
= pg
.readEntry(TabWidth
, 0);
494 m_sourceFilter
= pg
.readEntry(SourceFileFilter
, m_sourceFilter
);
495 m_headerFilter
= pg
.readEntry(HeaderFileFilter
, m_headerFilter
);
497 emit
setTabWidth(m_tabWidth
);
500 void DebuggerMainWnd::updateUI()
502 m_findAction
->setChecked(m_filesWindow
->m_findDlg
.isVisible());
503 m_findAction
->setEnabled(m_filesWindow
->hasWindows());
504 m_bpSetAction
->setEnabled(m_debugger
->canChangeBreakpoints());
505 m_bpSetTempAction
->setEnabled(m_debugger
->canChangeBreakpoints());
506 m_bpEnableAction
->setEnabled(m_debugger
->canChangeBreakpoints());
508 m_fileExecAction
->setEnabled(m_debugger
->isIdle());
509 m_settingsAction
->setEnabled(m_debugger
->haveExecutable());
510 m_coreDumpAction
->setEnabled(m_debugger
->canStart());
511 m_closeAction
->setEnabled(m_filesWindow
->hasWindows());
512 m_reloadAction
->setEnabled(m_filesWindow
->hasWindows());
513 m_stepIntoAction
->setEnabled(m_debugger
->canSingleStep());
514 m_stepIntoIAction
->setEnabled(m_debugger
->canSingleStep());
515 m_stepOverAction
->setEnabled(m_debugger
->canSingleStep());
516 m_stepOverIAction
->setEnabled(m_debugger
->canSingleStep());
517 m_stepOutAction
->setEnabled(m_debugger
->canSingleStep());
518 m_toCursorAction
->setEnabled(m_debugger
->canSingleStep());
519 m_execMovePCAction
->setEnabled(m_debugger
->canSingleStep());
520 m_restartAction
->setEnabled(m_debugger
->canSingleStep());
521 m_attachAction
->setEnabled(m_debugger
->isReady());
522 m_detachAction
->setEnabled(m_debugger
->canSingleStep());
523 m_runAction
->setEnabled(m_debugger
->canStart() || m_debugger
->canSingleStep());
524 m_killAction
->setEnabled(m_debugger
->haveExecutable() && m_debugger
->isProgramActive());
525 m_breakAction
->setEnabled(m_debugger
->isProgramRunning());
526 m_argumentsAction
->setEnabled(m_debugger
->haveExecutable());
527 m_editValueAction
->setEnabled(m_debugger
->canSingleStep());
530 if (m_debugger
->isIdle()) {
531 if (m_animRunning
&& m_animation
) {
533 m_animRunning
= false;
536 if (!m_animRunning
&& m_animation
) {
537 m_animation
->start();
538 m_animRunning
= true;
544 if (m_debugger
->isProgramActive())
545 newStatus
= m_statusActive
;
546 if (newStatus
!= m_lastActiveStatusText
) {
547 m_statusActiveLabel
->setText(newStatus
);
548 m_lastActiveStatusText
= newStatus
;
552 void DebuggerMainWnd::updateLineItems()
554 m_filesWindow
->updateLineItems(m_debugger
);
557 void DebuggerMainWnd::slotAddWatch()
559 if (m_debugger
!= 0) {
560 QString t
= m_watches
->watchText();
561 m_debugger
->addWatch(t
);
565 void DebuggerMainWnd::slotAddWatch(const QString
& text
)
567 if (m_debugger
!= 0) {
568 m_debugger
->addWatch(text
);
572 void DebuggerMainWnd::slotNewFileLoaded()
574 // updates program counter in the new file
576 m_filesWindow
->updateLineItems(m_debugger
);
579 QDockWidget
* DebuggerMainWnd::dockParent(QWidget
* w
)
581 while ((w
= w
->parentWidget()) != 0) {
582 if (QDockWidget
* dock
= qobject_cast
<QDockWidget
*>(w
))
588 void DebuggerMainWnd::makeDefaultLayout()
590 // +---------------+---------+
591 // | Source | Locals |
593 // |---------------+---------+
594 // |Stack, Brkpts, | Watches |
596 // +---------------+---------+
598 addDockWidget(Qt::RightDockWidgetArea
, dockParent(m_localVariables
));
599 addDockWidget(Qt::BottomDockWidgetArea
, dockParent(m_memoryWindow
));
600 splitDockWidget(dockParent(m_memoryWindow
), dockParent(m_threads
), Qt::Horizontal
);
601 tabifyDockWidget(dockParent(m_memoryWindow
), dockParent(m_registers
));
602 tabifyDockWidget(dockParent(m_registers
), dockParent(m_bpTable
));
603 tabifyDockWidget(dockParent(m_bpTable
), dockParent(m_ttyWindow
));
604 tabifyDockWidget(dockParent(m_ttyWindow
), dockParent(m_btWindow
));
605 tabifyDockWidget(dockParent(m_threads
), dockParent(m_watches
));
606 dockParent(m_localVariables
)->setVisible(true);
607 dockParent(m_ttyWindow
)->setVisible(true);
608 dockParent(m_watches
)->setVisible(true);
609 dockParent(m_btWindow
)->setVisible(true);
610 dockParent(m_bpTable
)->setVisible(true);
613 bool DebuggerMainWnd::debugProgram(const QString
& exe
, const QString
& lang
)
615 // check the file name
618 bool success
= fi
.isFile();
621 QString msg
= i18n("`%1' is not a file or does not exist");
622 KMessageBox::sorry(this, msg
.arg(exe
));
626 success
= startDriver(fi
.absoluteFilePath(), lang
);
631 m_recentExecAction
->addUrl(QUrl::fromLocalFile(fi
.absoluteFilePath()));
633 // keep the directory
634 m_lastDirectory
= fi
.absolutePath();
635 m_filesWindow
->setExtraDirectory(m_lastDirectory
);
637 // set caption to basename part of executable
638 QString caption
= fi
.fileName();
643 m_recentExecAction
->removeUrl(QUrl::fromLocalFile(fi
.absoluteFilePath()));
649 static const char GeneralGroup
[] = "General";
651 bool DebuggerMainWnd::startDriver(const QString
& executable
, QString lang
)
653 assert(m_debugger
!= 0);
655 TRACE(QString("trying language '%1'...").arg(lang
));
656 DebuggerDriver
* driver
= driverFromLang(lang
);
660 // see if there is a language in the per-program config file
661 QString configName
= m_debugger
->getConfigForExe(executable
);
662 if (QFile::exists(configName
))
664 KConfig
c(configName
, KConfig::SimpleConfig
);
666 // Using "GDB" as default here is for backwards compatibility:
667 // The config file exists but doesn't have an entry,
668 // so it must have been created by an old version of KDbg
669 // that had only the GDB driver.
670 lang
= c
.group(GeneralGroup
)
671 .readEntry(KDebugger::DriverNameEntry
, "GDB");
673 TRACE(QString("...bad, trying config driver %1...").arg(lang
));
674 driver
= driverFromLang(lang
);
680 QString name
= driverNameFromFile(executable
);
682 TRACE(QString("...no luck, trying %1 derived"
683 " from file contents").arg(name
));
684 driver
= driverFromLang(name
);
689 QString msg
= i18n("Don't know how to debug language `%1'");
690 KMessageBox::sorry(this, msg
.arg(lang
));
693 if (typeid(*driver
) == typeid(XsldbgDriver
)) {
694 KMessageBox::information(this, i18n("XSL debugging is no longer supported and will be removed in a future version of KDbg"));
697 driver
->setLogFileName(m_transcriptFile
);
699 bool success
= m_debugger
->debugProgram(executable
, driver
);
705 QString msg
= i18n("Could not start the debugger process.\n"
706 "Please shut down KDbg and resolve the problem.");
707 KMessageBox::sorry(this, msg
);
713 // derive driver from language
714 DebuggerDriver
* DebuggerMainWnd::driverFromLang(QString lang
)
716 // lang is needed in all lowercase
717 lang
= lang
.toLower();
719 // The following table relates languages and debugger drivers
720 static const struct L
{
721 const char* shortest
; // abbreviated to this is still unique
722 const char* full
; // full name of language
726 { "f", "fortran", 1 },
727 { "p", "python", 3 },
729 // the following are actually driver names
731 { "xsldbg", "xsldbg", 2 },
733 const int N
= sizeof(langs
)/sizeof(langs
[0]);
735 // lookup the language name
737 for (int i
= 0; i
< N
; i
++)
739 const L
& l
= langs
[i
];
741 // shortest must match
742 if (!lang
.startsWith(l
.shortest
))
745 // lang must not be longer than the full name, and it must match
746 if (QString(l
.full
).startsWith(lang
))
752 DebuggerDriver
* driver
= 0;
756 GdbDriver
* gdb
= new GdbDriver
;
757 gdb
->setDefaultInvocation(m_debuggerCmdStr
);
762 driver
= new XsldbgDriver
;
772 * Try to guess the language to use from the contents of the file.
774 QString
DebuggerMainWnd::driverNameFromFile(const QString
& exe
)
776 /* Inprecise but simple test to see if file is in XSLT language */
777 if (exe
.right(4).toLower() == ".xsl")
783 void DebuggerMainWnd::setCoreFile(const QString
& corefile
)
785 assert(m_debugger
!= 0);
786 m_debugger
->useCoreFile(corefile
, true);
789 void DebuggerMainWnd::setRemoteDevice(const QString
& remoteDevice
)
791 if (m_debugger
!= 0) {
792 m_debugger
->setRemoteDevice(remoteDevice
);
796 void DebuggerMainWnd::overrideProgramArguments(const QString
& args
)
798 assert(m_debugger
!= 0);
799 m_debugger
->overrideProgramArguments(args
);
802 void DebuggerMainWnd::setTranscript(const QString
& name
)
804 m_transcriptFile
= name
;
805 if (m_debugger
!= 0 && m_debugger
->driver() != 0)
806 m_debugger
->driver()->setLogFileName(m_transcriptFile
);
809 void DebuggerMainWnd::setAttachPid(const QString
& pid
)
811 assert(m_debugger
!= 0);
812 m_debugger
->setAttachPid(pid
);
815 void DebuggerMainWnd::slotNewStatusMsg()
817 QString msg
= m_debugger
->statusMessage();
818 m_statusMsgLabel
->setText(msg
.trimmed());
821 void DebuggerMainWnd::slotFileGlobalSettings()
823 int oldTabWidth
= m_tabWidth
;
825 KPageDialog
dlg(this);
826 dlg
.setWindowTitle(i18n("Global Options"));
828 PrefDebugger
prefDebugger(&dlg
);
829 prefDebugger
.setDebuggerCmd(m_debuggerCmdStr
.isEmpty() ?
830 GdbDriver::defaultGdb() : m_debuggerCmdStr
);
831 prefDebugger
.setTerminal(m_outputTermCmdStr
);
833 PrefMisc
prefMisc(&dlg
);
834 prefMisc
.setPopIntoForeground(m_popForeground
);
835 prefMisc
.setBackTimeout(m_backTimeout
);
836 prefMisc
.setTabWidth(m_tabWidth
);
837 prefMisc
.setSourceFilter(m_sourceFilter
);
838 prefMisc
.setHeaderFilter(m_headerFilter
);
840 dlg
.addPage(&prefDebugger
, i18n("Debugger"));
841 dlg
.addPage(&prefMisc
, i18n("Miscellaneous"));
842 if (dlg
.exec() == QDialog::Accepted
)
844 setDebuggerCmdStr(prefDebugger
.debuggerCmd());
845 setTerminalCmd(prefDebugger
.terminal());
846 m_popForeground
= prefMisc
.popIntoForeground();
847 m_backTimeout
= prefMisc
.backTimeout();
848 m_tabWidth
= prefMisc
.tabWidth();
849 m_sourceFilter
= prefMisc
.sourceFilter();
850 if (m_sourceFilter
.isEmpty())
851 m_sourceFilter
= defaultSourceFilter
;
852 m_headerFilter
= prefMisc
.headerFilter();
853 if (m_headerFilter
.isEmpty())
854 m_headerFilter
= defaultHeaderFilter
;
857 if (m_tabWidth
!= oldTabWidth
) {
858 emit
setTabWidth(m_tabWidth
);
862 void DebuggerMainWnd::setTerminalCmd(const QString
& cmd
)
864 m_outputTermCmdStr
= cmd
;
865 // revert to default if empty
866 if (m_outputTermCmdStr
.isEmpty()) {
867 m_outputTermCmdStr
= defaultTermCmdStr
;
871 void DebuggerMainWnd::setDebuggerCmdStr(const QString
& cmd
)
873 m_debuggerCmdStr
= cmd
;
874 // make empty if it is the default
875 if (m_debuggerCmdStr
== GdbDriver::defaultGdb()) {
876 m_debuggerCmdStr
= QString();
880 void DebuggerMainWnd::slotDebuggerStarting()
882 if (m_debugger
== 0) /* paranoia check */
885 if (m_ttyLevel
== m_debugger
->ttyLevel())
888 // shut down terminal emulations we will not need
889 switch (m_ttyLevel
) {
890 case KDebugger::ttySimpleOutputOnly
:
891 m_ttyWindow
->deactivate();
893 case KDebugger::ttyFull
:
894 m_outputTermProc
->kill();
899 m_ttyLevel
= m_debugger
->ttyLevel();
902 switch (m_ttyLevel
) {
903 case KDebugger::ttySimpleOutputOnly
:
904 ttyName
= m_ttyWindow
->activate();
906 case KDebugger::ttyFull
:
907 // create an output window
908 ttyName
= createOutputWindow();
909 TRACE(ttyName
.isEmpty() ?
910 "createOuputWindow failed" : "successfully created output window");
915 m_debugger
->setTerminal(ttyName
);
918 void DebuggerMainWnd::slotToggleBreak(const QString
& fileName
, int lineNo
,
919 const DbgAddr
& address
, bool temp
)
921 // lineNo is zero-based
922 if (m_debugger
!= 0) {
923 m_debugger
->setBreakpoint(fileName
, lineNo
, address
, temp
);
927 void DebuggerMainWnd::slotEnaDisBreak(const QString
& fileName
, int lineNo
,
928 const DbgAddr
& address
)
930 // lineNo is zero-based
931 if (m_debugger
!= 0) {
932 m_debugger
->enableDisableBreakpoint(fileName
, lineNo
, address
);
936 QString
DebuggerMainWnd::createOutputWindow()
938 // create a name for a fifo
940 fifoName
.sprintf("/tmp/kdbgttywin%05d", ::getpid());
942 // create a fifo that will pass in the tty name
943 QFile::remove(fifoName
); // remove remnants
945 if (::mkfifo(fifoName
.toLocal8Bit(), S_IRUSR
|S_IWUSR
) < 0) {
947 TRACE("mkfifo " + fifoName
+ " failed");
951 if (::mknod(fifoName
.toLocal8Bit(), S_IFIFO
| S_IRUSR
|S_IWUSR
, 0) < 0) {
953 TRACE("mknod " + fifoName
+ " failed");
959 * Spawn an xterm that in turn runs a shell script that passes us
960 * back the terminal name and then only sits and waits.
962 static const char shellScriptFmt
[] =
964 "trap \"\" INT QUIT TSTP;" /* ignore various signals */
965 "exec<&-;exec>&-;" /* close stdin and stdout */
966 "while :;do sleep 3600;done";
967 // let config file override this script
969 if (!m_outputTermKeepScript
.isEmpty()) {
970 shellScript
= m_outputTermKeepScript
;
972 shellScript
= shellScriptFmt
;
975 shellScript
.replace("%s", fifoName
);
976 TRACE("output window script is " + shellScript
);
978 QString title
= QGuiApplication::applicationDisplayName();
979 title
+= i18n(": Program output");
981 // parse the command line specified in the preferences
982 QStringList cmdParts
= m_outputTermCmdStr
.split(' ');
985 * Build the argv array. Thereby substitute special sequences:
992 { "%C", shellScript
}
995 for (QStringList::iterator i
= cmdParts
.begin(); i
!= cmdParts
.end(); ++i
)
998 for (int j
= sizeof(substitute
)/sizeof(substitute
[0])-1; j
>= 0; j
--) {
999 int pos
= str
.indexOf(substitute
[j
].seq
);
1001 str
.replace(pos
, 2, substitute
[j
].replace
);
1002 break; /* substitute only one sequence */
1007 QString tty
, pgm
= cmdParts
.takeFirst();
1009 m_outputTermProc
->start(pgm
, cmdParts
);
1010 if (m_outputTermProc
->waitForStarted())
1012 // read the ttyname from the fifo
1014 if (f
.open(QIODevice::ReadOnly
))
1016 QByteArray t
= f
.readAll();
1017 tty
= QString::fromLocal8Bit(t
, t
.size());
1022 // remove whitespace
1023 tty
= tty
.trimmed();
1024 TRACE("tty=" + tty
);
1028 // error, could not start xterm
1029 TRACE("fork failed for fifo " + fifoName
);
1030 QFile::remove(fifoName
);
1036 void DebuggerMainWnd::slotProgramStopped()
1038 // when the program stopped, move the window to the foreground
1039 if (m_popForeground
) {
1040 // unfortunately, this requires quite some force to work :-(
1041 KWindowSystem::raiseWindow(winId());
1042 KWindowSystem::forceActiveWindow(winId());
1047 void DebuggerMainWnd::intoBackground()
1049 if (m_popForeground
) {
1050 m_backTimer
.setSingleShot(true);
1051 m_backTimer
.start(m_backTimeout
);
1055 void DebuggerMainWnd::slotBackTimer()
1060 void DebuggerMainWnd::slotRecentExec(const QUrl
& url
)
1062 QString exe
= url
.toLocalFile();
1063 debugProgram(exe
, "");
1066 QString
DebuggerMainWnd::makeSourceFilter()
1069 f
= i18n("All source files") + " (" + m_sourceFilter
+ " " + m_headerFilter
+ ")";
1070 f
+= ";;" + i18n("Source files") + " (" + m_sourceFilter
+ ")";
1071 f
+= ";;" + i18n("Header files") + " (" + m_headerFilter
+ ")";
1072 f
+= ";;" + i18n("All files") + " (*)";
1077 * Pop up the context menu in the locals window
1079 void DebuggerMainWnd::slotLocalsPopup(const QPoint
& pt
)
1081 QMenu
* popup
= static_cast<QMenu
*>(factory()->container("popup_locals", this));
1085 if (popup
->isVisible()) {
1088 popup
->popup(m_localVariables
->viewport()->mapToGlobal(pt
));
1093 * Copies the currently selected item to the watch window.
1095 void DebuggerMainWnd::slotLocalsToWatch()
1097 VarTree
* item
= m_localVariables
->selectedItem();
1099 if (item
!= 0 && m_debugger
!= 0) {
1100 QString text
= item
->computeExpr();
1101 m_debugger
->addWatch(text
);
1106 * Starts editing a value in a value display
1108 void DebuggerMainWnd::slotEditValue()
1110 // does one of the value trees have the focus
1111 QWidget
* f
= QApplication::focusWidget();
1113 if (f
== m_localVariables
) {
1114 wnd
= m_localVariables
;
1115 } else if (f
== m_watches
->watchVariables()) {
1116 wnd
= m_watches
->watchVariables();
1121 if (m_localVariables
->isEditing() ||
1122 m_watches
->watchVariables()->isEditing())
1124 return; /* don't edit twice */
1127 VarTree
* expr
= wnd
->selectedItem();
1128 if (expr
!= 0 && m_debugger
!= 0 && m_debugger
->canSingleStep())
1130 TRACE("edit value");
1131 // determine the text to edit
1132 QString text
= m_debugger
->driver()->editableValue(expr
);
1133 wnd
->editValue(expr
, text
);
1137 void DebuggerMainWnd::slotFileOpen()
1139 // start browsing in the active file's directory
1140 // fall back to last used directory (executable)
1141 QString dir
= m_lastDirectory
;
1142 QString fileName
= m_filesWindow
->activeFileName();
1143 if (!fileName
.isEmpty()) {
1144 QFileInfo
fi(fileName
);
1148 fileName
= QFileDialog::getOpenFileName(this,
1149 i18n("Open"), dir
, makeSourceFilter());
1151 if (!fileName
.isEmpty())
1153 QFileInfo
fi(fileName
);
1154 m_lastDirectory
= fi
.path();
1155 m_filesWindow
->setExtraDirectory(m_lastDirectory
);
1156 m_filesWindow
->activateFile(fileName
);
1160 void DebuggerMainWnd::slotFileExe()
1162 if (m_debugger
->isIdle())
1164 // open a new executable
1165 QString executable
= QFileDialog::getOpenFileName(this,
1166 i18n("Select the Executable to Debug"),
1168 if (executable
.isEmpty())
1171 debugProgram(executable
, "");
1175 void DebuggerMainWnd::slotFileCore()
1177 if (m_debugger
->canStart())
1179 QString corefile
= QFileDialog::getOpenFileName(this,
1180 i18n("Select Core Dump"),
1182 if (!corefile
.isEmpty()) {
1183 m_debugger
->useCoreFile(corefile
, false);
1188 void DebuggerMainWnd::slotFileProgSettings()
1190 if (m_debugger
!= 0) {
1191 m_debugger
->programSettings(this);
1195 void DebuggerMainWnd::slotViewStatusbar()
1197 if (statusBar()->isVisible())
1198 statusBar()->hide();
1200 statusBar()->show();
1204 void DebuggerMainWnd::slotExecUntil()
1206 if (m_debugger
!= 0)
1210 if (m_filesWindow
->activeLine(file
, lineNo
))
1211 m_debugger
->runUntil(file
, lineNo
);
1215 void DebuggerMainWnd::slotExecAttach()
1218 ProcAttachPS
dlg(this);
1219 // seed filter with executable name
1220 QFileInfo fi
= m_debugger
->executable();
1221 dlg
.setFilterText(fi
.fileName());
1223 ProcAttach
dlg(this);
1224 dlg
.setText(m_debugger
->attachedPid());
1227 m_debugger
->attachProgram(dlg
.text());
1231 void DebuggerMainWnd::slotExecArgs()
1233 if (m_debugger
!= 0) {
1234 m_debugger
->programArgs(this);
1238 void DebuggerMainWnd::slotConfigureKeys()
1240 KShortcutsDialog::configure(actionCollection());
1243 #include "dbgmainwnd.moc"