Merge improved dialogs.
[kdbg.git] / kdbg / exprwnd.cpp
blobb55acffd7b8bd6c251752134c0e23986b588bef9
1 // $Id$
3 // Copyright by Johannes Sixt
4 // This file is under GPL, the GNU General Public Licence
6 #include "exprwnd.h"
7 #include "exprwnd.moc"
8 #include "typetable.h"
9 #include <qstrlist.h>
10 #include <qpainter.h>
11 #include <qscrollbar.h>
12 #include <kapp.h>
13 #include <kiconloader.h> /* icons */
14 #ifdef HAVE_CONFIG_H
15 #include "config.h"
16 #endif
17 #include "mydebug.h"
19 VarTree::VarTree(const QString& name, NameKind aKind) :
20 KTreeViewItem(name),
21 m_varKind(VKsimple),
22 m_nameKind(aKind),
23 m_valueChanged(false),
24 m_type(0),
25 m_exprIndex(0),
26 m_exprIndexUseGuard(false)
30 VarTree::~VarTree()
34 void VarTree::paintValue(QPainter* p)
36 p->save();
37 int cellHeight = height(p->fontMetrics());
38 int textX = 2;
39 int textY = (cellHeight - p->fontMetrics().height()) / 2 +
40 p->fontMetrics().ascent();
42 if (m_valueChanged) {
43 p->setPen(red);
45 // p->setBackgroundColor(cg.base());
46 p->drawText(textX, textY, m_value, m_value.length());
47 p->restore();
50 int VarTree::valueWidth()
52 assert(owner != 0);
53 return owner->fontMetrics().width(m_value) + 4;
56 QString VarTree::computeExpr() const
58 assert(getParent() != 0);
59 // just to be sure
60 if (getParent() == 0)
61 return QString();
63 // top-level items are special
64 if (getParent()->getParent() == 0)
65 return getText();
67 // get parent expr
68 VarTree* par = static_cast<VarTree*>(getParent());
69 QString parentExpr = par->computeExpr();
71 /* don't add this item's name if this is a base class sub-item */
72 if (m_nameKind == NKtype) {
73 return parentExpr;
75 /* augment by this item's text */
76 QString result;
77 /* if this is an address, dereference it */
78 if (m_nameKind == NKaddress) {
79 ASSERT(par->m_varKind == VKpointer);
80 result = "*" + parentExpr;
81 return result;
83 switch (par->m_varKind) {
84 case VKarray:
86 QString index = getText();
87 int i = 1;
88 // skip past the index
89 while (index[i].isDigit())
90 i++;
92 * Some array indices are actually ranges due to repeated array
93 * values. We use the first index in these cases.
95 if (index[i] != ']') {
96 // remove second index
97 index.remove(i, index.length()-i-1);
99 result = "(" + parentExpr + ")" + index;
101 break;
102 case VKstruct:
103 result = "(" + parentExpr + ")." + getText();
104 break;
105 case VKsimple: /* parent can't be simple */
106 case VKpointer: /* handled in NKaddress */
107 case VKdummy: /* can't occur at all */
108 ASSERT(false);
109 result = parentExpr; /* paranoia */
110 break;
112 return result;
115 bool VarTree::isToplevelExpr() const
117 return getParent() != 0 && getParent()->getParent() == 0;
120 bool VarTree::isAncestorEq(const VarTree* child) const
122 const KTreeViewItem* c = child;
123 while (c != 0 && c != this) {
124 c = c->getParent();
126 return c != 0;
129 bool VarTree::updateValue(const QString& newValue)
131 // check whether the value changed
132 bool prevValueChanged = m_valueChanged;
133 m_valueChanged = false;
134 if (m_value != newValue) {
135 m_value = newValue;
136 m_valueChanged = true;
139 * We must repaint the cell if the value changed. If it did not change,
140 * we still must repaint the cell if the value changed previously,
141 * because the color of the display must be changed (from red to
142 * black).
144 return m_valueChanged || prevValueChanged;
147 void VarTree::inferTypesOfChildren(ProgramTypeTable& typeTable)
150 * Type inference works like this: We use type information of those
151 * children that have a type name in their name (base classes) or in
152 * their value (pointers)
155 // first recurse children
156 VarTree* child = static_cast<VarTree*>(getChild());
157 while (child != 0) {
158 child->inferTypesOfChildren(typeTable);
159 child = static_cast<VarTree*>(child->getSibling());
162 // if this is a pointer, get the type from the value (less the pointer)
163 if (m_varKind == VKpointer) {
164 #ifndef I_know_a_way_to_do_this_cleanly
165 return;
166 #else
167 const char* p = m_value.data();
168 const char* start = p;
169 // the type of the pointer shows up in the value (sometimes)
170 if (p == 0 || *p != '(')
171 return;
172 skipNested(p, '(', ')');
174 * We only recognize pointers to data "(int *)" but not pointers
175 * to functions "(void (*)())".
177 if (p-start < 3 && /* at least 3 chars necessary: (*) */
178 p[-2] != '*') /* skip back before the closing paren */
180 return;
182 const QString& typeName =
183 FROM_LATIN1(start+1, p-start-3) // minus 3 chars
184 .stripWhiteSpace();
185 m_type = typeTable.lookup(typeName);
186 if (m_type == 0) {
187 m_type = TypeInfo::unknownType();
189 #endif
190 } else if (m_varKind == VKstruct) {
191 // check if this is a base class part
192 if (m_nameKind == NKtype) {
193 const QString& typeName =
194 text.mid(1, text.length()-2); // strip < and >
195 m_type = typeTable.lookup(typeName);
197 /* if we don't have a type yet, get it from the base class */
198 if (m_type == 0) {
199 m_type = inferTypeFromBaseClass();
201 * If there is a known type now, it is the one from the
202 * first base class whose type we know.
207 * If we still don't have a type, the type is really unknown.
209 if (m_type == 0) {
210 m_type = TypeInfo::unknownType();
212 } // else
214 * This is not a base class part. We don't assign a type so
215 * that later we can ask gdb.
221 * Get the type of the first base class whose type we know.
223 TypeInfo* VarTree::inferTypeFromBaseClass()
225 if (m_varKind == VKstruct) {
226 VarTree* child = static_cast<VarTree*>(getChild());
227 while (child != 0 &&
228 // only check base class parts (i.e. type names)
229 child->m_nameKind == NKtype)
231 if (child->m_type != 0 &&
232 child->m_type != TypeInfo::unknownType())
234 // got a type!
235 return child->m_type;
237 child = static_cast<VarTree*>(child->getSibling());
240 return 0;
244 ExprWnd::ExprWnd(QWidget* parent, const char* name) :
245 KTreeView(parent, name),
246 maxValueWidth(0),
247 m_edit(this)
249 setNumCols(2);
251 connect(this, SIGNAL(expanded(int)), SLOT(slotExpandOrCollapse(int)));
252 connect(this, SIGNAL(collapsed(int)), SLOT(slotExpandOrCollapse(int)));
254 m_pixPointer = UserIcon("pointer.xpm");
255 if (m_pixPointer.isNull())
256 TRACE("Can't load pointer.xpm");
259 ExprWnd::~ExprWnd()
263 void ExprWnd::exprList(QStrList& exprs)
265 // ASSERT(exprs does deep-copies)
266 KTreeViewItem* item;
267 for (item = itemAt(0); item != 0; item = item->getSibling()) {
268 exprs.append(item->getText());
272 void ExprWnd::insertExpr(VarTree* expr)
274 // append the expression
275 insertItem(expr);
277 collectUnknownTypes(expr);
279 updateValuesWidth();
282 void ExprWnd::updateExpr(VarTree* expr)
284 // search the root variable
285 QString p = expr->getText();
286 KPath path;
287 path.push(&p);
288 KTreeViewItem* item = itemAt(path);
289 path.pop();
290 if (item == 0) {
291 return;
293 // now update it
294 if (updateExprRec(static_cast<VarTree*>(item), expr)) {
295 updateVisibleItems();
296 updateValuesWidth();
297 repaint();
299 collectUnknownTypes(static_cast<VarTree*>(item));
302 void ExprWnd::updateExpr(VarTree* display, VarTree* newValues)
304 if (updateExprRec(display, newValues) && display->isVisible()) {
305 updateVisibleItems();
306 updateValuesWidth();
307 repaint();
309 collectUnknownTypes(display);
313 * returns true if there's a visible change
315 bool ExprWnd::updateExprRec(VarTree* display, VarTree* newValues)
317 bool isExpanded = display->isExpanded();
320 * If we are updating a pointer without children by a dummy, we don't
321 * collapse it, but simply insert the new children. This happens when a
322 * pointer has just been expanded by the user.
324 if (display->m_varKind == VarTree::VKpointer &&
325 display->childCount() == 0 &&
326 newValues->m_varKind == VarTree::VKdummy)
328 replaceChildren(display, newValues);
329 return isExpanded; /* no visible change if not expanded */
333 * If the display and newValues have different kind or if their number
334 * of children is different, replace the whole sub-tree.
336 if (// the next two lines mean: not(m_varKind remains unchanged)
337 !(newValues->m_varKind == VarTree::VKdummy ||
338 display->m_varKind == newValues->m_varKind)
340 (display->childCount() != newValues->childCount() &&
342 * If this is a pointer and newValues doesn't have children, we
343 * don't replace the sub-tree; instead, below we mark this
344 * sub-tree for requiring an update.
346 (display->m_varKind != VarTree::VKpointer ||
347 newValues->childCount() != 0)))
349 if (isExpanded) {
350 collapseSubTree(display, false);
353 // since children changed, it is likely that the type has also changed
354 display->m_type = 0; /* will re-evaluate the type */
356 // display the new value
357 updateSingleExpr(display, newValues);
358 replaceChildren(display, newValues);
360 // update the m_varKind
361 if (newValues->m_varKind != VarTree::VKdummy) {
362 display->m_varKind = newValues->m_varKind;
363 display->setDelayedExpanding(newValues->m_varKind == VarTree::VKpointer);
366 // (note that the new value might not have a sub-tree at all)
367 return display->m_valueChanged || isExpanded; /* no visible change if not expanded */
370 // display the new value
371 updateSingleExpr(display, newValues);
374 * If this is an expanded pointer, record it for being updated.
376 if (display->m_varKind == VarTree::VKpointer) {
377 if (isExpanded &&
378 // if newValues is a dummy, we have already updated this pointer
379 newValues->m_varKind != VarTree::VKdummy)
381 m_updatePtrs.append(display);
384 * If the visible sub-tree has children, but newValues doesn't, we
385 * can stop here.
387 if (newValues->childCount() == 0) {
388 return display->m_valueChanged;
392 ASSERT(display->childCount() == newValues->childCount());
394 // go for children
395 bool childChanged = false;
397 VarTree* vDisplay = static_cast<VarTree*>(display->getChild());
398 VarTree* vNew = static_cast<VarTree*>(newValues->getChild());
399 while (vDisplay != 0) {
400 // check whether the names are the same
401 if (strcmp(vDisplay->getText(), vNew->getText()) != 0) {
402 // set new name
403 vDisplay->setText(vNew->getText());
404 int i = itemRow(vDisplay);
405 if (i >= 0) {
406 updateCell(i, 0, true);
407 childChanged = true;
410 // recurse
411 if (updateExprRec(vDisplay, vNew)) {
412 childChanged = true;
414 vDisplay = static_cast<VarTree*>(vDisplay->getSibling());
415 vNew = static_cast<VarTree*>(vNew->getSibling());
418 // update of children propagates only if this node is expanded
419 return display->m_valueChanged || (display->isExpanded() && childChanged);
422 void ExprWnd::updateSingleExpr(VarTree* display, VarTree* newValue)
425 * If newValues is a VKdummy, we are only interested in its children.
426 * No need to update anything here.
428 if (newValue->m_varKind == VarTree::VKdummy) {
429 return;
433 * If this node is a struct and we know its type then don't update its
434 * value now. This is a node for which we know how to find a nested
435 * value. So register the node for an update.
437 if (display->m_varKind == VarTree::VKstruct &&
438 display->m_type != 0 &&
439 display->m_type != TypeInfo::unknownType())
441 ASSERT(newValue->m_varKind == VarTree::VKstruct);
442 display->m_partialValue = display->m_type->m_displayString[0];
443 m_updateStruct.append(display);
445 else
447 if (display->updateValue(newValue->m_value)) {
448 int i = itemRow(display);
449 if (i >= 0) {
450 updateCell(i, 1, true);
456 void ExprWnd::updateStructValue(VarTree* display)
458 ASSERT(display->m_varKind == VarTree::VKstruct);
460 if (display->updateValue(display->m_partialValue)) {
461 int i = itemRow(display);
462 if (i >= 0) {
463 updateValuesWidth();
464 updateCell(i, 1, true);
467 // reset the value
468 display->m_partialValue = "";
469 display->m_exprIndex = -1;
472 void ExprWnd::replaceChildren(VarTree* display, VarTree* newValues)
474 ASSERT(display->childCount() == 0 || display->m_varKind != VarTree::VKsimple);
476 // delete all children of display
477 KTreeViewItem* c;
478 while ((c = display->getChild()) != 0) {
479 display->removeChild(c);
481 // insert copies of the newValues
482 for (c = newValues->getChild(); c != 0; c = c->getSibling()) {
483 VarTree* v = static_cast<VarTree*>(c);
484 VarTree* vNew = new VarTree(v->getText(), v->m_nameKind);
485 vNew->m_varKind = v->m_varKind;
486 vNew->m_value = v->m_value;
487 vNew->m_type = v->m_type;
488 vNew->setDelayedExpanding(vNew->m_varKind == VarTree::VKpointer);
489 vNew->setExpanded(v->isExpanded());
490 display->appendChild(vNew);
491 // recurse
492 replaceChildren(vNew, v);
496 void ExprWnd::collectUnknownTypes(VarTree* var)
499 * forEveryItem does not scan the root item itself. So we must do it
500 * ourselves.
502 ASSERT(var->m_varKind != VarTree::VKpointer || var->m_nameKind != VarTree::NKtype);
503 if (var->m_type == 0 &&
504 var->m_varKind == VarTree::VKstruct &&
505 var->m_nameKind != VarTree::NKtype)
507 /* this struct node doesn't have a type yet: register it */
508 m_updateType.append(var);
511 // add pointer pixmap to pointers
512 if (var->m_varKind == VarTree::VKpointer) {
513 var->setPixmap(m_pixPointer);
516 forEveryItem(collectUnknownTypes, this, var);
519 bool ExprWnd::collectUnknownTypes(KTreeViewItem* item, void* user)
521 VarTree* var = static_cast<VarTree*>(item);
522 ExprWnd* tree = static_cast<ExprWnd*>(user);
523 ASSERT(var->m_varKind != VarTree::VKpointer || var->m_nameKind != VarTree::NKtype);
524 if (var->m_type == 0 &&
525 var->m_varKind == VarTree::VKstruct &&
526 var->m_nameKind != VarTree::NKtype)
528 /* this struct node doesn't have a type yet: register it */
529 tree->m_updateType.append(var);
531 // add pointer pixmap to pointers
532 if (var->m_varKind == VarTree::VKpointer) {
533 var->setPixmap(tree->m_pixPointer);
535 return false;
539 VarTree* ExprWnd::topLevelExprByName(const char* name)
541 QString p = name;
542 KPath path;
543 path.push(&p);
544 KTreeViewItem* item = itemAt(path);
545 path.pop();
547 return static_cast<VarTree*>(item);
550 void ExprWnd::removeExpr(VarTree* item)
552 // must remove any pointers scheduled for update from the list
553 sweepList(m_updatePtrs, item);
554 sweepList(m_updateType, item);
555 sweepList(m_updateStruct, item);
557 takeItem(item);
558 delete item;
560 updateValuesWidth();
563 void ExprWnd::sweepList(QList<VarTree>& list, VarTree* subTree)
565 if (subTree == 0)
566 return;
568 VarTree* checkItem = list.first();
569 while (checkItem != 0) {
570 if (!subTree->isAncestorEq(checkItem)) {
571 // checkItem is not an item from subTree
572 // advance
573 checkItem = list.next();
574 } else {
575 // checkItem is an item from subTree
577 * If checkItem is the last item in the list, we need a special
578 * treatment, because remove()ing it steps the current item of
579 * the list in the "wrong" direction.
581 if (checkItem == list.getLast()) { // does not set current item
582 list.remove();
583 /* we deleted the last element, so we've finished */
584 checkItem = 0;
585 } else {
586 list.remove();
587 /* remove() advanced already */
588 checkItem = list.current();
594 QString ExprWnd::exprStringAt(int index)
596 KTreeViewItem* item = itemAt(index);
597 if (item == 0) return QString(); /* paranoia */
598 VarTree* expr = static_cast<VarTree*>(item);
599 return expr->computeExpr();
602 void ExprWnd::clearPendingUpdates()
604 m_updatePtrs.clear();
605 m_updateType.clear();
606 m_updateStruct.clear();
609 VarTree* ExprWnd::nextUpdatePtr()
611 VarTree* ptr = m_updatePtrs.first();
612 if (ptr != 0) {
613 m_updatePtrs.remove();
615 return ptr;
618 VarTree* ExprWnd::nextUpdateType()
620 VarTree* ptr = m_updateType.first();
621 if (ptr != 0) {
622 m_updateType.remove();
624 return ptr;
627 VarTree* ExprWnd::nextUpdateStruct()
629 VarTree* ptr = m_updateStruct.first();
630 if (ptr != 0) {
631 m_updateStruct.remove();
633 return ptr;
636 void ExprWnd::paintCell(QPainter* painter, int row, int col)
638 if (col == 0) {
639 KTreeView::paintCell(painter, row, col);
640 } else {
641 VarTree* item = static_cast<VarTree*>(itemAt(row));
642 if (item != 0) {
643 item->paintValue(painter);
648 int ExprWnd::cellWidth(int col) const
650 if (col == 0) {
651 return KTreeView::cellWidth(col);
652 } else {
653 return maxValueWidth;
657 void ExprWnd::updateValuesWidth()
659 int maxW = 0;
660 forEveryVisibleItem(static_cast<KForEveryFunc>(&getMaxValueWidth), &maxW);
661 maxValueWidth = maxW;
662 updateTableSize();
665 // called by updateValuesWidth() for each item in the visible list
666 bool ExprWnd::getMaxValueWidth(KTreeViewItem* item, void* user)
668 int *maxW = (int *)user;
669 VarTree* v = static_cast<VarTree*>(item);
670 int w = v->valueWidth();
671 if(w > *maxW)
672 *maxW = w;
673 return false;
676 void ExprWnd::slotExpandOrCollapse(int)
678 updateValuesWidth();
681 void ExprWnd::editValue(int row, const QString& text)
683 int x;
684 colXPos(1, &x);
685 int y;
686 rowYPos(row, &y);
687 int w = cellWidth(1);
688 int h = cellHeight(row);
689 QScrollBar* sbV = static_cast<QScrollBar*>(child("table_sbV"));
692 * Make the edit widget at least 5 characters wide (but not wider than
693 * this widget). If less than half of this widget is used to display
694 * the text, scroll this widget so that half of it shows the text (or
695 * less than half of it if the text is shorter).
697 QFontMetrics metr = m_edit.font();
698 int wMin = metr.width("88888");
699 if (w < wMin)
700 w = wMin;
701 int wThis = width();
702 if (sbV->isVisible()) // subtract width of scrollbar
703 wThis -= sbV->width();
704 if (x >= wThis/2 && // less than half the width displays text
705 x+w > wThis) // not all text is visible
707 // scroll so that more text is visible
708 int wScroll = QMIN(x-wThis/2, x+w-wThis);
709 sbHor(xOffset()+wScroll);
710 colXPos(1, &x);
712 else if (x < 0)
714 // don't let the edit move out at the left
715 x = 0;
718 // make the edit box as wide as the visible column
719 QRect rect(x,y, wThis-x,h);
720 m_edit.setText(text);
721 m_edit.selectAll();
723 m_edit.setGeometry(rect);
724 m_edit.m_finished = false;
725 m_edit.m_row = row;
726 m_edit.show();
727 m_edit.setFocus();
730 bool ExprWnd::isEditing() const
732 return m_edit.isVisible();
736 ValueEdit::ValueEdit(ExprWnd* parent) :
737 QLineEdit(parent, "valueedit")
739 setFrame(false);
740 hide();
741 lower(); // lower the window below scrollbars
742 connect(parent, SIGNAL(selected(int)), SLOT(slotSelectionChanged()));
743 connect(parent, SIGNAL(collapsed(int)), SLOT(slotSelectionChanged()));
744 connect(parent, SIGNAL(expanded(int)), SLOT(slotSelectionChanged()));
745 connect(this, SIGNAL(done(int, const QString&)),
746 parent, SIGNAL(editValueCommitted(int, const QString&)));
749 ValueEdit::~ValueEdit()
753 void ValueEdit::terminate(bool commit)
755 TRACE(commit?"ValueEdit::terminate(true)":"ValueEdit::terminate(false)");
756 if (!m_finished)
758 m_finished = true;
759 hide(); // will call focusOutEvent, that's why we need m_finished
760 if (commit) {
761 emit done(m_row, text());
766 void ValueEdit::keyPressEvent(QKeyEvent *e)
768 if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)
769 terminate(true);
770 else if(e->key() == Qt::Key_Escape)
771 terminate(false);
772 else
773 QLineEdit::keyPressEvent(e);
776 void ValueEdit::paintEvent(QPaintEvent* e)
778 QLineEdit::paintEvent(e);
780 QPainter p(this);
781 p.drawRect(rect());
784 void ValueEdit::focusOutEvent(QFocusEvent* ev)
786 TRACE("ValueEdit::focusOutEvent");
787 QFocusEvent* focusEv = static_cast<QFocusEvent*>(ev);
788 // Don't let a RMB close the editor
789 if (focusEv->reason() != QFocusEvent::Popup &&
790 focusEv->reason() != QFocusEvent::ActiveWindow)
792 terminate(true);
796 void ValueEdit::slotSelectionChanged()
798 TRACE("ValueEdit::slotSelectionChanged");
799 terminate(false);