1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
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.
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
31 **************************************************************************/
33 #include "watchwindow.h"
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>
51 #include <QtCore/QDebug>
52 #include <QtCore/QMetaObject>
53 #include <QtCore/QMetaProperty>
54 #include <QtCore/QVariant>
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>
68 /////////////////////////////////////////////////////////////////////
72 /////////////////////////////////////////////////////////////////////
77 static DebuggerEngine *currentEngine()
79 return debuggerCore()->currentEngine();
82 class WatchDelegate : public QItemDelegate
85 explicit WatchDelegate(WatchWindow *parent)
86 : QItemDelegate(parent), m_watchWindow(parent)
89 QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
90 const QModelIndex &index) const
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());
98 return new BooleanComboBox(parent);
102 WatchLineEdit *edit = WatchLineEdit::create(type, parent);
103 edit->setFrame(false);
104 IntegerWatchLineEdit *intEdit
105 = qobject_cast<IntegerWatchLineEdit *>(edit);
107 intEdit->setBase(index.data(LocalsIntegerBaseRole).toInt());
111 // Standard line edits for the rest.
112 QLineEdit *lineEdit = new QLineEdit(parent);
113 lineEdit->setFrame(false);
117 void setModelData(QWidget *editor, QAbstractItemModel *model,
118 const QModelIndex &index) const
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);
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();
133 m_watchWindow->removeWatchExpression(exp);
134 m_watchWindow->watchExpression(value);
137 void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
138 const QModelIndex &) const
140 editor->setGeometry(option.rect);
144 WatchWindow *m_watchWindow;
147 // Watch model query helpers.
148 static inline quint64 addressOf(const QModelIndex &m)
150 return m.data(LocalsAddressRole).toULongLong();
153 static inline quint64 pointerValueOf(const QModelIndex &m)
155 return m.data(LocalsPointerValueRole).toULongLong();
158 static inline QString nameOf(const QModelIndex &m)
160 return m.data().toString();
163 static inline QString typeOf(const QModelIndex &m)
165 return m.data(LocalsTypeRole).toString();
168 static inline uint sizeOf(const QModelIndex &m)
170 return m.data(LocalsSizeRole).toUInt();
173 // Create a map of value->name for register markup
174 typedef QMap<quint64, QString> RegisterMap;
175 typedef RegisterMap::const_iterator RegisterMapConstIt;
177 RegisterMap registerMap(const DebuggerEngine *engine)
180 foreach (const Register ®, engine->registerHandler()->registers()) {
181 const QVariant v = reg.editValue();
182 if (v.type() == QVariant::ULongLong)
183 result.insert(v.toULongLong(), QString::fromAscii(reg.name));
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.
193 typedef QPair<int, QString> ColorNumberToolTipPair;
194 typedef QVector<ColorNumberToolTipPair> ColorNumberToolTipVector;
196 static inline QString variableToolTip(const QString &name,
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);
208 static int memberVariableRecursion(const QAbstractItemModel *model,
209 const QModelIndex &modelIndex,
211 quint64 start, quint64 end,
213 ColorNumberToolTipVector *cnmv)
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);
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);
236 childCount += memberVariableRecursion(model, childIndex, childName, start, end, colorNumberIn, cnmv);
243 \fn variableMemoryMarkup()
245 \brief Creates markup for a variable in the memory view.
247 Marks the visible children with alternating colors in the parent, that is, for
256 create something like:
260 2 base color (padding area of parent)
264 8 memberColor2 (pair.first)
266 12 memberColor1 (pair.second)
269 In addition, registers pointing into the area are shown as 1 byte-markers.
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.
277 \note To recurse over the top level items of the model, pass an invalid model
280 \sa Debugger::Internal::MemoryViewWidget
283 typedef QList<MemoryMarkup> MemoryMarkupList;
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 ®isterMap,
293 const QColor &defaultBackground)
296 enum { registerColorNumber = 0x3453 };
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;
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;
318 ColorNumberToolTipPair(registerColorNumber,
319 WatchWindow::tr("Register <i>%1</i>").arg(it.value()));
326 QDebug dbg = qDebug().nospace();
327 dbg << rootToolTip << ' ' << address << ' ' << size << '\n';
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;
336 // Condense ranges of identical color into markup ranges. Assign member colors
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();
343 int lastColorNumber = 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);
356 color = memberColor2;
357 memberColor2 = memberColor2.darker(120);
360 result.push_back(MemoryMarkup(address + i, 1, color, range.second));
362 result.back().length++;
367 QDebug dbg = qDebug().nospace();
368 dbg << rootName << ' ' << address << ' ' << size << '\n';
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;
376 foreach (const MemoryMarkup &m, result)
377 dbg << m.address << ' ' << m.length << ' ' << m.toolTip << '\n';
383 // Convenience to create a memory view of a variable.
384 static void addVariableMemoryView(DebuggerEngine *engine,
386 const QModelIndex &m, bool deferencePointer,
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;
399 const QList<MemoryMarkup> markup =
400 variableMemoryMarkup(m.model(), m, nameOf(m), rootToolTip,
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);
411 // Add a memory view of the stack layout showing local variables
413 static inline void addStackLayoutMemoryView(DebuggerEngine *engine,
415 const QAbstractItemModel *m, const QPoint &p,
418 typedef QPair<quint64, QString> RegisterValueNamePair;
420 // Determine suitable address range from locals
421 quint64 start = Q_UINT64_C(0xFFFFFFFFFFFFFFFF);
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);
433 if (const uint size = sizeOf(idx))
434 if (address + size > end)
435 end = address + size;
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."));
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) {
453 } else if (value > end && value - end < 512) {
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);
469 /////////////////////////////////////////////////////////////////////
473 /////////////////////////////////////////////////////////////////////
475 WatchWindow::WatchWindow(Type type, QWidget *parent)
479 setObjectName(QLatin1String("WatchWindow"));
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);
492 QAction *useColors = debuggerCore()->action(UseAlternatingRowColors);
493 setAlternatingRowColors(useColors->isChecked());
495 QAction *adjustColumns = debuggerCore()->action(AlwaysAdjustLocalsColumnWidths);
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)));
507 void WatchWindow::expandNode(const QModelIndex &idx)
509 setModelData(LocalsExpandedRole, true, idx);
512 void WatchWindow::collapseNode(const QModelIndex &idx)
514 setModelData(LocalsExpandedRole, false, idx);
517 void WatchWindow::keyPressEvent(QKeyEvent *ev)
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);
532 QTreeView::keyPressEvent(ev);
535 void WatchWindow::dragEnterEvent(QDragEnterEvent *ev)
537 //QTreeView::dragEnterEvent(ev);
538 if (ev->mimeData()->hasFormat("text/plain")) {
539 ev->setDropAction(Qt::CopyAction);
544 void WatchWindow::dragMoveEvent(QDragMoveEvent *ev)
546 //QTreeView::dragMoveEvent(ev);
547 if (ev->mimeData()->hasFormat("text/plain")) {
548 ev->setDropAction(Qt::CopyAction);
553 void WatchWindow::dropEvent(QDropEvent *ev)
555 if (ev->mimeData()->hasFormat("text/plain")) {
556 watchExpression(ev->mimeData()->text());
557 //ev->acceptProposedAction();
558 ev->setDropAction(Qt::CopyAction);
561 //QTreeView::dropEvent(ev);
564 void WatchWindow::mouseDoubleClickEvent(QMouseEvent *ev)
566 const QModelIndex idx = indexAt(ev->pos());
567 if (!idx.isValid()) {
568 // The "<Edit>" case.
569 watchExpression(QString());
572 QTreeView::mouseDoubleClickEvent(ev);
575 // Text for add watch action with truncated expression
576 static inline QString addWatchActionText(QString exp)
579 return WatchWindow::tr("Evaluate Expression");
580 if (exp.size() > 30) {
582 exp.append(QLatin1String("..."));
584 return WatchWindow::tr("Evaluate Expression \"%1\"").arg(exp);
587 // Text for add watch action with truncated expression
588 static inline QString removeWatchActionText(QString exp)
591 return WatchWindow::tr("Remove Evaluated Expression");
592 if (exp.size() > 30) {
594 exp.append(QLatin1String("..."));
596 return WatchWindow::tr("Remove Evaluated Expression \"%1\"").arg(exp);
599 static inline void copyToClipboard(const QString &clipboardText)
601 QClipboard *clipboard = QApplication::clipboard();
603 clipboard->setText(clipboardText, QClipboard::Selection);
605 clipboard->setText(clipboardText, QClipboard::Clipboard);
608 void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
610 DebuggerEngine *engine = currentEngine();
611 WatchHandler *handler = engine->watchHandler();
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();
624 // Offer to open address pointed to or variable address.
625 const bool createPointerActions = pointerValue && pointerValue != address;
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();
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);
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);
693 act->setChecked(true);
694 formatMenu.addAction(act);
695 typeFormatActions.append(act);
698 QAction *dummy = formatMenu.addAction(
699 tr("Change Display for Type or Item..."));
700 dummy->setEnabled(false);
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());
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());
730 actSetWatchpointAtVariableAddress =
731 new QAction(tr("Add Data Breakpoint"), &breakpointMenu);
732 actSetWatchpointAtVariableAddress->setEnabled(false);
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."));
738 QAction *actSetWatchpointAtExpression =
739 new QAction(tr("Add Data Breakpoint at Expression \"%1\"").arg(name),
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 "
746 breakpointMenu.addAction(actSetWatchpointAtVariableAddress);
747 if (actSetWatchpointAtPointerValue)
748 breakpointMenu.addAction(actSetWatchpointAtPointerValue);
749 breakpointMenu.addAction(actSetWatchpointAtExpression);
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()));
759 QAction *actWatchExpression = new QAction(addWatchActionText(exp), &menu);
760 actWatchExpression->setEnabled(canHandleWatches && !exp.isEmpty());
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());
769 if (m_type == LocalsType)
770 menu.addAction(actWatchExpression);
772 menu.addAction(actRemoveWatchExpression);
773 menu.addAction(actRemoveWatches);
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..."));
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));
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);
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));
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);
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);
825 memoryMenu.setEnabled(false);
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());
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);
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));
849 QAction *actAdjustColumnWidths =
850 menu.addAction(tr("Adjust Column Widths to Contents"));
851 menu.addAction(debuggerCore()->action(AlwaysAdjustLocalsColumnWidths));
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));
865 QAction *actCloseEditorToolTips = new QAction(tr("Close Editor Tooltips"), &menu);
866 actCloseEditorToolTips->setEnabled(DebuggerToolTipManager::instance()->hasToolTips());
867 menu.addAction(actCloseEditorToolTips);
869 QAction *act = menu.exec(ev->globalPos());
873 if (act == actAdjustColumnWidths) {
874 resizeColumnsToContents();
875 } else if (act == actInsertNewWatchItem) {
877 QString newExp = QInputDialog::getText(this, tr("Enter watch expression"),
878 tr("Expression:"), QLineEdit::Normal,
880 if (ok && !newExp.isEmpty()) {
881 watchExpression(newExp);
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;
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);
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();
936 for (int i = 0; i != typeFormatActions.size(); ++i) {
937 if (act == typeFormatActions.at(i))
938 setModelData(LocalsTypeFormatRole, i, mi1);
940 for (int i = 0; i != individualFormatActions.size(); ++i) {
941 if (act == individualFormatActions.at(i))
942 setModelData(LocalsIndividualFormatRole, i, mi1);
947 void WatchWindow::resizeColumnsToContents()
949 resizeColumnToContents(0);
950 resizeColumnToContents(1);
953 void WatchWindow::setAlwaysResizeColumnsToContents(bool on)
957 QHeaderView::ResizeMode mode = on
958 ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
959 header()->setResizeMode(0, mode);
960 header()->setResizeMode(1, mode);
963 bool WatchWindow::event(QEvent *ev)
965 if (m_grabbing && ev->type() == QEvent::MouseButtonPress) {
966 QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
969 currentEngine()->watchPoint(mapToGlobal(mev->pos()));
971 return QTreeView::event(ev);
974 void WatchWindow::editItem(const QModelIndex &idx)
976 Q_UNUSED(idx) // FIXME
979 void WatchWindow::setModel(QAbstractItemModel *model)
981 QTreeView::setModel(model);
983 setRootIsDecorated(true);
985 setAlwaysResizeColumnsToContents(
986 debuggerCore()->boolSetting(AlwaysAdjustLocalsColumnWidths));
987 header()->setDefaultAlignment(Qt::AlignLeft);
988 if (m_type != LocalsType)
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);
998 void WatchWindow::setUpdatesEnabled(bool enable)
1000 //qDebug() << "ENABLING UPDATES: " << enable;
1001 QTreeView::setUpdatesEnabled(enable);
1004 void WatchWindow::resetHelper()
1006 bool old = updatesEnabled();
1007 setUpdatesEnabled(false);
1008 resetHelper(model()->index(0, 0));
1009 setUpdatesEnabled(old);
1012 void WatchWindow::resetHelper(const QModelIndex &idx)
1014 if (idx.data(LocalsExpandedRole).toBool()) {
1015 //qDebug() << "EXPANDING " << model()->data(idx, INameRole);
1016 if (!isExpanded(idx)) {
1018 for (int i = 0, n = model()->rowCount(idx); i != n; ++i) {
1019 QModelIndex idx1 = model()->index(i, 0, idx);
1024 //qDebug() << "COLLAPSING " << model()->data(idx, INameRole);
1025 if (isExpanded(idx))
1030 void WatchWindow::watchExpression(const QString &exp)
1032 currentEngine()->watchHandler()->watchExpression(exp);
1035 void WatchWindow::removeWatchExpression(const QString &exp)
1037 currentEngine()->watchHandler()->removeWatchExpression(exp);
1040 void WatchWindow::setModelData
1041 (int role, const QVariant &value, const QModelIndex &index)
1043 QTC_ASSERT(model(), return);
1044 model()->setData(index, value, role);
1047 void WatchWindow::setWatchpointAtAddress(quint64 address, unsigned size)
1049 BreakpointParameters data(WatchpointAtAddress);
1050 data.address = address;
1052 BreakpointModelId id = breakHandler()->findWatchpoint(data);
1054 qDebug() << "WATCHPOINT EXISTS";
1055 // removeBreakpoint(index);
1058 breakHandler()->appendBreakpoint(data);
1061 void WatchWindow::setWatchpointAtExpression(const QString &exp)
1063 BreakpointParameters data(WatchpointAtExpression);
1064 data.expression = exp;
1065 BreakpointModelId id = breakHandler()->findWatchpoint(data);
1067 qDebug() << "WATCHPOINT EXISTS";
1068 // removeBreakpoint(index);
1071 breakHandler()->appendBreakpoint(data);
1074 } // namespace Internal
1075 } // namespace Debugger