3 // Copyright by Johannes Sixt
4 // This file is under GPL, the GNU General Public Licence
11 #include <qscrollbar.h>
13 #include <kiconloader.h> /* icons */
14 #include <klocale.h> /* i18n */
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_valueChanged(false),
27 m_exprIndexUseGuard(false)
29 QListViewItem::setText(0, v
->m_name
);
30 QListViewItem::setText(1, v
->m_value
);
31 setExpandable(m_varKind
== VarTree::VKpointer
);
32 setOpen(v
->m_initiallyExpanded
);
35 VarTree::VarTree(ExprWnd
* parent
, QListViewItem
* after
, const QString
& name
) :
36 QListViewItem(parent
, after
),
38 m_nameKind(VarTree::NKplain
),
39 m_valueChanged(false),
42 m_exprIndexUseGuard(false)
44 QListViewItem::setText(0, name
);
51 void VarTree::paintCell(QPainter
* p
, const QColorGroup
& cg
, int column
, int width
, int align
)
53 if (column
== 1 && m_valueChanged
) {
54 QColorGroup cgChg
= cg
;
55 cgChg
.setColor(QColorGroup::Text
, Qt::red
);
56 QListViewItem::paintCell(p
, cgChg
, column
, width
, align
);
58 QListViewItem::paintCell(p
, cg
, column
, width
, align
);
62 QString
VarTree::computeExpr() const
64 // top-level items are special
69 VarTree
* par
= static_cast<VarTree
*>(parent());
70 QString parentExpr
= par
->computeExpr();
72 /* don't add this item's name if this is a base class sub-item */
73 if (m_nameKind
== NKtype
) {
76 /* augment by this item's text */
78 /* if this is an address, dereference it */
79 if (m_nameKind
== NKaddress
) {
80 ASSERT(par
->m_varKind
== VKpointer
);
81 result
= "*" + parentExpr
;
84 switch (par
->m_varKind
) {
87 QString index
= getText();
89 // skip past the index
90 while (index
[i
].isDigit())
93 * Some array indices are actually ranges due to repeated array
94 * values. We use the first index in these cases.
96 if (index
[i
] != ']') {
97 // remove second index
98 index
.remove(i
, index
.length()-i
-1);
100 result
= "(" + parentExpr
+ ")" + index
;
104 result
= "(" + parentExpr
+ ")." + getText();
106 case VKsimple
: /* parent can't be simple */
107 case VKpointer
: /* handled in NKaddress */
108 case VKdummy
: /* can't occur at all */
110 result
= parentExpr
; /* paranoia */
116 bool VarTree::isToplevelExpr() const
118 return parent() == 0;
121 bool VarTree::isAncestorEq(const VarTree
* child
) const
123 const QListViewItem
* c
= child
;
124 while (c
!= 0 && c
!= this) {
130 bool VarTree::updateValue(const QString
& newValue
)
132 // check whether the value changed
133 bool prevValueChanged
= m_valueChanged
;
134 m_valueChanged
= false;
135 if (value() != newValue
) {
137 m_valueChanged
= true;
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_valueChanged
|| prevValueChanged
;
148 void VarTree::inferTypesOfChildren(ProgramTypeTable
& typeTable
)
151 * Type inference works like this: We use type information of those
152 * children that have a type name in their name (base classes) or in
153 * their value (pointers)
156 // first recurse children
157 VarTree
* child
= firstChild();
159 child
->inferTypesOfChildren(typeTable
);
160 child
= child
->nextSibling();
163 // if this is a pointer, get the type from the value (less the pointer)
164 if (m_varKind
== VKpointer
) {
168 * wchart_t pointers must be treated as struct, because the array
169 * of characters is printed similar to how QStrings are decoded.
171 m_varKind
= VKstruct
;
172 setExpandable(false);
174 // don't know how to do this cleanly
175 } else if (m_varKind
== VKstruct
) {
176 // check if this is a base class part
177 if (m_nameKind
== NKtype
) {
178 const QString
& typeName
=
179 getText().mid(1, getText().length()-2); // strip < and >
180 m_type
= typeTable
.lookup(typeName
);
182 /* if we don't have a type yet, get it from the base class */
184 m_type
= inferTypeFromBaseClass();
186 * If there is a known type now, it is the one from the
187 * first base class whose type we know.
192 * If we still don't have a type, the type is really unknown.
195 m_type
= TypeInfo::unknownType();
199 * This is not a base class part. We don't assign a type so
200 * that later we can ask gdb.
205 // the value contains the pointer type in parenthesis
206 bool VarTree::isWcharT() const
208 return value().startsWith("(const wchar_t *)") ||
209 value().startsWith("(wchar_t *)");
213 * Get the type of the first base class whose type we know.
215 TypeInfo
* VarTree::inferTypeFromBaseClass()
217 if (m_varKind
== VKstruct
) {
218 VarTree
* child
= firstChild();
220 // only check base class parts (i.e. type names)
221 child
->m_nameKind
== NKtype
)
223 if (child
->m_type
!= 0 &&
224 child
->m_type
!= TypeInfo::unknownType())
227 return child
->m_type
;
229 child
= child
->nextSibling();
235 ExprValue::ExprValue(const QString
& name
, VarTree::NameKind aKind
) :
237 m_varKind(VarTree::VKsimple
),
241 m_initiallyExpanded(false)
245 ExprValue::~ExprValue()
251 void ExprValue::appendChild(ExprValue
* newChild
)
256 // walk chain of children to find the last one
257 ExprValue
* last
= m_child
;
258 while (last
->m_next
!= 0)
260 last
->m_next
= newChild
;
262 newChild
->m_next
= 0; // just to be sure
265 int ExprValue::childCount() const
268 ExprValue
* c
= m_child
;
278 ExprWnd::ExprWnd(QWidget
* parent
, const QString
& colHeader
, const char* name
) :
279 QListView(parent
, name
),
282 addColumn(colHeader
);
283 addColumn(i18n("Value"));
284 setSorting(-1); // do not sort items
285 setColumnWidthMode(0, Manual
);
286 setColumnWidthMode(1, Maximum
);
287 setRootIsDecorated(true);
289 m_pixPointer
= UserIcon("pointer.xpm");
290 if (m_pixPointer
.isNull())
291 TRACE("Can't load pointer.xpm");
298 void ExprWnd::exprList(QStrList
& exprs
)
300 // ASSERT(exprs does deep-copies)
302 for (item
= firstChild(); item
!= 0; item
= item
->nextSibling()) {
303 exprs
.append(item
->getText());
307 VarTree
* ExprWnd::insertExpr(ExprValue
* expr
, ProgramTypeTable
& typeTable
)
309 // append a new dummy expression
310 VarTree
* last
= 0; // last top-level item
311 for (VarTree
* i
= firstChild(); i
!= 0; i
= i
->nextSibling()) {
314 VarTree
* display
= new VarTree(this, last
, expr
->m_name
);
316 // replace it right away
317 updateExpr(display
, expr
, typeTable
);
321 void ExprWnd::updateExpr(ExprValue
* expr
, ProgramTypeTable
& typeTable
)
323 // search the root variable
324 VarTree
* item
= firstChild();
325 while (item
!= 0 && item
->getText() != expr
->m_name
)
326 item
= item
->nextSibling();
331 if (updateExprRec(item
, expr
, typeTable
)) {
334 collectUnknownTypes(item
);
337 void ExprWnd::updateExpr(VarTree
* display
, ExprValue
* newValues
, ProgramTypeTable
& typeTable
)
339 if (updateExprRec(display
, newValues
, typeTable
) &&
340 display
->isVisible())
344 collectUnknownTypes(display
);
348 * returns true if there's a visible change
350 bool ExprWnd::updateExprRec(VarTree
* display
, ExprValue
* newValues
, ProgramTypeTable
& typeTable
)
352 bool isExpanded
= display
->isOpen();
355 * If we are updating a pointer without children by a dummy, we don't
356 * collapse it, but simply insert the new children. This happens when a
357 * pointer has just been expanded by the user.
359 if (display
->m_varKind
== VarTree::VKpointer
&&
360 display
->childCount() == 0 &&
361 newValues
->m_varKind
== VarTree::VKdummy
)
363 replaceChildren(display
, newValues
);
364 return isExpanded
; /* no visible change if not expanded */
368 * If the display and newValues have different kind or if their number
369 * of children is different, replace the whole sub-tree.
371 if (// the next two lines mean: not(m_varKind remains unchanged)
372 !(newValues
->m_varKind
== VarTree::VKdummy
||
373 display
->m_varKind
== newValues
->m_varKind
)
375 (display
->childCount() != newValues
->childCount() &&
377 * If this is a pointer and newValues doesn't have children, we
378 * don't replace the sub-tree; instead, below we mark this
379 * sub-tree for requiring an update.
381 (display
->m_varKind
!= VarTree::VKpointer
||
382 newValues
->m_child
!= 0)))
385 display
->setOpen(false);
388 // since children changed, it is likely that the type has also changed
389 display
->m_type
= 0; /* will re-evaluate the type */
391 // display the new value
392 updateSingleExpr(display
, newValues
);
393 replaceChildren(display
, newValues
);
395 // update the m_varKind
396 if (newValues
->m_varKind
!= VarTree::VKdummy
) {
397 display
->m_varKind
= newValues
->m_varKind
;
398 display
->setExpandable(newValues
->m_varKind
== VarTree::VKpointer
);
401 // get some types (after the new m_varKind has been set!)
402 display
->inferTypesOfChildren(typeTable
);
404 // (note that the new value might not have a sub-tree at all)
405 return display
->m_valueChanged
|| isExpanded
; /* no visible change if not expanded */
408 // display the new value
409 updateSingleExpr(display
, newValues
);
412 * If this is an expanded pointer, record it for being updated.
414 if (display
->m_varKind
== VarTree::VKpointer
) {
416 // if newValues is a dummy, we have already updated this pointer
417 newValues
->m_varKind
!= VarTree::VKdummy
)
419 m_updatePtrs
.append(display
);
422 * If the visible sub-tree has children, but newValues doesn't, we
425 if (newValues
->m_child
== 0) {
426 return display
->m_valueChanged
;
430 ASSERT(display
->childCount() == newValues
->childCount());
433 bool childChanged
= false;
435 VarTree
* vDisplay
= display
->firstChild();
436 ExprValue
* vNew
= newValues
->m_child
;
437 while (vDisplay
!= 0) {
438 // check whether the names are the same
439 if (vDisplay
->getText() != vNew
->m_name
) {
441 vDisplay
->setText(vNew
->m_name
);
447 if (updateExprRec(vDisplay
, vNew
, typeTable
)) {
450 vDisplay
= vDisplay
->nextSibling();
454 // update of children propagates only if this node is expanded
455 return display
->m_valueChanged
|| (display
->isOpen() && childChanged
);
458 void ExprWnd::updateSingleExpr(VarTree
* display
, ExprValue
* newValue
)
461 * If newValues is a VKdummy, we are only interested in its children.
462 * No need to update anything here.
464 if (newValue
->m_varKind
== VarTree::VKdummy
) {
469 * If this node is a struct and we know its type then don't update its
470 * value now. This is a node for which we know how to find a nested
471 * value. So register the node for an update.
473 * wchar_t types are also treated specially here: We consider them
474 * as struct (has been set in inferTypesOfChildren()).
476 if (display
->m_varKind
== VarTree::VKstruct
&&
477 display
->m_type
!= 0 &&
478 display
->m_type
!= TypeInfo::unknownType())
480 ASSERT(newValue
->m_varKind
== VarTree::VKstruct
);
481 if (display
->m_type
== TypeInfo::wchartType())
484 * We do not copy the new pointer value to the destination right
485 * away, but consider it as the first part of the nested value.
486 * Then the display will change its color only when the new value
489 display
->m_partialValue
= formatWCharPointer(newValue
->m_value
);
492 display
->m_partialValue
= display
->m_type
->m_displayString
[0];
493 m_updateStruct
.append(display
);
497 if (display
->updateValue(newValue
->m_value
)) {
503 void ExprWnd::updateStructValue(VarTree
* display
)
505 ASSERT(display
->m_varKind
== VarTree::VKstruct
);
507 if (display
->updateValue(display
->m_partialValue
)) {
511 display
->m_partialValue
= "";
512 display
->m_exprIndex
= -1;
515 void ExprWnd::replaceChildren(VarTree
* display
, ExprValue
* newValues
)
517 ASSERT(display
->childCount() == 0 || display
->m_varKind
!= VarTree::VKsimple
);
519 // delete all children of display
520 while (VarTree
* c
= display
->firstChild()) {
524 // insert copies of the newValues
526 for (ExprValue
* v
= newValues
->m_child
; v
!= 0; v
= v
->m_next
)
528 vNew
= new VarTree(display
, vNew
, v
);
530 replaceChildren(vNew
, v
);
534 void ExprWnd::collectUnknownTypes(VarTree
* var
)
536 QListViewItemIterator
i(var
);
537 for (; i
.current(); ++i
)
539 checkUnknownType(static_cast<VarTree
*>(i
.current()));
543 void ExprWnd::checkUnknownType(VarTree
* var
)
545 ASSERT(var
->m_varKind
!= VarTree::VKpointer
|| var
->m_nameKind
!= VarTree::NKtype
);
546 if (var
->m_type
== 0 &&
547 var
->m_varKind
== VarTree::VKstruct
&&
548 var
->m_nameKind
!= VarTree::NKtype
)
550 if (!var
->isWcharT())
552 /* this struct node doesn't have a type yet: register it */
553 m_updateType
.append(var
);
557 var
->m_type
= TypeInfo::wchartType();
558 // see updateSingleExpr() why we move the value
559 var
->m_partialValue
= formatWCharPointer(var
->value());
560 var
->setValue(QString());
561 m_updateStruct
.append(var
);
564 // add pointer pixmap to pointers
565 if (var
->m_varKind
== VarTree::VKpointer
) {
566 var
->setPixmap(m_pixPointer
);
570 QString
ExprWnd::formatWCharPointer(QString value
)
572 int pos
= value
.find(") ");
574 value
= value
.mid(pos
+2);
579 VarTree
* ExprWnd::topLevelExprByName(const char* name
)
581 VarTree
* item
= firstChild();
582 while (item
!= 0 && item
->getText() != name
)
583 item
= item
->nextSibling();
588 VarTree
* ExprWnd::ptrMemberByName(VarTree
* v
, const QString
& name
)
590 // v must be a pointer variable, must have children
591 if (v
->m_varKind
!= VarTree::VKpointer
|| v
->childCount() == 0)
594 // the only child of v is the pointer value that represents the struct
595 VarTree
* item
= v
->firstChild();
596 return memberByName(item
, name
);
599 VarTree
* ExprWnd::memberByName(VarTree
* v
, const QString
& name
)
601 // search immediate children for name
602 VarTree
* item
= v
->firstChild();
603 while (item
!= 0 && item
->getText() != name
)
604 item
= item
->nextSibling();
609 // try in base classes
610 item
= v
->firstChild();
612 item
->m_nameKind
== VarTree::NKtype
)
614 v
= memberByName(item
, name
);
617 item
= item
->nextSibling();
622 void ExprWnd::removeExpr(VarTree
* item
)
629 void ExprWnd::unhookSubtree(VarTree
* subTree
)
631 // must remove any pointers scheduled for update from the list
632 unhookSubtree(m_updatePtrs
, subTree
);
633 unhookSubtree(m_updateType
, subTree
);
634 unhookSubtree(m_updateStruct
, subTree
);
635 emit
removingItem(subTree
);
638 void ExprWnd::unhookSubtree(QList
<VarTree
>& list
, VarTree
* subTree
)
643 VarTree
* checkItem
= list
.first();
644 while (checkItem
!= 0) {
645 if (!subTree
->isAncestorEq(checkItem
)) {
646 // checkItem is not an item from subTree
648 checkItem
= list
.next();
650 // checkItem is an item from subTree
652 * If checkItem is the last item in the list, we need a special
653 * treatment, because remove()ing it steps the current item of
654 * the list in the "wrong" direction.
656 if (checkItem
== list
.getLast()) { // does not set current item
658 /* we deleted the last element, so we've finished */
662 /* remove() advanced already */
663 checkItem
= list
.current();
669 void ExprWnd::clearPendingUpdates()
671 m_updatePtrs
.clear();
672 m_updateType
.clear();
673 m_updateStruct
.clear();
676 VarTree
* ExprWnd::nextUpdatePtr()
678 VarTree
* ptr
= m_updatePtrs
.first();
680 m_updatePtrs
.remove();
685 VarTree
* ExprWnd::nextUpdateType()
687 VarTree
* ptr
= m_updateType
.first();
689 m_updateType
.remove();
694 VarTree
* ExprWnd::nextUpdateStruct()
696 VarTree
* ptr
= m_updateStruct
.first();
698 m_updateStruct
.remove();
704 void ExprWnd::editValue(VarTree
* item
, const QString
& text
)
707 m_edit
= new ValueEdit(this);
709 QRect r
= itemRect(item
);
710 int x
= r
.x()+columnWidth(0);
712 int w
= columnWidth(1);
714 QListView
* lv
= item
->listView();
717 * Make the edit widget at least 5 characters wide (but not wider than
718 * this widget). If less than half of this widget is used to display
719 * the text, scroll this widget so that half of it shows the text (or
720 * less than half of it if the text is shorter).
722 QFontMetrics metr
= m_edit
->font();
723 int wMin
= metr
.width("88888");
726 int wThis
= lv
->visibleWidth();
727 if (x
>= wThis
/2 && // less than half the width displays text
728 x
+w
> wThis
) // not all text is visible
730 // scroll so that more text is visible
731 int wScroll
= QMIN(x
-wThis
/2, x
+w
-wThis
);
732 lv
->scrollBy(wScroll
, 0);
737 // don't let the edit move out at the left
741 // make the edit box as wide as the visible column
742 QRect
rect(x
,y
, wThis
-x
,h
);
743 m_edit
->setText(text
);
746 m_edit
->setGeometry(rect
);
747 m_edit
->m_finished
= false;
748 m_edit
->m_item
= item
;
753 bool ExprWnd::isEditing() const
755 return m_edit
!= 0 && m_edit
->isVisible();
759 ValueEdit::ValueEdit(ExprWnd
* parent
) :
760 QLineEdit(parent
->viewport(), "valueedit")
764 lower(); // lower the window below scrollbars
765 connect(parent
, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged()));
766 connect(parent
, SIGNAL(currentChanged(QListViewItem
*)), SLOT(slotSelectionChanged()));
767 connect(parent
, SIGNAL(expanded(QListViewItem
*)), SLOT(slotSelectionChanged()));
768 connect(parent
, SIGNAL(collapsed(QListViewItem
*)), SLOT(slotSelectionChanged()));
769 connect(this, SIGNAL(done(VarTree
*, const QString
&)),
770 parent
, SIGNAL(editValueCommitted(VarTree
*, const QString
&)));
773 ValueEdit::~ValueEdit()
777 void ValueEdit::terminate(bool commit
)
779 TRACE(commit
?"ValueEdit::terminate(true)":"ValueEdit::terminate(false)");
783 hide(); // will call focusOutEvent, that's why we need m_finished
785 emit
done(m_item
, text());
790 void ValueEdit::keyPressEvent(QKeyEvent
*e
)
792 if(e
->key() == Qt::Key_Return
|| e
->key() == Qt::Key_Enter
)
794 else if(e
->key() == Qt::Key_Escape
)
797 QLineEdit::keyPressEvent(e
);
800 void ValueEdit::paintEvent(QPaintEvent
* e
)
802 QLineEdit::paintEvent(e
);
808 void ValueEdit::focusOutEvent(QFocusEvent
* ev
)
810 TRACE("ValueEdit::focusOutEvent");
811 QFocusEvent
* focusEv
= static_cast<QFocusEvent
*>(ev
);
812 // Don't let a RMB close the editor
813 if (focusEv
->reason() != QFocusEvent::Popup
&&
814 focusEv
->reason() != QFocusEvent::ActiveWindow
)
820 void ValueEdit::slotSelectionChanged()
822 TRACE("ValueEdit::slotSelectionChanged");