OSDN Git Service

10ed4fe1e65ec088567fab9ac60da38fc8b09cea
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / watchwindow.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
30 **
31 **************************************************************************/
32
33 #include "watchwindow.h"
34
35 #include "breakhandler.h"
36 #include "registerhandler.h"
37 #include "debuggeractions.h"
38 #include "debuggerconstants.h"
39 #include "debuggerinternalconstants.h"
40 #include "debuggercore.h"
41 #include "debuggerdialogs.h"
42 #include "debuggerengine.h"
43 #include "debuggerstartparameters.h"
44 #include "watchdelegatewidgets.h"
45 #include "watchhandler.h"
46 #include "debuggertooltipmanager.h"
47 #include "memoryagent.h"
48 #include <utils/qtcassert.h>
49 #include <utils/savedaction.h>
50
51 #include <QtCore/QDebug>
52 #include <QtCore/QMetaObject>
53 #include <QtCore/QMetaProperty>
54 #include <QtCore/QVariant>
55
56 #include <QtGui/QApplication>
57 #include <QtGui/QPalette>
58 #include <QtGui/QClipboard>
59 #include <QtGui/QContextMenuEvent>
60 #include <QtGui/QHeaderView>
61 #include <QtGui/QItemDelegate>
62 #include <QtGui/QMenu>
63 #include <QtGui/QPainter>
64 #include <QtGui/QResizeEvent>
65 #include <QtGui/QInputDialog>
66 #include <QtGui/QMessageBox>
67
68 /////////////////////////////////////////////////////////////////////
69 //
70 // WatchDelegate
71 //
72 /////////////////////////////////////////////////////////////////////
73
74 namespace Debugger {
75 namespace Internal {
76
77 static DebuggerEngine *currentEngine()
78 {
79     return debuggerCore()->currentEngine();
80 }
81
82 class WatchDelegate : public QItemDelegate
83 {
84 public:
85     explicit WatchDelegate(WatchWindow *parent)
86         : QItemDelegate(parent), m_watchWindow(parent)
87     {}
88
89     QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
90         const QModelIndex &index) const
91     {
92         // Value column: Custom editor. Apply integer-specific settings.
93         if (index.column() == 1) {
94             const QVariant::Type type =
95                 static_cast<QVariant::Type>(index.data(LocalsEditTypeRole).toInt());
96             switch (type) {
97             case QVariant::Bool:
98                 return new BooleanComboBox(parent);
99             default:
100                 break;
101             }
102             WatchLineEdit *edit = WatchLineEdit::create(type, parent);
103             edit->setFrame(false);
104             IntegerWatchLineEdit *intEdit
105                 = qobject_cast<IntegerWatchLineEdit *>(edit);
106             if (intEdit)
107                 intEdit->setBase(index.data(LocalsIntegerBaseRole).toInt());
108             return edit;
109         }
110
111         // Standard line edits for the rest.
112         QLineEdit *lineEdit = new QLineEdit(parent);
113         lineEdit->setFrame(false);
114         return lineEdit;
115     }
116
117     void setModelData(QWidget *editor, QAbstractItemModel *model,
118                       const QModelIndex &index) const
119     {
120         // Standard handling for anything but the watcher name column (change
121         // expression), which removes/recreates a row, which cannot be done
122         // in model->setData().
123         if (index.column() != 0) {
124             QItemDelegate::setModelData(editor, model, index);
125             return;
126         }
127         const QMetaProperty userProperty = editor->metaObject()->userProperty();
128         QTC_ASSERT(userProperty.isValid(), return);
129         const QString value = editor->property(userProperty.name()).toString();
130         const QString exp = index.data(LocalsExpressionRole).toString();
131         if (exp == value)
132             return;
133         m_watchWindow->removeWatchExpression(exp);
134         m_watchWindow->watchExpression(value);
135     }
136
137     void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
138         const QModelIndex &) const
139     {
140         editor->setGeometry(option.rect);
141     }
142
143 private:
144     WatchWindow *m_watchWindow;
145 };
146
147 // Watch model query helpers.
148 static inline quint64 addressOf(const QModelIndex &m)
149 {
150     return m.data(LocalsAddressRole).toULongLong();
151 }
152
153 static inline quint64 pointerValueOf(const QModelIndex &m)
154 {
155     return m.data(LocalsPointerValueRole).toULongLong();
156 }
157
158 static inline QString nameOf(const QModelIndex &m)
159 {
160     return m.data().toString();
161 }
162
163 static inline QString typeOf(const QModelIndex &m)
164 {
165     return m.data(LocalsTypeRole).toString();
166 }
167
168 static inline uint sizeOf(const QModelIndex &m)
169 {
170     return m.data(LocalsSizeRole).toUInt();
171 }
172
173 // Create a map of value->name for register markup
174 typedef QMap<quint64, QString> RegisterMap;
175 typedef RegisterMap::const_iterator RegisterMapConstIt;
176
177 RegisterMap registerMap(const DebuggerEngine *engine)
178 {
179     RegisterMap result;
180     foreach (const Register &reg, engine->registerHandler()->registers()) {
181         const QVariant v = reg.editValue();
182         if (v.type() == QVariant::ULongLong)
183             result.insert(v.toULongLong(), QString::fromAscii(reg.name));
184     }
185     return result;
186 }
187
188 // Helper functionality to indicate the area of a member variable in
189 // a vector representing the memory area by a unique color
190 // number and tooltip. Parts of it will be overwritten when recursing
191 // over the children.
192
193 typedef QPair<int, QString> ColorNumberToolTipPair;
194 typedef QVector<ColorNumberToolTipPair> ColorNumberToolTipVector;
195
196 static inline QString variableToolTip(const QString &name,
197                                       const QString &type,
198                                       quint64 offset)
199 {
200     return offset ?
201            //: HTML tooltip of a variable in the memory editor
202            WatchWindow::tr("<i>%1</i> %2 at #%3").
203                arg(type, name).arg(offset) :
204            //: HTML tooltip of a variable in the memory editor
205            WatchWindow::tr("<i>%1</i> %2").arg(type, name);
206 }
207
208 static int memberVariableRecursion(const QAbstractItemModel *model,
209                                    const QModelIndex &modelIndex,
210                                    const QString &name,
211                                    quint64 start, quint64 end,
212                                    int *colorNumberIn,
213                                    ColorNumberToolTipVector *cnmv)
214 {
215     int childCount = 0;
216     // Recurse over top level items if modelIndex is invalid.
217     const bool isRoot = !modelIndex.isValid();
218     const int rowCount = isRoot ? model->rowCount() : modelIndex.model()->rowCount(modelIndex);
219     if (!rowCount)
220         return childCount;
221     const QString nameRoot = name.isEmpty() ? name : name +  QLatin1Char('.');
222     for (int r = 0; r < rowCount; r++) {
223         const QModelIndex childIndex = isRoot ? model->index(r, 0) : modelIndex.child(r, 0);
224         const quint64 childAddress = addressOf(childIndex);
225         const uint childSize = sizeOf(childIndex);
226         if (childAddress && childAddress >= start
227                 && (childAddress + childSize) <= end) { // Non-static, within area?
228             const QString childName = nameRoot + nameOf(childIndex);
229             const quint64 childOffset = childAddress - start;
230             const QString toolTip
231                 = variableToolTip(childName, typeOf(childIndex), childOffset);
232             const ColorNumberToolTipPair colorNumberNamePair((*colorNumberIn)++, toolTip);
233             const ColorNumberToolTipVector::iterator begin = cnmv->begin() + childOffset;
234             qFill(begin, begin + childSize, colorNumberNamePair);
235             childCount++;
236             childCount += memberVariableRecursion(model, childIndex, childName, start, end, colorNumberIn, cnmv);
237         }
238     }
239     return childCount;
240 }
241
242 /*!
243     \fn variableMemoryMarkup()
244
245     \brief Creates markup for a variable in the memory view.
246
247     Marks the visible children with alternating colors in the parent, that is, for
248     \code
249     struct Foo {
250     char c1
251     char c2
252     int x2;
253     QPair<int, int> pair
254     }
255     \endcode
256     create something like:
257     \code
258     0 memberColor1
259     1 memberColor2
260     2 base color (padding area of parent)
261     3 base color
262     4 member color1
263     ...
264     8 memberColor2 (pair.first)
265     ...
266     12 memberColor1 (pair.second)
267     \endcode
268
269     In addition, registers pointing into the area are shown as 1 byte-markers.
270
271    Fixme: When dereferencing a pointer, the size of the pointee is not
272    known, currently. So, we take an area of 1024 and fill the background
273    with the default color so that just the members are shown
274    (sizeIsEstimate=true). This could be fixed by passing the pointee size
275    as well from the debugger, but would require expensive type manipulation.
276
277    \note To recurse over the top level items of the model, pass an invalid model
278    index.
279
280     \sa Debugger::Internal::MemoryViewWidget
281 */
282
283 typedef QList<MemoryMarkup> MemoryMarkupList;
284
285 static inline MemoryMarkupList
286     variableMemoryMarkup(const QAbstractItemModel *model,
287                          const QModelIndex &modelIndex,
288                          const QString &rootName,
289                          const QString &rootToolTip,
290                          quint64 address, quint64 size,
291                          const RegisterMap &registerMap,
292                          bool sizeIsEstimate,
293                          const QColor &defaultBackground)
294 {
295     enum { debug = 0 };
296     enum { registerColorNumber = 0x3453 };
297
298     if (debug)
299         qDebug() << address << ' ' << size << rootName << rootToolTip;
300     // Starting out from base, create an array representing the area
301     // filled with base color. Fill children with some unique color numbers,
302     // leaving the padding areas of the parent colored with the base color.
303     MemoryMarkupList result;
304     int colorNumber = 0;
305     ColorNumberToolTipVector ranges(size, ColorNumberToolTipPair(colorNumber, rootToolTip));
306     const int childCount = memberVariableRecursion(model, modelIndex,
307                                                    rootName, address, address + size,
308                                                    &colorNumber, &ranges);
309     if (sizeIsEstimate && !childCount)
310         return result; // Fixme: Exact size not known, no point in filling if no children.
311     // Punch in registers as 1-byte markers on top.
312     const RegisterMapConstIt regcEnd = registerMap.constEnd();
313     for (RegisterMapConstIt it = registerMap.constBegin(); it != regcEnd; ++it) {
314         if (it.key() >= address) {
315             const quint64 offset = it.key() - address;
316             if (offset < size) {
317                 ranges[offset] =
318                     ColorNumberToolTipPair(registerColorNumber,
319                                            WatchWindow::tr("Register <i>%1</i>").arg(it.value()));
320             } else {
321                 break; // Sorted.
322             }
323         }
324     } // for registers.
325     if (debug) {
326         QDebug dbg = qDebug().nospace();
327         dbg << rootToolTip << ' ' << address << ' ' << size << '\n';
328         QString name;
329         for (unsigned i = 0; i < size; ++i)
330             if (name != ranges.at(i).second) {
331                 dbg << ",[" << i << ' ' << ranges.at(i).first << ' ' << ranges.at(i).second << ']';
332                 name = ranges.at(i).second;
333             }
334     }
335
336     // Condense ranges of identical color into markup ranges. Assign member colors
337     // interchangeably.
338     const QColor baseColor = sizeIsEstimate ? defaultBackground : Qt::lightGray;
339     const QColor registerColor = Qt::green;
340     QColor memberColor1 = QColor(Qt::yellow).lighter();
341     QColor memberColor2 = QColor(Qt::cyan).lighter();
342
343     int lastColorNumber = 0;
344     int childNumber = 0;
345     for (unsigned i = 0; i < size; ++i) {
346         const ColorNumberToolTipPair &range = ranges.at(i);
347         if (result.isEmpty() || lastColorNumber != range.first) {
348             lastColorNumber = range.first;
349             // Base colors: Parent/register
350             QColor color = range.first == registerColorNumber ? registerColor : baseColor;
351             if (range.first && range.first != registerColorNumber) {
352                 if (childNumber++ & 1) { // Alternating member colors.
353                     color = memberColor1;
354                     memberColor1 = memberColor1.darker(120);
355                 } else {
356                     color = memberColor2;
357                     memberColor2 = memberColor2.darker(120);
358                 }
359             } // color switch
360             result.push_back(MemoryMarkup(address + i, 1, color, range.second));
361         } else {
362             result.back().length++;
363         }
364     }
365
366     if (debug) {
367         QDebug dbg = qDebug().nospace();
368         dbg << rootName << ' ' << address << ' ' << size << '\n';
369         QString name;
370         for (unsigned i = 0; i < size; ++i)
371             if (name != ranges.at(i).second) {
372                 dbg << ',' << i << ' ' << ranges.at(i).first << ' ' << ranges.at(i).second;
373                 name = ranges.at(i).second;
374             }
375         dbg << '\n';
376         foreach (const MemoryMarkup &m, result)
377             dbg << m.address <<  ' ' << m.length << ' '  << m.toolTip << '\n';
378     }
379
380     return result;
381 }
382
383 // Convenience to create a memory view of a variable.
384 static void addVariableMemoryView(DebuggerEngine *engine,
385                                   bool separateView,
386                                   const QModelIndex &m, bool deferencePointer,
387                                   const QPoint &p,
388                                   QWidget *parent)
389 {
390     const QColor background = parent->palette().color(QPalette::Normal, QPalette::Base);
391     const quint64 address = deferencePointer ? pointerValueOf(m) : addressOf(m);
392     // Fixme: Get the size of pointee (see variableMemoryMarkup())?
393     const QString rootToolTip = variableToolTip(nameOf(m), typeOf(m), 0);
394     const quint64 typeSize = sizeOf(m);
395     const bool sizeIsEstimate = deferencePointer || !typeSize;
396     const quint64 size    = sizeIsEstimate ? 1024 : typeSize;
397     if (!address)
398          return;
399     const QList<MemoryMarkup> markup =
400         variableMemoryMarkup(m.model(), m, nameOf(m), rootToolTip,
401                              address, size,
402                              registerMap(engine),
403                              sizeIsEstimate, background);
404     const unsigned flags = separateView ? (DebuggerEngine::MemoryView|DebuggerEngine::MemoryReadOnly) : 0;
405     const QString title = deferencePointer ?
406     WatchWindow::tr("Memory Referenced by Pointer '%1' (0x%2)").arg(nameOf(m)).arg(address, 0, 16) :
407     WatchWindow::tr("Memory at Variable '%1' (0x%2)").arg(nameOf(m)).arg(address, 0, 16);
408     engine->openMemoryView(address, flags, markup, p, title, parent);
409 }
410
411 // Add a memory view of the stack layout showing local variables
412 // and registers.
413 static inline void addStackLayoutMemoryView(DebuggerEngine *engine,
414                                             bool separateView,
415                                             const QAbstractItemModel *m, const QPoint &p,
416                                             QWidget *parent)
417 {
418     typedef QPair<quint64, QString> RegisterValueNamePair;
419
420     // Determine suitable address range from locals
421     quint64 start = Q_UINT64_C(0xFFFFFFFFFFFFFFFF);
422     quint64 end = 0;
423     const int rootItemCount = m->rowCount();
424     // Note: Unsorted by default. Exclude 'Automatically dereferenced
425     // pointer' items as they are outside the address range.
426     for (int r = 0; r < rootItemCount; r++) {
427         const QModelIndex idx = m->index(r, 0);
428         if (idx.data(LocalsReferencingAddressRole).toULongLong() == 0) {
429             const quint64 address = addressOf(idx);
430             if (address) {
431                 if (address < start)
432                     start = address;
433                 if (const uint size = sizeOf(idx))
434                     if (address + size > end)
435                         end = address + size;
436             }
437         }
438     }
439     // Anything found and everything in a sensible range (static data in-between)?
440     if (end <= start || end - start > 100 * 1024) {
441         QMessageBox::information(parent, WatchWindow::tr("Cannot Display Stack Layout"),
442             WatchWindow::tr("Could not determine a suitable address range."));
443         return;
444     }
445     // Take a look at the register values. Extend the range a bit if suitable
446     // to show stack/stack frame pointers.
447     const RegisterMap regMap = registerMap(engine);
448     const RegisterMapConstIt regcEnd = regMap.constEnd();
449     for (RegisterMapConstIt it = regMap.constBegin(); it != regcEnd; ++it) {
450         const quint64 value = it.key();
451         if (value < start && start - value < 512) {
452             start = value;
453         } else if (value > end && value - end < 512) {
454             end = value + 1;
455         }
456     }
457     // Indicate all variables.
458     const QColor background = parent->palette().color(QPalette::Normal, QPalette::Base);
459     const MemoryMarkupList markup =
460         variableMemoryMarkup(m, QModelIndex(), QString(),
461                              QString(), start, end - start,
462                              regMap, true, background);
463     const unsigned flags = separateView ? (DebuggerEngine::MemoryView|DebuggerEngine::MemoryReadOnly) : 0;
464     const QString title =
465         WatchWindow::tr("Memory Layout of Local Variables at 0x%2").arg(start, 0, 16);
466     engine->openMemoryView(start, flags, markup, p, title, parent);
467 }
468
469 /////////////////////////////////////////////////////////////////////
470 //
471 // WatchWindow
472 //
473 /////////////////////////////////////////////////////////////////////
474
475 WatchWindow::WatchWindow(Type type, QWidget *parent)
476   : QTreeView(parent),
477     m_type(type)
478 {
479     setObjectName(QLatin1String("WatchWindow"));
480     m_grabbing = false;
481
482     setFrameStyle(QFrame::NoFrame);
483     setAttribute(Qt::WA_MacShowFocusRect, false);
484     setWindowTitle(tr("Locals and Expressions"));
485     setIndentation(indentation() * 9/10);
486     setUniformRowHeights(true);
487     setItemDelegate(new WatchDelegate(this));
488     setDragEnabled(true);
489     setAcceptDrops(true);
490     setDropIndicatorShown(true);
491
492     QAction *useColors = debuggerCore()->action(UseAlternatingRowColors);
493     setAlternatingRowColors(useColors->isChecked());
494
495     QAction *adjustColumns = debuggerCore()->action(AlwaysAdjustLocalsColumnWidths);
496
497     connect(useColors, SIGNAL(toggled(bool)),
498         SLOT(setAlternatingRowColorsHelper(bool)));
499     connect(adjustColumns, SIGNAL(triggered(bool)),
500         SLOT(setAlwaysResizeColumnsToContents(bool)));
501     connect(this, SIGNAL(expanded(QModelIndex)),
502         SLOT(expandNode(QModelIndex)));
503     connect(this, SIGNAL(collapsed(QModelIndex)),
504         SLOT(collapseNode(QModelIndex)));
505 }
506
507 void WatchWindow::expandNode(const QModelIndex &idx)
508 {
509     setModelData(LocalsExpandedRole, true, idx);
510 }
511
512 void WatchWindow::collapseNode(const QModelIndex &idx)
513 {
514     setModelData(LocalsExpandedRole, false, idx);
515 }
516
517 void WatchWindow::keyPressEvent(QKeyEvent *ev)
518 {
519     if (ev->key() == Qt::Key_Delete && m_type == WatchersType) {
520         QModelIndex idx = currentIndex();
521         QModelIndex idx1 = idx.sibling(idx.row(), 0);
522         QString exp = idx1.data(LocalsRawExpressionRole).toString();
523         removeWatchExpression(exp);
524     } else if (ev->key() == Qt::Key_Return
525             && ev->modifiers() == Qt::ControlModifier
526             && m_type == LocalsType) {
527         QModelIndex idx = currentIndex();
528         QModelIndex idx1 = idx.sibling(idx.row(), 0);
529         QString exp = model()->data(idx1).toString();
530         watchExpression(exp);
531     }
532     QTreeView::keyPressEvent(ev);
533 }
534
535 void WatchWindow::dragEnterEvent(QDragEnterEvent *ev)
536 {
537     //QTreeView::dragEnterEvent(ev);
538     if (ev->mimeData()->hasFormat("text/plain")) {
539         ev->setDropAction(Qt::CopyAction);
540         ev->accept();
541     }
542 }
543
544 void WatchWindow::dragMoveEvent(QDragMoveEvent *ev)
545 {
546     //QTreeView::dragMoveEvent(ev);
547     if (ev->mimeData()->hasFormat("text/plain")) {
548         ev->setDropAction(Qt::CopyAction);
549         ev->accept();
550     }
551 }
552
553 void WatchWindow::dropEvent(QDropEvent *ev)
554 {
555     if (ev->mimeData()->hasFormat("text/plain")) {
556         watchExpression(ev->mimeData()->text());
557         //ev->acceptProposedAction();
558         ev->setDropAction(Qt::CopyAction);
559         ev->accept();
560     }
561     //QTreeView::dropEvent(ev);
562 }
563
564 void WatchWindow::mouseDoubleClickEvent(QMouseEvent *ev)
565 {
566     const QModelIndex idx = indexAt(ev->pos());
567     if (!idx.isValid()) {
568         // The "<Edit>" case.
569         watchExpression(QString());
570         return;
571     }
572     QTreeView::mouseDoubleClickEvent(ev);
573 }
574
575 // Text for add watch action with truncated expression
576 static inline QString addWatchActionText(QString exp)
577 {
578     if (exp.isEmpty())
579         return WatchWindow::tr("Evaluate Expression");
580     if (exp.size() > 30) {
581         exp.truncate(30);
582         exp.append(QLatin1String("..."));
583     }
584     return WatchWindow::tr("Evaluate Expression \"%1\"").arg(exp);
585 }
586
587 // Text for add watch action with truncated expression
588 static inline QString removeWatchActionText(QString exp)
589 {
590     if (exp.isEmpty())
591         return WatchWindow::tr("Remove Evaluated Expression");
592     if (exp.size() > 30) {
593         exp.truncate(30);
594         exp.append(QLatin1String("..."));
595     }
596     return WatchWindow::tr("Remove Evaluated Expression \"%1\"").arg(exp);
597 }
598
599 static inline void copyToClipboard(const QString &clipboardText)
600 {
601     QClipboard *clipboard = QApplication::clipboard();
602 #ifdef Q_WS_X11
603     clipboard->setText(clipboardText, QClipboard::Selection);
604 #endif
605     clipboard->setText(clipboardText, QClipboard::Clipboard);
606 }
607
608 void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
609 {
610     DebuggerEngine *engine = currentEngine();
611     WatchHandler *handler = engine->watchHandler();
612
613     const QModelIndex idx = indexAt(ev->pos());
614     const QModelIndex mi0 = idx.sibling(idx.row(), 0);
615     const QModelIndex mi1 = idx.sibling(idx.row(), 1);
616     const QModelIndex mi2 = idx.sibling(idx.row(), 2);
617     const quint64 address = addressOf(mi0);
618     const uint size = sizeOf(mi0);
619     const quint64 pointerValue = pointerValueOf(mi0);
620     const QString exp = mi0.data(LocalsExpressionRole).toString();
621     const QString name = mi0.data(LocalsNameRole).toString();
622     const QString type = mi2.data().toString();
623
624     // Offer to open address pointed to or variable address.
625     const bool createPointerActions = pointerValue && pointerValue != address;
626
627     const QStringList alternativeFormats =
628         mi0.data(LocalsTypeFormatListRole).toStringList();
629     const int typeFormat =
630         mi0.data(LocalsTypeFormatRole).toInt();
631     const int individualFormat =
632         mi0.data(LocalsIndividualFormatRole).toInt();
633     const int effectiveIndividualFormat =
634         individualFormat == -1 ? typeFormat : individualFormat;
635     const int unprintableBase = handler->unprintableBase();
636
637     QMenu formatMenu;
638     QList<QAction *> typeFormatActions;
639     QList<QAction *> individualFormatActions;
640     QAction *clearTypeFormatAction = 0;
641     QAction *clearIndividualFormatAction = 0;
642     QAction *showUnprintableUnicode = 0;
643     QAction *showUnprintableOctal = 0;
644     QAction *showUnprintableHexadecimal = 0;
645     formatMenu.setTitle(tr("Change Display Format..."));
646     showUnprintableUnicode =
647         formatMenu.addAction(tr("Treat All Characters as Printable"));
648     showUnprintableUnicode->setCheckable(true);
649     showUnprintableUnicode->setChecked(unprintableBase == 0);
650     showUnprintableOctal =
651         formatMenu.addAction(tr("Show Unprintable Characters as Octal"));
652     showUnprintableOctal->setCheckable(true);
653     showUnprintableOctal->setChecked(unprintableBase == 8);
654     showUnprintableHexadecimal =
655         formatMenu.addAction(tr("Show Unprintable Characters as Hexadecimal"));
656     showUnprintableHexadecimal->setCheckable(true);
657     showUnprintableHexadecimal->setChecked(unprintableBase == 16);
658     if (idx.isValid() /*&& !alternativeFormats.isEmpty() */) {
659         const QString spacer = QLatin1String("     ");
660         formatMenu.addSeparator();
661         QAction *dummy = formatMenu.addAction(
662             tr("Change Display for Object Named \"%1\":").arg(mi0.data().toString()));
663         dummy->setEnabled(false);
664         clearIndividualFormatAction
665             = formatMenu.addAction(spacer + tr("Use Display Format Based on Type"));
666         //clearIndividualFormatAction->setEnabled(individualFormat != -1);
667         clearIndividualFormatAction->setCheckable(true);
668         clearIndividualFormatAction->setChecked(effectiveIndividualFormat == -1);
669         for (int i = 0; i != alternativeFormats.size(); ++i) {
670             const QString format = spacer + alternativeFormats.at(i);
671             QAction *act = new QAction(format, &formatMenu);
672             act->setCheckable(true);
673             if (i == effectiveIndividualFormat)
674                 act->setChecked(true);
675             formatMenu.addAction(act);
676             individualFormatActions.append(act);
677         }
678         formatMenu.addSeparator();
679         dummy = formatMenu.addAction(
680             tr("Change Display for Type \"%1\":").arg(type));
681         dummy->setEnabled(false);
682         clearTypeFormatAction = formatMenu.addAction(spacer + tr("Automatic"));
683         //clearTypeFormatAction->setEnabled(typeFormat != -1);
684         //clearTypeFormatAction->setEnabled(individualFormat != -1);
685         clearTypeFormatAction->setCheckable(true);
686         clearTypeFormatAction->setChecked(typeFormat == -1);
687         for (int i = 0; i != alternativeFormats.size(); ++i) {
688             const QString format = spacer + alternativeFormats.at(i);
689             QAction *act = new QAction(format, &formatMenu);
690             act->setCheckable(true);
691             //act->setEnabled(individualFormat != -1);
692             if (i == typeFormat)
693                 act->setChecked(true);
694             formatMenu.addAction(act);
695             typeFormatActions.append(act);
696         }
697     } else {
698         QAction *dummy = formatMenu.addAction(
699             tr("Change Display for Type or Item..."));
700         dummy->setEnabled(false);
701     }
702
703     const bool actionsEnabled = engine->debuggerActionsEnabled();
704     const unsigned engineCapabilities = engine->debuggerCapabilities();
705     const bool canHandleWatches = engineCapabilities & AddWatcherCapability;
706     const DebuggerState state = engine->state();
707     const bool canInsertWatches = state == InferiorStopOk
708         || (state == InferiorRunOk && engine->acceptsWatchesWhileRunning());
709
710     QMenu breakpointMenu;
711     breakpointMenu.setTitle(tr("Add Data Breakpoint..."));
712     QAction *actSetWatchpointAtVariableAddress = 0;
713     QAction *actSetWatchpointAtPointerValue = 0;
714     const bool canSetWatchpoint = engineCapabilities & WatchpointByAddressCapability;
715     if (canSetWatchpoint && address) {
716         actSetWatchpointAtVariableAddress =
717             new QAction(tr("Add Data Breakpoint at Object's Address (0x%1)")
718                 .arg(address, 0, 16), &breakpointMenu);
719         actSetWatchpointAtVariableAddress->
720             setChecked(mi0.data(LocalsIsWatchpointAtAddressRole).toBool());
721         if (createPointerActions) {
722             actSetWatchpointAtPointerValue =
723                 new QAction(tr("Add Data Breakpoint at Referenced Address (0x%1)")
724                     .arg(pointerValue, 0, 16), &breakpointMenu);
725             actSetWatchpointAtPointerValue->setCheckable(true);
726             actSetWatchpointAtPointerValue->
727                 setChecked(mi0.data(LocalsIsWatchpointAtPointerValueRole).toBool());
728         }
729     } else {
730         actSetWatchpointAtVariableAddress =
731             new QAction(tr("Add Data Breakpoint"), &breakpointMenu);
732         actSetWatchpointAtVariableAddress->setEnabled(false);
733     }
734     actSetWatchpointAtVariableAddress->setToolTip(
735         tr("Setting a data breakpoint on an address will cause the program "
736            "to stop when the data at the address is modified."));
737
738     QAction *actSetWatchpointAtExpression =
739             new QAction(tr("Add Data Breakpoint at Expression \"%1\"").arg(name),
740                 &breakpointMenu);
741     actSetWatchpointAtExpression->setToolTip(
742         tr("Setting a data breakpoint on an expression will cause the program "
743            "to stop when the data at the address given by the expression "
744            "is modified."));
745
746     breakpointMenu.addAction(actSetWatchpointAtVariableAddress);
747     if (actSetWatchpointAtPointerValue)
748         breakpointMenu.addAction(actSetWatchpointAtPointerValue);
749     breakpointMenu.addAction(actSetWatchpointAtExpression);
750
751     QMenu menu;
752     QAction *actInsertNewWatchItem = menu.addAction(tr("Insert New Evaluated Expression"));
753     actInsertNewWatchItem->setEnabled(canHandleWatches && canInsertWatches);
754     QAction *actSelectWidgetToWatch = menu.addAction(tr("Select Widget to Watch"));
755     actSelectWidgetToWatch->setEnabled(canHandleWatches && (engine->canWatchWidgets()));
756
757     menu.addSeparator();
758
759     QAction *actWatchExpression = new QAction(addWatchActionText(exp), &menu);
760     actWatchExpression->setEnabled(canHandleWatches && !exp.isEmpty());
761
762     // Can remove watch if engine can handle it or session engine.
763     QAction *actRemoveWatchExpression = new QAction(removeWatchActionText(exp), &menu);
764     actRemoveWatchExpression->setEnabled(
765         (canHandleWatches || state == DebuggerNotReady) && !exp.isEmpty());
766     QAction *actRemoveWatches = new QAction(tr("Remove All Watch Items"), &menu);
767     actRemoveWatches->setEnabled(!WatchHandler::watcherNames().isEmpty());
768
769     if (m_type == LocalsType)
770         menu.addAction(actWatchExpression);
771     else {
772         menu.addAction(actRemoveWatchExpression);
773         menu.addAction(actRemoveWatches);
774     }
775
776     QMenu memoryMenu;
777     memoryMenu.setTitle(tr("Open Memory Editor..."));
778     QAction *actOpenMemoryEditAtVariableAddress = new QAction(&memoryMenu);
779     QAction *actOpenMemoryEditAtPointerValue = new QAction(&memoryMenu);
780     QAction *actOpenMemoryEditor = new QAction(&memoryMenu);
781     QAction *actOpenMemoryEditorStackLayout = new QAction(&memoryMenu);
782     QAction *actOpenMemoryViewAtVariableAddress = new QAction(&memoryMenu);
783     QAction *actOpenMemoryViewAtPointerValue = new QAction(&memoryMenu);
784     if (engineCapabilities & ShowMemoryCapability) {
785         actOpenMemoryEditor->setText(tr("Open Memory Editor..."));
786         if (address) {
787             actOpenMemoryEditAtVariableAddress->setText(
788                 tr("Open Memory Editor at Object's Address (0x%1)")
789                     .arg(address, 0, 16));
790             actOpenMemoryViewAtVariableAddress->setText(
791                     tr("Open Memory View at Object's Address (0x%1)")
792                         .arg(address, 0, 16));
793         } else {
794             actOpenMemoryEditAtVariableAddress->setText(
795                 tr("Open Memory Editor at Object's Address"));
796             actOpenMemoryEditAtVariableAddress->setEnabled(false);
797             actOpenMemoryViewAtVariableAddress->setText(
798                     tr("Open Memory View at Object's Address"));
799             actOpenMemoryViewAtVariableAddress->setEnabled(false);
800         }
801         if (createPointerActions) {
802             actOpenMemoryEditAtPointerValue->setText(
803                 tr("Open Memory Editor at Referenced Address (0x%1)")
804                     .arg(pointerValue, 0, 16));
805             actOpenMemoryViewAtPointerValue->setText(
806                 tr("Open Memory View at Referenced Address (0x%1)")
807                     .arg(pointerValue, 0, 16));
808         } else {
809             actOpenMemoryEditAtPointerValue->setText(
810                 tr("Open Memory Editor at Referenced Address"));
811             actOpenMemoryEditAtPointerValue->setEnabled(false);
812             actOpenMemoryViewAtPointerValue->setText(
813                 tr("Open Memory View at Referenced Address"));
814             actOpenMemoryViewAtPointerValue->setEnabled(false);
815         }
816         actOpenMemoryEditorStackLayout->setText(tr("Open Memory Editor Showing Stack Layout"));
817         actOpenMemoryEditorStackLayout->setEnabled(m_type == LocalsType);
818         memoryMenu.addAction(actOpenMemoryViewAtVariableAddress);
819         memoryMenu.addAction(actOpenMemoryViewAtPointerValue);
820         memoryMenu.addAction(actOpenMemoryEditAtVariableAddress);
821         memoryMenu.addAction(actOpenMemoryEditAtPointerValue);
822         memoryMenu.addAction(actOpenMemoryEditorStackLayout);
823         memoryMenu.addAction(actOpenMemoryEditor);
824     } else {
825         memoryMenu.setEnabled(false);
826     }
827
828     QAction *actCopy = new QAction(tr("Copy Contents to Clipboard"), &menu);
829     QAction *actCopyValue = new QAction(tr("Copy Value to Clipboard"), &menu);
830     actCopyValue->setEnabled(idx.isValid());
831
832
833     menu.addAction(actInsertNewWatchItem);
834     menu.addAction(actSelectWidgetToWatch);
835     menu.addMenu(&formatMenu);
836     menu.addMenu(&memoryMenu);
837     menu.addMenu(&breakpointMenu);
838     menu.addAction(actCopy);
839     menu.addAction(actCopyValue);
840     menu.addSeparator();
841
842     menu.addAction(debuggerCore()->action(UseDebuggingHelpers));
843     menu.addAction(debuggerCore()->action(UseToolTipsInLocalsView));
844     menu.addAction(debuggerCore()->action(AutoDerefPointers));
845     menu.addAction(debuggerCore()->action(ShowStdNamespace));
846     menu.addAction(debuggerCore()->action(ShowQtNamespace));
847     menu.addAction(debuggerCore()->action(SortStructMembers));
848
849     QAction *actAdjustColumnWidths =
850         menu.addAction(tr("Adjust Column Widths to Contents"));
851     menu.addAction(debuggerCore()->action(AlwaysAdjustLocalsColumnWidths));
852     menu.addSeparator();
853
854     QAction *actClearCodeModelSnapshot
855         = new QAction(tr("Refresh Code Model Snapshot"), &menu);
856     actClearCodeModelSnapshot->setEnabled(actionsEnabled
857         && debuggerCore()->action(UseCodeModel)->isChecked());
858     menu.addAction(actClearCodeModelSnapshot);
859     QAction *actShowInEditor
860         = new QAction(tr("Show View Contents in Editor"), &menu);
861     actShowInEditor->setEnabled(actionsEnabled);
862     menu.addAction(actShowInEditor);
863     menu.addAction(debuggerCore()->action(SettingsDialog));
864
865     QAction *actCloseEditorToolTips = new QAction(tr("Close Editor Tooltips"), &menu);
866     actCloseEditorToolTips->setEnabled(DebuggerToolTipManager::instance()->hasToolTips());
867     menu.addAction(actCloseEditorToolTips);
868
869     QAction *act = menu.exec(ev->globalPos());
870     if (act == 0)
871         return;
872
873     if (act == actAdjustColumnWidths) {
874         resizeColumnsToContents();
875     } else if (act == actInsertNewWatchItem) {
876         bool ok;
877         QString newExp = QInputDialog::getText(this, tr("Enter watch expression"),
878                                    tr("Expression:"), QLineEdit::Normal,
879                                    QString(), &ok);
880         if (ok && !newExp.isEmpty()) {
881             watchExpression(newExp);
882         }
883     } else if (act == actOpenMemoryEditAtVariableAddress) {
884         addVariableMemoryView(currentEngine(), false, mi0, false, ev->globalPos(), this);
885     } else if (act == actOpenMemoryEditAtPointerValue) {
886         addVariableMemoryView(currentEngine(), false, mi0, true, ev->globalPos(), this);
887     } else if (act == actOpenMemoryEditor) {
888         AddressDialog dialog;
889         if (address)
890             dialog.setAddress(address);
891         if (dialog.exec() == QDialog::Accepted)
892             currentEngine()->openMemoryView(dialog.address(), false, MemoryMarkupList(), QPoint());
893     } else if (act == actOpenMemoryViewAtVariableAddress) {
894         addVariableMemoryView(currentEngine(), true, mi0, false, ev->globalPos(), this);
895     } else if (act == actOpenMemoryViewAtPointerValue) {
896         addVariableMemoryView(currentEngine(), true, mi0, true, ev->globalPos(), this);
897     } else if (act == actOpenMemoryEditorStackLayout) {
898         addStackLayoutMemoryView(currentEngine(), false, mi0.model(), ev->globalPos(), this);
899     } else if (act == actSetWatchpointAtVariableAddress) {
900         setWatchpointAtAddress(address, size);
901     } else if (act == actSetWatchpointAtPointerValue) {
902         setWatchpointAtAddress(pointerValue, sizeof(void *)); // FIXME: an approximation..
903     } else if (act == actSetWatchpointAtExpression) {
904         setWatchpointAtExpression(name);
905     } else if (act == actSelectWidgetToWatch) {
906         grabMouse(Qt::CrossCursor);
907         m_grabbing = true;
908     } else if (act == actWatchExpression) {
909         watchExpression(exp);
910     } else if (act == actRemoveWatchExpression) {
911         removeWatchExpression(exp);
912     } else if (act == actCopy) {
913         copyToClipboard(DebuggerToolTipWidget::treeModelClipboardContents(model()));
914     } else if (act == actCopyValue) {
915         copyToClipboard(mi1.data().toString());
916     } else if (act == actRemoveWatches) {
917         currentEngine()->watchHandler()->clearWatches();
918     } else if (act == actClearCodeModelSnapshot) {
919         debuggerCore()->clearCppCodeModelSnapshot();
920     } else if (act == clearTypeFormatAction) {
921         setModelData(LocalsTypeFormatRole, -1, mi1);
922     } else if (act == clearIndividualFormatAction) {
923         setModelData(LocalsIndividualFormatRole, -1, mi1);
924     } else if (act == actShowInEditor) {
925         QString contents = handler->editorContents();
926         debuggerCore()->openTextEditor(tr("Locals & Watchers"), contents);
927     } else if (act == showUnprintableUnicode) {
928         handler->setUnprintableBase(0);
929     } else if (act == showUnprintableOctal) {
930         handler->setUnprintableBase(8);
931     } else if (act == showUnprintableHexadecimal) {
932         handler->setUnprintableBase(16);
933     } else if (act == actCloseEditorToolTips) {
934         DebuggerToolTipManager::instance()->closeAllToolTips();
935     } else {
936         for (int i = 0; i != typeFormatActions.size(); ++i) {
937             if (act == typeFormatActions.at(i))
938                 setModelData(LocalsTypeFormatRole, i, mi1);
939         }
940         for (int i = 0; i != individualFormatActions.size(); ++i) {
941             if (act == individualFormatActions.at(i))
942                 setModelData(LocalsIndividualFormatRole, i, mi1);
943         }
944     }
945 }
946
947 void WatchWindow::resizeColumnsToContents()
948 {
949     resizeColumnToContents(0);
950     resizeColumnToContents(1);
951 }
952
953 void WatchWindow::setAlwaysResizeColumnsToContents(bool on)
954 {
955     if (!header())
956         return;
957     QHeaderView::ResizeMode mode = on
958         ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
959     header()->setResizeMode(0, mode);
960     header()->setResizeMode(1, mode);
961 }
962
963 bool WatchWindow::event(QEvent *ev)
964 {
965     if (m_grabbing && ev->type() == QEvent::MouseButtonPress) {
966         QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
967         m_grabbing = false;
968         releaseMouse();
969         currentEngine()->watchPoint(mapToGlobal(mev->pos()));
970     }
971     return QTreeView::event(ev);
972 }
973
974 void WatchWindow::editItem(const QModelIndex &idx)
975 {
976     Q_UNUSED(idx) // FIXME
977 }
978
979 void WatchWindow::setModel(QAbstractItemModel *model)
980 {
981     QTreeView::setModel(model);
982
983     setRootIsDecorated(true);
984     if (header()) {
985         setAlwaysResizeColumnsToContents(
986             debuggerCore()->boolSetting(AlwaysAdjustLocalsColumnWidths));
987         header()->setDefaultAlignment(Qt::AlignLeft);
988         if (m_type != LocalsType)
989             header()->hide();
990     }
991
992     connect(model, SIGNAL(layoutChanged()), SLOT(resetHelper()));
993     connect(model, SIGNAL(enableUpdates(bool)), SLOT(setUpdatesEnabled(bool)));
994     // Potentially left in disabled state in case engine crashes when expanding.
995     setUpdatesEnabled(true);
996 }
997
998 void WatchWindow::setUpdatesEnabled(bool enable)
999 {
1000     //qDebug() << "ENABLING UPDATES: " << enable;
1001     QTreeView::setUpdatesEnabled(enable);
1002 }
1003
1004 void WatchWindow::resetHelper()
1005 {
1006     bool old = updatesEnabled();
1007     setUpdatesEnabled(false);
1008     resetHelper(model()->index(0, 0));
1009     setUpdatesEnabled(old);
1010 }
1011
1012 void WatchWindow::resetHelper(const QModelIndex &idx)
1013 {
1014     if (idx.data(LocalsExpandedRole).toBool()) {
1015         //qDebug() << "EXPANDING " << model()->data(idx, INameRole);
1016         if (!isExpanded(idx)) {
1017             expand(idx);
1018             for (int i = 0, n = model()->rowCount(idx); i != n; ++i) {
1019                 QModelIndex idx1 = model()->index(i, 0, idx);
1020                 resetHelper(idx1);
1021             }
1022         }
1023     } else {
1024         //qDebug() << "COLLAPSING " << model()->data(idx, INameRole);
1025         if (isExpanded(idx))
1026             collapse(idx);
1027     }
1028 }
1029
1030 void WatchWindow::watchExpression(const QString &exp)
1031 {
1032     currentEngine()->watchHandler()->watchExpression(exp);
1033 }
1034
1035 void WatchWindow::removeWatchExpression(const QString &exp)
1036 {
1037     currentEngine()->watchHandler()->removeWatchExpression(exp);
1038 }
1039
1040 void WatchWindow::setModelData
1041     (int role, const QVariant &value, const QModelIndex &index)
1042 {
1043     QTC_ASSERT(model(), return);
1044     model()->setData(index, value, role);
1045 }
1046
1047 void WatchWindow::setWatchpointAtAddress(quint64 address, unsigned size)
1048 {
1049     BreakpointParameters data(WatchpointAtAddress);
1050     data.address = address;
1051     data.size = size;
1052     BreakpointModelId id = breakHandler()->findWatchpoint(data);
1053     if (id) {
1054         qDebug() << "WATCHPOINT EXISTS";
1055         //   removeBreakpoint(index);
1056         return;
1057     }
1058     breakHandler()->appendBreakpoint(data);
1059 }
1060
1061 void WatchWindow::setWatchpointAtExpression(const QString &exp)
1062 {
1063     BreakpointParameters data(WatchpointAtExpression);
1064     data.expression = exp;
1065     BreakpointModelId id = breakHandler()->findWatchpoint(data);
1066     if (id) {
1067         qDebug() << "WATCHPOINT EXISTS";
1068         //   removeBreakpoint(index);
1069         return;
1070     }
1071     breakHandler()->appendBreakpoint(data);
1072 }
1073
1074 } // namespace Internal
1075 } // namespace Debugger
1076