summaryrefslogtreecommitdiffstats
path: root/languages/cpp/debugger/gdbbreakpointwidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'languages/cpp/debugger/gdbbreakpointwidget.cpp')
-rw-r--r--languages/cpp/debugger/gdbbreakpointwidget.cpp1262
1 files changed, 1262 insertions, 0 deletions
diff --git a/languages/cpp/debugger/gdbbreakpointwidget.cpp b/languages/cpp/debugger/gdbbreakpointwidget.cpp
new file mode 100644
index 00000000..f9a83d6c
--- /dev/null
+++ b/languages/cpp/debugger/gdbbreakpointwidget.cpp
@@ -0,0 +1,1262 @@
+/***************************************************************************
+ begin : Tue May 13 2003
+ copyright : (C) 2003 by John Birch
+ email : jbb@kdevelop.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "gdbbreakpointwidget.h"
+#include "gdbtable.h"
+#include "debuggertracingdialog.h"
+#include "gdbcommand.h"
+#include "gdbcontroller.h"
+
+#include "breakpoint.h"
+#include "domutil.h"
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kurl.h>
+#include <kmessagebox.h>
+
+#include <qvbuttongroup.h>
+#include <qfileinfo.h>
+#include <qheader.h>
+#include <qtable.h>
+#include <qtoolbutton.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+#include <qvbox.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qcheckbox.h>
+
+#include <stdlib.h>
+#include <ctype.h>
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+namespace GDBDebugger
+{
+
+enum Column {
+ Control = 0,
+ Enable = 1,
+ Type = 2,
+ Status = 3,
+ Location = 4,
+ Condition = 5,
+ IgnoreCount = 6,
+ Hits = 7,
+ Tracing = 8
+};
+
+
+#define numCols 9
+
+enum BW_ITEMS { BW_ITEM_Show, BW_ITEM_Edit, BW_ITEM_Disable, BW_ITEM_Delete,
+ BW_ITEM_DisableAll, BW_ITEM_EnableAll, BW_ITEM_DeleteAll};
+
+static int m_activeFlag = 0;
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class BreakpointTableRow : public QTableItem
+{
+public:
+
+ BreakpointTableRow(QTable* table, EditType editType, Breakpoint* bp);
+ ~BreakpointTableRow();
+
+ bool match (Breakpoint* bp) const;
+ void reset ();
+ void setRow();
+
+ Breakpoint* breakpoint() { return m_breakpoint; }
+
+private:
+ void appendEmptyRow();
+
+private:
+ Breakpoint* m_breakpoint;
+};
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+BreakpointTableRow::BreakpointTableRow(QTable* parent, EditType editType,
+ Breakpoint* bp) :
+ QTableItem(parent, editType, ""),
+ m_breakpoint(bp)
+{
+ appendEmptyRow();
+ setRow();
+}
+
+/***************************************************************************/
+
+BreakpointTableRow::~BreakpointTableRow()
+{
+ m_breakpoint->deleteLater();
+}
+
+/***************************************************************************/
+
+bool BreakpointTableRow::match(Breakpoint* breakpoint) const
+{
+ return m_breakpoint->match(breakpoint);
+}
+
+/***************************************************************************/
+
+void BreakpointTableRow::reset()
+{
+ m_breakpoint->reset();
+ setRow();
+}
+
+/***************************************************************************/
+
+void BreakpointTableRow::appendEmptyRow()
+{
+ int row = table()->numRows();
+ table()->setNumRows(row+1);
+
+ table()->setItem(row, Control, this);
+
+ QCheckTableItem* cti = new QCheckTableItem( table(), "");
+ table()->setItem(row, Enable, cti);
+
+ ComplexEditCell* act = new ComplexEditCell(table());
+ table()->setItem(row, Tracing, act);
+ QObject::connect(act, SIGNAL(edit(QTableItem*)),
+ table()->parent(), SLOT(editTracing(QTableItem*)));
+}
+
+/***************************************************************************/
+
+void BreakpointTableRow::setRow()
+{
+ if ( m_breakpoint )
+ {
+ QTableItem *item = table()->item ( row(), Enable );
+ Q_ASSERT(item->rtti() == 2);
+ ((QCheckTableItem*)item)->setChecked(m_breakpoint->isEnabled());
+
+ QString status=m_breakpoint->statusDisplay(m_activeFlag);
+
+ table()->setText(row(), Status, status);
+ table()->setText(row(), Condition, m_breakpoint->conditional());
+ table()->setText(row(), IgnoreCount, QString::number(m_breakpoint->ignoreCount() ));
+ table()->setText(row(), Hits, QString::number(m_breakpoint->hits() ));
+
+ QString displayType = m_breakpoint->displayType();
+ table()->setText(row(), Location, m_breakpoint->location());
+
+
+ QTableItem* ce = table()->item( row(), Tracing );
+ ce->setText(breakpoint()->tracingEnabled() ? "Enabled" : "Disabled");
+ // In case there's editor open in this cell, update it too.
+ static_cast<ComplexEditCell*>(ce)->updateValue();
+
+
+ if (m_breakpoint->isTemporary())
+ displayType = i18n(" temporary");
+ if (m_breakpoint->isHardwareBP())
+ displayType += i18n(" hw");
+
+ table()->setText(row(), Type, displayType);
+ table()->adjustColumn(Type);
+ table()->adjustColumn(Status);
+ table()->adjustColumn(Location);
+ table()->adjustColumn(Hits);
+ table()->adjustColumn(IgnoreCount);
+ table()->adjustColumn(Condition);
+ }
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+GDBBreakpointWidget::GDBBreakpointWidget(GDBController* controller,
+ QWidget *parent, const char *name) :
+QHBox(parent, name),
+controller_(controller)
+{
+ m_table = new GDBTable(0, numCols, this, name);
+ m_table->setSelectionMode(QTable::SingleRow);
+ m_table->setShowGrid (false);
+ m_table->setLeftMargin(0);
+ m_table->setFocusStyle(QTable::FollowStyle);
+
+ m_table->hideColumn(Control);
+ m_table->setColumnReadOnly(Type, true);
+ m_table->setColumnReadOnly(Status, true);
+ m_table->setColumnReadOnly(Hits, true);
+ m_table->setColumnWidth( Enable, 20);
+
+ QHeader *header = m_table->horizontalHeader();
+
+ header->setLabel( Enable, "" );
+ header->setLabel( Type, i18n("Type") );
+ header->setLabel( Status, i18n("Status") );
+ header->setLabel( Location, i18n("Location") );
+ header->setLabel( Condition, i18n("Condition") );
+ header->setLabel( IgnoreCount, i18n("Ignore Count") );
+ header->setLabel( Hits, i18n("Hits") );
+ header->setLabel( Tracing, i18n("Tracing") );
+
+ QPopupMenu* newBreakpoint = new QPopupMenu(this);
+ newBreakpoint->insertItem(i18n("Code breakpoint", "Code"),
+ BP_TYPE_FilePos);
+ newBreakpoint->insertItem(i18n("Data breakpoint", "Data write"),
+ BP_TYPE_Watchpoint);
+ newBreakpoint->insertItem(i18n("Data read breakpoint", "Data read"),
+ BP_TYPE_ReadWatchpoint);
+
+
+ m_ctxMenu = new QPopupMenu( this );
+ m_ctxMenu->insertItem( i18n("New breakpoint", "New"),
+ newBreakpoint);
+ m_ctxMenu->insertItem( i18n( "Show text" ), BW_ITEM_Show );
+ int edit_id =
+ m_ctxMenu->insertItem( i18n( "Edit" ), BW_ITEM_Edit );
+ m_ctxMenu->setAccel(Qt::Key_Enter, edit_id);
+ m_ctxMenu->insertItem( i18n( "Disable" ), BW_ITEM_Disable );
+ int del_id =
+ m_ctxMenu->insertItem( SmallIcon("breakpoint_delete"),
+ i18n( "Delete" ), BW_ITEM_Delete );
+ m_ctxMenu->setAccel(Qt::Key_Delete, del_id);
+ m_ctxMenu->insertSeparator();
+ m_ctxMenu->insertItem( i18n( "Disable all"), BW_ITEM_DisableAll );
+ m_ctxMenu->insertItem( i18n( "Enable all"), BW_ITEM_EnableAll );
+ m_ctxMenu->insertItem( i18n( "Delete all"), BW_ITEM_DeleteAll );
+
+ m_table->show();
+
+ connect( newBreakpoint, SIGNAL(activated(int)),
+ this, SLOT(slotAddBlankBreakpoint(int)) );
+
+ connect( m_table, SIGNAL(contextMenuRequested(int, int, const QPoint &)),
+ this, SLOT(slotContextMenuShow(int, int, const QPoint & )) );
+ connect( m_ctxMenu, SIGNAL(activated(int)),
+ this, SLOT(slotContextMenuSelect(int)) );
+
+ connect( m_table, SIGNAL(doubleClicked(int, int, int, const QPoint &)),
+ this, SLOT(slotRowDoubleClicked(int, int, int, const QPoint &)));
+
+ connect( m_table, SIGNAL(valueChanged(int, int)),
+ this, SLOT(slotNewValue(int, int)));
+
+ connect( m_table, SIGNAL(returnPressed()),
+ this, SLOT(slotEditBreakpoint()));
+// connect( m_table, SIGNAL(f2Pressed()),
+// this, SLOT(slotEditBreakpoint()));
+ connect( m_table, SIGNAL(deletePressed()),
+ this, SLOT(slotRemoveBreakpoint()));
+// This slot doesn't exist anymore
+// connect( m_table, SIGNAL(insertPressed()),
+// this, SLOT(slotAddBlankBreakpoint()));
+
+ // FIXME: maybe, all debugger components should derive from
+ // a base class that does this connect.
+ connect(controller, SIGNAL(event(GDBController::event_t)),
+ this, SLOT(slotEvent(GDBController::event_t)));
+
+ connect(controller,
+ SIGNAL(watchpointHit(int, const QString&, const QString&)),
+ this,
+ SLOT(slotWatchpointHit(int, const QString&, const QString&)));
+}
+
+/***************************************************************************/
+
+GDBBreakpointWidget::~GDBBreakpointWidget()
+{
+ delete m_table;
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::reset()
+{
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ btr->reset();
+ sendToGdb(*(btr->breakpoint()));
+ }
+ }
+}
+
+/***************************************************************************/
+
+// When a file is loaded then we need to tell the editor (display window)
+// which lines contain a breakpoint.
+void GDBBreakpointWidget::slotRefreshBP(const KURL &filename)
+{
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ FilePosBreakpoint* bp = dynamic_cast<FilePosBreakpoint*>(btr->breakpoint());
+ if (bp && bp->hasFileAndLine()
+ && (bp->fileName() == filename.path()))
+ emit refreshBPState(*bp);
+ }
+ }
+}
+
+void GDBBreakpointWidget::slotBreakpointHit(int id)
+{
+ BreakpointTableRow* br = findId(id);
+
+ // FIXME: should produce an message, this is most likely
+ // an error.
+ if (!br)
+ return;
+
+ Breakpoint* b = br->breakpoint();
+
+ if (b->tracingEnabled())
+ {
+ controller_->addCommand(
+ new CliCommand(("printf "
+ + b->traceRealFormatString()).latin1(),
+ this,
+ &GDBBreakpointWidget::handleTracingPrintf));
+
+ controller_->addCommand(new
+ GDBCommand("-exec-continue"));
+
+ }
+ else
+ {
+ controller_->demandAttention();
+ }
+}
+
+void GDBBreakpointWidget::slotWatchpointHit(int id,
+ const QString& oldValue,
+ const QString& newValue)
+{
+ BreakpointTableRow* br = findId(id);
+
+ // FIXME: should produce an message, this is most likely
+ // an error.
+ if (!br)
+ return;
+
+ Watchpoint* b = dynamic_cast<Watchpoint*>(br->breakpoint());
+
+
+ KMessageBox::information(
+ 0,
+ i18n("<b>Data write breakpoint</b><br>"
+ "Expression: %1<br>"
+ "Address: 0x%2<br>"
+ "Old value: %3<br>"
+ "New value: %4")
+ .arg(b->varName())
+ .arg(b->address(), 0, 16)
+ .arg(oldValue)
+ .arg(newValue));
+}
+
+/***************************************************************************/
+
+BreakpointTableRow* GDBBreakpointWidget::find(Breakpoint *breakpoint)
+{
+ // NOTE:- The match doesn't have to be equal. Each type of bp
+ // must decide on the match criteria.
+ Q_ASSERT (breakpoint);
+
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr && btr->match(breakpoint))
+ return btr;
+ }
+
+ return 0;
+}
+
+/***************************************************************************/
+
+// The Id is supplied by the debugger
+BreakpointTableRow* GDBBreakpointWidget::findId(int dbgId)
+{
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr && btr->breakpoint()->dbgId() == dbgId)
+ return btr;
+ }
+
+ return 0;
+}
+
+/***************************************************************************/
+
+// The key is a unique number supplied by us
+BreakpointTableRow* GDBBreakpointWidget::findKey(int BPKey)
+{
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr && btr->breakpoint()->key() == BPKey)
+ return btr;
+ }
+
+ return 0;
+}
+
+bool GDBBreakpointWidget::hasWatchpointForAddress(
+ unsigned long long address) const
+{
+ for(int i = 0; i < m_table->numRows(); ++i)
+ {
+ BreakpointTableRow* br = (BreakpointTableRow*)
+ m_table->item(i, Control);
+
+ Watchpoint* w = dynamic_cast<Watchpoint*>(br->breakpoint());
+ if (w && w->address() == address)
+ return true;
+ }
+ return false;
+}
+
+/***************************************************************************/
+
+BreakpointTableRow* GDBBreakpointWidget::addBreakpoint(Breakpoint *bp)
+{
+ BreakpointTableRow* btr =
+ new BreakpointTableRow( m_table, QTableItem::WhenCurrent, bp );
+
+ connect(bp, SIGNAL(modified(Breakpoint*)),
+ this, SLOT(slotBreakpointModified(Breakpoint*)));
+
+ sendToGdb(*bp);
+
+ return btr;
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::removeBreakpoint(BreakpointTableRow* btr)
+{
+ if (!btr)
+ return;
+
+ // Pending but the debugger hasn't started processing this bp so
+ // we can just remove it.
+ Breakpoint* bp = btr->breakpoint();
+ // No gdb breakpoint, and no breakpoint addition command in the
+ // queue. Just remove.
+ if (bp->dbgId() == -1 && !bp->isDbgProcessing())
+ {
+ bp->setActionDie();
+ sendToGdb(*bp);
+ m_table->removeRow(btr->row());
+ }
+ else
+ {
+ bp->setActionClear(true);
+ sendToGdb(*bp);
+ btr->setRow();
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotToggleBreakpoint(const QString &fileName, int lineNum)
+{
+ FilePosBreakpoint *fpBP = new FilePosBreakpoint(fileName, lineNum+1);
+
+ BreakpointTableRow* btr = find(fpBP);
+ if (btr)
+ {
+ removeBreakpoint(btr);
+ }
+ else
+ addBreakpoint(fpBP);
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotToggleBreakpointEnabled(const QString &fileName, int lineNum)
+{
+ FilePosBreakpoint *fpBP = new FilePosBreakpoint(fileName, lineNum+1);
+
+ BreakpointTableRow* btr = find(fpBP);
+ delete fpBP;
+ if (btr)
+ {
+ Breakpoint* bp=btr->breakpoint();
+ bp->setEnabled(!bp->isEnabled());
+ sendToGdb(*bp);
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotToggleWatchpoint(const QString &varName)
+{
+ Watchpoint *watchpoint = new Watchpoint(varName, false, true);
+ BreakpointTableRow* btr = find(watchpoint);
+ if (btr)
+ {
+ removeBreakpoint(btr);
+ delete watchpoint;
+ }
+ else
+ addBreakpoint(watchpoint);
+}
+
+void GDBBreakpointWidget::handleBreakpointList(const GDBMI::ResultRecord& r)
+{
+ m_activeFlag++;
+
+ const GDBMI::Value& blist = r["BreakpointTable"]["body"];
+
+ for(unsigned i = 0, e = blist.size(); i != e; ++i)
+ {
+ const GDBMI::Value& b = blist[i];
+
+ int id = b["number"].literal().toInt();
+ BreakpointTableRow* btr = findId(id);
+ if (btr)
+ {
+ Breakpoint *bp = btr->breakpoint();
+ bp->setActive(m_activeFlag, id);
+ bp->setHits(b["times"].toInt());
+ if (b.hasField("ignore"))
+ bp->setIgnoreCount(b["ignore"].toInt());
+ else
+ bp->setIgnoreCount(0);
+ if (b.hasField("cond"))
+ bp->setConditional(b["cond"].literal());
+ else
+ bp->setConditional(QString::null);
+ btr->setRow();
+ emit publishBPState(*bp);
+ }
+ else
+ {
+ // It's a breakpoint added outside, most probably
+ // via gdb console. Add it now.
+ QString type = b["type"].literal();
+
+ if (type == "breakpoint" || type == "hw breakpoint")
+ {
+ if (b.hasField("fullname") && b.hasField("line"))
+ {
+ Breakpoint* bp = new FilePosBreakpoint(
+ b["fullname"].literal(),
+ b["line"].literal().toInt());
+
+ bp->setActive(m_activeFlag, id);
+ bp->setActionAdd(false);
+ bp->setPending(false);
+
+ new BreakpointTableRow(m_table,
+ QTableItem::WhenCurrent,
+ bp);
+
+ emit publishBPState(*bp);
+ }
+ }
+
+ }
+ }
+
+ // Remove any inactive breakpoints.
+ for ( int row = m_table->numRows()-1; row >= 0 ; row-- )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ Breakpoint* bp = btr->breakpoint();
+ if (!(bp->isActive(m_activeFlag)))
+ {
+ // FIXME: need to review is this happens for
+ // as-yet unset breakpoint.
+ bp->removedInGdb();
+ }
+ }
+ }
+}
+
+void GDBBreakpointWidget::handleTracingPrintf(const QValueVector<QString>& s)
+{
+ // The first line of output is the command itself, which we don't need.
+ for(unsigned i = 1; i < s.size(); ++i)
+ emit tracingOutput(s[i].local8Bit());
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotBreakpointSet(Breakpoint* bp)
+{
+ // FIXME: why 'key' is used here?
+ BreakpointTableRow* btr = findKey(bp->key());
+ if (!btr)
+ {
+ kdDebug(9012) << "Early return\n";
+ return;
+ }
+
+ btr->setRow();
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotAddBlankBreakpoint(int idx)
+{
+ BreakpointTableRow* btr = 0;
+ switch (idx)
+ {
+ case BP_TYPE_FilePos:
+ btr = addBreakpoint(new FilePosBreakpoint());
+ break;
+
+ case BP_TYPE_Watchpoint:
+ btr = addBreakpoint(new Watchpoint(""));
+ break;
+
+ case BP_TYPE_ReadWatchpoint:
+ btr = addBreakpoint(new ReadWatchpoint(""));
+ break;
+
+ default:
+ break;
+ }
+
+ if (btr)
+ {
+ m_table->selectRow(btr->row());
+ m_table->editCell(btr->row(), Location, false);
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotRemoveBreakpoint()
+{
+ int row = m_table->currentRow();
+ if ( row != -1)
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ removeBreakpoint(btr);
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotRemoveAllBreakpoints()
+{
+ for ( int row = m_table->numRows()-1; row>=0; row-- )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ removeBreakpoint(btr);
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotRowDoubleClicked(int row, int col, int btn, const QPoint &)
+{
+ if ( btn == Qt::LeftButton )
+ {
+// kdDebug(9012) << "in slotRowSelected row=" << row << endl;
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ FilePosBreakpoint* bp = dynamic_cast<FilePosBreakpoint*>(btr->breakpoint());
+ if (bp && bp->hasFileAndLine())
+ emit gotoSourcePosition(bp->fileName(), bp->lineNum()-1);
+
+ // put the focus back on the clicked item if appropriate
+ if (col == Location || col == Condition || col == IgnoreCount)
+ m_table->editCell(row, col, false);
+ }
+ }
+}
+
+void GDBBreakpointWidget::slotContextMenuShow( int row, int /*col*/, const QPoint &mousePos )
+{
+ BreakpointTableRow *btr = (BreakpointTableRow *)m_table->item(row, Control );
+
+ if (btr == NULL)
+ {
+ btr = (BreakpointTableRow *)m_table->item(m_table->currentRow(),
+ Control );
+ }
+
+ if (btr != NULL)
+ {
+ m_ctxMenu->setItemEnabled(
+ BW_ITEM_Show,
+ btr->breakpoint()->hasFileAndLine());
+
+ if (btr->breakpoint( )->isEnabled( ))
+ {
+ m_ctxMenu->changeItem( BW_ITEM_Disable, i18n("Disable") );
+ }
+ else
+ {
+ m_ctxMenu->changeItem( BW_ITEM_Disable, i18n("Enable") );
+ }
+
+ m_ctxMenu->setItemEnabled(BW_ITEM_Disable, true);
+ m_ctxMenu->setItemEnabled(BW_ITEM_Delete, true);
+ m_ctxMenu->setItemEnabled(BW_ITEM_Edit, true);
+ }
+ else
+ {
+ m_ctxMenu->setItemEnabled(BW_ITEM_Show, false);
+ m_ctxMenu->setItemEnabled(BW_ITEM_Disable, false);
+ m_ctxMenu->setItemEnabled(BW_ITEM_Delete, false);
+ m_ctxMenu->setItemEnabled(BW_ITEM_Edit, false);
+ }
+
+ bool has_bps = (m_table->numRows() != 0);
+ m_ctxMenu->setItemEnabled(BW_ITEM_DisableAll, has_bps);
+ m_ctxMenu->setItemEnabled(BW_ITEM_EnableAll, has_bps);
+ m_ctxMenu->setItemEnabled(BW_ITEM_Delete, has_bps);
+
+ m_ctxMenu->popup( mousePos );
+}
+
+void GDBBreakpointWidget::slotContextMenuSelect( int item )
+{
+ int row, col;
+ BreakpointTableRow *btr;
+ Breakpoint *bp;
+ FilePosBreakpoint *fbp;
+
+ row= m_table->currentRow( );
+ if (row == -1)
+ return;
+ btr = (BreakpointTableRow *)m_table->item( row, Control );
+ if (btr == NULL)
+ return;
+ bp = btr->breakpoint( );
+ if (bp == NULL)
+ return;
+ fbp = dynamic_cast<FilePosBreakpoint*>(bp);
+
+ switch( item )
+ {
+ case BW_ITEM_Show:
+ if (fbp)
+ emit gotoSourcePosition(fbp->fileName(), fbp->lineNum()-1);
+ break;
+ case BW_ITEM_Edit:
+ col = m_table->currentColumn( );
+ if (col == Location || col == Condition || col == IgnoreCount)
+ m_table->editCell(row, col, false);
+ break;
+ case BW_ITEM_Disable:
+
+ bp->setEnabled( !bp->isEnabled( ) );
+ btr->setRow( );
+ sendToGdb( *bp );
+ break;
+ case BW_ITEM_Delete:
+ slotRemoveBreakpoint( );
+ break;
+ case BW_ITEM_DeleteAll:
+ slotRemoveAllBreakpoints();
+ break;
+ case BW_ITEM_DisableAll:
+ case BW_ITEM_EnableAll:
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *)
+ m_table->item(row, Control);
+
+ if (btr)
+ {
+ btr->breakpoint()->setEnabled(item == BW_ITEM_EnableAll);
+ btr->setRow();
+ sendToGdb(*btr->breakpoint());
+ }
+ }
+ break;
+ default:
+ // oops, check it out! this case is not in sync with the
+ // m_ctxMenu. Check the enum in the header file.
+ return;
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotEditRow(int row, int col, const QPoint &)
+{
+// kdDebug(9012) << "in slotEditRow row=" << row << endl;
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+ if (btr)
+ {
+ if (col == Location || col == Condition || col == IgnoreCount)
+ m_table->editCell(row, col, false);
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotNewValue(int row, int col)
+{
+ BreakpointTableRow* btr = (BreakpointTableRow *) m_table->item(row, Control);
+
+ QString new_value = m_table->text(row, col);
+
+ if (btr)
+ {
+ Breakpoint* bp = btr->breakpoint();
+ switch (col)
+ {
+ case Enable:
+ {
+ QCheckTableItem *item =
+ (QCheckTableItem*)m_table->item ( row, Enable );
+ bp->setEnabled(item->isChecked());
+ }
+ break;
+
+ case Location:
+ {
+ if (bp->location() != new_value)
+ {
+ // GDB does not allow to change location of
+ // an existing breakpoint. So, need to remove old
+ // breakpoint and add another.
+
+ // Announce to editor that breakpoit at its
+ // current location is dying.
+ bp->setActionDie();
+ emit publishBPState(*bp);
+
+ // However, we don't want the line in breakpoint
+ // widget to disappear and appear again.
+
+ // Emit delete command. This won't resync breakpoint
+ // table (unlike clearBreakpoint), so we won't have
+ // nasty effect where line in the table first disappears
+ // and then appears again, and won't have internal issues
+ // as well.
+ if (!controller_->stateIsOn(s_dbgNotStarted))
+ controller_->addCommand(bp->dbgRemoveCommand().latin1());
+
+ // Now add new breakpoint in gdb. It will correspond to
+ // the same 'Breakpoint' and 'BreakpointRow' objects in
+ // KDevelop is the previous, deleted, breakpoint.
+
+ // Note: clears 'actionDie' implicitly.
+ bp->setActionAdd(true);
+ bp->setLocation(new_value);
+ }
+ break;
+ }
+
+ case Condition:
+ {
+ bp->setConditional(new_value);
+ break;
+ }
+
+ case IgnoreCount:
+ {
+ bp->setIgnoreCount(new_value.toInt());
+ break;
+ }
+ default:
+ break;
+ }
+
+ bp->setActionModify(true);
+
+
+ // This is not needed for most changes, since we've
+ // just read a value from table cell to breakpoint, and
+ // setRow will write back the same value to the cell.
+ // It's only really needed for tracing column changes,
+ // where tracing config dialog directly changes breakpoint,
+ // so we need to send those changes to the table.
+ btr->setRow();
+
+
+ sendToGdb(*bp);
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotEditBreakpoint(const QString &fileName, int lineNum)
+{
+ FilePosBreakpoint *fpBP = new FilePosBreakpoint(fileName, lineNum+1);
+
+ BreakpointTableRow* btr = find(fpBP);
+ delete fpBP;
+
+ if (btr)
+ {
+ QTableSelection ts;
+ ts.init(btr->row(), 0);
+ ts.expandTo(btr->row(), numCols);
+ m_table->addSelection(ts);
+ m_table->editCell(btr->row(), Location, false);
+ }
+
+}
+
+void GDBBreakpointWidget::sendToGdb(Breakpoint& BP)
+{
+ // Announce the change in state. We need to do this before
+ // everything. For example, if debugger is not yet running, we'll
+ // immediate exit after setting pending flag, but we still want changes
+ // in "enabled" flag to be shown on the left border of the editor.
+ emit publishBPState(BP);
+
+ BP.sendToGdb(controller_);
+}
+
+void GDBBreakpointWidget::slotBreakpointModified(Breakpoint* b)
+{
+ emit publishBPState(*b);
+
+ if (BreakpointTableRow* btr = find(b))
+ {
+ if (b->isActionDie())
+ {
+ // Breakpoint was deleted, kill the table row.
+ m_table->removeRow(btr->row());
+ }
+ else
+ {
+ btr->setRow();
+ }
+ }
+}
+
+void GDBBreakpointWidget::slotEvent(GDBController::event_t e)
+{
+ switch(e)
+ {
+ case GDBController::program_state_changed:
+ {
+ controller_->addCommand(
+ new GDBCommand("-break-list",
+ this,
+ &GDBBreakpointWidget::handleBreakpointList));
+ break;
+ }
+
+ case GDBController::shared_library_loaded:
+ case GDBController::connected_to_program:
+ {
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr = (BreakpointTableRow *)
+ m_table->item(row, Control);
+
+ if (btr)
+ {
+ Breakpoint* bp = btr->breakpoint();
+ if ( (bp->dbgId() == -1 || bp->isPending())
+ && !bp->isDbgProcessing()
+ && bp->isValid())
+ {
+ sendToGdb(*bp);
+ }
+ }
+ }
+ break;
+ }
+ case GDBController::program_exited:
+ {
+ for(int row = 0; row < m_table->numRows(); ++row)
+ {
+ Breakpoint* b = static_cast<BreakpointTableRow*>(
+ m_table->item(row, Control))->breakpoint();
+
+ b->applicationExited(controller_);
+ }
+ }
+
+ default:
+ ;
+ }
+}
+
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::slotEditBreakpoint()
+{
+ m_table->editCell(m_table->currentRow(), Location, false);
+}
+
+
+void GDBBreakpointWidget::editTracing(QTableItem* item)
+{
+ BreakpointTableRow* btr = (BreakpointTableRow *)
+ m_table->item(item->row(), Control);
+
+ DebuggerTracingDialog* d = new DebuggerTracingDialog(
+ btr->breakpoint(), m_table, "");
+
+ int r = d->exec();
+
+ // Note: change cell text here and explicitly call slotNewValue here.
+ // We want this signal to be emitted when we close the tracing dialog
+ // and not when we select some other cell, as happens in Qt by default.
+ if (r == QDialog::Accepted)
+ {
+ // The dialog has modified "btr->breakpoint()" already.
+ // Calling 'slotNewValue' will flush the changes back
+ // to the table.
+ slotNewValue(item->row(), item->col());
+ }
+
+ delete d;
+}
+
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::savePartialProjectSession(QDomElement* el)
+{
+ QDomDocument domDoc = el->ownerDocument();
+ if (domDoc.isNull())
+ return;
+
+ QDomElement breakpointListEl = domDoc.createElement("breakpointList");
+ for ( int row = 0; row < m_table->numRows(); row++ )
+ {
+ BreakpointTableRow* btr =
+ (BreakpointTableRow *) m_table->item(row, Control);
+ Breakpoint* bp = btr->breakpoint();
+
+ QDomElement breakpointEl =
+ domDoc.createElement("breakpoint"+QString::number(row));
+
+ breakpointEl.setAttribute("type", bp->type());
+ breakpointEl.setAttribute("location", bp->location(false));
+ breakpointEl.setAttribute("enabled", bp->isEnabled());
+ breakpointEl.setAttribute("condition", bp->conditional());
+ breakpointEl.setAttribute("tracingEnabled",
+ QString::number(bp->tracingEnabled()));
+ breakpointEl.setAttribute("traceFormatStringEnabled",
+ QString::number(bp->traceFormatStringEnabled()));
+ breakpointEl.setAttribute("tracingFormatString",
+ bp->traceFormatString());
+
+ QDomElement tracedExpressions =
+ domDoc.createElement("tracedExpressions");
+
+ QStringList::const_iterator i, e;
+ for(i = bp->tracedExpressions().begin(),
+ e = bp->tracedExpressions().end();
+ i != e; ++i)
+ {
+ QDomElement expr = domDoc.createElement("expression");
+ expr.setAttribute("value", *i);
+ tracedExpressions.appendChild(expr);
+ }
+
+ breakpointEl.appendChild(tracedExpressions);
+
+ breakpointListEl.appendChild(breakpointEl);
+ }
+
+ if (!breakpointListEl.isNull())
+ el->appendChild(breakpointListEl);
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::restorePartialProjectSession(const QDomElement* el)
+{
+ /** Eventually, would be best to make each breakpoint type handle loading/
+ saving it's data. The only problem is that on load, we need to allocate
+ with new different objects, depending on type, and that requires some
+ kind of global registry. Gotta find out if a solution for that exists in
+ KDE (Boost.Serialization is too much dependency, and rolling my own is
+ boring).
+ */
+ QDomElement breakpointListEl = el->namedItem("breakpointList").toElement();
+ if (!breakpointListEl.isNull())
+ {
+ QDomElement breakpointEl;
+ for (breakpointEl = breakpointListEl.firstChild().toElement();
+ !breakpointEl.isNull();
+ breakpointEl = breakpointEl.nextSibling().toElement())
+ {
+ Breakpoint* bp=0;
+ BP_TYPES type = (BP_TYPES) breakpointEl.attribute( "type", "0").toInt();
+ switch (type)
+ {
+ case BP_TYPE_FilePos:
+ {
+ bp = new FilePosBreakpoint();
+ break;
+ }
+ case BP_TYPE_Watchpoint:
+ {
+ bp = new Watchpoint("");
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Common settings for any type of breakpoint
+ if (bp)
+ {
+ bp->setLocation(breakpointEl.attribute( "location", ""));
+ if (type == BP_TYPE_Watchpoint)
+ {
+ bp->setEnabled(false);
+ }
+ else
+ {
+ bp->setEnabled(
+ breakpointEl.attribute( "enabled", "1").toInt());
+ }
+ bp->setConditional(breakpointEl.attribute( "condition", ""));
+
+ bp->setTracingEnabled(
+ breakpointEl.attribute("tracingEnabled", "0").toInt());
+ bp->setTraceFormatString(
+ breakpointEl.attribute("tracingFormatString", ""));
+ bp->setTraceFormatStringEnabled(
+ breakpointEl.attribute("traceFormatStringEnabled", "0")
+ .toInt());
+
+ QDomNode tracedExpr =
+ breakpointEl.namedItem("tracedExpressions");
+
+ if (!tracedExpr.isNull())
+ {
+ QStringList l;
+
+ for(QDomNode c = tracedExpr.firstChild(); !c.isNull();
+ c = c.nextSibling())
+ {
+ QDomElement el = c.toElement();
+ l.push_back(el.attribute("value", ""));
+ }
+ bp->setTracedExpressions(l);
+ }
+
+ // Now add the breakpoint. Don't try to check if
+ // breakpoint already exists.
+ // It's easy to check that breakpoint on the same
+ // line already exists, but it might have different condition,
+ // and checking conditions for equality is too complex thing.
+ // And anyway, it's will be suprising of realoading a project
+ // changes the set of breakpoints.
+ addBreakpoint(bp);
+ }
+ }
+ }
+}
+
+/***************************************************************************/
+
+void GDBBreakpointWidget::focusInEvent( QFocusEvent */* e*/ )
+{
+ // Without the following 'if', when we first open the breakpoints
+ // widget, the background is all black. This happens only with
+ // m_table->setFocusStyle(QTable::FollowStyle);
+ // in constructor, so I suspect Qt bug. But anyway, without
+ // current cell keyboard actions like Enter for edit won't work,
+ // so keyboard focus does not makes much sense.
+ if (m_table->currentRow() == -1 ||
+ m_table->currentColumn() == -1)
+ {
+ m_table->setCurrentCell(0, 0);
+ }
+ m_table->setFocus();
+}
+
+ComplexEditCell::
+ComplexEditCell(QTable* table)
+: QTableItem(table, QTableItem::WhenCurrent)
+{
+}
+
+
+QWidget* ComplexEditCell::createEditor() const
+{
+ QHBox* box = new QHBox( table()->viewport() );
+ box->setPaletteBackgroundColor(
+ table()->palette().active().highlight());
+
+ label_ = new QLabel(text(), box, "label");
+ label_->setBackgroundMode(Qt::PaletteHighlight);
+ // Sorry for hardcode, but '2' is already hardcoded in
+ // Qt source, in QTableItem::paint. Since I don't want the
+ // text to jump 2 pixels to the right when editor is activated,
+ // need to set the same indent for label.
+ label_->setIndent(2);
+ QPalette p = label_->palette();
+
+ p.setColor(QPalette::Active, QColorGroup::Foreground,
+ table()->palette().active().highlightedText());
+ p.setColor(QPalette::Inactive, QColorGroup::Foreground,
+ table()->palette().active().highlightedText());
+
+ label_->setPalette(p);
+
+ QPushButton* b = new QPushButton("...", box);
+ // This is exactly what is done in QDesigner source in the
+ // similar context. Haven't had any success making the good look
+ // with layout, I suppose that sizeHint for button is always larger
+ // than 20.
+ b->setFixedWidth( 20 );
+
+ connect(b, SIGNAL(clicked()), this, SLOT(slotEdit()));
+
+ return box;
+}
+
+void ComplexEditCell::updateValue()
+{
+ if (!label_.isNull())
+ {
+ label_->setText(table()->text(row(), col()));
+ }
+}
+
+void ComplexEditCell::slotEdit()
+{
+ emit edit(this);
+}
+
+}
+
+
+#include "gdbbreakpointwidget.moc"