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.
10 #include <QHeaderView>
11 #include <QStringList>
13 #include <QPaintEvent>
14 #include <QFocusEvent>
17 #include <kiconloader.h> /* icons */
18 #include <klocalizedstring.h> /* i18n */
21 VarTree::VarTree(VarTree
* parent
, ExprValue
* v
) :
22 QTreeWidgetItem(parent
),
23 m_varKind(v
->m_varKind
),
24 m_nameKind(v
->m_nameKind
),
27 m_exprIndexUseGuard(false),
28 m_baseValue(v
->m_value
),
30 m_structChanged(false)
34 if (v
->m_child
!= 0 || m_varKind
== VarTree::VKpointer
)
35 setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator
);
37 setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator
);
38 setExpanded(v
->m_initiallyExpanded
);
41 VarTree::VarTree(ExprWnd
* parent
, ExprValue
* v
) :
42 QTreeWidgetItem(parent
),
44 m_nameKind(VarTree::NKplain
),
47 m_exprIndexUseGuard(false),
48 m_baseValue(v
->m_value
),
50 m_structChanged(false)
60 QString
VarTree::computeExpr() const
62 // top-level items are special
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
) {
74 /* augment by this item's text */
76 /* if this is an address, dereference it */
77 if (m_nameKind
== NKaddress
) {
78 ASSERT(par
->m_varKind
== VKpointer
);
79 result
= "*" + parentExpr
;
82 switch (par
->m_varKind
) {
85 QString index
= getText();
87 // skip past the index
88 while (index
[i
].isDigit())
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
;
102 result
= "(" + parentExpr
+ ")." + getText();
104 case VKsimple
: /* parent can't be simple */
105 case VKpointer
: /* handled in NKaddress */
106 case VKdummy
: /* can't occur at all */
108 result
= parentExpr
; /* paranoia */
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) {
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
;
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
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
;
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
165 return m_structChanged
|| prevValueChanged
;
168 QString
VarTree::displayedValue() const
170 QString text
= m_baseValue
;
172 text
= m_structValue
;
173 else if (!m_structValue
.isEmpty())
174 text
+= QLatin1String(" ") + m_structValue
;
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
) {
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 */
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.
229 m_type
= TypeInfo::unknownType();
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
)
258 if (child
->m_type
!= 0 &&
259 child
->m_type
!= TypeInfo::unknownType())
262 return child
->m_type
;
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
) :
278 m_varKind(VarTree::VKsimple
),
282 m_initiallyExpanded(false)
286 ExprValue::~ExprValue()
292 void ExprValue::appendChild(ExprValue
* newChild
)
297 // walk chain of children to find the last one
298 ExprValue
* last
= m_child
;
299 while (last
->m_next
!= 0)
301 last
->m_next
= newChild
;
303 newChild
->m_next
= 0; // just to be sure
306 int ExprValue::childCount() const
309 ExprValue
* c
= m_child
;
319 ExprWnd::ExprWnd(QWidget
* parent
, const QString
& colHeader
) :
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");
343 QStringList
ExprWnd::exprList() const
346 for (int i
= 0; i
< topLevelItemCount(); i
++)
348 exprs
.append(topLevelItem(i
)->getText());
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
);
363 void ExprWnd::updateExpr(ExprValue
* expr
, ProgramTypeTable
& typeTable
)
365 // search the root variable
367 for (int i
= 0; i
< topLevelItemCount(); i
++)
369 if (topLevelItem(i
)->getText() == expr
->m_name
) {
370 item
= topLevelItem(i
);
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
);
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)))
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
);
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)
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
) {
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
469 if (newValues
->m_child
== 0) {
474 ASSERT(display
->childCount() == newValues
->childCount());
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
) {
484 vDisplay
->setText(vNew
->m_name
);
487 updateExprRec(vDisplay
, vNew
, typeTable
);
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
) {
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";
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
);
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)) {
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
);
551 replaceChildren(vNew
, v
);
555 void ExprWnd::collectUnknownTypes(VarTree
* var
)
557 QTreeWidgetItemIterator
i(var
);
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
);
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(") ");
594 value
= value
.mid(pos
+2);
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
);
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)
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
)
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
);
644 void ExprWnd::removeExpr(VarTree
* 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
)
665 std::list
<VarTree
*>::iterator i
= list
.begin();
666 while (i
!= list
.end()) {
667 std::list
<VarTree
*>::iterator checkItem
= 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()
686 if (!m_updatePtrs
.empty()) {
687 ptr
= m_updatePtrs
.front();
688 m_updatePtrs
.pop_front();
693 VarTree
* ExprWnd::nextUpdateType()
696 if (!m_updateType
.empty()) {
697 ptr
= m_updateType
.front();
698 m_updateType
.pop_front();
703 VarTree
* ExprWnd::nextUpdateStruct()
706 if (!m_updateStruct
.empty()) {
707 ptr
= m_updateStruct
.front();
708 m_updateStruct
.pop_front();
714 void ExprWnd::editValue(VarTree
* item
, const QString
& text
)
717 m_edit
= new ValueEdit(this);
719 QRect r
= visualItemRect(item
);
720 int x
= columnViewportPosition(1);
722 int w
= columnWidth(1);
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");
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
);
747 // don't let the edit move out at the left
751 // make the edit box as wide as the visible column
752 QRect
rect(x
,y
, wThis
-x
,h
);
753 m_edit
->setText(text
);
756 m_edit
->setGeometry(rect
);
757 m_edit
->m_finished
= false;
758 m_edit
->m_item
= item
;
763 bool ExprWnd::isEditing() const
765 return m_edit
!= 0 && m_edit
->isVisible();
769 ValueEdit::ValueEdit(ExprWnd
* parent
) :
770 QLineEdit(parent
->viewport())
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)");
797 hide(); // will call focusOutEvent, that's why we need m_finished
799 emit
done(m_item
, text());
804 void ValueEdit::keyPressEvent(QKeyEvent
*e
)
806 if(e
->key() == Qt::Key_Return
|| e
->key() == Qt::Key_Enter
)
808 else if(e
->key() == Qt::Key_Escape
)
811 QLineEdit::keyPressEvent(e
);
814 void ValueEdit::paintEvent(QPaintEvent
* e
)
816 QLineEdit::paintEvent(e
);
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.
835 // Don't let a RMB close the editor
836 else if (focusEv
->reason() != Qt::PopupFocusReason
)
842 void ValueEdit::slotSelectionChanged()
844 TRACE("ValueEdit::slotSelectionChanged");