You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1263 lines
39 KiB
1263 lines
39 KiB
/***************************************************************************
|
|
begin : Tue May 13 2003
|
|
copyright : (C) 2003 by John Birch
|
|
email : jbb@tdevelop.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 <tqvbuttongroup.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqheader.h>
|
|
#include <tqtable.h>
|
|
#include <tqtoolbutton.h>
|
|
#include <tqtooltip.h>
|
|
#include <tqwhatsthis.h>
|
|
#include <tqvbox.h>
|
|
#include <tqlayout.h>
|
|
#include <tqlabel.h>
|
|
#include <tqpushbutton.h>
|
|
#include <tqcheckbox.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
|
|
namespace GDBDebugger
|
|
{
|
|
|
|
enum Column {
|
|
Control = 0,
|
|
Enable = 1,
|
|
Type = 2,
|
|
tqStatus = 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 TQTableItem
|
|
{
|
|
public:
|
|
|
|
BreakpointTableRow(TQTable* 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(TQTable* parent, EditType editType,
|
|
Breakpoint* bp) :
|
|
TQTableItem(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);
|
|
|
|
TQCheckTableItem* cti = new TQCheckTableItem( table(), "");
|
|
table()->setItem(row, Enable, cti);
|
|
|
|
ComplexEditCell* act = new ComplexEditCell(table());
|
|
table()->setItem(row, Tracing, act);
|
|
TQObject::connect(act, TQT_SIGNAL(edit(TQTableItem*)),
|
|
table()->parent(), TQT_SLOT(editTracing(TQTableItem*)));
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
void BreakpointTableRow::setRow()
|
|
{
|
|
if ( m_breakpoint )
|
|
{
|
|
TQTableItem *item = table()->item ( row(), Enable );
|
|
Q_ASSERT(item->rtti() == 2);
|
|
((TQCheckTableItem*)item)->setChecked(m_breakpoint->isEnabled());
|
|
|
|
TQString status=m_breakpoint->statusDisplay(m_activeFlag);
|
|
|
|
table()->setText(row(), tqStatus, status);
|
|
table()->setText(row(), Condition, m_breakpoint->conditional());
|
|
table()->setText(row(), IgnoreCount, TQString::number(m_breakpoint->ignoreCount() ));
|
|
table()->setText(row(), Hits, TQString::number(m_breakpoint->hits() ));
|
|
|
|
TQString displayType = m_breakpoint->displayType();
|
|
table()->setText(row(), Location, m_breakpoint->location());
|
|
|
|
|
|
TQTableItem* 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(tqStatus);
|
|
table()->adjustColumn(Location);
|
|
table()->adjustColumn(Hits);
|
|
table()->adjustColumn(IgnoreCount);
|
|
table()->adjustColumn(Condition);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
|
|
GDBBreakpointWidget::GDBBreakpointWidget(GDBController* controller,
|
|
TQWidget *parent, const char *name) :
|
|
TQHBox(parent, name),
|
|
controller_(controller)
|
|
{
|
|
m_table = new GDBTable(0, numCols, this, name);
|
|
m_table->setSelectionMode(TQTable::SingleRow);
|
|
m_table->setShowGrid (false);
|
|
m_table->setLeftMargin(0);
|
|
m_table->setFocusStyle(TQTable::FollowStyle);
|
|
|
|
m_table->hideColumn(Control);
|
|
m_table->setColumnReadOnly(Type, true);
|
|
m_table->setColumnReadOnly(tqStatus, true);
|
|
m_table->setColumnReadOnly(Hits, true);
|
|
m_table->setColumnWidth( Enable, 20);
|
|
|
|
TQHeader *header = m_table->horizontalHeader();
|
|
|
|
header->setLabel( Enable, "" );
|
|
header->setLabel( Type, i18n("Type") );
|
|
header->setLabel( tqStatus, 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") );
|
|
|
|
TQPopupMenu* newBreakpoint = new TQPopupMenu(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 TQPopupMenu( 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(TQt::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(TQt::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, TQT_SIGNAL(activated(int)),
|
|
this, TQT_SLOT(slotAddBlankBreakpoint(int)) );
|
|
|
|
connect( m_table, TQT_SIGNAL(contextMenuRequested(int, int, const TQPoint &)),
|
|
this, TQT_SLOT(slotContextMenuShow(int, int, const TQPoint & )) );
|
|
connect( m_ctxMenu, TQT_SIGNAL(activated(int)),
|
|
this, TQT_SLOT(slotContextMenuSelect(int)) );
|
|
|
|
connect( m_table, TQT_SIGNAL(doubleClicked(int, int, int, const TQPoint &)),
|
|
this, TQT_SLOT(slotRowDoubleClicked(int, int, int, const TQPoint &)));
|
|
|
|
connect( m_table, TQT_SIGNAL(valueChanged(int, int)),
|
|
this, TQT_SLOT(slotNewValue(int, int)));
|
|
|
|
connect( m_table, TQT_SIGNAL(returnPressed()),
|
|
this, TQT_SLOT(slotEditBreakpoint()));
|
|
// connect( m_table, TQT_SIGNAL(f2Pressed()),
|
|
// this, TQT_SLOT(slotEditBreakpoint()));
|
|
connect( m_table, TQT_SIGNAL(deletePressed()),
|
|
this, TQT_SLOT(slotRemoveBreakpoint()));
|
|
// This slot doesn't exist anymore
|
|
// connect( m_table, TQT_SIGNAL(insertPressed()),
|
|
// this, TQT_SLOT(slotAddBlankBreakpoint()));
|
|
|
|
// FIXME: maybe, all debugger components should derive from
|
|
// a base class that does this connect.
|
|
connect(controller, TQT_SIGNAL(event(GDBController::event_t)),
|
|
this, TQT_SLOT(slotEvent(GDBController::event_t)));
|
|
|
|
connect(controller,
|
|
TQT_SIGNAL(watchpointHit(int, const TQString&, const TQString&)),
|
|
this,
|
|
TQT_SLOT(slotWatchpointHit(int, const TQString&, const TQString&)));
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
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 TQString& oldValue,
|
|
const TQString& 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")
|
|
.tqarg(b->varName())
|
|
.tqarg(b->address(), 0, 16)
|
|
.tqarg(oldValue)
|
|
.tqarg(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, TQTableItem::WhenCurrent, bp );
|
|
|
|
connect(bp, TQT_SIGNAL(modified(Breakpoint*)),
|
|
this, TQT_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 TQString &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 TQString &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 TQString &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(TQString());
|
|
btr->setRow();
|
|
emit publishBPState(*bp);
|
|
}
|
|
else
|
|
{
|
|
// It's a breakpoint added outside, most probably
|
|
// via gdb console. Add it now.
|
|
TQString 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,
|
|
TQTableItem::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 TQValueVector<TQString>& 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 TQPoint &)
|
|
{
|
|
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 TQPoint &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 TQPoint &)
|
|
{
|
|
// 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);
|
|
|
|
TQString new_value = m_table->text(row, col);
|
|
|
|
if (btr)
|
|
{
|
|
Breakpoint* bp = btr->breakpoint();
|
|
switch (col)
|
|
{
|
|
case Enable:
|
|
{
|
|
TQCheckTableItem *item =
|
|
(TQCheckTableItem*)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 TQString &fileName, int lineNum)
|
|
{
|
|
FilePosBreakpoint *fpBP = new FilePosBreakpoint(fileName, lineNum+1);
|
|
|
|
BreakpointTableRow* btr = find(fpBP);
|
|
delete fpBP;
|
|
|
|
if (btr)
|
|
{
|
|
TQTableSelection 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(TQTableItem* 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 TQt by default.
|
|
if (r == TQDialog::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(TQDomElement* el)
|
|
{
|
|
TQDomDocument domDoc = el->ownerDocument();
|
|
if (domDoc.isNull())
|
|
return;
|
|
|
|
TQDomElement 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();
|
|
|
|
TQDomElement breakpointEl =
|
|
domDoc.createElement("breakpoint"+TQString::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",
|
|
TQString::number(bp->tracingEnabled()));
|
|
breakpointEl.setAttribute("traceFormatStringEnabled",
|
|
TQString::number(bp->traceFormatStringEnabled()));
|
|
breakpointEl.setAttribute("tracingFormatString",
|
|
bp->traceFormatString());
|
|
|
|
TQDomElement tracedExpressions =
|
|
domDoc.createElement("tracedExpressions");
|
|
|
|
TQStringList::const_iterator i, e;
|
|
for(i = bp->tracedExpressions().begin(),
|
|
e = bp->tracedExpressions().end();
|
|
i != e; ++i)
|
|
{
|
|
TQDomElement 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 TQDomElement* 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).
|
|
*/
|
|
TQDomElement breakpointListEl = el->namedItem("breakpointList").toElement();
|
|
if (!breakpointListEl.isNull())
|
|
{
|
|
TQDomElement 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());
|
|
|
|
TQDomNode tracedExpr =
|
|
breakpointEl.namedItem("tracedExpressions");
|
|
|
|
if (!tracedExpr.isNull())
|
|
{
|
|
TQStringList l;
|
|
|
|
for(TQDomNode c = tracedExpr.firstChild(); !c.isNull();
|
|
c = c.nextSibling())
|
|
{
|
|
TQDomElement 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( TQFocusEvent */* e*/ )
|
|
{
|
|
// Without the following 'if', when we first open the breakpoints
|
|
// widget, the background is all black. This happens only with
|
|
// m_table->setFocusStyle(TQTable::FollowStyle);
|
|
// in constructor, so I suspect TQt 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(TQTable* table)
|
|
: TQTableItem(table, TQTableItem::WhenCurrent)
|
|
{
|
|
}
|
|
|
|
|
|
TQWidget* ComplexEditCell::createEditor() const
|
|
{
|
|
TQHBox* box = new TQHBox( table()->viewport() );
|
|
box->setPaletteBackgroundColor(
|
|
table()->tqpalette().active().highlight());
|
|
|
|
label_ = new TQLabel(text(), box, "label");
|
|
label_->setBackgroundMode(TQt::PaletteHighlight);
|
|
// Sorry for hardcode, but '2' is already hardcoded in
|
|
// TQt source, in TQTableItem::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);
|
|
TQPalette p = label_->palette();
|
|
|
|
p.setColor(TQPalette::Active, TQColorGroup::Foreground,
|
|
table()->tqpalette().active().highlightedText());
|
|
p.setColor(TQPalette::Inactive, TQColorGroup::Foreground,
|
|
table()->tqpalette().active().highlightedText());
|
|
|
|
label_->setPalette(p);
|
|
|
|
TQPushButton* b = new TQPushButton("...", box);
|
|
// This is exactly what is done in TQDesigner source in the
|
|
// similar context. Haven't had any success making the good look
|
|
// with tqlayout, I suppose that tqsizeHint for button is always larger
|
|
// than 20.
|
|
b->setFixedWidth( 20 );
|
|
|
|
connect(b, TQT_SIGNAL(clicked()), this, TQT_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"
|