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 <qstringlist.h>
12 #include <qscrollbar.h>
13 #include <kapplication.h>
14 #include <kiconloader.h> /* icons */
15 #include <klocale.h> /* i18n */
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
),
27 m_exprIndexUseGuard(false),
28 m_baseValue(v
->m_value
),
30 m_structChanged(false)
32 QListViewItem::setText(0, v
->m_name
);
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
),
41 m_nameKind(VarTree::NKplain
),
44 m_exprIndexUseGuard(false),
46 m_structChanged(false)
48 QListViewItem::setText(0, name
);
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
);
62 QListViewItem::paintCell(p
, cg
, column
, width
, align
);
66 QString
VarTree::computeExpr() const
68 // top-level items are special
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
) {
80 /* augment by this item's text */
82 /* if this is an address, dereference it */
83 if (m_nameKind
== NKaddress
) {
84 ASSERT(par
->m_varKind
== VKpointer
);
85 result
= "*" + parentExpr
;
88 switch (par
->m_varKind
) {
91 QString index
= getText();
93 // skip past the index
94 while (index
[i
].isDigit())
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
;
108 result
= "(" + parentExpr
+ ")." + getText();
110 case VKsimple
: /* parent can't be simple */
111 case VKpointer
: /* handled in NKaddress */
112 case VKdummy
: /* can't occur at all */
114 result
= parentExpr
; /* paranoia */
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) {
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
;
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
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
;
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 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
);
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();
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
) {
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 */
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.
226 m_type
= TypeInfo::unknownType();
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();
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())
258 return child
->m_type
;
260 child
= child
->nextSibling();
266 ExprValue::ExprValue(const QString
& name
, VarTree::NameKind aKind
) :
268 m_varKind(VarTree::VKsimple
),
272 m_initiallyExpanded(false)
276 ExprValue::~ExprValue()
282 void ExprValue::appendChild(ExprValue
* newChild
)
287 // walk chain of children to find the last one
288 ExprValue
* last
= m_child
;
289 while (last
->m_next
!= 0)
291 last
->m_next
= newChild
;
293 newChild
->m_next
= 0; // just to be sure
296 int ExprValue::childCount() const
299 ExprValue
* c
= m_child
;
309 ExprWnd::ExprWnd(QWidget
* parent
, const QString
& colHeader
, const char* name
) :
310 QListView(parent
, name
),
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");
330 QStringList
ExprWnd::exprList() const
334 for (item
= firstChild(); item
!= 0; item
= item
->nextSibling()) {
335 exprs
.append(item
->getText());
340 VarTree
* ExprWnd::insertExpr(ExprValue
* expr
, ProgramTypeTable
& typeTable
)
342 // append a new dummy expression
343 VarTree
* last
= 0; // last top-level item
344 for (VarTree
* i
= firstChild(); i
!= 0; i
= i
->nextSibling()) {
347 VarTree
* display
= new VarTree(this, last
, expr
->m_name
);
349 // replace it right away
350 updateExpr(display
, expr
, typeTable
);
354 void ExprWnd::updateExpr(ExprValue
* expr
, ProgramTypeTable
& typeTable
)
356 // search the root variable
357 VarTree
* item
= firstChild();
358 while (item
!= 0 && item
->getText() != expr
->m_name
)
359 item
= item
->nextSibling();
364 updateExprRec(item
, expr
, typeTable
);
365 collectUnknownTypes(item
);
368 void ExprWnd::updateExpr(VarTree
* display
, ExprValue
* newValues
, ProgramTypeTable
& typeTable
)
370 updateExprRec(display
, newValues
, typeTable
);
371 collectUnknownTypes(display
);
375 * returns true if there's a visible change
377 void ExprWnd::updateExprRec(VarTree
* display
, ExprValue
* newValues
, ProgramTypeTable
& typeTable
)
379 bool isExpanded
= display
->isOpen();
382 * If we are updating a pointer without children by a dummy, we don't
383 * collapse it, but simply insert the new children. This happens when a
384 * pointer has just been expanded by the user.
386 if (display
->m_varKind
== VarTree::VKpointer
&&
387 display
->childCount() == 0 &&
388 newValues
->m_varKind
== VarTree::VKdummy
)
390 replaceChildren(display
, newValues
);
395 * If the display and newValues have different kind or if their number
396 * of children is different, replace the whole sub-tree.
398 if (// the next two lines mean: not(m_varKind remains unchanged)
399 !(newValues
->m_varKind
== VarTree::VKdummy
||
400 display
->m_varKind
== newValues
->m_varKind
)
402 (display
->childCount() != newValues
->childCount() &&
404 * If this is a pointer and newValues doesn't have children, we
405 * don't replace the sub-tree; instead, below we mark this
406 * sub-tree for requiring an update.
408 (display
->m_varKind
!= VarTree::VKpointer
||
409 newValues
->m_child
!= 0)))
412 display
->setOpen(false);
415 // since children changed, it is likely that the type has also changed
416 display
->m_type
= 0; /* will re-evaluate the type */
418 // display the new value
419 updateSingleExpr(display
, newValues
);
420 replaceChildren(display
, newValues
);
422 // update the m_varKind
423 if (newValues
->m_varKind
!= VarTree::VKdummy
) {
424 display
->m_varKind
= newValues
->m_varKind
;
425 display
->setExpandable(newValues
->m_varKind
== VarTree::VKpointer
);
428 // get some types (after the new m_varKind has been set!)
429 display
->inferTypesOfChildren(typeTable
);
431 // (note that the new value might not have a sub-tree at all)
435 // display the new value
436 updateSingleExpr(display
, newValues
);
439 * If this is an expanded pointer, record it for being updated.
441 if (display
->m_varKind
== VarTree::VKpointer
) {
443 // if newValues is a dummy, we have already updated this pointer
444 newValues
->m_varKind
!= VarTree::VKdummy
)
446 m_updatePtrs
.push_back(display
);
449 * If the visible sub-tree has children, but newValues doesn't, we
452 if (newValues
->m_child
== 0) {
457 ASSERT(display
->childCount() == newValues
->childCount());
460 VarTree
* vDisplay
= display
->firstChild();
461 ExprValue
* vNew
= newValues
->m_child
;
462 while (vDisplay
!= 0) {
463 // check whether the names are the same
464 if (vDisplay
->getText() != vNew
->m_name
) {
466 vDisplay
->setText(vNew
->m_name
);
469 updateExprRec(vDisplay
, vNew
, typeTable
);
471 vDisplay
= vDisplay
->nextSibling();
476 void ExprWnd::updateSingleExpr(VarTree
* display
, ExprValue
* newValue
)
479 * If newValues is a VKdummy, we are only interested in its children.
480 * No need to update anything here.
482 if (newValue
->m_varKind
== VarTree::VKdummy
) {
487 * If this node is a struct and we know its type then we know how to
488 * find a nested value. So register the node for an update.
490 * wchar_t types are also treated specially here: We consider them
491 * as struct (has been set in inferTypesOfChildren()).
493 if (display
->m_varKind
== VarTree::VKstruct
&&
494 display
->m_type
!= 0 &&
495 display
->m_type
!= TypeInfo::unknownType())
497 ASSERT(newValue
->m_varKind
== VarTree::VKstruct
);
498 if (display
->m_type
== TypeInfo::wchartType())
500 display
->m_partialValue
= "L";
503 display
->m_partialValue
= display
->m_type
->m_displayString
[0];
504 m_updateStruct
.push_back(display
);
507 if (display
->updateValue(newValue
->m_value
)) {
512 void ExprWnd::updateStructValue(VarTree
* display
)
514 ASSERT(display
->m_varKind
== VarTree::VKstruct
);
516 if (display
->updateStructValue(display
->m_partialValue
)) {
520 display
->m_partialValue
= "";
521 display
->m_exprIndex
= -1;
524 void ExprWnd::replaceChildren(VarTree
* display
, ExprValue
* newValues
)
526 ASSERT(display
->childCount() == 0 || display
->m_varKind
!= VarTree::VKsimple
);
528 // delete all children of display
529 while (VarTree
* c
= display
->firstChild()) {
533 // insert copies of the newValues
535 for (ExprValue
* v
= newValues
->m_child
; v
!= 0; v
= v
->m_next
)
537 vNew
= new VarTree(display
, vNew
, v
);
539 replaceChildren(vNew
, v
);
543 void ExprWnd::collectUnknownTypes(VarTree
* var
)
545 QListViewItemIterator
i(var
);
546 for (; i
.current(); ++i
)
548 checkUnknownType(static_cast<VarTree
*>(i
.current()));
552 void ExprWnd::checkUnknownType(VarTree
* var
)
554 ASSERT(var
->m_varKind
!= VarTree::VKpointer
|| var
->m_nameKind
!= VarTree::NKtype
);
555 if (var
->m_type
== 0 &&
556 var
->m_varKind
== VarTree::VKstruct
&&
557 var
->m_nameKind
!= VarTree::NKtype
&&
558 var
->m_nameKind
!= VarTree::NKanonymous
)
560 if (!var
->isWcharT())
562 /* this struct node doesn't have a type yet: register it */
563 m_updateType
.push_back(var
);
567 var
->m_type
= TypeInfo::wchartType();
568 var
->m_partialValue
= "L";
569 m_updateStruct
.push_back(var
);
572 // add pointer pixmap to pointers
573 if (var
->m_varKind
== VarTree::VKpointer
) {
574 var
->setPixmap(m_pixPointer
);
578 QString
ExprWnd::formatWCharPointer(QString value
)
580 int pos
= value
.find(") ");
582 value
= value
.mid(pos
+2);
587 VarTree
* ExprWnd::topLevelExprByName(const QString
& name
) const
589 VarTree
* item
= firstChild();
590 while (item
!= 0 && item
->getText() != name
)
591 item
= item
->nextSibling();
596 VarTree
* ExprWnd::ptrMemberByName(VarTree
* v
, const QString
& name
)
598 // v must be a pointer variable, must have children
599 if (v
->m_varKind
!= VarTree::VKpointer
|| v
->childCount() == 0)
602 // the only child of v is the pointer value that represents the struct
603 VarTree
* item
= v
->firstChild();
604 return memberByName(item
, name
);
607 VarTree
* ExprWnd::memberByName(VarTree
* v
, const QString
& name
)
609 // search immediate children for name
610 VarTree
* item
= v
->firstChild();
611 while (item
!= 0 && item
->getText() != name
)
612 item
= item
->nextSibling();
617 // try in base classes and members that are anonymous structs or unions
618 item
= v
->firstChild();
621 if (item
->m_nameKind
== VarTree::NKtype
||
622 item
->m_nameKind
== VarTree::NKanonymous
)
624 v
= memberByName(item
, name
);
628 item
= item
->nextSibling();
633 void ExprWnd::removeExpr(VarTree
* item
)
640 void ExprWnd::unhookSubtree(VarTree
* subTree
)
642 // must remove any pointers scheduled for update from the list
643 unhookSubtree(m_updatePtrs
, subTree
);
644 unhookSubtree(m_updateType
, subTree
);
645 unhookSubtree(m_updateStruct
, subTree
);
646 emit
removingItem(subTree
);
649 void ExprWnd::unhookSubtree(std::list
<VarTree
*>& list
, VarTree
* subTree
)
654 std::list
<VarTree
*>::iterator i
= list
.begin();
655 while (i
!= list
.end()) {
656 std::list
<VarTree
*>::iterator checkItem
= i
;
658 if (subTree
->isAncestorEq(*checkItem
)) {
659 // checkItem is an item from subTree
660 list
.erase(checkItem
);
665 void ExprWnd::clearPendingUpdates()
667 m_updatePtrs
.clear();
668 m_updateType
.clear();
669 m_updateStruct
.clear();
672 VarTree
* ExprWnd::nextUpdatePtr()
675 if (!m_updatePtrs
.empty()) {
676 ptr
= m_updatePtrs
.front();
677 m_updatePtrs
.pop_front();
682 VarTree
* ExprWnd::nextUpdateType()
685 if (!m_updateType
.empty()) {
686 ptr
= m_updateType
.front();
687 m_updateType
.pop_front();
692 VarTree
* ExprWnd::nextUpdateStruct()
695 if (!m_updateStruct
.empty()) {
696 ptr
= m_updateStruct
.front();
697 m_updateStruct
.pop_front();
703 void ExprWnd::editValue(VarTree
* item
, const QString
& text
)
706 m_edit
= new ValueEdit(this);
708 QRect r
= itemRect(item
);
709 int x
= r
.x()+columnWidth(0);
711 int w
= columnWidth(1);
713 QListView
* lv
= item
->listView();
716 * Make the edit widget at least 5 characters wide (but not wider than
717 * this widget). If less than half of this widget is used to display
718 * the text, scroll this widget so that half of it shows the text (or
719 * less than half of it if the text is shorter).
721 QFontMetrics metr
= m_edit
->font();
722 int wMin
= metr
.width("88888");
725 int wThis
= lv
->visibleWidth();
726 if (x
>= wThis
/2 && // less than half the width displays text
727 x
+w
> wThis
) // not all text is visible
729 // scroll so that more text is visible
730 int wScroll
= QMIN(x
-wThis
/2, x
+w
-wThis
);
731 lv
->scrollBy(wScroll
, 0);
736 // don't let the edit move out at the left
740 // make the edit box as wide as the visible column
741 QRect
rect(x
,y
, wThis
-x
,h
);
742 m_edit
->setText(text
);
745 m_edit
->setGeometry(rect
);
746 m_edit
->m_finished
= false;
747 m_edit
->m_item
= item
;
752 bool ExprWnd::isEditing() const
754 return m_edit
!= 0 && m_edit
->isVisible();
758 ValueEdit::ValueEdit(ExprWnd
* parent
) :
759 QLineEdit(parent
->viewport(), "valueedit")
763 lower(); // lower the window below scrollbars
764 connect(parent
, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged()));
765 connect(parent
, SIGNAL(currentChanged(QListViewItem
*)), SLOT(slotSelectionChanged()));
766 connect(parent
, SIGNAL(expanded(QListViewItem
*)), SLOT(slotSelectionChanged()));
767 connect(parent
, SIGNAL(collapsed(QListViewItem
*)), SLOT(slotSelectionChanged()));
768 connect(this, SIGNAL(done(VarTree
*, const QString
&)),
769 parent
, SIGNAL(editValueCommitted(VarTree
*, const QString
&)));
772 ValueEdit::~ValueEdit()
776 void ValueEdit::terminate(bool commit
)
778 TRACE(commit
?"ValueEdit::terminate(true)":"ValueEdit::terminate(false)");
782 hide(); // will call focusOutEvent, that's why we need m_finished
784 emit
done(m_item
, text());
789 void ValueEdit::keyPressEvent(QKeyEvent
*e
)
791 if(e
->key() == Qt::Key_Return
|| e
->key() == Qt::Key_Enter
)
793 else if(e
->key() == Qt::Key_Escape
)
796 QLineEdit::keyPressEvent(e
);
799 void ValueEdit::paintEvent(QPaintEvent
* e
)
801 QLineEdit::paintEvent(e
);
807 void ValueEdit::focusOutEvent(QFocusEvent
* ev
)
809 TRACE("ValueEdit::focusOutEvent");
810 QFocusEvent
* focusEv
= static_cast<QFocusEvent
*>(ev
);
811 if (focusEv
->reason() == QFocusEvent::ActiveWindow
)
813 // Switching to a different window should terminate the edit,
814 // because if the window with this variable display is floating
815 // then that different window could be the main window, where
816 // the user had clicked one of the Execute buttons. This in turn
817 // may pull the item away that we are editing here.
820 // Don't let a RMB close the editor
821 else if (focusEv
->reason() != QFocusEvent::Popup
)
827 void ValueEdit::slotSelectionChanged()
829 TRACE("ValueEdit::slotSelectionChanged");