Introduce a new object type that represents value trees of the parse results.
[kdbg.git] / kdbg / exprwnd.cpp
blob570b1e96d044728d9cd17903f341a6f52e7414a1
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 = firstChild();
157 while (child != 0) {
158 child->inferTypesOfChildren(typeTable);
159 child = child->nextSibling();
162 // if this is a pointer, get the type from the value (less the pointer)
163 if (m_varKind == VKpointer) {
164 if (isWcharT())
167 * wchart_t pointers must be treated as struct, because the array
168 * of characters is printed similar to how QStrings are decoded.
170 m_varKind = VKstruct;
171 setDelayedExpanding(false);
172 return;
174 #ifndef I_know_a_way_to_do_this_cleanly
175 return;
176 #else
177 const char* p = m_value.data();
178 const char* start = p;
179 // the type of the pointer shows up in the value (sometimes)
180 if (p == 0 || *p != '(')
181 return;
182 skipNested(p, '(', ')');
184 * We only recognize pointers to data "(int *)" but not pointers
185 * to functions "(void (*)())".
187 if (p-start < 3 && /* at least 3 chars necessary: (*) */
188 p[-2] != '*') /* skip back before the closing paren */
190 return;
192 const QString& typeName =
193 QString::fromLatin1(start+1, p-start-3) // minus 3 chars
194 .stripWhiteSpace();
195 m_type = typeTable.lookup(typeName);
196 if (m_type == 0) {
197 m_type = TypeInfo::unknownType();
199 #endif
200 } else if (m_varKind == VKstruct) {
201 // check if this is a base class part
202 if (m_nameKind == NKtype) {
203 const QString& typeName =
204 text.mid(1, text.length()-2); // strip < and >
205 m_type = typeTable.lookup(typeName);
207 /* if we don't have a type yet, get it from the base class */
208 if (m_type == 0) {
209 m_type = inferTypeFromBaseClass();
211 * If there is a known type now, it is the one from the
212 * first base class whose type we know.
217 * If we still don't have a type, the type is really unknown.
219 if (m_type == 0) {
220 m_type = TypeInfo::unknownType();
222 } // else
224 * This is not a base class part. We don't assign a type so
225 * that later we can ask gdb.
230 // the value contains the pointer type in parenthesis
231 bool VarTree::isWcharT() const
233 return m_value.startsWith("(const wchar_t *)") ||
234 m_value.startsWith("(wchar_t *)");
238 * Get the type of the first base class whose type we know.
240 TypeInfo* VarTree::inferTypeFromBaseClass()
242 if (m_varKind == VKstruct) {
243 VarTree* child = firstChild();
244 while (child != 0 &&
245 // only check base class parts (i.e. type names)
246 child->m_nameKind == NKtype)
248 if (child->m_type != 0 &&
249 child->m_type != TypeInfo::unknownType())
251 // got a type!
252 return child->m_type;
254 child = child->nextSibling();
257 return 0;
260 ExprValue::ExprValue(const QString& name, VarTree::NameKind aKind) :
261 m_name(name),
262 m_varKind(VarTree::VKsimple),
263 m_nameKind(aKind),
264 m_child(0),
265 m_next(0),
266 m_initiallyExpanded(false)
270 ExprValue::~ExprValue()
272 delete m_child;
273 delete m_next;
276 void ExprValue::appendChild(ExprValue* newChild)
278 if (m_child == 0) {
279 m_child = newChild;
280 } else {
281 // walk chain of children to find the last one
282 ExprValue* last = m_child;
283 while (last->m_next != 0)
284 last = last->m_next;
285 last->m_next = newChild;
287 newChild->m_next = 0; // just to be sure
290 uint ExprValue::childCount() const
292 uint i = 0;
293 ExprValue* c = m_child;
294 while (c) {
295 ++i;
296 c = c->m_next;
298 return i;
303 ExprWnd::ExprWnd(QWidget* parent, const char* name) :
304 KTreeView(parent, name),
305 maxValueWidth(0),
306 m_edit(this)
308 setNumCols(2);
310 connect(this, SIGNAL(expanded(int)), SLOT(slotExpandOrCollapse(int)));
311 connect(this, SIGNAL(collapsed(int)), SLOT(slotExpandOrCollapse(int)));
313 m_pixPointer = UserIcon("pointer.xpm");
314 if (m_pixPointer.isNull())
315 TRACE("Can't load pointer.xpm");
318 ExprWnd::~ExprWnd()
322 void ExprWnd::exprList(QStrList& exprs)
324 // ASSERT(exprs does deep-copies)
325 VarTree* item;
326 for (item = firstChild(); item != 0; item = item->nextSibling()) {
327 exprs.append(item->getText());
331 VarTree* ExprWnd::insertExpr(ExprValue* expr, ProgramTypeTable& typeTable)
333 // append a new dummy expression
334 VarTree* display = new VarTree(expr->m_name, VarTree::NKplain);
335 insertItem(display);
337 // replace it right away
338 updateExpr(display, expr, typeTable);
339 return display;
342 void ExprWnd::updateExpr(ExprValue* expr, ProgramTypeTable& typeTable)
344 // search the root variable
345 VarTree* item = firstChild();
346 while (item != 0 && item->getText() != expr->m_name)
347 item = item->nextSibling();
348 if (item == 0) {
349 return;
351 // now update it
352 if (updateExprRec(item, expr, typeTable)) {
353 updateVisibleItems();
354 updateValuesWidth();
355 repaint();
357 collectUnknownTypes(item);
360 void ExprWnd::updateExpr(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable)
362 if (updateExprRec(display, newValues, typeTable) &&
363 display->isVisible())
365 updateVisibleItems();
366 updateValuesWidth();
367 repaint();
369 collectUnknownTypes(display);
373 * returns true if there's a visible change
375 bool ExprWnd::updateExprRec(VarTree* display, ExprValue* newValues, ProgramTypeTable& typeTable)
377 bool isExpanded = display->isExpanded();
380 * If we are updating a pointer without children by a dummy, we don't
381 * collapse it, but simply insert the new children. This happens when a
382 * pointer has just been expanded by the user.
384 if (display->m_varKind == VarTree::VKpointer &&
385 display->childCount() == 0 &&
386 newValues->m_varKind == VarTree::VKdummy)
388 replaceChildren(display, newValues);
389 return isExpanded; /* no visible change if not expanded */
393 * If the display and newValues have different kind or if their number
394 * of children is different, replace the whole sub-tree.
396 if (// the next two lines mean: not(m_varKind remains unchanged)
397 !(newValues->m_varKind == VarTree::VKdummy ||
398 display->m_varKind == newValues->m_varKind)
400 (display->childCount() != newValues->childCount() &&
402 * If this is a pointer and newValues doesn't have children, we
403 * don't replace the sub-tree; instead, below we mark this
404 * sub-tree for requiring an update.
406 (display->m_varKind != VarTree::VKpointer ||
407 newValues->m_child != 0)))
409 if (isExpanded) {
410 collapseSubTree(display, false);
413 // since children changed, it is likely that the type has also changed
414 display->m_type = 0; /* will re-evaluate the type */
416 // display the new value
417 updateSingleExpr(display, newValues);
418 replaceChildren(display, newValues);
420 // update the m_varKind
421 if (newValues->m_varKind != VarTree::VKdummy) {
422 display->m_varKind = newValues->m_varKind;
423 display->setDelayedExpanding(newValues->m_varKind == VarTree::VKpointer);
426 // get some types (after the new m_varKind has been set!)
427 display->inferTypesOfChildren(typeTable);
429 // (note that the new value might not have a sub-tree at all)
430 return display->m_valueChanged || isExpanded; /* no visible change if not expanded */
433 // display the new value
434 updateSingleExpr(display, newValues);
437 * If this is an expanded pointer, record it for being updated.
439 if (display->m_varKind == VarTree::VKpointer) {
440 if (isExpanded &&
441 // if newValues is a dummy, we have already updated this pointer
442 newValues->m_varKind != VarTree::VKdummy)
444 m_updatePtrs.append(display);
447 * If the visible sub-tree has children, but newValues doesn't, we
448 * can stop here.
450 if (newValues->m_child == 0) {
451 return display->m_valueChanged;
455 ASSERT(display->childCount() == newValues->childCount());
457 // go for children
458 bool childChanged = false;
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) {
465 // set new name
466 vDisplay->setText(vNew->m_name);
467 int i = itemRow(vDisplay);
468 if (i >= 0) {
469 updateCell(i, 0, true);
470 childChanged = true;
473 // recurse
474 if (updateExprRec(vDisplay, vNew, typeTable)) {
475 childChanged = true;
477 vDisplay = vDisplay->nextSibling();
478 vNew = vNew->m_next;
481 // update of children propagates only if this node is expanded
482 return display->m_valueChanged || (display->isExpanded() && childChanged);
485 void ExprWnd::updateSingleExpr(VarTree* display, ExprValue* newValue)
488 * If newValues is a VKdummy, we are only interested in its children.
489 * No need to update anything here.
491 if (newValue->m_varKind == VarTree::VKdummy) {
492 return;
496 * If this node is a struct and we know its type then don't update its
497 * value now. This is a node for which we know how to find a nested
498 * value. So register the node for an update.
500 * wchar_t types are also treated specially here: We consider them
501 * as struct (has been set in inferTypesOfChildren()).
503 if (display->m_varKind == VarTree::VKstruct &&
504 display->m_type != 0 &&
505 display->m_type != TypeInfo::unknownType())
507 ASSERT(newValue->m_varKind == VarTree::VKstruct);
508 if (display->m_type == TypeInfo::wchartType())
511 * We do not copy the new pointer value to the destination right
512 * away, but consider it as the first part of the nested value.
513 * Then the display will change its color only when the new value
514 * is completed.
516 display->m_partialValue = formatWCharPointer(newValue->m_value);
518 else
519 display->m_partialValue = display->m_type->m_displayString[0];
520 m_updateStruct.append(display);
522 else
524 if (display->updateValue(newValue->m_value)) {
525 int i = itemRow(display);
526 if (i >= 0) {
527 updateCell(i, 1, true);
533 void ExprWnd::updateStructValue(VarTree* display)
535 ASSERT(display->m_varKind == VarTree::VKstruct);
537 if (display->updateValue(display->m_partialValue)) {
538 int i = itemRow(display);
539 if (i >= 0) {
540 updateValuesWidth();
541 updateCell(i, 1, true);
544 // reset the value
545 display->m_partialValue = "";
546 display->m_exprIndex = -1;
549 void ExprWnd::replaceChildren(VarTree* display, ExprValue* newValues)
551 ASSERT(display->childCount() == 0 || display->m_varKind != VarTree::VKsimple);
553 // delete all children of display
554 while (VarTree* c = display->firstChild()) {
555 unhookSubtree(c);
556 display->removeChild(c);
557 delete c;
559 // insert copies of the newValues
560 for (ExprValue* v = newValues->m_child; v != 0; v = v->m_next)
562 VarTree* vNew = new VarTree(v->m_name, v->m_nameKind);
563 vNew->m_varKind = v->m_varKind;
564 vNew->m_value = v->m_value;
565 vNew->setDelayedExpanding(vNew->m_varKind == VarTree::VKpointer);
566 vNew->setExpanded(v->m_initiallyExpanded);
567 display->appendChild(vNew);
568 // recurse
569 replaceChildren(vNew, v);
573 void ExprWnd::collectUnknownTypes(VarTree* var)
576 * forEveryItem does not scan the root item itself. So we must do it
577 * ourselves.
579 ASSERT(var->m_varKind != VarTree::VKpointer || var->m_nameKind != VarTree::NKtype);
580 if (var->m_type == 0 &&
581 var->m_varKind == VarTree::VKstruct &&
582 var->m_nameKind != VarTree::NKtype)
584 if (!var->isWcharT())
586 /* this struct node doesn't have a type yet: register it */
587 m_updateType.append(var);
589 else
591 var->m_type = TypeInfo::wchartType();
592 // see updateSingleExpr() why we move the value
593 var->m_partialValue = formatWCharPointer(var->m_value);
594 var->m_value.truncate(0);
595 m_updateStruct.append(var);
599 // add pointer pixmap to pointers
600 if (var->m_varKind == VarTree::VKpointer) {
601 var->setPixmap(m_pixPointer);
604 forEveryItem(collectUnknownTypes, this, var);
607 bool ExprWnd::collectUnknownTypes(KTreeViewItem* item, void* user)
609 VarTree* var = static_cast<VarTree*>(item);
610 ExprWnd* tree = static_cast<ExprWnd*>(user);
611 ASSERT(var->m_varKind != VarTree::VKpointer || var->m_nameKind != VarTree::NKtype);
612 if (var->m_type == 0 &&
613 var->m_varKind == VarTree::VKstruct &&
614 var->m_nameKind != VarTree::NKtype)
616 if (!var->isWcharT())
618 /* this struct node doesn't have a type yet: register it */
619 tree->m_updateType.append(var);
621 else
623 var->m_type = TypeInfo::wchartType();
624 // see updateSingleExpr() why we move the value
625 var->m_partialValue = formatWCharPointer(var->m_value);
626 var->m_value.truncate(0);
627 tree->m_updateStruct.append(var);
630 // add pointer pixmap to pointers
631 if (var->m_varKind == VarTree::VKpointer) {
632 var->setPixmap(tree->m_pixPointer);
634 return false;
637 QString ExprWnd::formatWCharPointer(QString value)
639 int pos = value.find(") ");
640 if (pos > 0)
641 value = value.mid(pos+2);
642 return value + " L";
646 VarTree* ExprWnd::topLevelExprByName(const char* name)
648 VarTree* item = firstChild();
649 while (item != 0 && item->getText() != name)
650 item = item->nextSibling();
652 return item;
655 VarTree* ExprWnd::ptrMemberByName(VarTree* v, const QString& name)
657 // v must be a pointer variable, must have children
658 if (v->m_varKind != VarTree::VKpointer || v->childCount() == 0)
659 return 0;
661 // the only child of v is the pointer value that represents the struct
662 VarTree* item = v->firstChild();
663 return memberByName(item, name);
666 VarTree* ExprWnd::memberByName(VarTree* v, const QString& name)
668 // search immediate children for name
669 VarTree* item = v->firstChild();
670 while (item != 0 && item->getText() != name)
671 item = item->nextSibling();
673 if (item != 0)
674 return item;
676 // try in base classes
677 item = v->firstChild();
678 while (item != 0 &&
679 item->m_nameKind == VarTree::NKtype)
681 v = memberByName(item, name);
682 if (v != 0)
683 return v;
684 item = item->nextSibling();
686 return 0;
689 void ExprWnd::removeExpr(VarTree* item)
691 unhookSubtree(item);
693 takeItem(item);
694 delete item;
696 updateValuesWidth();
699 void ExprWnd::unhookSubtree(VarTree* subTree)
701 // must remove any pointers scheduled for update from the list
702 unhookSubtree(m_updatePtrs, subTree);
703 unhookSubtree(m_updateType, subTree);
704 unhookSubtree(m_updateStruct, subTree);
705 emit removingItem(subTree);
708 void ExprWnd::unhookSubtree(QList<VarTree>& list, VarTree* subTree)
710 if (subTree == 0)
711 return;
713 VarTree* checkItem = list.first();
714 while (checkItem != 0) {
715 if (!subTree->isAncestorEq(checkItem)) {
716 // checkItem is not an item from subTree
717 // advance
718 checkItem = list.next();
719 } else {
720 // checkItem is an item from subTree
722 * If checkItem is the last item in the list, we need a special
723 * treatment, because remove()ing it steps the current item of
724 * the list in the "wrong" direction.
726 if (checkItem == list.getLast()) { // does not set current item
727 list.remove();
728 /* we deleted the last element, so we've finished */
729 checkItem = 0;
730 } else {
731 list.remove();
732 /* remove() advanced already */
733 checkItem = list.current();
739 void ExprWnd::clearPendingUpdates()
741 m_updatePtrs.clear();
742 m_updateType.clear();
743 m_updateStruct.clear();
746 VarTree* ExprWnd::nextUpdatePtr()
748 VarTree* ptr = m_updatePtrs.first();
749 if (ptr != 0) {
750 m_updatePtrs.remove();
752 return ptr;
755 VarTree* ExprWnd::nextUpdateType()
757 VarTree* ptr = m_updateType.first();
758 if (ptr != 0) {
759 m_updateType.remove();
761 return ptr;
764 VarTree* ExprWnd::nextUpdateStruct()
766 VarTree* ptr = m_updateStruct.first();
767 if (ptr != 0) {
768 m_updateStruct.remove();
770 return ptr;
773 void ExprWnd::paintCell(QPainter* painter, int row, int col)
775 if (col == 0) {
776 KTreeView::paintCell(painter, row, col);
777 } else {
778 VarTree* item = static_cast<VarTree*>(itemAt(row));
779 if (item != 0) {
780 item->paintValue(painter);
785 int ExprWnd::cellWidth(int col) const
787 if (col == 0) {
788 return KTreeView::cellWidth(col);
789 } else {
790 return maxValueWidth;
794 void ExprWnd::updateValuesWidth()
796 int maxW = 0;
797 forEveryVisibleItem(static_cast<KForEveryFunc>(&getMaxValueWidth), &maxW);
798 maxValueWidth = maxW;
799 updateTableSize();
802 // called by updateValuesWidth() for each item in the visible list
803 bool ExprWnd::getMaxValueWidth(KTreeViewItem* item, void* user)
805 int *maxW = (int *)user;
806 VarTree* v = static_cast<VarTree*>(item);
807 int w = v->valueWidth();
808 if(w > *maxW)
809 *maxW = w;
810 return false;
813 void ExprWnd::slotExpandOrCollapse(int)
815 updateValuesWidth();
818 void ExprWnd::editValue(int row, const QString& text)
820 int x;
821 colXPos(1, &x);
822 int y;
823 rowYPos(row, &y);
824 int w = cellWidth(1);
825 int h = cellHeight(row);
826 QScrollBar* sbV = static_cast<QScrollBar*>(child("table_sbV"));
829 * Make the edit widget at least 5 characters wide (but not wider than
830 * this widget). If less than half of this widget is used to display
831 * the text, scroll this widget so that half of it shows the text (or
832 * less than half of it if the text is shorter).
834 QFontMetrics metr = m_edit.font();
835 int wMin = metr.width("88888");
836 if (w < wMin)
837 w = wMin;
838 int wThis = width();
839 if (sbV->isVisible()) // subtract width of scrollbar
840 wThis -= sbV->width();
841 if (x >= wThis/2 && // less than half the width displays text
842 x+w > wThis) // not all text is visible
844 // scroll so that more text is visible
845 int wScroll = QMIN(x-wThis/2, x+w-wThis);
846 sbHor(xOffset()+wScroll);
847 colXPos(1, &x);
849 else if (x < 0)
851 // don't let the edit move out at the left
852 x = 0;
855 // make the edit box as wide as the visible column
856 QRect rect(x,y, wThis-x,h);
857 m_edit.setText(text);
858 m_edit.selectAll();
860 m_edit.setGeometry(rect);
861 m_edit.m_finished = false;
862 m_edit.m_row = row;
863 m_edit.show();
864 m_edit.setFocus();
867 bool ExprWnd::isEditing() const
869 return m_edit.isVisible();
873 ValueEdit::ValueEdit(ExprWnd* parent) :
874 QLineEdit(parent, "valueedit")
876 setFrame(false);
877 hide();
878 lower(); // lower the window below scrollbars
879 connect(parent, SIGNAL(selected(int)), SLOT(slotSelectionChanged()));
880 connect(parent, SIGNAL(collapsed(int)), SLOT(slotSelectionChanged()));
881 connect(parent, SIGNAL(expanded(int)), SLOT(slotSelectionChanged()));
882 connect(this, SIGNAL(done(int, const QString&)),
883 parent, SIGNAL(editValueCommitted(int, const QString&)));
886 ValueEdit::~ValueEdit()
890 void ValueEdit::terminate(bool commit)
892 TRACE(commit?"ValueEdit::terminate(true)":"ValueEdit::terminate(false)");
893 if (!m_finished)
895 m_finished = true;
896 hide(); // will call focusOutEvent, that's why we need m_finished
897 if (commit) {
898 emit done(m_row, text());
903 void ValueEdit::keyPressEvent(QKeyEvent *e)
905 if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)
906 terminate(true);
907 else if(e->key() == Qt::Key_Escape)
908 terminate(false);
909 else
910 QLineEdit::keyPressEvent(e);
913 void ValueEdit::paintEvent(QPaintEvent* e)
915 QLineEdit::paintEvent(e);
917 QPainter p(this);
918 p.drawRect(rect());
921 void ValueEdit::focusOutEvent(QFocusEvent* ev)
923 TRACE("ValueEdit::focusOutEvent");
924 QFocusEvent* focusEv = static_cast<QFocusEvent*>(ev);
925 // Don't let a RMB close the editor
926 if (focusEv->reason() != QFocusEvent::Popup &&
927 focusEv->reason() != QFocusEvent::ActiveWindow)
929 terminate(true);
933 void ValueEdit::slotSelectionChanged()
935 TRACE("ValueEdit::slotSelectionChanged");
936 terminate(false);