Keep the regular values and the "quick member" values separated.
[kdbg.git] / kdbg / exprwnd.cpp
blob6a439f98fede037206aa667940dd3659724a6364
1 // $Id$
3 // Copyright by Johannes Sixt
4 // This file is under GPL, the GNU General Public Licence
6 #include "exprwnd.h"
7 #include "exprwnd.moc"
8 #include "typetable.h"
9 #include <qstrlist.h>
10 #include <qpainter.h>
11 #include <qscrollbar.h>
12 #include <kapp.h>
13 #include <kiconloader.h> /* icons */
14 #include <klocale.h> /* i18n */
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif
18 #include "mydebug.h"
20 VarTree::VarTree(VarTree* parent, QListViewItem* after, ExprValue* v) :
21 QListViewItem(parent, after),
22 m_varKind(v->m_varKind),
23 m_nameKind(v->m_nameKind),
24 m_type(0),
25 m_exprIndex(0),
26 m_exprIndexUseGuard(false),
27 m_baseChanged(false),
28 m_structChanged(false)
30 QListViewItem::setText(0, v->m_name);
31 QListViewItem::setText(1, v->m_value);
32 setExpandable(m_varKind == VarTree::VKpointer);
33 setOpen(v->m_initiallyExpanded);
36 VarTree::VarTree(ExprWnd* parent, QListViewItem* after, const QString& name) :
37 QListViewItem(parent, after),
38 m_varKind(VKsimple),
39 m_nameKind(VarTree::NKplain),
40 m_type(0),
41 m_exprIndex(0),
42 m_exprIndexUseGuard(false),
43 m_baseChanged(false),
44 m_structChanged(false)
46 QListViewItem::setText(0, name);
49 VarTree::~VarTree()
53 void VarTree::paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align)
55 if (column == 1 && (m_baseChanged || m_structChanged)) {
56 QColorGroup cgChg = cg;
57 cgChg.setColor(QColorGroup::Text, Qt::red);
58 QListViewItem::paintCell(p, cgChg, column, width, align);
59 } else {
60 QListViewItem::paintCell(p, cg, column, width, align);
64 QString VarTree::computeExpr() const
66 // top-level items are special
67 if (isToplevelExpr())
68 return getText();
70 // get parent expr
71 VarTree* par = static_cast<VarTree*>(parent());
72 QString parentExpr = par->computeExpr();
74 /* don't add this item's name if this is a base class sub-item */
75 if (m_nameKind == NKtype) {
76 return parentExpr;
78 /* augment by this item's text */
79 QString result;
80 /* if this is an address, dereference it */
81 if (m_nameKind == NKaddress) {
82 ASSERT(par->m_varKind == VKpointer);
83 result = "*" + parentExpr;
84 return result;
86 switch (par->m_varKind) {
87 case VKarray:
89 QString index = getText();
90 int i = 1;
91 // skip past the index
92 while (index[i].isDigit())
93 i++;
95 * Some array indices are actually ranges due to repeated array
96 * values. We use the first index in these cases.
98 if (index[i] != ']') {
99 // remove second index
100 index.remove(i, index.length()-i-1);
102 result = "(" + parentExpr + ")" + index;
104 break;
105 case VKstruct:
106 result = "(" + parentExpr + ")." + getText();
107 break;
108 case VKsimple: /* parent can't be simple */
109 case VKpointer: /* handled in NKaddress */
110 case VKdummy: /* can't occur at all */
111 ASSERT(false);
112 result = parentExpr; /* paranoia */
113 break;
115 return result;
118 bool VarTree::isToplevelExpr() const
120 return parent() == 0;
123 bool VarTree::isAncestorEq(const VarTree* child) const
125 const QListViewItem* c = child;
126 while (c != 0 && c != this) {
127 c = c->parent();
129 return c != 0;
132 bool VarTree::updateValue(const QString& newValue)
134 // check whether the value changed
135 bool prevValueChanged = m_baseChanged;
136 if ((m_baseChanged = m_baseValue != newValue)) {
137 m_baseValue = newValue;
138 updateValueText();
141 * We must repaint the cell if the value changed. If it did not change,
142 * we still must repaint the cell if the value changed previously,
143 * because the color of the display must be changed (from red to
144 * black).
146 return m_baseChanged || prevValueChanged;
149 bool VarTree::updateStructValue(const QString& newValue)
151 // check whether the value changed
152 bool prevValueChanged = m_structChanged;
153 if ((m_structChanged = m_structValue != newValue)) {
154 m_structValue = newValue;
155 updateValueText();
158 * We must repaint the cell if the value changed. If it did not change,
159 * we still must repaint the cell if the value changed previously,
160 * because the color of the display must be changed (from red to
161 * black).
163 return m_structChanged || prevValueChanged;
166 void VarTree::updateValueText()
168 if (m_baseValue.isEmpty()) {
169 QListViewItem::setText(1, m_structValue);
170 } else if (m_structValue.isEmpty()) {
171 QListViewItem::setText(1, m_baseValue);
172 } else {
173 QListViewItem::setText(1, m_baseValue + " " + m_structValue);
177 void VarTree::inferTypesOfChildren(ProgramTypeTable& typeTable)
180 * Type inference works like this: We use type information of those
181 * children that have a type name in their name (base classes) or in
182 * their value (pointers)
185 // first recurse children
186 VarTree* child = firstChild();
187 while (child != 0) {
188 child->inferTypesOfChildren(typeTable);
189 child = child->nextSibling();
192 // if this is a pointer, get the type from the value (less the pointer)
193 if (m_varKind == VKpointer) {
194 if (isWcharT())
197 * wchart_t pointers must be treated as struct, because the array
198 * of characters is printed similar to how QStrings are decoded.
200 m_varKind = VKstruct;
201 setExpandable(false);
203 // don't know how to do this cleanly
204 } else if (m_varKind == VKstruct) {
205 // check if this is a base class part
206 if (m_nameKind == NKtype) {
207 const QString& typeName =
208 getText().mid(1, getText().length()-2); // strip < and >
209 m_type = typeTable.lookup(typeName);
211 /* if we don't have a type yet, get it from the base class */
212 if (m_type == 0) {
213 m_type = inferTypeFromBaseClass();
215 * If there is a known type now, it is the one from the
216 * first base class whose type we know.
221 * If we still don't have a type, the type is really unknown.
223 if (m_type == 0) {
224 m_type = TypeInfo::unknownType();
226 } // else
228 * This is not a base class part. We don't assign a type so
229 * that later we can ask gdb.
234 // the value contains the pointer type in parenthesis
235 bool VarTree::isWcharT() const
237 return value().startsWith("(const wchar_t *)") ||
238 value().startsWith("(wchar_t *)");
242 * Get the type of the first base class whose type we know.
244 TypeInfo* VarTree::inferTypeFromBaseClass()
246 if (m_varKind == VKstruct) {
247 VarTree* child = firstChild();
248 while (child != 0 &&
249 // only check base class parts (i.e. type names)
250 child->m_nameKind == NKtype)
252 if (child->m_type != 0 &&
253 child->m_type != TypeInfo::unknownType())
255 // got a type!
256 return child->m_type;
258 child = child->nextSibling();
261 return 0;
264 ExprValue::ExprValue(const QString& name, VarTree::NameKind aKind) :
265 m_name(name),
266 m_varKind(VarTree::VKsimple),
267 m_nameKind(aKind),
268 m_child(0),
269 m_next(0),
270 m_initiallyExpanded(false)
274 ExprValue::~ExprValue()
276 delete m_child;
277 delete m_next;
280 void ExprValue::appendChild(ExprValue* newChild)
282 if (m_child == 0) {
283 m_child = newChild;
284 } else {
285 // walk chain of children to find the last one
286 ExprValue* last = m_child;
287 while (last->m_next != 0)
288 last = last->m_next;
289 last->m_next = newChild;
291 newChild->m_next = 0; // just to be sure
294 int ExprValue::childCount() const
296 int i = 0;
297 ExprValue* c = m_child;
298 while (c) {
299 ++i;
300 c = c->m_next;
302 return i;
307 ExprWnd::ExprWnd(QWidget* parent, const QString& colHeader, const char* name) :
308 QListView(parent, name),
309 m_edit(0)
311 addColumn(colHeader);
312 addColumn(i18n("Value"));
313 setSorting(-1); // do not sort items
314 setColumnWidthMode(0, Manual);
315 setColumnWidthMode(1, Maximum);
316 setRootIsDecorated(true);
318 m_pixPointer = UserIcon("pointer.xpm");
319 if (m_pixPointer.isNull())
320 TRACE("Can't load pointer.xpm");
323 ExprWnd::~ExprWnd()
327 void ExprWnd::exprList(QStrList& exprs)
329 // ASSERT(exprs does deep-copies)
330 VarTree* item;
331 for (item = firstChild(); item != 0; item = item->nextSibling()) {
332 exprs.append(item->getText());
336 VarTree* ExprWnd::insertExpr(ExprValue* expr, ProgramTypeTable& typeTable)
338 // append a new dummy expression
339 VarTree* last = 0; // last top-level item
340 for (VarTree* i = firstChild(); i != 0; i = i->nextSibling()) {
341 last = i;
343 VarTree* display = new VarTree(this, last, expr->m_name);
345 // replace it right away
346 updateExpr(display, expr, typeTable);
347 return display;
350 void ExprWnd::updateExpr(ExprValue* expr, ProgramTypeTable& typeTable)
352 // search the root variable
353 VarTree* item = firstChild();
354 while (item != 0 && item->getText() != expr->m_name)
355 item = item->nextSibling();
356 if (item == 0) {
357 return;
359 // now update it
360 if (updateExprRec(item, expr, typeTable)) {
361 triggerUpdate();
363 collectUnknownTypes(item);
366 void ExprWnd::updateExpr(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable)
368 if (updateExprRec(display, newValues, typeTable) &&
369 display->isVisible())
371 triggerUpdate();
373 collectUnknownTypes(display);
377 * returns true if there's a visible change
379 bool ExprWnd::updateExprRec(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable)
381 bool isExpanded = display->isOpen();
384 * If we are updating a pointer without children by a dummy, we don't
385 * collapse it, but simply insert the new children. This happens when a
386 * pointer has just been expanded by the user.
388 if (display->m_varKind == VarTree::VKpointer &&
389 display->childCount() == 0 &&
390 newValues->m_varKind == VarTree::VKdummy)
392 replaceChildren(display, newValues);
393 return isExpanded; /* no visible change if not expanded */
397 * If the display and newValues have different kind or if their number
398 * of children is different, replace the whole sub-tree.
400 if (// the next two lines mean: not(m_varKind remains unchanged)
401 !(newValues->m_varKind == VarTree::VKdummy ||
402 display->m_varKind == newValues->m_varKind)
404 (display->childCount() != newValues->childCount() &&
406 * If this is a pointer and newValues doesn't have children, we
407 * don't replace the sub-tree; instead, below we mark this
408 * sub-tree for requiring an update.
410 (display->m_varKind != VarTree::VKpointer ||
411 newValues->m_child != 0)))
413 if (isExpanded) {
414 display->setOpen(false);
417 // since children changed, it is likely that the type has also changed
418 display->m_type = 0; /* will re-evaluate the type */
420 // display the new value
421 updateSingleExpr(display, newValues);
422 replaceChildren(display, newValues);
424 // update the m_varKind
425 if (newValues->m_varKind != VarTree::VKdummy) {
426 display->m_varKind = newValues->m_varKind;
427 display->setExpandable(newValues->m_varKind == VarTree::VKpointer);
430 // get some types (after the new m_varKind has been set!)
431 display->inferTypesOfChildren(typeTable);
433 // (note that the new value might not have a sub-tree at all)
434 return isExpanded; /* no visible change if not expanded */
437 // display the new value
438 updateSingleExpr(display, newValues);
441 * If this is an expanded pointer, record it for being updated.
443 if (display->m_varKind == VarTree::VKpointer) {
444 if (isExpanded &&
445 // if newValues is a dummy, we have already updated this pointer
446 newValues->m_varKind != VarTree::VKdummy)
448 m_updatePtrs.append(display);
451 * If the visible sub-tree has children, but newValues doesn't, we
452 * can stop here.
454 if (newValues->m_child == 0) {
455 return false;
459 ASSERT(display->childCount() == newValues->childCount());
461 // go for children
462 bool childChanged = false;
464 VarTree* vDisplay = display->firstChild();
465 ExprValue* vNew = newValues->m_child;
466 while (vDisplay != 0) {
467 // check whether the names are the same
468 if (vDisplay->getText() != vNew->m_name) {
469 // set new name
470 vDisplay->setText(vNew->m_name);
472 childChanged = true;
475 // recurse
476 if (updateExprRec(vDisplay, vNew, typeTable)) {
477 childChanged = true;
479 vDisplay = vDisplay->nextSibling();
480 vNew = vNew->m_next;
483 // update of children propagates only if this node is expanded
484 return display->isOpen() && childChanged;
487 void ExprWnd::updateSingleExpr(VarTree* display, ExprValue* newValue)
490 * If newValues is a VKdummy, we are only interested in its children.
491 * No need to update anything here.
493 if (newValue->m_varKind == VarTree::VKdummy) {
494 return;
498 * If this node is a struct and we know its type then we know how to
499 * find a nested value. So register the node for an update.
501 * wchar_t types are also treated specially here: We consider them
502 * as struct (has been set in inferTypesOfChildren()).
504 if (display->m_varKind == VarTree::VKstruct &&
505 display->m_type != 0 &&
506 display->m_type != TypeInfo::unknownType())
508 ASSERT(newValue->m_varKind == VarTree::VKstruct);
509 if (display->m_type == TypeInfo::wchartType())
511 display->m_partialValue = "L";
513 else
514 display->m_partialValue = display->m_type->m_displayString[0];
515 m_updateStruct.append(display);
518 if (display->updateValue(newValue->m_value)) {
519 triggerUpdate();
523 void ExprWnd::updateStructValue(VarTree* display)
525 ASSERT(display->m_varKind == VarTree::VKstruct);
527 if (display->updateStructValue(display->m_partialValue)) {
528 triggerUpdate();
530 // reset the value
531 display->m_partialValue = "";
532 display->m_exprIndex = -1;
535 void ExprWnd::replaceChildren(VarTree* display, ExprValue* newValues)
537 ASSERT(display->childCount() == 0 || display->m_varKind != VarTree::VKsimple);
539 // delete all children of display
540 while (VarTree* c = display->firstChild()) {
541 unhookSubtree(c);
542 delete c;
544 // insert copies of the newValues
545 VarTree* vNew = 0;
546 for (ExprValue* v = newValues->m_child; v != 0; v = v->m_next)
548 vNew = new VarTree(display, vNew, v);
549 // recurse
550 replaceChildren(vNew, v);
554 void ExprWnd::collectUnknownTypes(VarTree* var)
556 QListViewItemIterator i(var);
557 for (; i.current(); ++i)
559 checkUnknownType(static_cast<VarTree*>(i.current()));
563 void ExprWnd::checkUnknownType(VarTree* var)
565 ASSERT(var->m_varKind != VarTree::VKpointer || var->m_nameKind != VarTree::NKtype);
566 if (var->m_type == 0 &&
567 var->m_varKind == VarTree::VKstruct &&
568 var->m_nameKind != VarTree::NKtype)
570 if (!var->isWcharT())
572 /* this struct node doesn't have a type yet: register it */
573 m_updateType.append(var);
575 else
577 var->m_type = TypeInfo::wchartType();
578 var->m_partialValue = "L";
579 m_updateStruct.append(var);
582 // add pointer pixmap to pointers
583 if (var->m_varKind == VarTree::VKpointer) {
584 var->setPixmap(m_pixPointer);
588 QString ExprWnd::formatWCharPointer(QString value)
590 int pos = value.find(") ");
591 if (pos > 0)
592 value = value.mid(pos+2);
593 return value + " L";
597 VarTree* ExprWnd::topLevelExprByName(const char* name)
599 VarTree* item = firstChild();
600 while (item != 0 && item->getText() != name)
601 item = item->nextSibling();
603 return item;
606 VarTree* ExprWnd::ptrMemberByName(VarTree* v, const QString& name)
608 // v must be a pointer variable, must have children
609 if (v->m_varKind != VarTree::VKpointer || v->childCount() == 0)
610 return 0;
612 // the only child of v is the pointer value that represents the struct
613 VarTree* item = v->firstChild();
614 return memberByName(item, name);
617 VarTree* ExprWnd::memberByName(VarTree* v, const QString& name)
619 // search immediate children for name
620 VarTree* item = v->firstChild();
621 while (item != 0 && item->getText() != name)
622 item = item->nextSibling();
624 if (item != 0)
625 return item;
627 // try in base classes
628 item = v->firstChild();
629 while (item != 0 &&
630 item->m_nameKind == VarTree::NKtype)
632 v = memberByName(item, name);
633 if (v != 0)
634 return v;
635 item = item->nextSibling();
637 return 0;
640 void ExprWnd::removeExpr(VarTree* item)
642 unhookSubtree(item);
644 delete item;
647 void ExprWnd::unhookSubtree(VarTree* subTree)
649 // must remove any pointers scheduled for update from the list
650 unhookSubtree(m_updatePtrs, subTree);
651 unhookSubtree(m_updateType, subTree);
652 unhookSubtree(m_updateStruct, subTree);
653 emit removingItem(subTree);
656 void ExprWnd::unhookSubtree(QList<VarTree>& list, VarTree* subTree)
658 if (subTree == 0)
659 return;
661 VarTree* checkItem = list.first();
662 while (checkItem != 0) {
663 if (!subTree->isAncestorEq(checkItem)) {
664 // checkItem is not an item from subTree
665 // advance
666 checkItem = list.next();
667 } else {
668 // checkItem is an item from subTree
670 * If checkItem is the last item in the list, we need a special
671 * treatment, because remove()ing it steps the current item of
672 * the list in the "wrong" direction.
674 if (checkItem == list.getLast()) { // does not set current item
675 list.remove();
676 /* we deleted the last element, so we've finished */
677 checkItem = 0;
678 } else {
679 list.remove();
680 /* remove() advanced already */
681 checkItem = list.current();
687 void ExprWnd::clearPendingUpdates()
689 m_updatePtrs.clear();
690 m_updateType.clear();
691 m_updateStruct.clear();
694 VarTree* ExprWnd::nextUpdatePtr()
696 VarTree* ptr = m_updatePtrs.first();
697 if (ptr != 0) {
698 m_updatePtrs.remove();
700 return ptr;
703 VarTree* ExprWnd::nextUpdateType()
705 VarTree* ptr = m_updateType.first();
706 if (ptr != 0) {
707 m_updateType.remove();
709 return ptr;
712 VarTree* ExprWnd::nextUpdateStruct()
714 VarTree* ptr = m_updateStruct.first();
715 if (ptr != 0) {
716 m_updateStruct.remove();
718 return ptr;
722 void ExprWnd::editValue(VarTree* item, const QString& text)
724 if (m_edit == 0)
725 m_edit = new ValueEdit(this);
727 QRect r = itemRect(item);
728 int x = r.x()+columnWidth(0);
729 int y = r.y();
730 int w = columnWidth(1);
731 int h = r.height();
732 QListView* lv = item->listView();
735 * Make the edit widget at least 5 characters wide (but not wider than
736 * this widget). If less than half of this widget is used to display
737 * the text, scroll this widget so that half of it shows the text (or
738 * less than half of it if the text is shorter).
740 QFontMetrics metr = m_edit->font();
741 int wMin = metr.width("88888");
742 if (w < wMin)
743 w = wMin;
744 int wThis = lv->visibleWidth();
745 if (x >= wThis/2 && // less than half the width displays text
746 x+w > wThis) // not all text is visible
748 // scroll so that more text is visible
749 int wScroll = QMIN(x-wThis/2, x+w-wThis);
750 lv->scrollBy(wScroll, 0);
751 x -= wScroll;
753 else if (x < 0)
755 // don't let the edit move out at the left
756 x = 0;
759 // make the edit box as wide as the visible column
760 QRect rect(x,y, wThis-x,h);
761 m_edit->setText(text);
762 m_edit->selectAll();
764 m_edit->setGeometry(rect);
765 m_edit->m_finished = false;
766 m_edit->m_item = item;
767 m_edit->show();
768 m_edit->setFocus();
771 bool ExprWnd::isEditing() const
773 return m_edit != 0 && m_edit->isVisible();
777 ValueEdit::ValueEdit(ExprWnd* parent) :
778 QLineEdit(parent->viewport(), "valueedit")
780 setFrame(false);
781 hide();
782 lower(); // lower the window below scrollbars
783 connect(parent, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged()));
784 connect(parent, SIGNAL(currentChanged(QListViewItem*)), SLOT(slotSelectionChanged()));
785 connect(parent, SIGNAL(expanded(QListViewItem*)), SLOT(slotSelectionChanged()));
786 connect(parent, SIGNAL(collapsed(QListViewItem*)), SLOT(slotSelectionChanged()));
787 connect(this, SIGNAL(done(VarTree*, const QString&)),
788 parent, SIGNAL(editValueCommitted(VarTree*, const QString&)));
791 ValueEdit::~ValueEdit()
795 void ValueEdit::terminate(bool commit)
797 TRACE(commit?"ValueEdit::terminate(true)":"ValueEdit::terminate(false)");
798 if (!m_finished)
800 m_finished = true;
801 hide(); // will call focusOutEvent, that's why we need m_finished
802 if (commit) {
803 emit done(m_item, text());
808 void ValueEdit::keyPressEvent(QKeyEvent *e)
810 if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)
811 terminate(true);
812 else if(e->key() == Qt::Key_Escape)
813 terminate(false);
814 else
815 QLineEdit::keyPressEvent(e);
818 void ValueEdit::paintEvent(QPaintEvent* e)
820 QLineEdit::paintEvent(e);
822 QPainter p(this);
823 p.drawRect(rect());
826 void ValueEdit::focusOutEvent(QFocusEvent* ev)
828 TRACE("ValueEdit::focusOutEvent");
829 QFocusEvent* focusEv = static_cast<QFocusEvent*>(ev);
830 // Don't let a RMB close the editor
831 if (focusEv->reason() != QFocusEvent::Popup &&
832 focusEv->reason() != QFocusEvent::ActiveWindow)
834 terminate(true);
838 void ValueEdit::slotSelectionChanged()
840 TRACE("ValueEdit::slotSelectionChanged");
841 terminate(false);