/*************************************************************************** begin : Tue Oct 5 1999 copyright : (C) 1999 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 "memviewdlg.h" #include "gdbcontroller.h" #include "gdbcommand.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ************************************************************************** // // 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 tqlayout: 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, TQT_SIGNAL(returnPressed()), okButton, TQT_SLOT(animateClick())); connect(amountLineEdit, TQT_SIGNAL(returnPressed()), okButton, TQT_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, TQT_SIGNAL(bufferChanged(int, int)), this, TQT_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, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotChangeMemoryRange())); connect(rangeSelector_->cancelButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotHideRangeDialog())); connect(rangeSelector_->startAddressLineEdit, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotEnableOrDisable())); connect(rangeSelector_->amountLineEdit, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotEnableOrDisable())); l->addWidget(khexedit2_widget); } else { TQTextEdit* edit = new TQTextEdit(this); l->addWidget(edit); edit->setText( "

Not available

" "

Could not open the khexedit2 library. " "Make sure that the KHexEdit package (part of tdeutils) is installed. " "Specifically, check for the following files:" "

  • libkhexeditcommon.so.0.0.0\n" "
  • libkbyteseditwidget.so\n" "
  • kbyteseditwidget.desktop\n" "
"); } } 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, TQT_SIGNAL(captionChanged(const TQString&)), this, TQT_SLOT(slotChildCaptionChanged(const TQString&))); connect(widget, TQT_SIGNAL(destroyed(TQObject*)), this, TQT_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(sender()); TQWidget* ncs = const_cast(s); TQString cap = caption; // Prevent intepreting '&' as accelerator specifier. cap.replace("&", "&&"); toolBox_->setItemLabel(toolBox_->indexOf(ncs), cap); } void ViewerWidget::slotChildDestroyed(TQObject* child) { TQValueVector::iterator i, e; for(i = memoryViews_.begin(), e = memoryViews_.end(); i != e; ++i) { if (TQT_BASE_OBJECT(*i) == TQT_BASE_OBJECT(child)) { memoryViews_.erase(i); break; } } if (toolBox_->count() == 0) setViewShown(false); } // ************************************************************************** // ************************************************************************** // ************************************************************************** } #include "memviewdlg.moc"