Merge branch 'maint'
[kdbg.git] / kdbg / exprwnd.cpp
blobcf9944b5405d06e7068a75749f0f280b9b28e32d
1 /*
2 * Copyright Johannes Sixt
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
5 */
7 #include "exprwnd.h"
8 #include "exprwnd.moc"
9 #include "typetable.h"
10 #include <qstrlist.h>
11 #include <qpainter.h>
12 #include <qscrollbar.h>
13 #include <kapplication.h>
14 #include <kiconloader.h> /* icons */
15 #include <klocale.h> /* i18n */
16 #ifdef HAVE_CONFIG_H
17 #include "config.h"
18 #endif
19 #include "mydebug.h"
21 VarTree::VarTree(VarTree* parent, QListViewItem* after, ExprValue* v) :
22 QListViewItem(parent, after),
23 m_varKind(v->m_varKind),
24 m_nameKind(v->m_nameKind),
25 m_type(0),
26 m_exprIndex(0),
27 m_exprIndexUseGuard(false),
28 m_baseValue(v->m_value),
29 m_baseChanged(false),
30 m_structChanged(false)
32 QListViewItem::setText(0, v->m_name);
33 updateValueText();
34 setExpandable(m_varKind == VarTree::VKpointer);
35 setOpen(v->m_initiallyExpanded);
38 VarTree::VarTree(ExprWnd* parent, QListViewItem* after, const QString& name) :
39 QListViewItem(parent, after),
40 m_varKind(VKsimple),
41 m_nameKind(VarTree::NKplain),
42 m_type(0),
43 m_exprIndex(0),
44 m_exprIndexUseGuard(false),
45 m_baseChanged(false),
46 m_structChanged(false)
48 QListViewItem::setText(0, name);
51 VarTree::~VarTree()
55 void VarTree::paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align)
57 if (column == 1 && (m_baseChanged || m_structChanged)) {
58 QColorGroup cgChg = cg;
59 cgChg.setColor(QColorGroup::Text, Qt::red);
60 QListViewItem::paintCell(p, cgChg, column, width, align);
61 } else {
62 QListViewItem::paintCell(p, cg, column, width, align);
66 QString VarTree::computeExpr() const
68 // top-level items are special
69 if (isToplevelExpr())
70 return getText();
72 // get parent expr
73 VarTree* par = static_cast<VarTree*>(parent());
74 QString parentExpr = par->computeExpr();
76 // skip this item's name if it is a base class or anonymous struct or union
77 if (m_nameKind == NKtype || m_nameKind == NKanonymous) {
78 return parentExpr;
80 /* augment by this item's text */
81 QString result;
82 /* if this is an address, dereference it */
83 if (m_nameKind == NKaddress) {
84 ASSERT(par->m_varKind == VKpointer);
85 result = "*" + parentExpr;
86 return result;
88 switch (par->m_varKind) {
89 case VKarray:
91 QString index = getText();
92 int i = 1;
93 // skip past the index
94 while (index[i].isDigit())
95 i++;
97 * Some array indices are actually ranges due to repeated array
98 * values. We use the first index in these cases.
100 if (index[i] != ']') {
101 // remove second index
102 index.remove(i, index.length()-i-1);
104 result = "(" + parentExpr + ")" + index;
106 break;
107 case VKstruct:
108 result = "(" + parentExpr + ")." + getText();
109 break;
110 case VKsimple: /* parent can't be simple */
111 case VKpointer: /* handled in NKaddress */
112 case VKdummy: /* can't occur at all */
113 ASSERT(false);
114 result = parentExpr; /* paranoia */
115 break;
117 return result;
120 bool VarTree::isToplevelExpr() const
122 return parent() == 0;
125 bool VarTree::isAncestorEq(const VarTree* child) const
127 const QListViewItem* c = child;
128 while (c != 0 && c != this) {
129 c = c->parent();
131 return c != 0;
134 bool VarTree::updateValue(const QString& newValue)
136 // check whether the value changed
137 bool prevValueChanged = m_baseChanged;
138 if ((m_baseChanged = m_baseValue != newValue)) {
139 m_baseValue = newValue;
140 updateValueText();
143 * We must repaint the cell if the value changed. If it did not change,
144 * we still must repaint the cell if the value changed previously,
145 * because the color of the display must be changed (from red to
146 * black).
148 return m_baseChanged || prevValueChanged;
151 bool VarTree::updateStructValue(const QString& newValue)
153 // check whether the value changed
154 bool prevValueChanged = m_structChanged;
155 if ((m_structChanged = m_structValue != newValue)) {
156 m_structValue = newValue;
157 updateValueText();
160 * We must repaint the cell if the value changed. If it did not change,
161 * we still must repaint the cell if the value changed previously,
162 * because the color of the display must be changed (from red to
163 * black).
165 return m_structChanged || prevValueChanged;
168 void VarTree::updateValueText()
170 if (m_baseValue.isEmpty()) {
171 QListViewItem::setText(1, m_structValue);
172 } else if (m_structValue.isEmpty()) {
173 QListViewItem::setText(1, m_baseValue);
174 } else {
175 QListViewItem::setText(1, m_baseValue + " " + m_structValue);
179 void VarTree::inferTypesOfChildren(ProgramTypeTable& typeTable)
182 * Type inference works like this: We use type information of those
183 * children that have a type name in their name (base classes) or in
184 * their value (pointers)
187 // first recurse children
188 VarTree* child = firstChild();
189 while (child != 0) {
190 child->inferTypesOfChildren(typeTable);
191 child = child->nextSibling();
194 // if this is a pointer, get the type from the value (less the pointer)
195 if (m_varKind == VKpointer) {
196 if (isWcharT())
199 * wchart_t pointers must be treated as struct, because the array
200 * of characters is printed similar to how QStrings are decoded.
202 m_varKind = VKstruct;
203 setExpandable(false);
205 // don't know how to do this cleanly
206 } else if (m_varKind == VKstruct) {
207 // check if this is a base class part
208 if (m_nameKind == NKtype) {
209 const QString& typeName =
210 getText().mid(1, getText().length()-2); // strip < and >
211 m_type = typeTable.lookup(typeName);
213 /* if we don't have a type yet, get it from the base class */
214 if (m_type == 0) {
215 m_type = inferTypeFromBaseClass();
217 * If there is a known type now, it is the one from the
218 * first base class whose type we know.
223 * If we still don't have a type, the type is really unknown.
225 if (m_type == 0) {
226 m_type = TypeInfo::unknownType();
228 } // else
230 * This is not a base class part. We don't assign a type so
231 * that later we can ask gdb.
236 // the value contains the pointer type in parenthesis
237 bool VarTree::isWcharT() const
239 return value().startsWith("(const wchar_t *)") ||
240 value().startsWith("(wchar_t *)");
244 * Get the type of the first base class whose type we know.
246 TypeInfo* VarTree::inferTypeFromBaseClass()
248 if (m_varKind == VKstruct) {
249 VarTree* child = firstChild();
250 while (child != 0 &&
251 // only check base class parts (i.e. type names)
252 child->m_nameKind == NKtype)
254 if (child->m_type != 0 &&
255 child->m_type != TypeInfo::unknownType())
257 // got a type!
258 return child->m_type;
260 child = child->nextSibling();
263 return 0;
266 ExprValue::ExprValue(const QString& name, VarTree::NameKind aKind) :
267 m_name(name),
268 m_varKind(VarTree::VKsimple),
269 m_nameKind(aKind),
270 m_child(0),
271 m_next(0),
272 m_initiallyExpanded(false)
276 ExprValue::~ExprValue()
278 delete m_child;
279 delete m_next;
282 void ExprValue::appendChild(ExprValue* newChild)
284 if (m_child == 0) {
285 m_child = newChild;
286 } else {
287 // walk chain of children to find the last one
288 ExprValue* last = m_child;
289 while (last->m_next != 0)
290 last = last->m_next;
291 last->m_next = newChild;
293 newChild->m_next = 0; // just to be sure
296 int ExprValue::childCount() const
298 int i = 0;
299 ExprValue* c = m_child;
300 while (c) {
301 ++i;
302 c = c->m_next;
304 return i;
309 ExprWnd::ExprWnd(QWidget* parent, const QString& colHeader, const char* name) :
310 QListView(parent, name),
311 m_edit(0)
313 addColumn(colHeader);
314 addColumn(i18n("Value"));
315 setSorting(-1); // do not sort items
316 setColumnWidthMode(0, Manual);
317 setColumnWidthMode(1, Maximum);
318 setRootIsDecorated(true);
319 setAllColumnsShowFocus(true);
321 m_pixPointer = UserIcon("pointer.xpm");
322 if (m_pixPointer.isNull())
323 TRACE("Can't load pointer.xpm");
326 ExprWnd::~ExprWnd()
330 void ExprWnd::exprList(QStrList& exprs)
332 // ASSERT(exprs does deep-copies)
333 VarTree* item;
334 for (item = firstChild(); item != 0; item = item->nextSibling()) {
335 exprs.append(item->getText());
339 VarTree* ExprWnd::insertExpr(ExprValue* expr, ProgramTypeTable& typeTable)
341 // append a new dummy expression
342 VarTree* last = 0; // last top-level item
343 for (VarTree* i = firstChild(); i != 0; i = i->nextSibling()) {
344 last = i;
346 VarTree* display = new VarTree(this, last, expr->m_name);
348 // replace it right away
349 updateExpr(display, expr, typeTable);
350 return display;
353 void ExprWnd::updateExpr(ExprValue* expr, ProgramTypeTable& typeTable)
355 // search the root variable
356 VarTree* item = firstChild();
357 while (item != 0 && item->getText() != expr->m_name)
358 item = item->nextSibling();
359 if (item == 0) {
360 return;
362 // now update it
363 updateExprRec(item, expr, typeTable);
364 collectUnknownTypes(item);
367 void ExprWnd::updateExpr(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable)
369 updateExprRec(display, newValues, typeTable);
370 collectUnknownTypes(display);
374 * returns true if there's a visible change
376 void ExprWnd::updateExprRec(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable)
378 bool isExpanded = display->isOpen();
381 * If we are updating a pointer without children by a dummy, we don't
382 * collapse it, but simply insert the new children. This happens when a
383 * pointer has just been expanded by the user.
385 if (display->m_varKind == VarTree::VKpointer &&
386 display->childCount() == 0 &&
387 newValues->m_varKind == VarTree::VKdummy)
389 replaceChildren(display, newValues);
390 return;
394 * If the display and newValues have different kind or if their number
395 * of children is different, replace the whole sub-tree.
397 if (// the next two lines mean: not(m_varKind remains unchanged)
398 !(newValues->m_varKind == VarTree::VKdummy ||
399 display->m_varKind == newValues->m_varKind)
401 (display->childCount() != newValues->childCount() &&
403 * If this is a pointer and newValues doesn't have children, we
404 * don't replace the sub-tree; instead, below we mark this
405 * sub-tree for requiring an update.
407 (display->m_varKind != VarTree::VKpointer ||
408 newValues->m_child != 0)))
410 if (isExpanded) {
411 display->setOpen(false);
414 // since children changed, it is likely that the type has also changed
415 display->m_type = 0; /* will re-evaluate the type */
417 // display the new value
418 updateSingleExpr(display, newValues);
419 replaceChildren(display, newValues);
421 // update the m_varKind
422 if (newValues->m_varKind != VarTree::VKdummy) {
423 display->m_varKind = newValues->m_varKind;
424 display->setExpandable(newValues->m_varKind == VarTree::VKpointer);
427 // get some types (after the new m_varKind has been set!)
428 display->inferTypesOfChildren(typeTable);
430 // (note that the new value might not have a sub-tree at all)
431 return;
434 // display the new value
435 updateSingleExpr(display, newValues);
438 * If this is an expanded pointer, record it for being updated.
440 if (display->m_varKind == VarTree::VKpointer) {
441 if (isExpanded &&
442 // if newValues is a dummy, we have already updated this pointer
443 newValues->m_varKind != VarTree::VKdummy)
445 m_updatePtrs.append(display);
448 * If the visible sub-tree has children, but newValues doesn't, we
449 * can stop here.
451 if (newValues->m_child == 0) {
452 return;
456 ASSERT(display->childCount() == newValues->childCount());
458 // go for children
459 VarTree* vDisplay = display->firstChild();
460 ExprValue* vNew = newValues->m_child;
461 while (vDisplay != 0) {
462 // check whether the names are the same
463 if (vDisplay->getText() != vNew->m_name) {
464 // set new name
465 vDisplay->setText(vNew->m_name);
467 // recurse
468 updateExprRec(vDisplay, vNew, typeTable);
470 vDisplay = vDisplay->nextSibling();
471 vNew = vNew->m_next;
475 void ExprWnd::updateSingleExpr(VarTree* display, ExprValue* newValue)
478 * If newValues is a VKdummy, we are only interested in its children.
479 * No need to update anything here.
481 if (newValue->m_varKind == VarTree::VKdummy) {
482 return;
486 * If this node is a struct and we know its type then we know how to
487 * find a nested value. So register the node for an update.
489 * wchar_t types are also treated specially here: We consider them
490 * as struct (has been set in inferTypesOfChildren()).
492 if (display->m_varKind == VarTree::VKstruct &&
493 display->m_type != 0 &&
494 display->m_type != TypeInfo::unknownType())
496 ASSERT(newValue->m_varKind == VarTree::VKstruct);
497 if (display->m_type == TypeInfo::wchartType())
499 display->m_partialValue = "L";
501 else
502 display->m_partialValue = display->m_type->m_displayString[0];
503 m_updateStruct.append(display);
506 if (display->updateValue(newValue->m_value)) {
507 triggerUpdate();
511 void ExprWnd::updateStructValue(VarTree* display)
513 ASSERT(display->m_varKind == VarTree::VKstruct);
515 if (display->updateStructValue(display->m_partialValue)) {
516 triggerUpdate();
518 // reset the value
519 display->m_partialValue = "";
520 display->m_exprIndex = -1;
523 void ExprWnd::replaceChildren(VarTree* display, ExprValue* newValues)
525 ASSERT(display->childCount() == 0 || display->m_varKind != VarTree::VKsimple);
527 // delete all children of display
528 while (VarTree* c = display->firstChild()) {
529 unhookSubtree(c);
530 delete c;
532 // insert copies of the newValues
533 VarTree* vNew = 0;
534 for (ExprValue* v = newValues->m_child; v != 0; v = v->m_next)
536 vNew = new VarTree(display, vNew, v);
537 // recurse
538 replaceChildren(vNew, v);
542 void ExprWnd::collectUnknownTypes(VarTree* var)
544 QListViewItemIterator i(var);
545 for (; i.current(); ++i)
547 checkUnknownType(static_cast<VarTree*>(i.current()));
551 void ExprWnd::checkUnknownType(VarTree* var)
553 ASSERT(var->m_varKind != VarTree::VKpointer || var->m_nameKind != VarTree::NKtype);
554 if (var->m_type == 0 &&
555 var->m_varKind == VarTree::VKstruct &&
556 var->m_nameKind != VarTree::NKtype &&
557 var->m_nameKind != VarTree::NKanonymous)
559 if (!var->isWcharT())
561 /* this struct node doesn't have a type yet: register it */
562 m_updateType.append(var);
564 else
566 var->m_type = TypeInfo::wchartType();
567 var->m_partialValue = "L";
568 m_updateStruct.append(var);
571 // add pointer pixmap to pointers
572 if (var->m_varKind == VarTree::VKpointer) {
573 var->setPixmap(m_pixPointer);
577 QString ExprWnd::formatWCharPointer(QString value)
579 int pos = value.find(") ");
580 if (pos > 0)
581 value = value.mid(pos+2);
582 return value + " L";
586 VarTree* ExprWnd::topLevelExprByName(const char* name)
588 VarTree* item = firstChild();
589 while (item != 0 && item->getText() != name)
590 item = item->nextSibling();
592 return item;
595 VarTree* ExprWnd::ptrMemberByName(VarTree* v, const QString& name)
597 // v must be a pointer variable, must have children
598 if (v->m_varKind != VarTree::VKpointer || v->childCount() == 0)
599 return 0;
601 // the only child of v is the pointer value that represents the struct
602 VarTree* item = v->firstChild();
603 return memberByName(item, name);
606 VarTree* ExprWnd::memberByName(VarTree* v, const QString& name)
608 // search immediate children for name
609 VarTree* item = v->firstChild();
610 while (item != 0 && item->getText() != name)
611 item = item->nextSibling();
613 if (item != 0)
614 return item;
616 // try in base classes and members that are anonymous structs or unions
617 item = v->firstChild();
618 while (item != 0)
620 if (item->m_nameKind == VarTree::NKtype ||
621 item->m_nameKind == VarTree::NKanonymous)
623 v = memberByName(item, name);
624 if (v != 0)
625 return v;
627 item = item->nextSibling();
629 return 0;
632 void ExprWnd::removeExpr(VarTree* item)
634 unhookSubtree(item);
636 delete item;
639 void ExprWnd::unhookSubtree(VarTree* subTree)
641 // must remove any pointers scheduled for update from the list
642 unhookSubtree(m_updatePtrs, subTree);
643 unhookSubtree(m_updateType, subTree);
644 unhookSubtree(m_updateStruct, subTree);
645 emit removingItem(subTree);
648 void ExprWnd::unhookSubtree(QList<VarTree>& list, VarTree* subTree)
650 if (subTree == 0)
651 return;
653 VarTree* checkItem = list.first();
654 while (checkItem != 0) {
655 if (!subTree->isAncestorEq(checkItem)) {
656 // checkItem is not an item from subTree
657 // advance
658 checkItem = list.next();
659 } else {
660 // checkItem is an item from subTree
662 * If checkItem is the last item in the list, we need a special
663 * treatment, because remove()ing it steps the current item of
664 * the list in the "wrong" direction.
666 if (checkItem == list.getLast()) { // does not set current item
667 list.remove();
668 /* we deleted the last element, so we've finished */
669 checkItem = 0;
670 } else {
671 list.remove();
672 /* remove() advanced already */
673 checkItem = list.current();
679 void ExprWnd::clearPendingUpdates()
681 m_updatePtrs.clear();
682 m_updateType.clear();
683 m_updateStruct.clear();
686 VarTree* ExprWnd::nextUpdatePtr()
688 VarTree* ptr = m_updatePtrs.first();
689 if (ptr != 0) {
690 m_updatePtrs.remove();
692 return ptr;
695 VarTree* ExprWnd::nextUpdateType()
697 VarTree* ptr = m_updateType.first();
698 if (ptr != 0) {
699 m_updateType.remove();
701 return ptr;
704 VarTree* ExprWnd::nextUpdateStruct()
706 VarTree* ptr = m_updateStruct.first();
707 if (ptr != 0) {
708 m_updateStruct.remove();
710 return ptr;
714 void ExprWnd::editValue(VarTree* item, const QString& text)
716 if (m_edit == 0)
717 m_edit = new ValueEdit(this);
719 QRect r = itemRect(item);
720 int x = r.x()+columnWidth(0);
721 int y = r.y();
722 int w = columnWidth(1);
723 int h = r.height();
724 QListView* lv = item->listView();
727 * Make the edit widget at least 5 characters wide (but not wider than
728 * this widget). If less than half of this widget is used to display
729 * the text, scroll this widget so that half of it shows the text (or
730 * less than half of it if the text is shorter).
732 QFontMetrics metr = m_edit->font();
733 int wMin = metr.width("88888");
734 if (w < wMin)
735 w = wMin;
736 int wThis = lv->visibleWidth();
737 if (x >= wThis/2 && // less than half the width displays text
738 x+w > wThis) // not all text is visible
740 // scroll so that more text is visible
741 int wScroll = QMIN(x-wThis/2, x+w-wThis);
742 lv->scrollBy(wScroll, 0);
743 x -= wScroll;
745 else if (x < 0)
747 // don't let the edit move out at the left
748 x = 0;
751 // make the edit box as wide as the visible column
752 QRect rect(x,y, wThis-x,h);
753 m_edit->setText(text);
754 m_edit->selectAll();
756 m_edit->setGeometry(rect);
757 m_edit->m_finished = false;
758 m_edit->m_item = item;
759 m_edit->show();
760 m_edit->setFocus();
763 bool ExprWnd::isEditing() const
765 return m_edit != 0 && m_edit->isVisible();
769 ValueEdit::ValueEdit(ExprWnd* parent) :
770 QLineEdit(parent->viewport(), "valueedit")
772 setFrame(false);
773 hide();
774 lower(); // lower the window below scrollbars
775 connect(parent, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged()));
776 connect(parent, SIGNAL(currentChanged(QListViewItem*)), SLOT(slotSelectionChanged()));
777 connect(parent, SIGNAL(expanded(QListViewItem*)), SLOT(slotSelectionChanged()));
778 connect(parent, SIGNAL(collapsed(QListViewItem*)), SLOT(slotSelectionChanged()));
779 connect(this, SIGNAL(done(VarTree*, const QString&)),
780 parent, SIGNAL(editValueCommitted(VarTree*, const QString&)));
783 ValueEdit::~ValueEdit()
787 void ValueEdit::terminate(bool commit)
789 TRACE(commit?"ValueEdit::terminate(true)":"ValueEdit::terminate(false)");
790 if (!m_finished)
792 m_finished = true;
793 hide(); // will call focusOutEvent, that's why we need m_finished
794 if (commit) {
795 emit done(m_item, text());
800 void ValueEdit::keyPressEvent(QKeyEvent *e)
802 if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)
803 terminate(true);
804 else if(e->key() == Qt::Key_Escape)
805 terminate(false);
806 else
807 QLineEdit::keyPressEvent(e);
810 void ValueEdit::paintEvent(QPaintEvent* e)
812 QLineEdit::paintEvent(e);
814 QPainter p(this);
815 p.drawRect(rect());
818 void ValueEdit::focusOutEvent(QFocusEvent* ev)
820 TRACE("ValueEdit::focusOutEvent");
821 QFocusEvent* focusEv = static_cast<QFocusEvent*>(ev);
822 if (focusEv->reason() == QFocusEvent::ActiveWindow)
824 // Switching to a different window should terminate the edit,
825 // because if the window with this variable display is floating
826 // then that different window could be the main window, where
827 // the user had clicked one of the Execute buttons. This in turn
828 // may pull the item away that we are editing here.
829 terminate(false);
831 // Don't let a RMB close the editor
832 else if (focusEv->reason() != QFocusEvent::Popup)
834 terminate(true);
838 void ValueEdit::slotSelectionChanged()
840 TRACE("ValueEdit::slotSelectionChanged");
841 terminate(false);