Merge with 2.0.
[kdbg.git] / kdbg / exprwnd.cpp
blob4730e7c5f35a5b286af21648a5168c78d979922f
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_baseValue(v->m_value),
28 m_baseChanged(false),
29 m_structChanged(false)
31 QListViewItem::setText(0, v->m_name);
32 updateValueText();
33 setExpandable(m_varKind == VarTree::VKpointer);
34 setOpen(v->m_initiallyExpanded);
37 VarTree::VarTree(ExprWnd* parent, QListViewItem* after, const QString& name) :
38 QListViewItem(parent, after),
39 m_varKind(VKsimple),
40 m_nameKind(VarTree::NKplain),
41 m_type(0),
42 m_exprIndex(0),
43 m_exprIndexUseGuard(false),
44 m_baseChanged(false),
45 m_structChanged(false)
47 QListViewItem::setText(0, name);
50 VarTree::~VarTree()
54 void VarTree::paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align)
56 if (column == 1 && (m_baseChanged || m_structChanged)) {
57 QColorGroup cgChg = cg;
58 cgChg.setColor(QColorGroup::Text, Qt::red);
59 QListViewItem::paintCell(p, cgChg, column, width, align);
60 } else {
61 QListViewItem::paintCell(p, cg, column, width, align);
65 QString VarTree::computeExpr() const
67 // top-level items are special
68 if (isToplevelExpr())
69 return getText();
71 // get parent expr
72 VarTree* par = static_cast<VarTree*>(parent());
73 QString parentExpr = par->computeExpr();
75 // skip this item's name if it is a base class or anonymous struct or union
76 if (m_nameKind == NKtype || m_nameKind == NKanonymous) {
77 return parentExpr;
79 /* augment by this item's text */
80 QString result;
81 /* if this is an address, dereference it */
82 if (m_nameKind == NKaddress) {
83 ASSERT(par->m_varKind == VKpointer);
84 result = "*" + parentExpr;
85 return result;
87 switch (par->m_varKind) {
88 case VKarray:
90 QString index = getText();
91 int i = 1;
92 // skip past the index
93 while (index[i].isDigit())
94 i++;
96 * Some array indices are actually ranges due to repeated array
97 * values. We use the first index in these cases.
99 if (index[i] != ']') {
100 // remove second index
101 index.remove(i, index.length()-i-1);
103 result = "(" + parentExpr + ")" + index;
105 break;
106 case VKstruct:
107 result = "(" + parentExpr + ")." + getText();
108 break;
109 case VKsimple: /* parent can't be simple */
110 case VKpointer: /* handled in NKaddress */
111 case VKdummy: /* can't occur at all */
112 ASSERT(false);
113 result = parentExpr; /* paranoia */
114 break;
116 return result;
119 bool VarTree::isToplevelExpr() const
121 return parent() == 0;
124 bool VarTree::isAncestorEq(const VarTree* child) const
126 const QListViewItem* c = child;
127 while (c != 0 && c != this) {
128 c = c->parent();
130 return c != 0;
133 bool VarTree::updateValue(const QString& newValue)
135 // check whether the value changed
136 bool prevValueChanged = m_baseChanged;
137 if ((m_baseChanged = m_baseValue != newValue)) {
138 m_baseValue = newValue;
139 updateValueText();
142 * We must repaint the cell if the value changed. If it did not change,
143 * we still must repaint the cell if the value changed previously,
144 * because the color of the display must be changed (from red to
145 * black).
147 return m_baseChanged || prevValueChanged;
150 bool VarTree::updateStructValue(const QString& newValue)
152 // check whether the value changed
153 bool prevValueChanged = m_structChanged;
154 if ((m_structChanged = m_structValue != newValue)) {
155 m_structValue = newValue;
156 updateValueText();
159 * We must repaint the cell if the value changed. If it did not change,
160 * we still must repaint the cell if the value changed previously,
161 * because the color of the display must be changed (from red to
162 * black).
164 return m_structChanged || prevValueChanged;
167 void VarTree::updateValueText()
169 if (m_baseValue.isEmpty()) {
170 QListViewItem::setText(1, m_structValue);
171 } else if (m_structValue.isEmpty()) {
172 QListViewItem::setText(1, m_baseValue);
173 } else {
174 QListViewItem::setText(1, m_baseValue + " " + m_structValue);
178 void VarTree::inferTypesOfChildren(ProgramTypeTable& typeTable)
181 * Type inference works like this: We use type information of those
182 * children that have a type name in their name (base classes) or in
183 * their value (pointers)
186 // first recurse children
187 VarTree* child = firstChild();
188 while (child != 0) {
189 child->inferTypesOfChildren(typeTable);
190 child = child->nextSibling();
193 // if this is a pointer, get the type from the value (less the pointer)
194 if (m_varKind == VKpointer) {
195 if (isWcharT())
198 * wchart_t pointers must be treated as struct, because the array
199 * of characters is printed similar to how QStrings are decoded.
201 m_varKind = VKstruct;
202 setExpandable(false);
204 // don't know how to do this cleanly
205 } else if (m_varKind == VKstruct) {
206 // check if this is a base class part
207 if (m_nameKind == NKtype) {
208 const QString& typeName =
209 getText().mid(1, getText().length()-2); // strip < and >
210 m_type = typeTable.lookup(typeName);
212 /* if we don't have a type yet, get it from the base class */
213 if (m_type == 0) {
214 m_type = inferTypeFromBaseClass();
216 * If there is a known type now, it is the one from the
217 * first base class whose type we know.
222 * If we still don't have a type, the type is really unknown.
224 if (m_type == 0) {
225 m_type = TypeInfo::unknownType();
227 } // else
229 * This is not a base class part. We don't assign a type so
230 * that later we can ask gdb.
235 // the value contains the pointer type in parenthesis
236 bool VarTree::isWcharT() const
238 return value().startsWith("(const wchar_t *)") ||
239 value().startsWith("(wchar_t *)");
243 * Get the type of the first base class whose type we know.
245 TypeInfo* VarTree::inferTypeFromBaseClass()
247 if (m_varKind == VKstruct) {
248 VarTree* child = firstChild();
249 while (child != 0 &&
250 // only check base class parts (i.e. type names)
251 child->m_nameKind == NKtype)
253 if (child->m_type != 0 &&
254 child->m_type != TypeInfo::unknownType())
256 // got a type!
257 return child->m_type;
259 child = child->nextSibling();
262 return 0;
265 ExprValue::ExprValue(const QString& name, VarTree::NameKind aKind) :
266 m_name(name),
267 m_varKind(VarTree::VKsimple),
268 m_nameKind(aKind),
269 m_child(0),
270 m_next(0),
271 m_initiallyExpanded(false)
275 ExprValue::~ExprValue()
277 delete m_child;
278 delete m_next;
281 void ExprValue::appendChild(ExprValue* newChild)
283 if (m_child == 0) {
284 m_child = newChild;
285 } else {
286 // walk chain of children to find the last one
287 ExprValue* last = m_child;
288 while (last->m_next != 0)
289 last = last->m_next;
290 last->m_next = newChild;
292 newChild->m_next = 0; // just to be sure
295 int ExprValue::childCount() const
297 int i = 0;
298 ExprValue* c = m_child;
299 while (c) {
300 ++i;
301 c = c->m_next;
303 return i;
308 ExprWnd::ExprWnd(QWidget* parent, const QString& colHeader, const char* name) :
309 QListView(parent, name),
310 m_edit(0)
312 addColumn(colHeader);
313 addColumn(i18n("Value"));
314 setSorting(-1); // do not sort items
315 setColumnWidthMode(0, Manual);
316 setColumnWidthMode(1, Maximum);
317 setRootIsDecorated(true);
318 setAllColumnsShowFocus(true);
320 m_pixPointer = UserIcon("pointer.xpm");
321 if (m_pixPointer.isNull())
322 TRACE("Can't load pointer.xpm");
325 ExprWnd::~ExprWnd()
329 void ExprWnd::exprList(QStrList& exprs)
331 // ASSERT(exprs does deep-copies)
332 VarTree* item;
333 for (item = firstChild(); item != 0; item = item->nextSibling()) {
334 exprs.append(item->getText());
338 VarTree* ExprWnd::insertExpr(ExprValue* expr, ProgramTypeTable& typeTable)
340 // append a new dummy expression
341 VarTree* last = 0; // last top-level item
342 for (VarTree* i = firstChild(); i != 0; i = i->nextSibling()) {
343 last = i;
345 VarTree* display = new VarTree(this, last, expr->m_name);
347 // replace it right away
348 updateExpr(display, expr, typeTable);
349 return display;
352 void ExprWnd::updateExpr(ExprValue* expr, ProgramTypeTable& typeTable)
354 // search the root variable
355 VarTree* item = firstChild();
356 while (item != 0 && item->getText() != expr->m_name)
357 item = item->nextSibling();
358 if (item == 0) {
359 return;
361 // now update it
362 updateExprRec(item, expr, typeTable);
363 collectUnknownTypes(item);
366 void ExprWnd::updateExpr(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable)
368 updateExprRec(display, newValues, typeTable);
369 collectUnknownTypes(display);
373 * returns true if there's a visible change
375 void ExprWnd::updateExprRec(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable)
377 bool isExpanded = display->isOpen();
380 * If we are updating a pointer without children by a dummy, we don't
381 * collapse it, but simply insert the new children. This happens when a
382 * pointer has just been expanded by the user.
384 if (display->m_varKind == VarTree::VKpointer &&
385 display->childCount() == 0 &&
386 newValues->m_varKind == VarTree::VKdummy)
388 replaceChildren(display, newValues);
389 return;
393 * If the display and newValues have different kind or if their number
394 * of children is different, replace the whole sub-tree.
396 if (// the next two lines mean: not(m_varKind remains unchanged)
397 !(newValues->m_varKind == VarTree::VKdummy ||
398 display->m_varKind == newValues->m_varKind)
400 (display->childCount() != newValues->childCount() &&
402 * If this is a pointer and newValues doesn't have children, we
403 * don't replace the sub-tree; instead, below we mark this
404 * sub-tree for requiring an update.
406 (display->m_varKind != VarTree::VKpointer ||
407 newValues->m_child != 0)))
409 if (isExpanded) {
410 display->setOpen(false);
413 // since children changed, it is likely that the type has also changed
414 display->m_type = 0; /* will re-evaluate the type */
416 // display the new value
417 updateSingleExpr(display, newValues);
418 replaceChildren(display, newValues);
420 // update the m_varKind
421 if (newValues->m_varKind != VarTree::VKdummy) {
422 display->m_varKind = newValues->m_varKind;
423 display->setExpandable(newValues->m_varKind == VarTree::VKpointer);
426 // get some types (after the new m_varKind has been set!)
427 display->inferTypesOfChildren(typeTable);
429 // (note that the new value might not have a sub-tree at all)
430 return;
433 // display the new value
434 updateSingleExpr(display, newValues);
437 * If this is an expanded pointer, record it for being updated.
439 if (display->m_varKind == VarTree::VKpointer) {
440 if (isExpanded &&
441 // if newValues is a dummy, we have already updated this pointer
442 newValues->m_varKind != VarTree::VKdummy)
444 m_updatePtrs.append(display);
447 * If the visible sub-tree has children, but newValues doesn't, we
448 * can stop here.
450 if (newValues->m_child == 0) {
451 return;
455 ASSERT(display->childCount() == newValues->childCount());
457 // go for children
458 VarTree* vDisplay = display->firstChild();
459 ExprValue* vNew = newValues->m_child;
460 while (vDisplay != 0) {
461 // check whether the names are the same
462 if (vDisplay->getText() != vNew->m_name) {
463 // set new name
464 vDisplay->setText(vNew->m_name);
466 // recurse
467 updateExprRec(vDisplay, vNew, typeTable);
469 vDisplay = vDisplay->nextSibling();
470 vNew = vNew->m_next;
474 void ExprWnd::updateSingleExpr(VarTree* display, ExprValue* newValue)
477 * If newValues is a VKdummy, we are only interested in its children.
478 * No need to update anything here.
480 if (newValue->m_varKind == VarTree::VKdummy) {
481 return;
485 * If this node is a struct and we know its type then we know how to
486 * find a nested value. So register the node for an update.
488 * wchar_t types are also treated specially here: We consider them
489 * as struct (has been set in inferTypesOfChildren()).
491 if (display->m_varKind == VarTree::VKstruct &&
492 display->m_type != 0 &&
493 display->m_type != TypeInfo::unknownType())
495 ASSERT(newValue->m_varKind == VarTree::VKstruct);
496 if (display->m_type == TypeInfo::wchartType())
498 display->m_partialValue = "L";
500 else
501 display->m_partialValue = display->m_type->m_displayString[0];
502 m_updateStruct.append(display);
505 if (display->updateValue(newValue->m_value)) {
506 triggerUpdate();
510 void ExprWnd::updateStructValue(VarTree* display)
512 ASSERT(display->m_varKind == VarTree::VKstruct);
514 if (display->updateStructValue(display->m_partialValue)) {
515 triggerUpdate();
517 // reset the value
518 display->m_partialValue = "";
519 display->m_exprIndex = -1;
522 void ExprWnd::replaceChildren(VarTree* display, ExprValue* newValues)
524 ASSERT(display->childCount() == 0 || display->m_varKind != VarTree::VKsimple);
526 // delete all children of display
527 while (VarTree* c = display->firstChild()) {
528 unhookSubtree(c);
529 delete c;
531 // insert copies of the newValues
532 VarTree* vNew = 0;
533 for (ExprValue* v = newValues->m_child; v != 0; v = v->m_next)
535 vNew = new VarTree(display, vNew, v);
536 // recurse
537 replaceChildren(vNew, v);
541 void ExprWnd::collectUnknownTypes(VarTree* var)
543 QListViewItemIterator i(var);
544 for (; i.current(); ++i)
546 checkUnknownType(static_cast<VarTree*>(i.current()));
550 void ExprWnd::checkUnknownType(VarTree* var)
552 ASSERT(var->m_varKind != VarTree::VKpointer || var->m_nameKind != VarTree::NKtype);
553 if (var->m_type == 0 &&
554 var->m_varKind == VarTree::VKstruct &&
555 var->m_nameKind != VarTree::NKtype &&
556 var->m_nameKind != VarTree::NKanonymous)
558 if (!var->isWcharT())
560 /* this struct node doesn't have a type yet: register it */
561 m_updateType.append(var);
563 else
565 var->m_type = TypeInfo::wchartType();
566 var->m_partialValue = "L";
567 m_updateStruct.append(var);
570 // add pointer pixmap to pointers
571 if (var->m_varKind == VarTree::VKpointer) {
572 var->setPixmap(m_pixPointer);
576 QString ExprWnd::formatWCharPointer(QString value)
578 int pos = value.find(") ");
579 if (pos > 0)
580 value = value.mid(pos+2);
581 return value + " L";
585 VarTree* ExprWnd::topLevelExprByName(const char* name)
587 VarTree* item = firstChild();
588 while (item != 0 && item->getText() != name)
589 item = item->nextSibling();
591 return item;
594 VarTree* ExprWnd::ptrMemberByName(VarTree* v, const QString& name)
596 // v must be a pointer variable, must have children
597 if (v->m_varKind != VarTree::VKpointer || v->childCount() == 0)
598 return 0;
600 // the only child of v is the pointer value that represents the struct
601 VarTree* item = v->firstChild();
602 return memberByName(item, name);
605 VarTree* ExprWnd::memberByName(VarTree* v, const QString& name)
607 // search immediate children for name
608 VarTree* item = v->firstChild();
609 while (item != 0 && item->getText() != name)
610 item = item->nextSibling();
612 if (item != 0)
613 return item;
615 // try in base classes and members that are anonymous structs or unions
616 item = v->firstChild();
617 while (item != 0)
619 if (item->m_nameKind == VarTree::NKtype ||
620 item->m_nameKind == VarTree::NKanonymous)
622 v = memberByName(item, name);
623 if (v != 0)
624 return v;
626 item = item->nextSibling();
628 return 0;
631 void ExprWnd::removeExpr(VarTree* item)
633 unhookSubtree(item);
635 delete item;
638 void ExprWnd::unhookSubtree(VarTree* subTree)
640 // must remove any pointers scheduled for update from the list
641 unhookSubtree(m_updatePtrs, subTree);
642 unhookSubtree(m_updateType, subTree);
643 unhookSubtree(m_updateStruct, subTree);
644 emit removingItem(subTree);
647 void ExprWnd::unhookSubtree(QList<VarTree>& list, VarTree* subTree)
649 if (subTree == 0)
650 return;
652 VarTree* checkItem = list.first();
653 while (checkItem != 0) {
654 if (!subTree->isAncestorEq(checkItem)) {
655 // checkItem is not an item from subTree
656 // advance
657 checkItem = list.next();
658 } else {
659 // checkItem is an item from subTree
661 * If checkItem is the last item in the list, we need a special
662 * treatment, because remove()ing it steps the current item of
663 * the list in the "wrong" direction.
665 if (checkItem == list.getLast()) { // does not set current item
666 list.remove();
667 /* we deleted the last element, so we've finished */
668 checkItem = 0;
669 } else {
670 list.remove();
671 /* remove() advanced already */
672 checkItem = list.current();
678 void ExprWnd::clearPendingUpdates()
680 m_updatePtrs.clear();
681 m_updateType.clear();
682 m_updateStruct.clear();
685 VarTree* ExprWnd::nextUpdatePtr()
687 VarTree* ptr = m_updatePtrs.first();
688 if (ptr != 0) {
689 m_updatePtrs.remove();
691 return ptr;
694 VarTree* ExprWnd::nextUpdateType()
696 VarTree* ptr = m_updateType.first();
697 if (ptr != 0) {
698 m_updateType.remove();
700 return ptr;
703 VarTree* ExprWnd::nextUpdateStruct()
705 VarTree* ptr = m_updateStruct.first();
706 if (ptr != 0) {
707 m_updateStruct.remove();
709 return ptr;
713 void ExprWnd::editValue(VarTree* item, const QString& text)
715 if (m_edit == 0)
716 m_edit = new ValueEdit(this);
718 QRect r = itemRect(item);
719 int x = r.x()+columnWidth(0);
720 int y = r.y();
721 int w = columnWidth(1);
722 int h = r.height();
723 QListView* lv = item->listView();
726 * Make the edit widget at least 5 characters wide (but not wider than
727 * this widget). If less than half of this widget is used to display
728 * the text, scroll this widget so that half of it shows the text (or
729 * less than half of it if the text is shorter).
731 QFontMetrics metr = m_edit->font();
732 int wMin = metr.width("88888");
733 if (w < wMin)
734 w = wMin;
735 int wThis = lv->visibleWidth();
736 if (x >= wThis/2 && // less than half the width displays text
737 x+w > wThis) // not all text is visible
739 // scroll so that more text is visible
740 int wScroll = QMIN(x-wThis/2, x+w-wThis);
741 lv->scrollBy(wScroll, 0);
742 x -= wScroll;
744 else if (x < 0)
746 // don't let the edit move out at the left
747 x = 0;
750 // make the edit box as wide as the visible column
751 QRect rect(x,y, wThis-x,h);
752 m_edit->setText(text);
753 m_edit->selectAll();
755 m_edit->setGeometry(rect);
756 m_edit->m_finished = false;
757 m_edit->m_item = item;
758 m_edit->show();
759 m_edit->setFocus();
762 bool ExprWnd::isEditing() const
764 return m_edit != 0 && m_edit->isVisible();
768 ValueEdit::ValueEdit(ExprWnd* parent) :
769 QLineEdit(parent->viewport(), "valueedit")
771 setFrame(false);
772 hide();
773 lower(); // lower the window below scrollbars
774 connect(parent, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged()));
775 connect(parent, SIGNAL(currentChanged(QListViewItem*)), SLOT(slotSelectionChanged()));
776 connect(parent, SIGNAL(expanded(QListViewItem*)), SLOT(slotSelectionChanged()));
777 connect(parent, SIGNAL(collapsed(QListViewItem*)), SLOT(slotSelectionChanged()));
778 connect(this, SIGNAL(done(VarTree*, const QString&)),
779 parent, SIGNAL(editValueCommitted(VarTree*, const QString&)));
782 ValueEdit::~ValueEdit()
786 void ValueEdit::terminate(bool commit)
788 TRACE(commit?"ValueEdit::terminate(true)":"ValueEdit::terminate(false)");
789 if (!m_finished)
791 m_finished = true;
792 hide(); // will call focusOutEvent, that's why we need m_finished
793 if (commit) {
794 emit done(m_item, text());
799 void ValueEdit::keyPressEvent(QKeyEvent *e)
801 if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)
802 terminate(true);
803 else if(e->key() == Qt::Key_Escape)
804 terminate(false);
805 else
806 QLineEdit::keyPressEvent(e);
809 void ValueEdit::paintEvent(QPaintEvent* e)
811 QLineEdit::paintEvent(e);
813 QPainter p(this);
814 p.drawRect(rect());
817 void ValueEdit::focusOutEvent(QFocusEvent* ev)
819 TRACE("ValueEdit::focusOutEvent");
820 QFocusEvent* focusEv = static_cast<QFocusEvent*>(ev);
821 // Don't let a RMB close the editor
822 if (focusEv->reason() != QFocusEvent::Popup &&
823 focusEv->reason() != QFocusEvent::ActiveWindow)
825 terminate(true);
829 void ValueEdit::slotSelectionChanged()
831 TRACE("ValueEdit::slotSelectionChanged");
832 terminate(false);