/* * Copyright Johannes Sixt * This file is licensed under the GNU General Public License Version 2. * See the file COPYING in the toplevel directory of the source directory. */ #include "exprwnd.h" #include "exprwnd.moc" #include "typetable.h" #include #include #include #include #include /* icons */ #include /* i18n */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mydebug.h" VarTree::VarTree(VarTree* parent, TQListViewItem* after, ExprValue* v) : TQListViewItem(parent, after), m_varKind(v->m_varKind), m_nameKind(v->m_nameKind), m_type(0), m_exprIndex(0), m_exprIndexUseGuard(false), m_baseValue(v->m_value), m_baseChanged(false), m_structChanged(false) { TQListViewItem::setText(0, v->m_name); updateValueText(); setExpandable(m_varKind == VarTree::VKpointer); setOpen(v->m_initiallyExpanded); } VarTree::VarTree(ExprWnd* parent, TQListViewItem* after, const TQString& name) : TQListViewItem(parent, after), m_varKind(VKsimple), m_nameKind(VarTree::NKplain), m_type(0), m_exprIndex(0), m_exprIndexUseGuard(false), m_baseChanged(false), m_structChanged(false) { TQListViewItem::setText(0, name); } VarTree::~VarTree() { } void VarTree::paintCell(TQPainter* p, const TQColorGroup& cg, int column, int width, int align) { if (column == 1 && (m_baseChanged || m_structChanged)) { TQColorGroup cgChg = cg; cgChg.setColor(TQColorGroup::Text, TQt::red); TQListViewItem::paintCell(p, cgChg, column, width, align); } else { TQListViewItem::paintCell(p, cg, column, width, align); } } TQString VarTree::computeExpr() const { // top-level items are special if (isToplevelExpr()) return getText(); // get parent expr VarTree* par = static_cast(parent()); TQString parentExpr = par->computeExpr(); // skip this item's name if it is a base class or anonymous struct or union if (m_nameKind == NKtype || m_nameKind == NKanonymous) { return parentExpr; } /* augment by this item's text */ TQString result; /* if this is an address, dereference it */ if (m_nameKind == NKaddress) { ASSERT(par->m_varKind == VKpointer); result = "*" + parentExpr; return result; } switch (par->m_varKind) { case VKarray: { TQString index = getText(); int i = 1; // skip past the index while (index[i].isDigit()) i++; /* * Some array indices are actually ranges due to repeated array * values. We use the first index in these cases. */ if (index[i] != ']') { // remove second index index.remove(i, index.length()-i-1); } result = "(" + parentExpr + ")" + index; } break; case VKstruct: result = "(" + parentExpr + ")." + getText(); break; case VKsimple: /* parent can't be simple */ case VKpointer: /* handled in NKaddress */ case VKdummy: /* can't occur at all */ ASSERT(false); result = parentExpr; /* paranoia */ break; } return result; } bool VarTree::isToplevelExpr() const { return parent() == 0; } bool VarTree::isAncestorEq(const VarTree* child) const { const TQListViewItem* c = child; while (c != 0 && c != this) { c = c->parent(); } return c != 0; } bool VarTree::updateValue(const TQString& newValue) { // check whether the value changed bool prevValueChanged = m_baseChanged; if ((m_baseChanged = m_baseValue != newValue)) { m_baseValue = newValue; updateValueText(); } /* * We must repaint the cell if the value changed. If it did not change, * we still must repaint the cell if the value changed previously, * because the color of the display must be changed (from red to * black). */ return m_baseChanged || prevValueChanged; } bool VarTree::updateStructValue(const TQString& newValue) { // check whether the value changed bool prevValueChanged = m_structChanged; if ((m_structChanged = m_structValue != newValue)) { m_structValue = newValue; updateValueText(); } /* * We must repaint the cell if the value changed. If it did not change, * we still must repaint the cell if the value changed previously, * because the color of the display must be changed (from red to * black). */ return m_structChanged || prevValueChanged; } void VarTree::updateValueText() { if (m_baseValue.isEmpty()) { TQListViewItem::setText(1, m_structValue); } else if (m_structValue.isEmpty()) { TQListViewItem::setText(1, m_baseValue); } else { TQListViewItem::setText(1, m_baseValue + " " + m_structValue); } } void VarTree::inferTypesOfChildren(ProgramTypeTable& typeTable) { /* * Type inference works like this: We use type information of those * children that have a type name in their name (base classes) or in * their value (pointers) */ // first recurse children VarTree* child = firstChild(); while (child != 0) { child->inferTypesOfChildren(typeTable); child = child->nextSibling(); } // if this is a pointer, get the type from the value (less the pointer) if (m_varKind == VKpointer) { if (isWcharT()) { /* * wchart_t pointers must be treated as struct, because the array * of characters is printed similar to how TQStrings are decoded. */ m_varKind = VKstruct; setExpandable(false); } // don't know how to do this cleanly } else if (m_varKind == VKstruct) { // check if this is a base class part if (m_nameKind == NKtype) { const TQString& typeName = getText().mid(1, getText().length()-2); // strip < and > m_type = typeTable.lookup(typeName); /* if we don't have a type yet, get it from the base class */ if (m_type == 0) { m_type = inferTypeFromBaseClass(); /* * If there is a known type now, it is the one from the * first base class whose type we know. */ } /* * If we still don't have a type, the type is really unknown. */ if (m_type == 0) { m_type = TypeInfo::unknownType(); } } // else /* * This is not a base class part. We don't assign a type so * that later we can ask gdb. */ } } // the value contains the pointer type in parenthesis bool VarTree::isWcharT() const { return value().startsWith("(const wchar_t *)") || value().startsWith("(wchar_t *)"); } /* * Get the type of the first base class whose type we know. */ TypeInfo* VarTree::inferTypeFromBaseClass() { if (m_varKind == VKstruct) { VarTree* child = firstChild(); while (child != 0 && // only check base class parts (i.e. type names) child->m_nameKind == NKtype) { if (child->m_type != 0 && child->m_type != TypeInfo::unknownType()) { // got a type! return child->m_type; } child = child->nextSibling(); } } return 0; } ExprValue::ExprValue(const TQString& name, VarTree::NameKind aKind) : m_name(name), m_varKind(VarTree::VKsimple), m_nameKind(aKind), m_child(0), m_next(0), m_initiallyExpanded(false) { } ExprValue::~ExprValue() { delete m_child; delete m_next; } void ExprValue::appendChild(ExprValue* newChild) { if (m_child == 0) { m_child = newChild; } else { // walk chain of children to find the last one ExprValue* last = m_child; while (last->m_next != 0) last = last->m_next; last->m_next = newChild; } newChild->m_next = 0; // just to be sure } int ExprValue::childCount() const { int i = 0; ExprValue* c = m_child; while (c) { ++i; c = c->m_next; } return i; } ExprWnd::ExprWnd(TQWidget* parent, const TQString& colHeader, const char* name) : TQListView(parent, name), m_edit(0) { addColumn(colHeader); addColumn(i18n("Value")); setSorting(-1); // do not sort items setColumnWidthMode(0, Manual); setColumnWidthMode(1, Maximum); setRootIsDecorated(true); setAllColumnsShowFocus(true); m_pixPointer = UserIcon("pointer.xpm"); if (m_pixPointer.isNull()) TRACE("Can't load pointer.xpm"); } ExprWnd::~ExprWnd() { } TQStringList ExprWnd::exprList() const { TQStringList exprs; VarTree* item; for (item = firstChild(); item != 0; item = item->nextSibling()) { exprs.append(item->getText()); } return exprs; } VarTree* ExprWnd::insertExpr(ExprValue* expr, ProgramTypeTable& typeTable) { // append a new dummy expression VarTree* last = 0; // last top-level item for (VarTree* i = firstChild(); i != 0; i = i->nextSibling()) { last = i; } VarTree* display = new VarTree(this, last, expr->m_name); // replace it right away updateExpr(display, expr, typeTable); return display; } void ExprWnd::updateExpr(ExprValue* expr, ProgramTypeTable& typeTable) { // search the root variable VarTree* item = firstChild(); while (item != 0 && item->getText() != expr->m_name) item = item->nextSibling(); if (item == 0) { return; } // now update it updateExprRec(item, expr, typeTable); collectUnknownTypes(item); } void ExprWnd::updateExpr(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable) { updateExprRec(display, newValues, typeTable); collectUnknownTypes(display); } /* * returns true if there's a visible change */ void ExprWnd::updateExprRec(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable) { bool isExpanded = display->isOpen(); /* * If we are updating a pointer without children by a dummy, we don't * collapse it, but simply insert the new children. This happens when a * pointer has just been expanded by the user. */ if (display->m_varKind == VarTree::VKpointer && display->childCount() == 0 && newValues->m_varKind == VarTree::VKdummy) { replaceChildren(display, newValues); return; } /* * If the display and newValues have different kind or if their number * of children is different, replace the whole sub-tree. */ if (// the next two lines mean: not(m_varKind remains unchanged) !(newValues->m_varKind == VarTree::VKdummy || display->m_varKind == newValues->m_varKind) || (display->childCount() != newValues->childCount() && /* * If this is a pointer and newValues doesn't have children, we * don't replace the sub-tree; instead, below we mark this * sub-tree for requiring an update. */ (display->m_varKind != VarTree::VKpointer || newValues->m_child != 0))) { if (isExpanded) { display->setOpen(false); } // since children changed, it is likely that the type has also changed display->m_type = 0; /* will re-evaluate the type */ // display the new value updateSingleExpr(display, newValues); replaceChildren(display, newValues); // update the m_varKind if (newValues->m_varKind != VarTree::VKdummy) { display->m_varKind = newValues->m_varKind; display->setExpandable(newValues->m_varKind == VarTree::VKpointer); } // get some types (after the new m_varKind has been set!) display->inferTypesOfChildren(typeTable); // (note that the new value might not have a sub-tree at all) return; } // display the new value updateSingleExpr(display, newValues); /* * If this is an expanded pointer, record it for being updated. */ if (display->m_varKind == VarTree::VKpointer) { if (isExpanded && // if newValues is a dummy, we have already updated this pointer newValues->m_varKind != VarTree::VKdummy) { m_updatePtrs.push_back(display); } /* * If the visible sub-tree has children, but newValues doesn't, we * can stop here. */ if (newValues->m_child == 0) { return; } } ASSERT(display->childCount() == newValues->childCount()); // go for children VarTree* vDisplay = display->firstChild(); ExprValue* vNew = newValues->m_child; while (vDisplay != 0) { // check whether the names are the same if (vDisplay->getText() != vNew->m_name) { // set new name vDisplay->setText(vNew->m_name); } // recurse updateExprRec(vDisplay, vNew, typeTable); vDisplay = vDisplay->nextSibling(); vNew = vNew->m_next; } } void ExprWnd::updateSingleExpr(VarTree* display, ExprValue* newValue) { /* * If newValues is a VKdummy, we are only interested in its children. * No need to update anything here. */ if (newValue->m_varKind == VarTree::VKdummy) { return; } /* * If this node is a struct and we know its type then we know how to * find a nested value. So register the node for an update. * * wchar_t types are also treated specially here: We consider them * as struct (has been set in inferTypesOfChildren()). */ if (display->m_varKind == VarTree::VKstruct && display->m_type != 0 && display->m_type != TypeInfo::unknownType()) { ASSERT(newValue->m_varKind == VarTree::VKstruct); if (display->m_type == TypeInfo::wchartType()) { display->m_partialValue = "L"; } else display->m_partialValue = display->m_type->m_displayString[0]; m_updateStruct.push_back(display); } if (display->updateValue(newValue->m_value)) { triggerUpdate(); } } void ExprWnd::updateStructValue(VarTree* display) { ASSERT(display->m_varKind == VarTree::VKstruct); if (display->updateStructValue(display->m_partialValue)) { triggerUpdate(); } // reset the value display->m_partialValue = ""; display->m_exprIndex = -1; } void ExprWnd::replaceChildren(VarTree* display, ExprValue* newValues) { ASSERT(display->childCount() == 0 || display->m_varKind != VarTree::VKsimple); // delete all children of display while (VarTree* c = display->firstChild()) { unhookSubtree(c); delete c; } // insert copies of the newValues VarTree* vNew = 0; for (ExprValue* v = newValues->m_child; v != 0; v = v->m_next) { vNew = new VarTree(display, vNew, v); // recurse replaceChildren(vNew, v); } } void ExprWnd::collectUnknownTypes(VarTree* var) { TQListViewItemIterator i(var); for (; i.current(); ++i) { checkUnknownType(static_cast(i.current())); } } void ExprWnd::checkUnknownType(VarTree* var) { ASSERT(var->m_varKind != VarTree::VKpointer || var->m_nameKind != VarTree::NKtype); if (var->m_type == 0 && var->m_varKind == VarTree::VKstruct && var->m_nameKind != VarTree::NKtype && var->m_nameKind != VarTree::NKanonymous) { if (!var->isWcharT()) { /* this struct node doesn't have a type yet: register it */ m_updateType.push_back(var); } else { var->m_type = TypeInfo::wchartType(); var->m_partialValue = "L"; m_updateStruct.push_back(var); } } // add pointer pixmap to pointers if (var->m_varKind == VarTree::VKpointer) { var->setPixmap(m_pixPointer); } } TQString ExprWnd::formatWCharPointer(TQString value) { int pos = value.find(") "); if (pos > 0) value = value.mid(pos+2); return value + " L"; } VarTree* ExprWnd::topLevelExprByName(const TQString& name) const { VarTree* item = firstChild(); while (item != 0 && item->getText() != name) item = item->nextSibling(); return item; } VarTree* ExprWnd::ptrMemberByName(VarTree* v, const TQString& name) { // v must be a pointer variable, must have children if (v->m_varKind != VarTree::VKpointer || v->childCount() == 0) return 0; // the only child of v is the pointer value that represents the struct VarTree* item = v->firstChild(); return memberByName(item, name); } VarTree* ExprWnd::memberByName(VarTree* v, const TQString& name) { // search immediate children for name VarTree* item = v->firstChild(); while (item != 0 && item->getText() != name) item = item->nextSibling(); if (item != 0) return item; // try in base classes and members that are anonymous structs or unions item = v->firstChild(); while (item != 0) { if (item->m_nameKind == VarTree::NKtype || item->m_nameKind == VarTree::NKanonymous) { v = memberByName(item, name); if (v != 0) return v; } item = item->nextSibling(); } return 0; } void ExprWnd::removeExpr(VarTree* item) { unhookSubtree(item); delete item; } void ExprWnd::unhookSubtree(VarTree* subTree) { // must remove any pointers scheduled for update from the list unhookSubtree(m_updatePtrs, subTree); unhookSubtree(m_updateType, subTree); unhookSubtree(m_updateStruct, subTree); emit removingItem(subTree); } void ExprWnd::unhookSubtree(std::list& list, VarTree* subTree) { if (subTree == 0) return; std::list::iterator i = list.begin(); while (i != list.end()) { std::list::iterator checkItem = i; ++i; if (subTree->isAncestorEq(*checkItem)) { // checkItem is an item from subTree list.erase(checkItem); } } } void ExprWnd::clearPendingUpdates() { m_updatePtrs.clear(); m_updateType.clear(); m_updateStruct.clear(); } VarTree* ExprWnd::nextUpdatePtr() { VarTree* ptr = 0; if (!m_updatePtrs.empty()) { ptr = m_updatePtrs.front(); m_updatePtrs.pop_front(); } return ptr; } VarTree* ExprWnd::nextUpdateType() { VarTree* ptr = 0; if (!m_updateType.empty()) { ptr = m_updateType.front(); m_updateType.pop_front(); } return ptr; } VarTree* ExprWnd::nextUpdateStruct() { VarTree* ptr = 0; if (!m_updateStruct.empty()) { ptr = m_updateStruct.front(); m_updateStruct.pop_front(); } return ptr; } void ExprWnd::editValue(VarTree* item, const TQString& text) { if (m_edit == 0) m_edit = new ValueEdit(this); TQRect r = itemRect(item); int x = r.x()+columnWidth(0); int y = r.y(); int w = columnWidth(1); int h = r.height(); TQListView* lv = item->listView(); /* * Make the edit widget at least 5 characters wide (but not wider than * this widget). If less than half of this widget is used to display * the text, scroll this widget so that half of it shows the text (or * less than half of it if the text is shorter). */ TQFontMetrics metr = m_edit->font(); int wMin = metr.width("88888"); if (w < wMin) w = wMin; int wThis = lv->visibleWidth(); if (x >= wThis/2 && // less than half the width displays text x+w > wThis) // not all text is visible { // scroll so that more text is visible int wScroll = TQMIN(x-wThis/2, x+w-wThis); lv->scrollBy(wScroll, 0); x -= wScroll; } else if (x < 0) { // don't let the edit move out at the left x = 0; } // make the edit box as wide as the visible column TQRect rect(x,y, wThis-x,h); m_edit->setText(text); m_edit->selectAll(); m_edit->setGeometry(rect); m_edit->m_finished = false; m_edit->m_item = item; m_edit->show(); m_edit->setFocus(); } bool ExprWnd::isEditing() const { return m_edit != 0 && m_edit->isVisible(); } ValueEdit::ValueEdit(ExprWnd* parent) : TQLineEdit(parent->viewport(), "valueedit") { setFrame(false); hide(); lower(); // lower the window below scrollbars connect(parent, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged())); connect(parent, SIGNAL(currentChanged(TQListViewItem*)), SLOT(slotSelectionChanged())); connect(parent, SIGNAL(expanded(TQListViewItem*)), SLOT(slotSelectionChanged())); connect(parent, SIGNAL(collapsed(TQListViewItem*)), SLOT(slotSelectionChanged())); connect(this, SIGNAL(done(VarTree*, const TQString&)), parent, SIGNAL(editValueCommitted(VarTree*, const TQString&))); } ValueEdit::~ValueEdit() { } void ValueEdit::terminate(bool commit) { TRACE(commit?"ValueEdit::terminate(true)":"ValueEdit::terminate(false)"); if (!m_finished) { m_finished = true; hide(); // will call focusOutEvent, that's why we need m_finished if (commit) { emit done(m_item, text()); } } } void ValueEdit::keyPressEvent(TQKeyEvent *e) { if(e->key() == TQt::Key_Return || e->key() == TQt::Key_Enter) terminate(true); else if(e->key() == TQt::Key_Escape) terminate(false); else TQLineEdit::keyPressEvent(e); } void ValueEdit::paintEvent(TQPaintEvent* e) { TQLineEdit::paintEvent(e); TQPainter p(this); p.drawRect(rect()); } void ValueEdit::focusOutEvent(TQFocusEvent* ev) { TRACE("ValueEdit::focusOutEvent"); TQFocusEvent* focusEv = static_cast(ev); if (focusEv->reason() == TQFocusEvent::ActiveWindow) { // Switching to a different window should terminate the edit, // because if the window with this variable display is floating // then that different window could be the main window, where // the user had clicked one of the Execute buttons. This in turn // may pull the item away that we are editing here. terminate(false); } // Don't let a RMB close the editor else if (focusEv->reason() != TQFocusEvent::Popup) { terminate(true); } } void ValueEdit::slotSelectionChanged() { TRACE("ValueEdit::slotSelectionChanged"); terminate(false); }