Make the public breakpoint API simpler to use for callers.
[kdbg.git] / kdbg / brkpt.cpp
blob239daa97af0156009b5418be39ae566012292b70
1 /*
2 * Copyright Johannes Sixt
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
5 */
7 #include <kapplication.h>
8 #include <klocale.h> /* i18n */
9 #include <kiconloader.h>
10 #include <ksimpleconfig.h>
11 #include <qdialog.h>
12 #include <qkeycode.h>
13 #include <qpainter.h>
14 #include <qlabel.h>
15 #include <qbitmap.h>
16 #include "debugger.h"
17 #include "brkpt.h"
18 #include "dbgdriver.h"
19 #include <ctype.h>
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include "mydebug.h"
26 class BreakpointItem : public QListViewItem, public Breakpoint
28 public:
29 BreakpointItem(QListView* list, const Breakpoint& bp);
30 void updateFrom(const Breakpoint& bp);
31 void display(); /* sets icon and visible texts */
32 bool enabled() const { return Breakpoint::enabled; }
36 BreakpointTable::BreakpointTable(QWidget* parent, const char* name) :
37 QWidget(parent, name),
38 m_debugger(0),
39 m_bpEdit(this, "bpedit"),
40 m_list(this, "bptable"),
41 m_btAddBP(this, "addbp"),
42 m_btAddWP(this, "addwp"),
43 m_btRemove(this, "remove"),
44 m_btEnaDis(this, "enadis"),
45 m_btViewCode(this, "view"),
46 m_btConditional(this, "conditional"),
47 m_layout(this, 8),
48 m_listandedit(8),
49 m_buttons(8)
51 m_bpEdit.setMinimumSize(m_bpEdit.sizeHint());
52 connect(&m_bpEdit, SIGNAL(returnPressed()), this, SLOT(addBP()));
54 initListAndIcons();
55 connect(&m_list, SIGNAL(currentChanged(QListViewItem*)), SLOT(updateUI()));
56 // double click on item is same as View code
57 connect(&m_list, SIGNAL(doubleClicked(QListViewItem*)), this, SLOT(viewBP()));
59 // need mouse button events
60 m_list.viewport()->installEventFilter(this);
62 m_btAddBP.setText(i18n("Add &Breakpoint"));
63 m_btAddBP.setMinimumSize(m_btAddBP.sizeHint());
64 connect(&m_btAddBP, SIGNAL(clicked()), this, SLOT(addBP()));
66 m_btAddWP.setText(i18n("Add &Watchpoint"));
67 m_btAddWP.setMinimumSize(m_btAddWP.sizeHint());
68 connect(&m_btAddWP, SIGNAL(clicked()), this, SLOT(addWP()));
70 m_btRemove.setText(i18n("&Remove"));
71 m_btRemove.setMinimumSize(m_btRemove.sizeHint());
72 connect(&m_btRemove, SIGNAL(clicked()), this, SLOT(removeBP()));
74 // the Enable/Disable button changes its label
75 m_btEnaDis.setText(i18n("&Disable"));
76 // make a dummy button to get the size of the alternate label
78 QSize size = m_btEnaDis.sizeHint();
79 QPushButton dummy(this);
80 dummy.setText(i18n("&Enable"));
81 QSize sizeAlt = dummy.sizeHint();
82 if (sizeAlt.width() > size.width())
83 size.setWidth(sizeAlt.width());
84 if (sizeAlt.height() > size.height())
85 size.setHeight(sizeAlt.height());
86 m_btEnaDis.setMinimumSize(size);
88 connect(&m_btEnaDis, SIGNAL(clicked()), this, SLOT(enadisBP()));
90 m_btViewCode.setText(i18n("&View Code"));
91 m_btViewCode.setMinimumSize(m_btViewCode.sizeHint());
92 connect(&m_btViewCode, SIGNAL(clicked()), this, SLOT(viewBP()));
94 m_btConditional.setText(i18n("&Conditional..."));
95 m_btConditional.setMinimumSize(m_btConditional.sizeHint());
96 connect(&m_btConditional, SIGNAL(clicked()), this, SLOT(conditionalBP()));
98 m_layout.addLayout(&m_listandedit, 10);
99 m_layout.addLayout(&m_buttons);
100 m_listandedit.addWidget(&m_bpEdit);
101 m_listandedit.addWidget(&m_list, 10);
102 m_buttons.addWidget(&m_btAddBP);
103 m_buttons.addWidget(&m_btAddWP);
104 m_buttons.addWidget(&m_btRemove);
105 m_buttons.addWidget(&m_btEnaDis);
106 m_buttons.addWidget(&m_btViewCode);
107 m_buttons.addWidget(&m_btConditional);
108 m_buttons.addStretch(10);
110 m_layout.activate();
112 resize(350, 300);
114 m_bpEdit.setFocus();
117 BreakpointTable::~BreakpointTable()
121 void BreakpointTable::updateBreakList()
123 QList<BreakpointItem> deletedItems;
125 for (QListViewItem* it = m_list.firstChild(); it != 0; it = it->nextSibling()) {
126 deletedItems.append(static_cast<BreakpointItem*>(it));
129 // get the new list
130 for (int i = m_debugger->numBreakpoints()-1; i >= 0; i--) {
131 const Breakpoint* bp = m_debugger->breakpoint(i);
132 // look up this item
133 for (BreakpointItem* oldbp = deletedItems.first(); oldbp != 0;
134 oldbp = deletedItems.next())
136 if (oldbp->id == bp->id) {
137 oldbp->updateFrom(*bp);
138 deletedItems.take(); /* don't delete */
139 goto nextItem;
142 // not in the list; add it
143 new BreakpointItem(&m_list, *bp);
144 nextItem:;
147 // delete all untouched breakpoints
148 deletedItems.setAutoDelete(true);
151 BreakpointItem::BreakpointItem(QListView* list, const Breakpoint& bp) :
152 QListViewItem(list),
153 Breakpoint(bp)
155 display();
158 void BreakpointItem::updateFrom(const Breakpoint& bp)
160 Breakpoint::operator=(bp); /* assign new values */
161 display();
164 void BreakpointTable::addBP()
166 // set a breakpoint at the specified text
167 QString bpText = m_bpEdit.text();
168 bpText = bpText.stripWhiteSpace();
169 if (m_debugger->isReady())
171 Breakpoint* bp = new Breakpoint;
172 bp->text = bpText;
174 m_debugger->setBreakpoint(bp, false);
178 void BreakpointTable::addWP()
180 // set a watchpoint for the specified expression
181 QString wpExpr = m_bpEdit.text();
182 wpExpr = wpExpr.stripWhiteSpace();
183 if (m_debugger->isReady()) {
184 Breakpoint* bp = new Breakpoint;
185 bp->type = Breakpoint::watchpoint;
186 bp->text = wpExpr;
188 m_debugger->setBreakpoint(bp, false);
192 void BreakpointTable::removeBP()
194 BreakpointItem* bp = static_cast<BreakpointItem*>(m_list.currentItem());
195 if (bp != 0) {
196 m_debugger->deleteBreakpoint(bp->id);
197 // note that bp may be deleted by now
198 // (if bp was an orphaned breakpoint)
202 void BreakpointTable::enadisBP()
204 BreakpointItem* bp = static_cast<BreakpointItem*>(m_list.currentItem());
205 if (bp != 0) {
206 m_debugger->enableDisableBreakpoint(bp->id);
210 void BreakpointTable::viewBP()
212 BreakpointItem* bp = static_cast<BreakpointItem*>(m_list.currentItem());
213 if (bp == 0)
214 return;
216 emit activateFileLine(bp->fileName, bp->lineNo, bp->address);
219 void BreakpointTable::updateUI()
221 bool enableChkpt = m_debugger->canChangeBreakpoints();
222 m_btAddBP.setEnabled(enableChkpt);
223 m_btAddWP.setEnabled(enableChkpt);
225 BreakpointItem* bp = static_cast<BreakpointItem*>(m_list.currentItem());
226 m_btViewCode.setEnabled(bp != 0);
228 if (bp == 0) {
229 enableChkpt = false;
230 } else {
231 if (bp->enabled()) {
232 m_btEnaDis.setText(i18n("&Disable"));
233 } else {
234 m_btEnaDis.setText(i18n("&Enable"));
237 m_btRemove.setEnabled(enableChkpt);
238 m_btEnaDis.setEnabled(enableChkpt);
239 m_btConditional.setEnabled(enableChkpt);
242 bool BreakpointTable::eventFilter(QObject* ob, QEvent* ev)
244 if (ev->type() == QEvent::MouseButtonPress)
246 QMouseEvent* mev = static_cast<QMouseEvent*>(ev);
247 if (mev->button() == MidButton) {
248 // enable or disable the clicked-on item
249 BreakpointItem* bp =
250 static_cast<BreakpointItem*>(m_list.itemAt(mev->pos()));
251 if (bp != 0)
253 m_debugger->enableDisableBreakpoint(bp->id);
255 return true;
258 return QWidget::eventFilter(ob, ev);
261 class ConditionalDlg : public QDialog
263 public:
264 ConditionalDlg(QWidget* parent);
265 ~ConditionalDlg();
267 void setCondition(const char* text) { m_condition.setText(text); }
268 const char* condition() { return m_condition.text(); }
269 void setIgnoreCount(uint count);
270 uint ignoreCount();
272 protected:
273 QLabel m_conditionLabel;
274 QLineEdit m_condition;
275 QLabel m_ignoreLabel;
276 QLineEdit m_ignoreCount;
277 QPushButton m_buttonOK;
278 QPushButton m_buttonCancel;
279 QVBoxLayout m_layout;
280 QGridLayout m_inputs;
281 QHBoxLayout m_buttons;
284 void BreakpointTable::conditionalBP()
286 BreakpointItem* bp = static_cast<BreakpointItem*>(m_list.currentItem());
287 if (bp == 0)
288 return;
291 * Important: we must not keep a pointer to the Breakpoint around,
292 * since it may vanish while the modal dialog is open through other
293 * user interactions (like clicking at the breakpoint in the source
294 * window)!
296 int id = bp->id;
298 ConditionalDlg dlg(this);
299 dlg.setCondition(bp->condition);
300 dlg.setIgnoreCount(bp->ignoreCount);
302 if (dlg.exec() != QDialog::Accepted)
303 return;
305 QString conditionInput = dlg.condition();
306 int ignoreCount = dlg.ignoreCount();
307 m_debugger->conditionalBreakpoint(id, conditionInput, ignoreCount);
311 void BreakpointTable::initListAndIcons()
313 m_list.addColumn(i18n("Location"), 220);
314 m_list.addColumn(i18n("Address"), 65);
315 m_list.addColumn(i18n("Hits"), 30);
316 m_list.addColumn(i18n("Ignore"), 30);
317 m_list.addColumn(i18n("Condition"), 200);
319 m_list.setMinimumSize(200, 100);
321 m_list.setSorting(-1);
323 // add pixmaps
324 QPixmap brkena = UserIcon("brkena.xpm");
325 QPixmap brkdis = UserIcon("brkdis.xpm");
326 QPixmap watchena = UserIcon("watchena.xpm");
327 QPixmap watchdis = UserIcon("watchdis.xpm");
328 QPixmap brktmp = UserIcon("brktmp.xpm");
329 QPixmap brkcond = UserIcon("brkcond.xpm");
330 QPixmap brkorph = UserIcon("brkorph.xpm");
333 * There are 32 different pixmaps: The basic enabled or disabled
334 * breakpoint, plus an optional overlaid brktmp icon plus an optional
335 * overlaid brkcond icon, plus an optional overlaid brkorph icon. Then
336 * the same sequence for watchpoints.
338 m_icons.resize(32);
339 QPixmap canvas(16,16);
341 for (int i = 0; i < 32; i++) {
343 QPainter p(&canvas);
344 // clear canvas
345 p.fillRect(0,0, canvas.width(),canvas.height(), cyan);
346 // basic icon
347 if (i & 1) {
348 p.drawPixmap(1,1, (i & 8) ? watchena : brkena);
349 } else {
350 p.drawPixmap(1,1, (i & 8) ? watchdis : brkdis);
352 // temporary overlay
353 if (i & 2) {
354 p.drawPixmap(1,1, brktmp);
356 // conditional overlay
357 if (i & 4) {
358 p.drawPixmap(1,1, brkcond);
360 // orphan overlay
361 if (i & 16) {
362 p.drawPixmap(1,1, brkorph);
365 canvas.setMask(canvas.createHeuristicMask());
366 m_icons[i] = canvas;
370 void BreakpointItem::display()
372 BreakpointTable* lb = static_cast<BreakpointTable*>(listView()->parent());
374 /* breakpoint icon code; keep order the same as in BreakpointTable::initListAndIcons */
375 int code = enabled() ? 1 : 0;
376 if (temporary)
377 code += 2;
378 if (!condition.isEmpty() || ignoreCount > 0)
379 code += 4;
380 if (type == watchpoint)
381 code += 8;
382 if (isOrphaned())
383 code += 16;
384 setPixmap(0, lb->m_icons[code]);
386 // more breakpoint info
387 if (!location.isEmpty()) {
388 setText(0, location);
389 } else if (!Breakpoint::text.isEmpty()) {
390 setText(0, Breakpoint::text);
391 } else if (!fileName.isEmpty()) {
392 // use only the file name portion
393 QString file = fileName;
394 int slash = file.findRev('/');
395 if (slash >= 0) {
396 file = file.mid(slash+1);
398 // correct zero-based line-numbers
399 setText(0, file + ":" + QString::number(lineNo+1));
400 } else {
401 setText(0, "*" + address.asString());
404 int c = 0;
405 setText(++c, address.asString());
406 QString tmp;
407 if (hitCount == 0) {
408 setText(++c, QString());
409 } else {
410 tmp.setNum(hitCount);
411 setText(++c, tmp);
413 if (ignoreCount == 0) {
414 setText(++c, QString());
415 } else {
416 tmp.setNum(ignoreCount);
417 setText(++c, tmp);
419 if (condition.isEmpty()) {
420 setText(++c, QString());
421 } else {
422 setText(++c, condition);
427 ConditionalDlg::ConditionalDlg(QWidget* parent) :
428 QDialog(parent, "conditional", true),
429 m_conditionLabel(this, "condLabel"),
430 m_condition(this, "condition"),
431 m_ignoreLabel(this, "ignoreLabel"),
432 m_ignoreCount(this, "ignoreCount"),
433 m_buttonOK(this, "ok"),
434 m_buttonCancel(this, "cancel"),
435 m_layout(this, 10),
436 m_inputs(2, 2, 10),
437 m_buttons(4)
439 QString title = kapp->caption();
440 title += i18n(": Conditional breakpoint");
441 setCaption(title);
443 m_conditionLabel.setText(i18n("&Condition:"));
444 m_conditionLabel.setMinimumSize(m_conditionLabel.sizeHint());
445 m_ignoreLabel.setText(i18n("Ignore &next hits:"));
446 m_ignoreLabel.setMinimumSize(m_ignoreLabel.sizeHint());
448 m_condition.setMinimumSize(150, 24);
449 m_condition.setMaxLength(10000);
450 m_condition.setFrame(true);
451 m_ignoreCount.setMinimumSize(150, 24);
452 m_ignoreCount.setMaxLength(10000);
453 m_ignoreCount.setFrame(true);
455 m_conditionLabel.setBuddy(&m_condition);
456 m_ignoreLabel.setBuddy(&m_ignoreCount);
458 m_buttonOK.setMinimumSize(100, 30);
459 connect(&m_buttonOK, SIGNAL(clicked()), SLOT(accept()));
460 m_buttonOK.setText(i18n("OK"));
461 m_buttonOK.setDefault(true);
463 m_buttonCancel.setMinimumSize(100, 30);
464 connect(&m_buttonCancel, SIGNAL(clicked()), SLOT(reject()));
465 m_buttonCancel.setText(i18n("Cancel"));
467 m_layout.addLayout(&m_inputs);
468 m_inputs.addWidget(&m_conditionLabel, 0, 0);
469 m_inputs.addWidget(&m_condition, 0, 1);
470 m_inputs.addWidget(&m_ignoreLabel, 1, 0);
471 m_inputs.addWidget(&m_ignoreCount, 1, 1);
472 m_inputs.setColStretch(1, 10);
473 m_layout.addLayout(&m_buttons);
474 m_layout.addStretch(10);
475 m_buttons.addStretch(10);
476 m_buttons.addWidget(&m_buttonOK);
477 m_buttons.addSpacing(40);
478 m_buttons.addWidget(&m_buttonCancel);
479 m_buttons.addStretch(10);
481 m_layout.activate();
483 m_condition.setFocus();
484 resize(400, 100);
487 ConditionalDlg::~ConditionalDlg()
491 uint ConditionalDlg::ignoreCount()
493 bool ok;
494 QString input = m_ignoreCount.text();
495 uint result = input.toUInt(&ok);
496 return ok ? result : 0;
499 void ConditionalDlg::setIgnoreCount(uint count)
501 QString text;
502 // set empty if ignore count is zero
503 if (count > 0) {
504 text.setNum(count);
506 m_ignoreCount.setText(text);
510 #include "brkpt.moc"