Do not insert new values in reversed order.
[kdbg.git] / kdbg / exprwnd.cpp
blobfc462ed81dc60877ac77950eeed47d0d2ef3e77e
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_valueChanged(false),
25 m_type(0),
26 m_exprIndex(0),
27 m_exprIndexUseGuard(false)
29 QListViewItem::setText(0, v->m_name);
30 QListViewItem::setText(1, v->m_value);
31 setExpandable(m_varKind == VarTree::VKpointer);
32 setOpen(v->m_initiallyExpanded);
35 VarTree::VarTree(ExprWnd* parent, QListViewItem* after, const QString& name) :
36 QListViewItem(parent, after),
37 m_varKind(VKsimple),
38 m_nameKind(VarTree::NKplain),
39 m_valueChanged(false),
40 m_type(0),
41 m_exprIndex(0),
42 m_exprIndexUseGuard(false)
44 QListViewItem::setText(0, name);
47 VarTree::~VarTree()
51 void VarTree::paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align)
53 if (column == 1 && m_valueChanged) {
54 QColorGroup cgChg = cg;
55 cgChg.setColor(QColorGroup::Text, Qt::red);
56 QListViewItem::paintCell(p, cgChg, column, width, align);
57 } else {
58 QListViewItem::paintCell(p, cg, column, width, align);
62 QString VarTree::computeExpr() const
64 // top-level items are special
65 if (isToplevelExpr())
66 return getText();
68 // get parent expr
69 VarTree* par = static_cast<VarTree*>(parent());
70 QString parentExpr = par->computeExpr();
72 /* don't add this item's name if this is a base class sub-item */
73 if (m_nameKind == NKtype) {
74 return parentExpr;
76 /* augment by this item's text */
77 QString result;
78 /* if this is an address, dereference it */
79 if (m_nameKind == NKaddress) {
80 ASSERT(par->m_varKind == VKpointer);
81 result = "*" + parentExpr;
82 return result;
84 switch (par->m_varKind) {
85 case VKarray:
87 QString index = getText();
88 int i = 1;
89 // skip past the index
90 while (index[i].isDigit())
91 i++;
93 * Some array indices are actually ranges due to repeated array
94 * values. We use the first index in these cases.
96 if (index[i] != ']') {
97 // remove second index
98 index.remove(i, index.length()-i-1);
100 result = "(" + parentExpr + ")" + index;
102 break;
103 case VKstruct:
104 result = "(" + parentExpr + ")." + getText();
105 break;
106 case VKsimple: /* parent can't be simple */
107 case VKpointer: /* handled in NKaddress */
108 case VKdummy: /* can't occur at all */
109 ASSERT(false);
110 result = parentExpr; /* paranoia */
111 break;
113 return result;
116 bool VarTree::isToplevelExpr() const
118 return parent() == 0;
121 bool VarTree::isAncestorEq(const VarTree* child) const
123 const QListViewItem* c = child;
124 while (c != 0 && c != this) {
125 c = c->parent();
127 return c != 0;
130 bool VarTree::updateValue(const QString& newValue)
132 // check whether the value changed
133 bool prevValueChanged = m_valueChanged;
134 m_valueChanged = false;
135 if (value() != newValue) {
136 setValue(newValue);
137 m_valueChanged = true;
140 * We must repaint the cell if the value changed. If it did not change,
141 * we still must repaint the cell if the value changed previously,
142 * because the color of the display must be changed (from red to
143 * black).
145 return m_valueChanged || prevValueChanged;
148 void VarTree::inferTypesOfChildren(ProgramTypeTable& typeTable)
151 * Type inference works like this: We use type information of those
152 * children that have a type name in their name (base classes) or in
153 * their value (pointers)
156 // first recurse children
157 VarTree* child = firstChild();
158 while (child != 0) {
159 child->inferTypesOfChildren(typeTable);
160 child = child->nextSibling();
163 // if this is a pointer, get the type from the value (less the pointer)
164 if (m_varKind == VKpointer) {
165 if (isWcharT())
168 * wchart_t pointers must be treated as struct, because the array
169 * of characters is printed similar to how QStrings are decoded.
171 m_varKind = VKstruct;
172 setExpandable(false);
174 // don't know how to do this cleanly
175 } else if (m_varKind == VKstruct) {
176 // check if this is a base class part
177 if (m_nameKind == NKtype) {
178 const QString& typeName =
179 getText().mid(1, getText().length()-2); // strip < and >
180 m_type = typeTable.lookup(typeName);
182 /* if we don't have a type yet, get it from the base class */
183 if (m_type == 0) {
184 m_type = inferTypeFromBaseClass();
186 * If there is a known type now, it is the one from the
187 * first base class whose type we know.
192 * If we still don't have a type, the type is really unknown.
194 if (m_type == 0) {
195 m_type = TypeInfo::unknownType();
197 } // else
199 * This is not a base class part. We don't assign a type so
200 * that later we can ask gdb.
205 // the value contains the pointer type in parenthesis
206 bool VarTree::isWcharT() const
208 return value().startsWith("(const wchar_t *)") ||
209 value().startsWith("(wchar_t *)");
213 * Get the type of the first base class whose type we know.
215 TypeInfo* VarTree::inferTypeFromBaseClass()
217 if (m_varKind == VKstruct) {
218 VarTree* child = firstChild();
219 while (child != 0 &&
220 // only check base class parts (i.e. type names)
221 child->m_nameKind == NKtype)
223 if (child->m_type != 0 &&
224 child->m_type != TypeInfo::unknownType())
226 // got a type!
227 return child->m_type;
229 child = child->nextSibling();
232 return 0;
235 ExprValue::ExprValue(const QString& name, VarTree::NameKind aKind) :
236 m_name(name),
237 m_varKind(VarTree::VKsimple),
238 m_nameKind(aKind),
239 m_child(0),
240 m_next(0),
241 m_initiallyExpanded(false)
245 ExprValue::~ExprValue()
247 delete m_child;
248 delete m_next;
251 void ExprValue::appendChild(ExprValue* newChild)
253 if (m_child == 0) {
254 m_child = newChild;
255 } else {
256 // walk chain of children to find the last one
257 ExprValue* last = m_child;
258 while (last->m_next != 0)
259 last = last->m_next;
260 last->m_next = newChild;
262 newChild->m_next = 0; // just to be sure
265 int ExprValue::childCount() const
267 int i = 0;
268 ExprValue* c = m_child;
269 while (c) {
270 ++i;
271 c = c->m_next;
273 return i;
278 ExprWnd::ExprWnd(QWidget* parent, const QString& colHeader, const char* name) :
279 QListView(parent, name),
280 m_edit(0)
282 addColumn(colHeader);
283 addColumn(i18n("Value"));
284 setSorting(-1); // do not sort items
285 setColumnWidthMode(0, Manual);
286 setColumnWidthMode(1, Maximum);
287 setRootIsDecorated(true);
289 m_pixPointer = UserIcon("pointer.xpm");
290 if (m_pixPointer.isNull())
291 TRACE("Can't load pointer.xpm");
294 ExprWnd::~ExprWnd()
298 void ExprWnd::exprList(QStrList& exprs)
300 // ASSERT(exprs does deep-copies)
301 VarTree* item;
302 for (item = firstChild(); item != 0; item = item->nextSibling()) {
303 exprs.append(item->getText());
307 VarTree* ExprWnd::insertExpr(ExprValue* expr, ProgramTypeTable& typeTable)
309 // append a new dummy expression
310 VarTree* last = 0; // last top-level item
311 for (VarTree* i = firstChild(); i != 0; i = i->nextSibling()) {
312 last = i;
314 VarTree* display = new VarTree(this, last, expr->m_name);
316 // replace it right away
317 updateExpr(display, expr, typeTable);
318 return display;
321 void ExprWnd::updateExpr(ExprValue* expr, ProgramTypeTable& typeTable)
323 // search the root variable
324 VarTree* item = firstChild();
325 while (item != 0 && item->getText() != expr->m_name)
326 item = item->nextSibling();
327 if (item == 0) {
328 return;
330 // now update it
331 if (updateExprRec(item, expr, typeTable)) {
332 triggerUpdate();
334 collectUnknownTypes(item);
337 void ExprWnd::updateExpr(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable)
339 if (updateExprRec(display, newValues, typeTable) &&
340 display->isVisible())
342 triggerUpdate();
344 collectUnknownTypes(display);
348 * returns true if there's a visible change
350 bool ExprWnd::updateExprRec(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable)
352 bool isExpanded = display->isOpen();
355 * If we are updating a pointer without children by a dummy, we don't
356 * collapse it, but simply insert the new children. This happens when a
357 * pointer has just been expanded by the user.
359 if (display->m_varKind == VarTree::VKpointer &&
360 display->childCount() == 0 &&
361 newValues->m_varKind == VarTree::VKdummy)
363 replaceChildren(display, newValues);
364 return isExpanded; /* no visible change if not expanded */
368 * If the display and newValues have different kind or if their number
369 * of children is different, replace the whole sub-tree.
371 if (// the next two lines mean: not(m_varKind remains unchanged)
372 !(newValues->m_varKind == VarTree::VKdummy ||
373 display->m_varKind == newValues->m_varKind)
375 (display->childCount() != newValues->childCount() &&
377 * If this is a pointer and newValues doesn't have children, we
378 * don't replace the sub-tree; instead, below we mark this
379 * sub-tree for requiring an update.
381 (display->m_varKind != VarTree::VKpointer ||
382 newValues->m_child != 0)))
384 if (isExpanded) {
385 display->setOpen(false);
388 // since children changed, it is likely that the type has also changed
389 display->m_type = 0; /* will re-evaluate the type */
391 // display the new value
392 updateSingleExpr(display, newValues);
393 replaceChildren(display, newValues);
395 // update the m_varKind
396 if (newValues->m_varKind != VarTree::VKdummy) {
397 display->m_varKind = newValues->m_varKind;
398 display->setExpandable(newValues->m_varKind == VarTree::VKpointer);
401 // get some types (after the new m_varKind has been set!)
402 display->inferTypesOfChildren(typeTable);
404 // (note that the new value might not have a sub-tree at all)
405 return display->m_valueChanged || isExpanded; /* no visible change if not expanded */
408 // display the new value
409 updateSingleExpr(display, newValues);
412 * If this is an expanded pointer, record it for being updated.
414 if (display->m_varKind == VarTree::VKpointer) {
415 if (isExpanded &&
416 // if newValues is a dummy, we have already updated this pointer
417 newValues->m_varKind != VarTree::VKdummy)
419 m_updatePtrs.append(display);
422 * If the visible sub-tree has children, but newValues doesn't, we
423 * can stop here.
425 if (newValues->m_child == 0) {
426 return display->m_valueChanged;
430 ASSERT(display->childCount() == newValues->childCount());
432 // go for children
433 bool childChanged = false;
435 VarTree* vDisplay = display->firstChild();
436 ExprValue* vNew = newValues->m_child;
437 while (vDisplay != 0) {
438 // check whether the names are the same
439 if (vDisplay->getText() != vNew->m_name) {
440 // set new name
441 vDisplay->setText(vNew->m_name);
443 childChanged = true;
446 // recurse
447 if (updateExprRec(vDisplay, vNew, typeTable)) {
448 childChanged = true;
450 vDisplay = vDisplay->nextSibling();
451 vNew = vNew->m_next;
454 // update of children propagates only if this node is expanded
455 return display->m_valueChanged || (display->isOpen() && childChanged);
458 void ExprWnd::updateSingleExpr(VarTree* display, ExprValue* newValue)
461 * If newValues is a VKdummy, we are only interested in its children.
462 * No need to update anything here.
464 if (newValue->m_varKind == VarTree::VKdummy) {
465 return;
469 * If this node is a struct and we know its type then don't update its
470 * value now. This is a node for which we know how to find a nested
471 * value. So register the node for an update.
473 * wchar_t types are also treated specially here: We consider them
474 * as struct (has been set in inferTypesOfChildren()).
476 if (display->m_varKind == VarTree::VKstruct &&
477 display->m_type != 0 &&
478 display->m_type != TypeInfo::unknownType())
480 ASSERT(newValue->m_varKind == VarTree::VKstruct);
481 if (display->m_type == TypeInfo::wchartType())
484 * We do not copy the new pointer value to the destination right
485 * away, but consider it as the first part of the nested value.
486 * Then the display will change its color only when the new value
487 * is completed.
489 display->m_partialValue = formatWCharPointer(newValue->m_value);
491 else
492 display->m_partialValue = display->m_type->m_displayString[0];
493 m_updateStruct.append(display);
495 else
497 if (display->updateValue(newValue->m_value)) {
498 triggerUpdate();
503 void ExprWnd::updateStructValue(VarTree* display)
505 ASSERT(display->m_varKind == VarTree::VKstruct);
507 if (display->updateValue(display->m_partialValue)) {
508 triggerUpdate();
510 // reset the value
511 display->m_partialValue = "";
512 display->m_exprIndex = -1;
515 void ExprWnd::replaceChildren(VarTree* display, ExprValue* newValues)
517 ASSERT(display->childCount() == 0 || display->m_varKind != VarTree::VKsimple);
519 // delete all children of display
520 while (VarTree* c = display->firstChild()) {
521 unhookSubtree(c);
522 delete c;
524 // insert copies of the newValues
525 VarTree* vNew = 0;
526 for (ExprValue* v = newValues->m_child; v != 0; v = v->m_next)
528 vNew = new VarTree(display, vNew, v);
529 // recurse
530 replaceChildren(vNew, v);
534 void ExprWnd::collectUnknownTypes(VarTree* var)
536 QListViewItemIterator i(var);
537 for (; i.current(); ++i)
539 checkUnknownType(static_cast<VarTree*>(i.current()));
543 void ExprWnd::checkUnknownType(VarTree* var)
545 ASSERT(var->m_varKind != VarTree::VKpointer || var->m_nameKind != VarTree::NKtype);
546 if (var->m_type == 0 &&
547 var->m_varKind == VarTree::VKstruct &&
548 var->m_nameKind != VarTree::NKtype)
550 if (!var->isWcharT())
552 /* this struct node doesn't have a type yet: register it */
553 m_updateType.append(var);
555 else
557 var->m_type = TypeInfo::wchartType();
558 // see updateSingleExpr() why we move the value
559 var->m_partialValue = formatWCharPointer(var->value());
560 var->setValue(QString());
561 m_updateStruct.append(var);
564 // add pointer pixmap to pointers
565 if (var->m_varKind == VarTree::VKpointer) {
566 var->setPixmap(m_pixPointer);
570 QString ExprWnd::formatWCharPointer(QString value)
572 int pos = value.find(") ");
573 if (pos > 0)
574 value = value.mid(pos+2);
575 return value + " L";
579 VarTree* ExprWnd::topLevelExprByName(const char* name)
581 VarTree* item = firstChild();
582 while (item != 0 && item->getText() != name)
583 item = item->nextSibling();
585 return item;
588 VarTree* ExprWnd::ptrMemberByName(VarTree* v, const QString& name)
590 // v must be a pointer variable, must have children
591 if (v->m_varKind != VarTree::VKpointer || v->childCount() == 0)
592 return 0;
594 // the only child of v is the pointer value that represents the struct
595 VarTree* item = v->firstChild();
596 return memberByName(item, name);
599 VarTree* ExprWnd::memberByName(VarTree* v, const QString& name)
601 // search immediate children for name
602 VarTree* item = v->firstChild();
603 while (item != 0 && item->getText() != name)
604 item = item->nextSibling();
606 if (item != 0)
607 return item;
609 // try in base classes
610 item = v->firstChild();
611 while (item != 0 &&
612 item->m_nameKind == VarTree::NKtype)
614 v = memberByName(item, name);
615 if (v != 0)
616 return v;
617 item = item->nextSibling();
619 return 0;
622 void ExprWnd::removeExpr(VarTree* item)
624 unhookSubtree(item);
626 delete item;
629 void ExprWnd::unhookSubtree(VarTree* subTree)
631 // must remove any pointers scheduled for update from the list
632 unhookSubtree(m_updatePtrs, subTree);
633 unhookSubtree(m_updateType, subTree);
634 unhookSubtree(m_updateStruct, subTree);
635 emit removingItem(subTree);
638 void ExprWnd::unhookSubtree(QList<VarTree>& list, VarTree* subTree)
640 if (subTree == 0)
641 return;
643 VarTree* checkItem = list.first();
644 while (checkItem != 0) {
645 if (!subTree->isAncestorEq(checkItem)) {
646 // checkItem is not an item from subTree
647 // advance
648 checkItem = list.next();
649 } else {
650 // checkItem is an item from subTree
652 * If checkItem is the last item in the list, we need a special
653 * treatment, because remove()ing it steps the current item of
654 * the list in the "wrong" direction.
656 if (checkItem == list.getLast()) { // does not set current item
657 list.remove();
658 /* we deleted the last element, so we've finished */
659 checkItem = 0;
660 } else {
661 list.remove();
662 /* remove() advanced already */
663 checkItem = list.current();
669 void ExprWnd::clearPendingUpdates()
671 m_updatePtrs.clear();
672 m_updateType.clear();
673 m_updateStruct.clear();
676 VarTree* ExprWnd::nextUpdatePtr()
678 VarTree* ptr = m_updatePtrs.first();
679 if (ptr != 0) {
680 m_updatePtrs.remove();
682 return ptr;
685 VarTree* ExprWnd::nextUpdateType()
687 VarTree* ptr = m_updateType.first();
688 if (ptr != 0) {
689 m_updateType.remove();
691 return ptr;
694 VarTree* ExprWnd::nextUpdateStruct()
696 VarTree* ptr = m_updateStruct.first();
697 if (ptr != 0) {
698 m_updateStruct.remove();
700 return ptr;
704 void ExprWnd::editValue(VarTree* item, const QString& text)
706 if (m_edit == 0)
707 m_edit = new ValueEdit(this);
709 QRect r = itemRect(item);
710 int x = r.x()+columnWidth(0);
711 int y = r.y();
712 int w = columnWidth(1);
713 int h = r.height();
714 QListView* lv = item->listView();
717 * Make the edit widget at least 5 characters wide (but not wider than
718 * this widget). If less than half of this widget is used to display
719 * the text, scroll this widget so that half of it shows the text (or
720 * less than half of it if the text is shorter).
722 QFontMetrics metr = m_edit->font();
723 int wMin = metr.width("88888");
724 if (w < wMin)
725 w = wMin;
726 int wThis = lv->visibleWidth();
727 if (x >= wThis/2 && // less than half the width displays text
728 x+w > wThis) // not all text is visible
730 // scroll so that more text is visible
731 int wScroll = QMIN(x-wThis/2, x+w-wThis);
732 lv->scrollBy(wScroll, 0);
733 x -= wScroll;
735 else if (x < 0)
737 // don't let the edit move out at the left
738 x = 0;
741 // make the edit box as wide as the visible column
742 QRect rect(x,y, wThis-x,h);
743 m_edit->setText(text);
744 m_edit->selectAll();
746 m_edit->setGeometry(rect);
747 m_edit->m_finished = false;
748 m_edit->m_item = item;
749 m_edit->show();
750 m_edit->setFocus();
753 bool ExprWnd::isEditing() const
755 return m_edit != 0 && m_edit->isVisible();
759 ValueEdit::ValueEdit(ExprWnd* parent) :
760 QLineEdit(parent->viewport(), "valueedit")
762 setFrame(false);
763 hide();
764 lower(); // lower the window below scrollbars
765 connect(parent, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged()));
766 connect(parent, SIGNAL(currentChanged(QListViewItem*)), SLOT(slotSelectionChanged()));
767 connect(parent, SIGNAL(expanded(QListViewItem*)), SLOT(slotSelectionChanged()));
768 connect(parent, SIGNAL(collapsed(QListViewItem*)), SLOT(slotSelectionChanged()));
769 connect(this, SIGNAL(done(VarTree*, const QString&)),
770 parent, SIGNAL(editValueCommitted(VarTree*, const QString&)));
773 ValueEdit::~ValueEdit()
777 void ValueEdit::terminate(bool commit)
779 TRACE(commit?"ValueEdit::terminate(true)":"ValueEdit::terminate(false)");
780 if (!m_finished)
782 m_finished = true;
783 hide(); // will call focusOutEvent, that's why we need m_finished
784 if (commit) {
785 emit done(m_item, text());
790 void ValueEdit::keyPressEvent(QKeyEvent *e)
792 if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)
793 terminate(true);
794 else if(e->key() == Qt::Key_Escape)
795 terminate(false);
796 else
797 QLineEdit::keyPressEvent(e);
800 void ValueEdit::paintEvent(QPaintEvent* e)
802 QLineEdit::paintEvent(e);
804 QPainter p(this);
805 p.drawRect(rect());
808 void ValueEdit::focusOutEvent(QFocusEvent* ev)
810 TRACE("ValueEdit::focusOutEvent");
811 QFocusEvent* focusEv = static_cast<QFocusEvent*>(ev);
812 // Don't let a RMB close the editor
813 if (focusEv->reason() != QFocusEvent::Popup &&
814 focusEv->reason() != QFocusEvent::ActiveWindow)
816 terminate(true);
820 void ValueEdit::slotSelectionChanged()
822 TRACE("ValueEdit::slotSelectionChanged");
823 terminate(false);