Implemented orphaned breakpoints to simplify debuggin of dynamic modules.
[kdbg.git] / kdbg / brkpt.cpp
blob00c1ae7235626298419124e65b525ecd551351d7
1 // $Id$
3 // Copyright by Johannes Sixt
4 // This file is under GPL, the GNU General Public Licence
6 #include <kapp.h> /* i18n */
7 #include <klocale.h> /* i18n */
8 #include <kiconloader.h>
9 #include <ksimpleconfig.h>
10 #include <qdialog.h>
11 #include <qkeycode.h>
12 #include <qpainter.h>
13 #include <qlabel.h>
14 #include <qbitmap.h>
15 #include "debugger.h"
16 #include "brkpt.h"
17 #include "dbgdriver.h"
18 #include <ctype.h>
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 #include "mydebug.h"
25 class BreakpointItem : public QListViewItem, public Breakpoint
27 public:
28 BreakpointItem(QListView* list, const Breakpoint& bp);
29 void updateFrom(const Breakpoint& bp);
30 void display(); /* sets icon and visible texts */
31 bool enabled() const { return Breakpoint::enabled; }
35 BreakpointTable::BreakpointTable(QWidget* parent, const char* name) :
36 QWidget(parent, name),
37 m_debugger(0),
38 m_bpEdit(this, "bpedit"),
39 m_list(this, "bptable"),
40 m_btAddBP(this, "addbp"),
41 m_btAddWP(this, "addwp"),
42 m_btRemove(this, "remove"),
43 m_btEnaDis(this, "enadis"),
44 m_btViewCode(this, "view"),
45 m_btConditional(this, "conditional"),
46 m_layout(this, 8),
47 m_listandedit(8),
48 m_buttons(8)
50 m_bpEdit.setMinimumSize(m_bpEdit.sizeHint());
51 connect(&m_bpEdit, SIGNAL(returnPressed()), this, SLOT(addBP()));
53 initListAndIcons();
54 connect(&m_list, SIGNAL(currentChanged(QListViewItem*)), SLOT(updateUI()));
55 // double click on item is same as View code
56 connect(&m_list, SIGNAL(doubleClicked(QListViewItem*)), this, SLOT(viewBP()));
58 // need mouse button events
59 m_list.viewport()->installEventFilter(this);
61 m_btAddBP.setText(i18n("Add &Breakpoint"));
62 m_btAddBP.setMinimumSize(m_btAddBP.sizeHint());
63 connect(&m_btAddBP, SIGNAL(clicked()), this, SLOT(addBP()));
65 m_btAddWP.setText(i18n("Add &Watchpoint"));
66 m_btAddWP.setMinimumSize(m_btAddWP.sizeHint());
67 connect(&m_btAddWP, SIGNAL(clicked()), this, SLOT(addWP()));
69 m_btRemove.setText(i18n("&Remove"));
70 m_btRemove.setMinimumSize(m_btRemove.sizeHint());
71 connect(&m_btRemove, SIGNAL(clicked()), this, SLOT(removeBP()));
73 // the Enable/Disable button changes its label
74 m_btEnaDis.setText(i18n("&Disable"));
75 // make a dummy button to get the size of the alternate label
77 QSize size = m_btEnaDis.sizeHint();
78 QPushButton dummy(this);
79 dummy.setText(i18n("&Enable"));
80 QSize sizeAlt = dummy.sizeHint();
81 if (sizeAlt.width() > size.width())
82 size.setWidth(sizeAlt.width());
83 if (sizeAlt.height() > size.height())
84 size.setHeight(sizeAlt.height());
85 m_btEnaDis.setMinimumSize(size);
87 connect(&m_btEnaDis, SIGNAL(clicked()), this, SLOT(enadisBP()));
89 m_btViewCode.setText(i18n("&View Code"));
90 m_btViewCode.setMinimumSize(m_btViewCode.sizeHint());
91 connect(&m_btViewCode, SIGNAL(clicked()), this, SLOT(viewBP()));
93 m_btConditional.setText(i18n("&Conditional..."));
94 m_btConditional.setMinimumSize(m_btConditional.sizeHint());
95 connect(&m_btConditional, SIGNAL(clicked()), this, SLOT(conditionalBP()));
97 m_layout.addLayout(&m_listandedit, 10);
98 m_layout.addLayout(&m_buttons);
99 m_listandedit.addWidget(&m_bpEdit);
100 m_listandedit.addWidget(&m_list, 10);
101 m_buttons.addWidget(&m_btAddBP);
102 m_buttons.addWidget(&m_btAddWP);
103 m_buttons.addWidget(&m_btRemove);
104 m_buttons.addWidget(&m_btEnaDis);
105 m_buttons.addWidget(&m_btViewCode);
106 m_buttons.addWidget(&m_btConditional);
107 m_buttons.addStretch(10);
109 m_layout.activate();
111 resize(350, 300);
113 m_bpEdit.setFocus();
116 BreakpointTable::~BreakpointTable()
120 void BreakpointTable::updateBreakList()
122 QList<BreakpointItem> deletedItems;
124 for (QListViewItem* it = m_list.firstChild(); it != 0; it = it->nextSibling()) {
125 deletedItems.append(static_cast<BreakpointItem*>(it));
128 // get the new list
129 for (int i = m_debugger->numBreakpoints()-1; i >= 0; i--) {
130 const Breakpoint* bp = m_debugger->breakpoint(i);
131 // look up this item
132 for (BreakpointItem* oldbp = deletedItems.first(); oldbp != 0;
133 oldbp = deletedItems.next())
135 if (oldbp->id == bp->id) {
136 oldbp->updateFrom(*bp);
137 deletedItems.take(); /* don't delete */
138 goto nextItem;
141 // not in the list; add it
142 new BreakpointItem(&m_list, *bp);
143 nextItem:;
146 // delete all untouched breakpoints
147 deletedItems.setAutoDelete(true);
150 BreakpointItem::BreakpointItem(QListView* list, const Breakpoint& bp) :
151 QListViewItem(list),
152 Breakpoint(bp)
154 display();
157 void BreakpointItem::updateFrom(const Breakpoint& bp)
159 Breakpoint::operator=(bp); /* assign new values */
160 display();
163 void BreakpointTable::addBP()
165 // set a breakpoint at the specified text
166 QString bpText = m_bpEdit.text();
167 bpText = bpText.stripWhiteSpace();
168 if (m_debugger->isReady())
170 Breakpoint* bp = new Breakpoint;
171 bp->text = bpText;
173 m_debugger->setBreakpoint(bp, false);
177 void BreakpointTable::addWP()
179 // set a watchpoint for the specified expression
180 QString wpExpr = m_bpEdit.text();
181 wpExpr = wpExpr.stripWhiteSpace();
182 if (m_debugger->isReady()) {
183 Breakpoint* bp = new Breakpoint;
184 bp->type = Breakpoint::watchpoint;
185 bp->text = wpExpr;
187 m_debugger->setBreakpoint(bp, false);
191 void BreakpointTable::removeBP()
193 BreakpointItem* bp = static_cast<BreakpointItem*>(m_list.currentItem());
194 if (bp == 0)
195 return;
197 Breakpoint* brk = m_debugger->breakpointById(bp->id);
198 if (brk != 0) {
199 m_debugger->deleteBreakpoint(brk);
200 // note that both brk and bp may be deleted by now
201 // (if bp was an orphaned breakpoint)
205 void BreakpointTable::enadisBP()
207 BreakpointItem* bp = static_cast<BreakpointItem*>(m_list.currentItem());
208 if (bp == 0)
209 return;
211 Breakpoint* brk = m_debugger->breakpointById(bp->id);
212 if (brk != 0) {
213 m_debugger->enableDisableBreakpoint(brk);
217 void BreakpointTable::viewBP()
219 BreakpointItem* bp = static_cast<BreakpointItem*>(m_list.currentItem());
220 if (bp == 0)
221 return;
223 emit activateFileLine(bp->fileName, bp->lineNo, bp->address);
226 void BreakpointTable::updateUI()
228 bool enableChkpt = m_debugger->canChangeBreakpoints();
229 m_btAddBP.setEnabled(enableChkpt);
230 m_btAddWP.setEnabled(enableChkpt);
232 BreakpointItem* bp = static_cast<BreakpointItem*>(m_list.currentItem());
233 m_btViewCode.setEnabled(bp != 0);
235 if (bp == 0) {
236 enableChkpt = false;
237 } else {
238 if (bp->enabled()) {
239 m_btEnaDis.setText(i18n("&Disable"));
240 } else {
241 m_btEnaDis.setText(i18n("&Enable"));
244 m_btRemove.setEnabled(enableChkpt);
245 m_btEnaDis.setEnabled(enableChkpt);
246 m_btConditional.setEnabled(enableChkpt);
249 bool BreakpointTable::eventFilter(QObject* ob, QEvent* ev)
251 if (ev->type() == QEvent::MouseButtonPress)
253 QMouseEvent* mev = static_cast<QMouseEvent*>(ev);
254 if (mev->button() == MidButton) {
255 // enable or disable the clicked-on item
256 BreakpointItem* bp =
257 static_cast<BreakpointItem*>(m_list.itemAt(mev->pos()));
258 if (bp != 0)
260 Breakpoint* brk = m_debugger->breakpointById(bp->id);
261 if (brk != 0) {
262 m_debugger->enableDisableBreakpoint(brk);
265 return true;
268 return QWidget::eventFilter(ob, ev);
271 class ConditionalDlg : public QDialog
273 public:
274 ConditionalDlg(QWidget* parent);
275 ~ConditionalDlg();
277 void setCondition(const char* text) { m_condition.setText(text); }
278 const char* condition() { return m_condition.text(); }
279 void setIgnoreCount(uint count);
280 uint ignoreCount();
282 protected:
283 QLabel m_conditionLabel;
284 QLineEdit m_condition;
285 QLabel m_ignoreLabel;
286 QLineEdit m_ignoreCount;
287 QPushButton m_buttonOK;
288 QPushButton m_buttonCancel;
289 QVBoxLayout m_layout;
290 QGridLayout m_inputs;
291 QHBoxLayout m_buttons;
294 void BreakpointTable::conditionalBP()
296 BreakpointItem* bp = static_cast<BreakpointItem*>(m_list.currentItem());
297 if (bp == 0)
298 return;
301 * Important: we must not keep a pointer to the Breakpoint around,
302 * since it may vanish while the modal dialog is open through other
303 * user interactions (like clicking at the breakpoint in the source
304 * window)!
306 int id = bp->id;
308 ConditionalDlg dlg(this);
309 dlg.setCondition(bp->condition);
310 dlg.setIgnoreCount(bp->ignoreCount);
312 if (dlg.exec() != QDialog::Accepted)
313 return;
315 QString conditionInput = dlg.condition();
316 int ignoreCount = dlg.ignoreCount();
317 updateBreakpointCondition(id, conditionInput, ignoreCount);
320 void BreakpointTable::updateBreakpointCondition(int id,
321 const QString& condition,
322 int ignoreCount)
324 Breakpoint* brk = m_debugger->breakpointById(id);
325 if (brk != 0) {
326 m_debugger->conditionalBreakpoint(brk, condition, ignoreCount);
331 void BreakpointTable::initListAndIcons()
333 m_list.addColumn(i18n("Location"), 220);
334 m_list.addColumn(i18n("Address"), 65);
335 m_list.addColumn(i18n("Hits"), 30);
336 m_list.addColumn(i18n("Ignore"), 30);
337 m_list.addColumn(i18n("Condition"), 200);
339 m_list.setMinimumSize(200, 100);
341 m_list.setSorting(-1);
343 // add pixmaps
344 QPixmap brkena = BarIcon("brkena.xpm");
345 QPixmap brkdis = BarIcon("brkdis.xpm");
346 QPixmap watchena = BarIcon("watchena.xpm");
347 QPixmap watchdis = BarIcon("watchdis.xpm");
348 QPixmap brktmp = BarIcon("brktmp.xpm");
349 QPixmap brkcond = BarIcon("brkcond.xpm");
350 QPixmap brkorph = BarIcon("brkorph.xpm");
353 * There are 32 different pixmaps: The basic enabled or disabled
354 * breakpoint, plus an optional overlaid brktmp icon plus an optional
355 * overlaid brkcond icon, plus an optional overlaid brkorph icon. Then
356 * the same sequence for watchpoints.
358 m_icons.setSize(32);
359 QPixmap canvas(16,16);
361 for (int i = 0; i < 32; i++) {
363 QPainter p(&canvas);
364 // clear canvas
365 p.fillRect(0,0, canvas.width(),canvas.height(), cyan);
366 // basic icon
367 if (i & 1) {
368 p.drawPixmap(1,1, (i & 8) ? watchena : brkena);
369 } else {
370 p.drawPixmap(1,1, (i & 8) ? watchdis : brkdis);
372 // temporary overlay
373 if (i & 2) {
374 p.drawPixmap(1,1, brktmp);
376 // conditional overlay
377 if (i & 4) {
378 p.drawPixmap(1,1, brkcond);
380 // orphan overlay
381 if (i & 16) {
382 p.drawPixmap(1,1, brkorph);
385 canvas.setMask(canvas.createHeuristicMask());
386 m_icons[i] = canvas;
390 void BreakpointItem::display()
392 BreakpointTable* lb = static_cast<BreakpointTable*>(listView()->parent());
394 /* breakpoint icon code; keep order the same as in BreakpointTable::initListAndIcons */
395 int code = enabled() ? 1 : 0;
396 if (temporary)
397 code += 2;
398 if (!condition.isEmpty() || ignoreCount > 0)
399 code += 4;
400 if (type == watchpoint)
401 code += 8;
402 if (isOrphaned())
403 code += 16;
404 setPixmap(0, lb->m_icons[code]);
406 // more breakpoint info
407 if (!location.isEmpty()) {
408 setText(0, location);
409 } else if (!Breakpoint::text.isEmpty()) {
410 setText(0, Breakpoint::text);
411 } else if (!fileName.isEmpty()) {
412 // use only the file name portion
413 QString file = fileName;
414 int slash = file.findRev('/');
415 if (slash >= 0) {
416 file = file.mid(slash+1);
418 // correct zero-based line-numbers
419 setText(0, file + ":" + QString::number(lineNo+1));
420 } else {
421 setText(0, "*" + address.asString());
424 int c = 0;
425 setText(++c, address.asString());
426 QString tmp;
427 if (hitCount == 0) {
428 setText(++c, QString());
429 } else {
430 tmp.setNum(hitCount);
431 setText(++c, tmp);
433 if (ignoreCount == 0) {
434 setText(++c, QString());
435 } else {
436 tmp.setNum(ignoreCount);
437 setText(++c, tmp);
439 if (condition.isEmpty()) {
440 setText(++c, QString());
441 } else {
442 setText(++c, condition);
447 ConditionalDlg::ConditionalDlg(QWidget* parent) :
448 QDialog(parent, "conditional", true),
449 m_conditionLabel(this, "condLabel"),
450 m_condition(this, "condition"),
451 m_ignoreLabel(this, "ignoreLabel"),
452 m_ignoreCount(this, "ignoreCount"),
453 m_buttonOK(this, "ok"),
454 m_buttonCancel(this, "cancel"),
455 m_layout(this, 10),
456 m_inputs(2, 2, 10),
457 m_buttons(4)
459 QString title = kapp->caption();
460 title += i18n(": Conditional breakpoint");
461 setCaption(title);
463 m_conditionLabel.setText(i18n("&Condition:"));
464 m_conditionLabel.setMinimumSize(m_conditionLabel.sizeHint());
465 m_ignoreLabel.setText(i18n("Ignore &next hits:"));
466 m_ignoreLabel.setMinimumSize(m_ignoreLabel.sizeHint());
468 m_condition.setMinimumSize(150, 24);
469 m_condition.setMaxLength(10000);
470 m_condition.setFrame(true);
471 m_ignoreCount.setMinimumSize(150, 24);
472 m_ignoreCount.setMaxLength(10000);
473 m_ignoreCount.setFrame(true);
475 m_conditionLabel.setBuddy(&m_condition);
476 m_ignoreLabel.setBuddy(&m_ignoreCount);
478 m_buttonOK.setMinimumSize(100, 30);
479 connect(&m_buttonOK, SIGNAL(clicked()), SLOT(accept()));
480 m_buttonOK.setText(i18n("OK"));
481 m_buttonOK.setDefault(true);
483 m_buttonCancel.setMinimumSize(100, 30);
484 connect(&m_buttonCancel, SIGNAL(clicked()), SLOT(reject()));
485 m_buttonCancel.setText(i18n("Cancel"));
487 m_layout.addLayout(&m_inputs);
488 m_inputs.addWidget(&m_conditionLabel, 0, 0);
489 m_inputs.addWidget(&m_condition, 0, 1);
490 m_inputs.addWidget(&m_ignoreLabel, 1, 0);
491 m_inputs.addWidget(&m_ignoreCount, 1, 1);
492 m_inputs.setColStretch(1, 10);
493 m_layout.addLayout(&m_buttons);
494 m_layout.addStretch(10);
495 m_buttons.addStretch(10);
496 m_buttons.addWidget(&m_buttonOK);
497 m_buttons.addSpacing(40);
498 m_buttons.addWidget(&m_buttonCancel);
499 m_buttons.addStretch(10);
501 m_layout.activate();
503 m_condition.setFocus();
504 resize(400, 100);
507 ConditionalDlg::~ConditionalDlg()
511 uint ConditionalDlg::ignoreCount()
513 bool ok;
514 QString input = m_ignoreCount.text();
515 uint result = input.toUInt(&ok);
516 return ok ? result : 0;
519 void ConditionalDlg::setIgnoreCount(uint count)
521 QString text;
522 // set empty if ignore count is zero
523 if (count > 0) {
524 text.setNum(count);
526 m_ignoreCount.setText(text);
530 #include "brkpt.moc"