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.
tdevelop/languages/cpp/debugger/memviewdlg.cpp

487 lines
15 KiB

/***************************************************************************
begin : Tue Oct 5 1999
copyright : (C) 1999 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 "memviewdlg.h"
#include "gdbcontroller.h"
#include "gdbcommand.h"
#include <kbuttonbox.h>
#include <klineedit.h>
#include <tdeglobalsettings.h>
#include <tdelocale.h>
#include <kstdguiitem.h>
#include <tdeversion.h>
#include <kdebug.h>
#include <kiconloader.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqmultilineedit.h>
#include <tqpushbutton.h>
#include <tqvariant.h>
#include <tqpopupmenu.h>
#include <tqhbox.h>
#include <tqtoolbox.h>
#include <tqtextedit.h>
#include <tdemessagebox.h>
#include <khexedit/byteseditinterface.h>
#include <ctype.h>
// **************************************************************************
//
// Dialog allows the user to enter
// - A starting address
// - An ending address
//
// this can be in the form
// functiom/method name
// variable address (ie &Var, str)
// Memory address 0x8040abc
//
// When disassembling and you enter a method name without an
// ending address then the whole method is disassembled.
// No data means disassemble the method we're curently in.(from the
// start of the method)
//
// click ok buton to send the request to gdb
// the output is returned (some time later) in the raw data slot
// and displayed as is, so it's rather crude, but it works!
// **************************************************************************
namespace GDBDebugger
{
/** Container for controls that select memory range.
The memory range selection is embedded into memory view widget,
it's not a standalone dialog. However, we want to have easy way
to hide/show all controls, so we group them in this class.
*/
class MemoryRangeSelector : public TQWidget
{
public:
KLineEdit* startAddressLineEdit;
KLineEdit* amountLineEdit;
TQPushButton* okButton;
TQPushButton* cancelButton;
MemoryRangeSelector(TQWidget* parent)
: TQWidget(parent)
{
TQVBoxLayout* l = new TQVBoxLayout(this);
// Grid layout: labels + address field
TQGridLayout* gl = new TQGridLayout(l);
gl->setColSpacing(0, 2);
gl->setColSpacing(1, 4);
gl->setRowSpacing(1, 2);
TQLabel* l1 = new TQLabel(i18n("Start"), this);
gl->addWidget(l1, 0, 1);
startAddressLineEdit = new KLineEdit(this);
gl->addWidget(startAddressLineEdit, 0, 3);
TQLabel* l2 = new TQLabel(i18n("Amount"), this);
gl->addWidget(l2, 2, 1);
amountLineEdit = new KLineEdit(this);
gl->addWidget(amountLineEdit, 2, 3);
l->addSpacing(2);
TQHBoxLayout* hb = new TQHBoxLayout(l);
hb->addStretch();
okButton = new TQPushButton(i18n("OK"), this);
hb->addWidget(okButton);
cancelButton = new TQPushButton(i18n("Cancel"), this);
hb->addWidget(cancelButton);
l->addSpacing(2);
connect(startAddressLineEdit, TQ_SIGNAL(returnPressed()),
okButton, TQ_SLOT(animateClick()));
connect(amountLineEdit, TQ_SIGNAL(returnPressed()),
okButton, TQ_SLOT(animateClick()));
}
};
MemoryView::MemoryView(GDBController* controller,
TQWidget* parent, const char* name)
: TQWidget(parent, name),
controller_(controller),
// New memory view can be created only when debugger is active,
// so don't set s_appNotStarted here.
khexedit2_real_widget(0),
amount_(0), data_(0),
debuggerState_(0)
{
setCaption(i18n("Memory view"));
emit captionChanged(caption());
initWidget();
if (isOk())
slotEnableOrDisable();
}
void MemoryView::initWidget()
{
TQVBoxLayout *l = new TQVBoxLayout(this, 0, 0);
khexedit2_widget = KHE::createBytesEditWidget(this);
bool ok_ = false;
if (khexedit2_widget)
{
TQWidget* real_widget = (TQWidget*)
khexedit2_widget->child("BytesEdit");
if (real_widget)
{
ok_ = true;
connect(real_widget, TQ_SIGNAL(bufferChanged(int, int)),
this, TQ_SLOT(memoryEdited(int, int)));
khexedit2_real_widget = real_widget;
TQVariant resize_style(2); // full size usage.
real_widget->setProperty("ResizeStyle", resize_style);
//TQVariant group(8);
//real_widget->setProperty("StartOffset", start);
//real_widget->setProperty("NoOfBytesPerLine", group);
// HACK: use hardcoded constant taht should match
// khexedit2
// 3 -- binary
// 1 -- decimal
// 0 -- hex
//TQVariant coding(3);
//real_widget->setProperty("Coding", coding);
//TQVariant gap(32);
//real_widget->setProperty("BinaryGapWidth", gap);
}
else
{
delete khexedit2_widget;
}
}
if (ok_) {
rangeSelector_ = new MemoryRangeSelector(this);
l->addWidget(rangeSelector_);
connect(rangeSelector_->okButton, TQ_SIGNAL(clicked()),
this, TQ_SLOT(slotChangeMemoryRange()));
connect(rangeSelector_->cancelButton, TQ_SIGNAL(clicked()),
this, TQ_SLOT(slotHideRangeDialog()));
connect(rangeSelector_->startAddressLineEdit,
TQ_SIGNAL(textChanged(const TQString&)),
this,
TQ_SLOT(slotEnableOrDisable()));
connect(rangeSelector_->amountLineEdit,
TQ_SIGNAL(textChanged(const TQString&)),
this,
TQ_SLOT(slotEnableOrDisable()));
l->addWidget(khexedit2_widget);
} else {
TQTextEdit* edit = new TQTextEdit(this);
l->addWidget(edit);
edit->setText(
"<h1>Not available</h1>"
"<p>Could not open the khexedit2 library. "
"Make sure that the KHexEdit package (part of tdeutils) is installed. "
"Specifically, check for the following files:"
"<ul><li>libkhexeditcommon.so.0.0.0\n"
"<li>libkbyteseditwidget.so\n"
"<li>kbyteseditwidget.desktop\n"
"</ul>");
}
}
void MemoryView::debuggerStateChanged(int state)
{
if (isOk())
{
debuggerState_ = state;
slotEnableOrDisable();
}
}
void MemoryView::slotHideRangeDialog()
{
rangeSelector_->hide();
}
void MemoryView::slotChangeMemoryRange()
{
controller_->addCommand(
new ExpressionValueCommand(
rangeSelector_->amountLineEdit->text(),
this, &MemoryView::sizeComputed));
}
void MemoryView::sizeComputed(const TQString& size)
{
controller_->addCommand(
new
GDBCommand(
TQString("-data-read-memory %1 x 1 1 %2")
.arg(rangeSelector_->startAddressLineEdit->text())
.arg(size).ascii(),
this,
&MemoryView::memoryRead));
}
void MemoryView::memoryRead(const GDBMI::ResultRecord& r)
{
const GDBMI::Value& content = r["memory"][0]["data"];
amount_ = content.size();
startAsString_ = rangeSelector_->startAddressLineEdit->text();
amountAsString_ = rangeSelector_->amountLineEdit->text();
start_ = startAsString_.toUInt(0, 0);
setCaption(TQString("%1 (%2 bytes)")
.arg(startAsString_).arg(amount_));
emit captionChanged(caption());
KHE::BytesEditInterface* bytesEditor
= KHE::bytesEditInterface(khexedit2_widget);
delete[] this->data_;
this->data_ = new char[amount_];
for(unsigned i = 0; i < content.size(); ++i)
{
this->data_[i] = content[i].literal().toInt(0, 16);
}
bytesEditor->setData( this->data_, amount_ );
bytesEditor->setReadOnly(false);
// Overwrite data, not insert new
bytesEditor->setOverwriteMode( true );
// Not sure this is needed, but prevent
// inserting new data.
bytesEditor->setOverwriteOnly( true );
TQVariant start_v(start_);
khexedit2_real_widget->setProperty("FirstLineOffset", start_v);
//TQVariant bsw(0);
//khexedit2_real_widget->setProperty("ByteSpacingWidth", bsw);
// HACK: use hardcoded constant taht should match
// khexedit2
// 3 -- binary
// 1 -- decimal
// 0 -- hex
//TQVariant coding(1);
//khexedit2_real_widget->setProperty("Coding", coding);
slotHideRangeDialog();
}
void MemoryView::memoryEdited(int start, int end)
{
for(int i = start; i <= end; ++i)
{
controller_->addCommand(
new GDBCommand(
TQString("set *(char*)(%1 + %2) = %3")
.arg(start_)
.arg(i)
.arg(TQString::number(data_[i]))));
}
}
void MemoryView::contextMenuEvent ( TQContextMenuEvent * e )
{
if (!isOk())
return;
TQPopupMenu menu;
bool app_running = !(debuggerState_ & s_appNotStarted);
int idRange = menu.insertItem(i18n("Change memory range"));
// If address selector is show, 'set memory range' can't
// do anything more.
menu.setItemEnabled(idRange,
app_running && !rangeSelector_->isShown());
int idReload = menu.insertItem(i18n("Reload"));
// If amount is zero, it means there's not data yet, so
// reloading does not make sense.
menu.setItemEnabled(idReload, app_running && amount_ != 0);
int idClose = menu.insertItem(i18n("Close this view"));
int result = menu.exec(e->globalPos());
if (result == idRange)
{
rangeSelector_->startAddressLineEdit->setText(startAsString_);
rangeSelector_->amountLineEdit->setText(amountAsString_);
rangeSelector_->show();
rangeSelector_->startAddressLineEdit->setFocus();
}
if (result == idReload)
{
// We use numeric start_ and amount_ stored in this,
// not textual startAsString_ and amountAsString_,
// because program position might have changes and expressions
// are no longer valid.
controller_->addCommand(
new
GDBCommand(
TQString("-data-read-memory %1 x 1 1 %2")
.arg(start_).arg(amount_).ascii(),
this,
&MemoryView::memoryRead));
}
if (result == idClose)
delete this;
}
bool MemoryView::isOk() const
{
return khexedit2_real_widget;
}
void MemoryView::slotEnableOrDisable()
{
bool app_started = !(debuggerState_ & s_appNotStarted);
bool enabled_ = app_started &&
!rangeSelector_->startAddressLineEdit->text().isEmpty() &&
!rangeSelector_->amountLineEdit->text().isEmpty();
rangeSelector_->okButton->setEnabled(enabled_);
}
ViewerWidget::ViewerWidget(GDBController* controller,
TQWidget* parent,
const char* name)
: TQWidget(parent, name),
controller_(controller)
{
setIcon(SmallIcon("math_brace"));
TQVBoxLayout *l = new TQVBoxLayout(this, 0, 0);
toolBox_ = new TQToolBox(this);
l->addWidget(toolBox_);
}
void ViewerWidget::slotAddMemoryView()
{
// For unclear reasons, this call, that indirectly
// does
//
// mainWindow()->setViewAvailable(this)
// mainWindow()->raiseView(this)
//
// should be done before creating the child widget.
// Otherwise, the child widget won't be freely resizable --
// there will be not-so-small minimum size.
// Problem exists both with KMDI and S/IDEAL.
setViewShown(true);
MemoryView* widget = new MemoryView(controller_, this);
toolBox_->addItem(widget, widget->caption());
toolBox_->setCurrentItem(widget);
memoryViews_.push_back(widget);
connect(widget, TQ_SIGNAL(captionChanged(const TQString&)),
this, TQ_SLOT(slotChildCaptionChanged(const TQString&)));
connect(widget, TQ_SIGNAL(destroyed(TQObject*)),
this, TQ_SLOT(slotChildDestroyed(TQObject*)));
}
void ViewerWidget::slotDebuggerState(const TQString&, int state)
{
for(unsigned i = 0; i < memoryViews_.size(); ++i)
{
memoryViews_[i]->debuggerStateChanged(state);
}
}
void ViewerWidget::slotChildCaptionChanged(const TQString& caption)
{
const TQWidget* s = static_cast<const TQWidget*>(sender());
TQWidget* ncs = const_cast<TQWidget*>(s);
TQString cap = caption;
// Prevent intepreting '&' as accelerator specifier.
cap.replace("&", "&&");
toolBox_->setItemLabel(toolBox_->indexOf(ncs), cap);
}
void ViewerWidget::slotChildDestroyed(TQObject* child)
{
TQValueVector<MemoryView*>::iterator i, e;
for(i = memoryViews_.begin(), e = memoryViews_.end(); i != e; ++i)
{
if (*i == child)
{
memoryViews_.erase(i);
break;
}
}
if (toolBox_->count() == 0)
setViewShown(false);
}
// **************************************************************************
// **************************************************************************
// **************************************************************************
}
#include "memviewdlg.moc"