Extend copyright to 2018.
[kdbg.git] / kdbg / exprwnd.cpp
blobc9ac4e362cdbab35a1e324ea9880c3480a772dd8
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 <QHeaderView>
11 #include <QStringList>
12 #include <QPainter>
13 #include <QPaintEvent>
14 #include <QFocusEvent>
15 #include <QKeyEvent>
16 #include <QScrollBar>
17 #include <kiconloader.h> /* icons */
18 #include <klocalizedstring.h> /* i18n */
19 #include "mydebug.h"
21 VarTree::VarTree(VarTree* parent, ExprValue* v) :
22 QTreeWidgetItem(parent),
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 setText(v->m_name);
33 updateValueText();
34 if (v->m_child != 0 || m_varKind == VarTree::VKpointer)
35 setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
36 else
37 setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator);
38 setExpanded(v->m_initiallyExpanded);
41 VarTree::VarTree(ExprWnd* parent, ExprValue* v) :
42 QTreeWidgetItem(parent),
43 m_varKind(VKsimple),
44 m_nameKind(VarTree::NKplain),
45 m_type(0),
46 m_exprIndex(0),
47 m_exprIndexUseGuard(false),
48 m_baseValue(v->m_value),
49 m_baseChanged(false),
50 m_structChanged(false)
52 setText(v->m_name);
53 updateValueText();
56 VarTree::~VarTree()
60 QString VarTree::computeExpr() const
62 // top-level items are special
63 if (isToplevelExpr())
64 return getText();
66 // get parent expr
67 VarTree* par = static_cast<VarTree*>(parent());
68 QString parentExpr = par->computeExpr();
70 // skip this item's name if it is a base class or anonymous struct or union
71 if (m_nameKind == NKtype || m_nameKind == NKanonymous) {
72 return parentExpr;
74 /* augment by this item's text */
75 QString result;
76 /* if this is an address, dereference it */
77 if (m_nameKind == NKaddress) {
78 ASSERT(par->m_varKind == VKpointer);
79 result = "*" + parentExpr;
80 return result;
82 switch (par->m_varKind) {
83 case VKarray:
85 QString index = getText();
86 int i = 1;
87 // skip past the index
88 while (index[i].isDigit())
89 i++;
91 * Some array indices are actually ranges due to repeated array
92 * values. We use the first index in these cases.
94 if (index[i] != ']') {
95 // remove second index
96 index.remove(i, index.length()-i-1);
98 result = "(" + parentExpr + ")" + index;
100 break;
101 case VKstruct:
102 result = "(" + parentExpr + ")." + getText();
103 break;
104 case VKsimple: /* parent can't be simple */
105 case VKpointer: /* handled in NKaddress */
106 case VKdummy: /* can't occur at all */
107 ASSERT(false);
108 result = parentExpr; /* paranoia */
109 break;
111 return result;
114 bool VarTree::isToplevelExpr() const
116 return parent() == 0;
119 bool VarTree::isAncestorEq(const VarTree* child) const
121 const QTreeWidgetItem* c = child;
122 while (c != 0 && c != this) {
123 c = c->parent();
125 return c != 0;
128 bool VarTree::updateValue(const QString& newValue)
130 // check whether the value changed
131 bool prevValueChanged = m_baseChanged;
132 if ((m_baseChanged = m_baseValue != newValue)) {
133 m_baseValue = newValue;
134 updateValueText();
135 setForeground(1, QBrush(QColor(Qt::red)));
136 } else if (prevValueChanged) {
137 setForeground(1, treeWidget()->palette().text());
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_baseChanged || prevValueChanged;
148 bool VarTree::updateStructValue(const QString& newValue)
150 // check whether the value changed
151 bool prevValueChanged = m_structChanged;
152 if ((m_structChanged = m_structValue != newValue)) {
153 m_structValue = newValue;
154 updateValueText();
155 setForeground(1, QBrush(QColor(Qt::red)));
156 } else if (prevValueChanged) {
157 setForeground(1, treeWidget()->palette().text());
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 QString VarTree::displayedValue() const
170 QString text = m_baseValue;
171 if (text.isEmpty())
172 text = m_structValue;
173 else if (!m_structValue.isEmpty())
174 text += QLatin1String(" ") + m_structValue;
175 return text;
178 void VarTree::updateValueText()
180 setText(1, displayedValue());
183 void VarTree::inferTypesOfChildren(ProgramTypeTable& typeTable)
186 * Type inference works like this: We use type information of those
187 * children that have a type name in their name (base classes) or in
188 * their value (pointers)
191 // first recurse children
192 for (int i = 0; i < childCount(); i++)
194 child(i)->inferTypesOfChildren(typeTable);
197 // if this is a pointer, get the type from the value (less the pointer)
198 if (m_varKind == VKpointer) {
199 if (isWcharT())
202 * wchart_t pointers must be treated as struct, because the array
203 * of characters is printed similar to how QStrings are decoded.
205 m_varKind = VKstruct;
206 setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator);
208 // don't know how to do this cleanly
209 } else if (m_varKind == VKstruct) {
210 // check if this is a base class part
211 if (m_nameKind == NKtype) {
212 const QString& typeName =
213 getText().mid(1, getText().length()-2); // strip < and >
214 m_type = typeTable.lookup(typeName);
216 /* if we don't have a type yet, get it from the base class */
217 if (m_type == 0) {
218 m_type = inferTypeFromBaseClass();
220 * If there is a known type now, it is the one from the
221 * first base class whose type we know.
226 * If we still don't have a type, the type is really unknown.
228 if (m_type == 0) {
229 m_type = TypeInfo::unknownType();
231 } // else
233 * This is not a base class part. We don't assign a type so
234 * that later we can ask gdb.
239 // the value contains the pointer type in parenthesis
240 bool VarTree::isWcharT() const
242 return value().startsWith("(const wchar_t *)") ||
243 value().startsWith("(wchar_t *)");
247 * Get the type of the first base class whose type we know.
249 const TypeInfo* VarTree::inferTypeFromBaseClass()
251 if (m_varKind == VKstruct) {
252 for (int i = 0; i < childCount(); i++)
254 VarTree* child = VarTree::child(i);
255 // only check base class parts (i.e. type names)
256 if (child->m_nameKind != NKtype)
257 break;
258 if (child->m_type != 0 &&
259 child->m_type != TypeInfo::unknownType())
261 // got a type!
262 return child->m_type;
266 return 0;
269 QVariant VarTree::data(int column, int role) const
271 if (role != Qt::ToolTipRole || column != 1)
272 return QTreeWidgetItem::data(column, role);
273 return QVariant(displayedValue());
276 ExprValue::ExprValue(const QString& name, VarTree::NameKind aKind) :
277 m_name(name),
278 m_varKind(VarTree::VKsimple),
279 m_nameKind(aKind),
280 m_child(0),
281 m_next(0),
282 m_initiallyExpanded(false)
286 ExprValue::~ExprValue()
288 delete m_child;
289 delete m_next;
292 void ExprValue::appendChild(ExprValue* newChild)
294 if (m_child == 0) {
295 m_child = newChild;
296 } else {
297 // walk chain of children to find the last one
298 ExprValue* last = m_child;
299 while (last->m_next != 0)
300 last = last->m_next;
301 last->m_next = newChild;
303 newChild->m_next = 0; // just to be sure
306 int ExprValue::childCount() const
308 int i = 0;
309 ExprValue* c = m_child;
310 while (c) {
311 ++i;
312 c = c->m_next;
314 return i;
319 ExprWnd::ExprWnd(QWidget* parent, const QString& colHeader) :
320 QTreeWidget(parent),
321 m_edit(0)
323 QTreeWidgetItem* pHeaderItem = new QTreeWidgetItem();
324 pHeaderItem->setText(0, colHeader);
325 pHeaderItem->setText(1, i18n("Value"));
326 setHeaderItem(pHeaderItem);
327 header()->setSectionResizeMode(0, QHeaderView::Interactive);
328 header()->setSectionResizeMode(1, QHeaderView::Interactive);
330 setSortingEnabled(false); // do not sort items
331 setRootIsDecorated(true);
332 setAllColumnsShowFocus(true);
334 m_pixPointer = UserIcon("pointer.xpm");
335 if (m_pixPointer.isNull())
336 TRACE("Can't load pointer.xpm");
339 ExprWnd::~ExprWnd()
343 QStringList ExprWnd::exprList() const
345 QStringList exprs;
346 for (int i = 0; i < topLevelItemCount(); i++)
348 exprs.append(topLevelItem(i)->getText());
350 return exprs;
353 VarTree* ExprWnd::insertExpr(ExprValue* expr, ProgramTypeTable& typeTable)
355 // append a new dummy expression
356 VarTree* display = new VarTree(this, expr);
358 // replace it right away
359 updateExpr(display, expr, typeTable);
360 return display;
363 void ExprWnd::updateExpr(ExprValue* expr, ProgramTypeTable& typeTable)
365 // search the root variable
366 VarTree* item = 0;
367 for (int i = 0; i < topLevelItemCount(); i++)
369 if (topLevelItem(i)->getText() == expr->m_name) {
370 item = topLevelItem(i);
371 break;
374 if (item == 0) {
375 return;
377 // now update it
378 updateExprRec(item, expr, typeTable);
379 collectUnknownTypes(item);
382 void ExprWnd::updateExpr(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable)
384 updateExprRec(display, newValues, typeTable);
385 collectUnknownTypes(display);
389 * returns true if there's a visible change
391 void ExprWnd::updateExprRec(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable)
393 bool isExpanded = display->isExpanded();
396 * If we are updating a pointer without children by a dummy, we don't
397 * collapse it, but simply insert the new children. This happens when a
398 * pointer has just been expanded by the user.
400 if (display->m_varKind == VarTree::VKpointer &&
401 display->childCount() == 0 &&
402 newValues->m_varKind == VarTree::VKdummy)
404 replaceChildren(display, newValues);
405 return;
409 * If the display and newValues have different kind or if their number
410 * of children is different, replace the whole sub-tree.
412 if (// the next two lines mean: not(m_varKind remains unchanged)
413 !(newValues->m_varKind == VarTree::VKdummy ||
414 display->m_varKind == newValues->m_varKind)
416 (display->childCount() != newValues->childCount() &&
418 * If this is a pointer and newValues doesn't have children, we
419 * don't replace the sub-tree; instead, below we mark this
420 * sub-tree for requiring an update.
422 (display->m_varKind != VarTree::VKpointer ||
423 newValues->m_child != 0)))
425 if (isExpanded) {
426 display->setExpanded(false);
429 // since children changed, it is likely that the type has also changed
430 display->m_type = 0; /* will re-evaluate the type */
432 // display the new value
433 updateSingleExpr(display, newValues);
434 replaceChildren(display, newValues);
436 // update the m_varKind
437 if (newValues->m_varKind != VarTree::VKdummy) {
438 display->m_varKind = newValues->m_varKind;
439 if (newValues->m_child != 0 || newValues->m_varKind == VarTree::VKpointer)
440 display->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
441 else
442 display->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator);
445 // get some types (after the new m_varKind has been set!)
446 display->inferTypesOfChildren(typeTable);
448 // (note that the new value might not have a sub-tree at all)
449 return;
452 // display the new value
453 updateSingleExpr(display, newValues);
456 * If this is an expanded pointer, record it for being updated.
458 if (display->m_varKind == VarTree::VKpointer) {
459 if (isExpanded &&
460 // if newValues is a dummy, we have already updated this pointer
461 newValues->m_varKind != VarTree::VKdummy)
463 m_updatePtrs.push_back(display);
466 * If the visible sub-tree has children, but newValues doesn't, we
467 * can stop here.
469 if (newValues->m_child == 0) {
470 return;
474 ASSERT(display->childCount() == newValues->childCount());
476 // go for children
477 ExprValue* vNew = newValues->m_child;
478 for (int i = 0; i < display->childCount(); i++)
480 VarTree* vDisplay = display->child(i);
481 // check whether the names are the same
482 if (vDisplay->getText() != vNew->m_name) {
483 // set new name
484 vDisplay->setText(vNew->m_name);
486 // recurse
487 updateExprRec(vDisplay, vNew, typeTable);
489 vNew = vNew->m_next;
493 void ExprWnd::updateSingleExpr(VarTree* display, ExprValue* newValue)
496 * If newValues is a VKdummy, we are only interested in its children.
497 * No need to update anything here.
499 if (newValue->m_varKind == VarTree::VKdummy) {
500 return;
504 * If this node is a struct and we know its type then we know how to
505 * find a nested value. So register the node for an update.
507 * wchar_t types are also treated specially here: We consider them
508 * as struct (has been set in inferTypesOfChildren()).
510 if (display->m_varKind == VarTree::VKstruct &&
511 display->m_type != 0 &&
512 display->m_type != TypeInfo::unknownType())
514 ASSERT(newValue->m_varKind == VarTree::VKstruct);
515 if (display->m_type == TypeInfo::wchartType())
517 display->m_partialValue = "L";
519 else
520 display->m_partialValue = display->m_type->m_displayString[0];
521 m_updateStruct.push_back(display);
524 display->updateValue(newValue->m_value);
527 void ExprWnd::updateStructValue(VarTree* display)
529 ASSERT(display->m_varKind == VarTree::VKstruct);
531 display->updateStructValue(display->m_partialValue);
532 // reset the value
533 display->m_partialValue = "";
534 display->m_exprIndex = -1;
537 void ExprWnd::replaceChildren(VarTree* display, ExprValue* newValues)
539 ASSERT(display->childCount() == 0 || display->m_varKind != VarTree::VKsimple);
541 // delete all children of display
542 while (VarTree* c = display->child(0)) {
543 unhookSubtree(c);
544 delete c;
546 // insert copies of the newValues
547 for (ExprValue* v = newValues->m_child; v != 0; v = v->m_next)
549 VarTree* vNew = new VarTree(display, v);
550 // recurse
551 replaceChildren(vNew, v);
555 void ExprWnd::collectUnknownTypes(VarTree* var)
557 QTreeWidgetItemIterator i(var);
558 for (; *i; ++i)
560 checkUnknownType(static_cast<VarTree*>(*i));
564 void ExprWnd::checkUnknownType(VarTree* var)
566 ASSERT(var->m_varKind != VarTree::VKpointer || var->m_nameKind != VarTree::NKtype);
567 if (var->m_type == 0 &&
568 var->m_varKind == VarTree::VKstruct &&
569 var->m_nameKind != VarTree::NKtype &&
570 var->m_nameKind != VarTree::NKanonymous)
572 if (!var->isWcharT())
574 /* this struct node doesn't have a type yet: register it */
575 m_updateType.push_back(var);
577 else
579 var->m_type = TypeInfo::wchartType();
580 var->m_partialValue = "L";
581 m_updateStruct.push_back(var);
584 // add pointer pixmap to pointers
585 if (var->m_varKind == VarTree::VKpointer) {
586 var->setPixmap(m_pixPointer);
590 QString ExprWnd::formatWCharPointer(QString value)
592 int pos = value.indexOf(") ");
593 if (pos > 0)
594 value = value.mid(pos+2);
595 return value + " L";
599 VarTree* ExprWnd::topLevelExprByName(const QString& name) const
601 for (int i = 0; i < topLevelItemCount(); i++)
603 if (topLevelItem(i)->getText() == name)
604 return topLevelItem(i);
606 return 0;
609 VarTree* ExprWnd::ptrMemberByName(VarTree* v, const QString& name)
611 // v must be a pointer variable, must have children
612 if (v->m_varKind != VarTree::VKpointer || v->childCount() == 0)
613 return 0;
615 // the only child of v is the pointer value that represents the struct
616 VarTree* item = v->child(0);
617 return memberByName(item, name);
620 VarTree* ExprWnd::memberByName(VarTree* v, const QString& name)
622 // search immediate children for name
623 for (int i = 0; i < v->childCount(); i++)
625 if (v->child(i)->getText() == name)
626 return v->child(i);
629 // try in base classes and members that are anonymous structs or unions
630 for (int i = 0; i < v->childCount(); i++)
632 VarTree* item = v->child(i);
633 if (item->m_nameKind == VarTree::NKtype ||
634 item->m_nameKind == VarTree::NKanonymous)
636 item = memberByName(item, name);
637 if (item != 0)
638 return item;
641 return 0;
644 void ExprWnd::removeExpr(VarTree* item)
646 unhookSubtree(item);
648 delete item;
651 void ExprWnd::unhookSubtree(VarTree* subTree)
653 // must remove any pointers scheduled for update from the list
654 unhookSubtree(m_updatePtrs, subTree);
655 unhookSubtree(m_updateType, subTree);
656 unhookSubtree(m_updateStruct, subTree);
657 emit removingItem(subTree);
660 void ExprWnd::unhookSubtree(std::list<VarTree*>& list, VarTree* subTree)
662 if (subTree == 0)
663 return;
665 std::list<VarTree*>::iterator i = list.begin();
666 while (i != list.end()) {
667 std::list<VarTree*>::iterator checkItem = i;
668 ++i;
669 if (subTree->isAncestorEq(*checkItem)) {
670 // checkItem is an item from subTree
671 list.erase(checkItem);
676 void ExprWnd::clearPendingUpdates()
678 m_updatePtrs.clear();
679 m_updateType.clear();
680 m_updateStruct.clear();
683 VarTree* ExprWnd::nextUpdatePtr()
685 VarTree* ptr = 0;
686 if (!m_updatePtrs.empty()) {
687 ptr = m_updatePtrs.front();
688 m_updatePtrs.pop_front();
690 return ptr;
693 VarTree* ExprWnd::nextUpdateType()
695 VarTree* ptr = 0;
696 if (!m_updateType.empty()) {
697 ptr = m_updateType.front();
698 m_updateType.pop_front();
700 return ptr;
703 VarTree* ExprWnd::nextUpdateStruct()
705 VarTree* ptr = 0;
706 if (!m_updateStruct.empty()) {
707 ptr = m_updateStruct.front();
708 m_updateStruct.pop_front();
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 = visualItemRect(item);
720 int x = columnViewportPosition(1);
721 int y = r.y();
722 int w = columnWidth(1);
723 int h = r.height();
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 = viewport()->width();
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 QScrollBar* pScrollBar = horizontalScrollBar();
741 int wScroll = qMin(x-wThis/2, x+w-wThis);
742 pScrollBar->setValue(pScrollBar->value() + wScroll);
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())
772 setFrame(false);
773 hide();
774 lower(); // lower the window below scrollbars
775 connect(parent, SIGNAL(itemActivated(QTreeWidgetItem*,int)),
776 SLOT(slotSelectionChanged()));
777 connect(parent, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
778 SLOT(slotSelectionChanged()));
779 connect(parent, SIGNAL(itemExpanded(QTreeWidgetItem*)),
780 SLOT(slotSelectionChanged()));
781 connect(parent, SIGNAL(itemCollapsed(QTreeWidgetItem*)),
782 SLOT(slotSelectionChanged()));
783 connect(this, SIGNAL(done(VarTree*, const QString&)),
784 parent, SIGNAL(editValueCommitted(VarTree*, const QString&)));
787 ValueEdit::~ValueEdit()
791 void ValueEdit::terminate(bool commit)
793 TRACE(commit?"ValueEdit::terminate(true)":"ValueEdit::terminate(false)");
794 if (!m_finished)
796 m_finished = true;
797 hide(); // will call focusOutEvent, that's why we need m_finished
798 if (commit) {
799 emit done(m_item, text());
804 void ValueEdit::keyPressEvent(QKeyEvent *e)
806 if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)
807 terminate(true);
808 else if(e->key() == Qt::Key_Escape)
809 terminate(false);
810 else
811 QLineEdit::keyPressEvent(e);
814 void ValueEdit::paintEvent(QPaintEvent* e)
816 QLineEdit::paintEvent(e);
818 QPainter p(this);
819 p.drawRect(rect());
822 void ValueEdit::focusOutEvent(QFocusEvent* ev)
824 TRACE("ValueEdit::focusOutEvent");
825 QFocusEvent* focusEv = static_cast<QFocusEvent*>(ev);
826 if (focusEv->reason() == Qt::ActiveWindowFocusReason)
828 // Switching to a different window should terminate the edit,
829 // because if the window with this variable display is floating
830 // then that different window could be the main window, where
831 // the user had clicked one of the Execute buttons. This in turn
832 // may pull the item away that we are editing here.
833 terminate(false);
835 // Don't let a RMB close the editor
836 else if (focusEv->reason() != Qt::PopupFocusReason)
838 terminate(true);
842 void ValueEdit::slotSelectionChanged()
844 TRACE("ValueEdit::slotSelectionChanged");
845 terminate(false);