watchwindow.cpp 34.4 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28 29
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32

con's avatar
con committed
33 34
#include "watchwindow.h"

35
#include "breakhandler.h"
36
#include "debuggeractions.h"
37
#include "debuggerconstants.h"
38
#include "debuggercore.h"
39
#include "debuggerdialogs.h"
40
#include "debuggerengine.h"
41
#include "debuggerstartparameters.h"
42
#include "watchdelegatewidgets.h"
43
#include "watchhandler.h"
44
#include "debuggertooltipmanager.h"
45
#include "memoryagent.h"
46
#include <utils/qtcassert.h>
47
#include <utils/savedaction.h>
48

con's avatar
con committed
49
#include <QtCore/QDebug>
50
#include <QtCore/QMetaObject>
51 52
#include <QtCore/QMetaProperty>
#include <QtCore/QVariant>
con's avatar
con committed
53

54
#include <QtGui/QApplication>
55
#include <QtGui/QPalette>
56
#include <QtGui/QClipboard>
con's avatar
con committed
57 58
#include <QtGui/QContextMenuEvent>
#include <QtGui/QHeaderView>
59
#include <QtGui/QItemDelegate>
con's avatar
con committed
60
#include <QtGui/QMenu>
61
#include <QtGui/QPainter>
con's avatar
con committed
62
#include <QtGui/QResizeEvent>
63
#include <QtGui/QInputDialog>
con's avatar
con committed
64

65 66 67 68 69 70
/////////////////////////////////////////////////////////////////////
//
// WatchDelegate
//
/////////////////////////////////////////////////////////////////////

71 72 73 74 75
namespace Debugger {
namespace Internal {

static DebuggerEngine *currentEngine()
{
76
    return debuggerCore()->currentEngine();
77 78
}

79 80 81
class WatchDelegate : public QItemDelegate
{
public:
82 83 84
    explicit WatchDelegate(WatchWindow *parent)
        : QItemDelegate(parent), m_watchWindow(parent)
    {}
85 86

    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
87
        const QModelIndex &index) const
88
    {
89 90
        // Value column: Custom editor. Apply integer-specific settings.
        if (index.column() == 1) {
91 92
            const QVariant::Type type =
                static_cast<QVariant::Type>(index.data(LocalsEditTypeRole).toInt());
93 94 95 96 97 98 99
            switch (type) {
            case QVariant::Bool:
                return new BooleanComboBox(parent);
            default:
                break;
            }
            WatchLineEdit *edit = WatchLineEdit::create(type, parent);
100 101 102 103
            edit->setFrame(false);
            IntegerWatchLineEdit *intEdit
                = qobject_cast<IntegerWatchLineEdit *>(edit);
            if (intEdit)
104 105 106
                intEdit->setBase(index.data(LocalsIntegerBaseRole).toInt());
            return edit;
        }
107 108 109 110 111

        // Standard line edits for the rest.
        QLineEdit *lineEdit = new QLineEdit(parent);
        lineEdit->setFrame(false);
        return lineEdit;
112 113
    }

114
    void setModelData(QWidget *editor, QAbstractItemModel *model,
115
                      const QModelIndex &index) const
116
    {
117 118 119 120 121
        // Standard handling for anything but the watcher name column (change
        // expression), which removes/recreates a row, which cannot be done
        // in model->setData().
        if (index.column() != 0) {
            QItemDelegate::setModelData(editor, model, index);
122 123
            return;
        }
124 125
        const QMetaProperty userProperty = editor->metaObject()->userProperty();
        QTC_ASSERT(userProperty.isValid(), return);
126
        const QString value = editor->property(userProperty.name()).toString();
127
        const QString exp = index.data(LocalsExpressionRole).toString();
128 129 130 131
        if (exp == value)
            return;
        m_watchWindow->removeWatchExpression(exp);
        m_watchWindow->watchExpression(value);
132 133 134 135 136 137 138
    }

    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
        const QModelIndex &) const
    {
        editor->setGeometry(option.rect);
    }
139 140 141

private:
    WatchWindow *m_watchWindow;
142 143
};

144 145 146 147 148 149 150
// Watch model query helpers.
static inline quint64 addressOf(const QModelIndex &m)
    { return m.data(LocalsAddressRole).toULongLong(); }
static inline quint64 pointerValueOf(const QModelIndex &m)
    { return m.data(LocalsPointerValueRole).toULongLong(); }
static inline QString nameOf(const QModelIndex &m)
    { return m.data().toString(); }
151 152
static inline QString typeOf(const QModelIndex &m)
    { return m.data(LocalsTypeRole).toString(); }
153 154 155
static inline uint sizeOf(const QModelIndex &m)
    { return m.data(LocalsSizeRole).toUInt(); }

156 157 158 159 160 161 162 163
// Helper functionality to indicate the area of a member variable in
// a vector representing the memory area by a unique color
// number and tooltip. Parts of it will be overwritten when recursing
// over the children.

typedef QPair<int, QString> ColorNumberToolTipPair;
typedef QVector<ColorNumberToolTipPair> ColorNumberToolTipVector;

164 165 166 167 168 169 170 171 172 173 174 175
static inline QString variableToolTip(const QString &name,
                                      const QString &type,
                                      quint64 offset)
{
    return offset ?
           //: HTML tooltip of a variable in the memory editor
           WatchWindow::tr("<i>%1</i> %2 at #%3").
               arg(type, name).arg(offset) :
           //: HTML tooltip of a variable in the memory editor
           WatchWindow::tr("<i>%1</i> %2").arg(type, name);
}

176 177 178 179 180
static int memberVariableRecursion(const QModelIndex &m,
                                    const QString &name,
                                    quint64 start, quint64 end,
                                    int *colorNumberIn,
                                    ColorNumberToolTipVector *cnmv)
181
{
182
    int childCount = 0;
183 184
    const int rowCount = m.model()->rowCount(m);
    if (!rowCount)
185 186
        return childCount;
    const QString nameRoot = name + QLatin1Char('.');
187 188 189 190 191
    for (int r = 0; r < rowCount; r++) {
        const QModelIndex childIndex = m.child(r, 0);
        const quint64 childAddress = addressOf(childIndex);
        const uint childSize = sizeOf(childIndex);
        if (childAddress && childAddress >= start
192 193 194
                && (childAddress + childSize) <= end) { // Non-static, within area?
            const QString childName = nameRoot + nameOf(childIndex);
            const quint64 childOffset = childAddress - start;
195 196
            const QString toolTip
                = variableToolTip(childName, typeOf(childIndex), childOffset);
197 198 199 200 201
            const ColorNumberToolTipPair colorNumberNamePair((*colorNumberIn)++, toolTip);
            const ColorNumberToolTipVector::iterator begin = cnmv->begin() + childOffset;
            qFill(begin, begin + childSize, colorNumberNamePair);
            childCount++;
            childCount += memberVariableRecursion(childIndex, childName, start, end, colorNumberIn, cnmv);
202 203
        }
    }
204
    return childCount;
205 206 207 208 209 210 211
}

/*!
    \fn variableMemoryMarkup()

    \brief Creates markup for a variable in the memory view.

212
    Marks the visible children with alternating colors in the parent, that is, for
213 214 215 216 217
    \code
    struct Foo {
    char c1
    char c2
    int x2;
218
    QPair<int, int> pair
219 220 221 222 223 224 225 226 227 228
    }
    \endcode
    create something like:
    \code
    0 memberColor1
    1 memberColor2
    2 base color (padding area of parent)
    3 base color
    4 member color1
    ...
229 230 231
    8 memberColor2 (pair.first)
    ...
    12 memberColor1 (pair.second)
232 233 234 235 236 237 238 239 240 241 242
    \endcode

   Fixme: When dereferencing a pointer, the size of the pointee is not
   known, currently. So, we take an area of 1024 and fill the background
   with the default color so that just the members are shown
   (sizeIsEstimate=true). This could be fixed by passing the pointee size
   as well from the debugger, but would require expensive type manipulation.

    \sa Debugger::Internal::MemoryViewWidget
*/

243
typedef QList<MemoryMarkup> MemoryMarkupList;
244

245
static inline MemoryMarkupList
246 247 248 249 250 251 252
    variableMemoryMarkup(const QModelIndex &m, quint64 address, quint64 size,
                         bool sizeIsEstimate,
                         const QColor &defaultBackground)
{
    enum { debug = 0 };

    // Starting out from base, create an array representing the area filled with base
253
    // color. Fill children with some unique color numbers,
254
    // leaving the padding areas of the parent colored with the base color.
255
    MemoryMarkupList result;
256
    const QString name = nameOf(m);
257
    int colorNumber = 0;
258 259
    const QString rootToolTip = variableToolTip(name, typeOf(m), 0);
    ColorNumberToolTipVector ranges(size, ColorNumberToolTipPair(colorNumber, rootToolTip));
260 261 262
    const int childCount = memberVariableRecursion(m, name, address, address + size, &colorNumber, &ranges);
    if (sizeIsEstimate && !childCount)
        return result; // Fixme: Exact size not known, no point in filling if no children.
263 264 265 266 267 268 269 270 271 272 273
    if (debug) {
        QDebug dbg = qDebug().nospace();
        dbg << name << ' ' << address << ' ' << size << '\n';
        QString name;
        for (unsigned i = 0; i < size; i++)
            if (name != ranges.at(i).second) {
                dbg << ",[" << i << ' ' << ranges.at(i).first << ' ' << ranges.at(i).second << ']';
                name = ranges.at(i).second;
            }
    }

274 275 276 277 278 279 280 281
    // Condense ranges of identical color into markup ranges. Assign member colors
    // interchangeably.
    const QColor baseColor = sizeIsEstimate ? defaultBackground : Qt::lightGray;
    QColor memberColor1 = QColor(Qt::yellow).lighter();
    QColor memberColor2 = QColor(Qt::cyan).lighter();

    int lastColorNumber = 0;
    int childNumber = 0;
282
    for (unsigned i = 0; i < size; i++) {
283 284 285
        const ColorNumberToolTipPair &range = ranges.at(i);
        if (result.isEmpty() || lastColorNumber != range.first) {
            lastColorNumber = range.first;
286 287
            QColor color = baseColor; // Base color: Parent
            if (range.first) {
288
                if (childNumber++ & 1) { // Alternating member colors.
289
                    color = memberColor1;
290 291
                    memberColor1 = memberColor1.darker(120);
                } else {
292
                    color = memberColor2;
293 294 295
                    memberColor2 = memberColor2.darker(120);
                }
            } // color switch
296
            result.push_back(MemoryMarkup(address + i, 1, color, range.second));
297
        } else {
298
            result.back().length++;
299 300 301 302 303 304 305 306 307 308 309 310 311
        }
    }

    if (debug) {
        QDebug dbg = qDebug().nospace();
        dbg << name << ' ' << address << ' ' << size << '\n';
        QString name;
        for (unsigned i = 0; i < size; i++)
            if (name != ranges.at(i).second) {
                dbg << ',' << i << ' ' << ranges.at(i).first << ' ' << ranges.at(i).second;
                name = ranges.at(i).second;
            }
        dbg << '\n';
312 313
        foreach (const MemoryMarkup &m, result)
            dbg << m.address <<  ' ' << m.length << ' '  << m.toolTip << '\n';
314 315 316 317 318 319 320
    }

    return result;
}

// Convenience to create a memory view of a variable.
static void addVariableMemoryView(DebuggerEngine *engine,
321
                                  bool separateView,
322
                                  const QModelIndex &m, bool deferencePointer,
323 324
                                  const QPoint &p,
                                  QWidget *parent)
325 326 327 328 329 330 331 332 333 334
{
    const QColor background = parent->palette().color(QPalette::Normal, QPalette::Base);
    const quint64 address = deferencePointer ? pointerValueOf(m) : addressOf(m);
    // Fixme: Get the size of pointee (see variableMemoryMarkup())?
    // Also, gdb does not report the size yet as of 8.4.2011
    const quint64 typeSize = sizeOf(m);
    const bool sizeIsEstimate = deferencePointer || !typeSize;
    const quint64 size    = sizeIsEstimate ? 1024 : typeSize;
    if (!address)
         return;
335 336 337
    const QList<MemoryMarkup> markup =
        variableMemoryMarkup(m, address, size, sizeIsEstimate, background);
    const unsigned flags = separateView ? (DebuggerEngine::MemoryView|DebuggerEngine::MemoryReadOnly) : 0;
338 339 340
    const QString title = deferencePointer ?
    WatchWindow::tr("Memory Referenced by Pointer '%1' (0x%2)").arg(nameOf(m)).arg(address, 0, 16) :
    WatchWindow::tr("Memory at Variable '%1' (0x%2)").arg(nameOf(m)).arg(address, 0, 16);
341
    engine->openMemoryView(address, flags, markup, p, title, parent);
342 343
}

con's avatar
con committed
344 345 346 347 348 349
/////////////////////////////////////////////////////////////////////
//
// WatchWindow
//
/////////////////////////////////////////////////////////////////////

350 351 352
WatchWindow::WatchWindow(Type type, QWidget *parent)
  : QTreeView(parent),
    m_type(type)
con's avatar
con committed
353
{
354
    setObjectName(QLatin1String("WatchWindow"));
355 356
    m_grabbing = false;

357
    setFrameStyle(QFrame::NoFrame);
358
    setAttribute(Qt::WA_MacShowFocusRect, false);
con's avatar
con committed
359 360 361
    setWindowTitle(tr("Locals and Watchers"));
    setIndentation(indentation() * 9/10);
    setUniformRowHeights(true);
362
    setItemDelegate(new WatchDelegate(this));
363 364 365
    setDragEnabled(true);
    setAcceptDrops(true);
    setDropIndicatorShown(true);
con's avatar
con committed
366

367 368 369 370 371 372 373 374 375
    QAction *useColors = debuggerCore()->action(UseAlternatingRowColors);
    setAlternatingRowColors(useColors->isChecked());

    QAction *adjustColumns = debuggerCore()->action(AlwaysAdjustLocalsColumnWidths);

    connect(useColors, SIGNAL(toggled(bool)),
        SLOT(setAlternatingRowColorsHelper(bool)));
    connect(adjustColumns, SIGNAL(triggered(bool)),
        SLOT(setAlwaysResizeColumnsToContents(bool)));
376
    connect(this, SIGNAL(expanded(QModelIndex)),
377
        SLOT(expandNode(QModelIndex)));
378
    connect(this, SIGNAL(collapsed(QModelIndex)),
379
        SLOT(collapseNode(QModelIndex)));
380 381 382 383
}

void WatchWindow::expandNode(const QModelIndex &idx)
{
384
    setModelData(LocalsExpandedRole, true, idx);
385 386 387 388
}

void WatchWindow::collapseNode(const QModelIndex &idx)
{
389
    setModelData(LocalsExpandedRole, false, idx);
con's avatar
con committed
390 391
}

392 393
void WatchWindow::keyPressEvent(QKeyEvent *ev)
{
hjk's avatar
hjk committed
394
    if (ev->key() == Qt::Key_Delete && m_type == WatchersType) {
395 396
        QModelIndex idx = currentIndex();
        QModelIndex idx1 = idx.sibling(idx.row(), 0);
397
        QString exp = idx1.data(LocalsRawExpressionRole).toString();
398
        removeWatchExpression(exp);
399 400 401
    } else if (ev->key() == Qt::Key_Return
            && ev->modifiers() == Qt::ControlModifier
            && m_type == LocalsType) {
hjk's avatar
hjk committed
402 403 404
        QModelIndex idx = currentIndex();
        QModelIndex idx1 = idx.sibling(idx.row(), 0);
        QString exp = model()->data(idx1).toString();
405
        watchExpression(exp);
406 407 408 409
    }
    QTreeView::keyPressEvent(ev);
}

410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
void WatchWindow::dragEnterEvent(QDragEnterEvent *ev)
{
    //QTreeView::dragEnterEvent(ev);
    if (ev->mimeData()->hasFormat("text/plain")) {
        ev->setDropAction(Qt::CopyAction);
        ev->accept();
    }
}

void WatchWindow::dragMoveEvent(QDragMoveEvent *ev)
{
    //QTreeView::dragMoveEvent(ev);
    if (ev->mimeData()->hasFormat("text/plain")) {
        ev->setDropAction(Qt::CopyAction);
        ev->accept();
    }
}

void WatchWindow::dropEvent(QDropEvent *ev)
{
    if (ev->mimeData()->hasFormat("text/plain")) {
431
        watchExpression(ev->mimeData()->text());
432 433 434 435 436 437 438
        //ev->acceptProposedAction();
        ev->setDropAction(Qt::CopyAction);
        ev->accept();
    }
    //QTreeView::dropEvent(ev);
}

439 440 441 442
void WatchWindow::mouseDoubleClickEvent(QMouseEvent *ev)
{
    const QModelIndex idx = indexAt(ev->pos());
    if (!idx.isValid()) {
443 444
        // The "<Edit>" case.
        watchExpression(QString());
445 446 447 448 449
        return;
    }
    QTreeView::mouseDoubleClickEvent(ev);
}

450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
// Text for add watch action with truncated expression
static inline QString addWatchActionText(QString exp)
{
    if (exp.isEmpty())
        return WatchWindow::tr("Watch Expression");
    if (exp.size() > 30) {
        exp.truncate(30);
        exp.append(QLatin1String("..."));
    }
    return WatchWindow::tr("Watch Expression \"%1\"").arg(exp);
}

// Text for add watch action with truncated expression
static inline QString removeWatchActionText(QString exp)
{
    if (exp.isEmpty())
        return WatchWindow::tr("Remove Watch Expression");
    if (exp.size() > 30) {
        exp.truncate(30);
        exp.append(QLatin1String("..."));
    }
    return WatchWindow::tr("Remove Watch Expression \"%1\"").arg(exp);
}

474 475 476 477 478 479 480 481 482
static inline void copyToClipboard(const QString &clipboardText)
{
    QClipboard *clipboard = QApplication::clipboard();
#ifdef Q_WS_X11
    clipboard->setText(clipboardText, QClipboard::Selection);
#endif
    clipboard->setText(clipboardText, QClipboard::Clipboard);
}

con's avatar
con committed
483 484
void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
{
485 486 487
    DebuggerEngine *engine = currentEngine();
    WatchHandler *handler = engine->watchHandler();

488 489 490 491
    const QModelIndex idx = indexAt(ev->pos());
    const QModelIndex mi0 = idx.sibling(idx.row(), 0);
    const QModelIndex mi1 = idx.sibling(idx.row(), 1);
    const QModelIndex mi2 = idx.sibling(idx.row(), 2);
492 493 494
    const quint64 address = addressOf(mi0);
    const uint size = sizeOf(mi0);
    const quint64 pointerValue = pointerValueOf(mi0);
495 496
    const QString exp = mi0.data(LocalsExpressionRole).toString();
    const QString type = mi2.data().toString();
497

498
    const QStringList alternativeFormats =
499
        mi0.data(LocalsTypeFormatListRole).toStringList();
500
    const int typeFormat =
501
        mi0.data(LocalsTypeFormatRole).toInt();
502
    const int individualFormat =
503
        mi0.data(LocalsIndividualFormatRole).toInt();
504 505
    const int effectiveIndividualFormat =
        individualFormat == -1 ? typeFormat : individualFormat;
506
    const int unprintableBase = handler->unprintableBase();
507

508
    QMenu formatMenu;
509
    QList<QAction *> typeFormatActions;
510
    QList<QAction *> individualFormatActions;
511
    QAction *clearTypeFormatAction = 0;
512
    QAction *clearIndividualFormatAction = 0;
513 514 515
    QAction *showUnprintableUnicode = 0;
    QAction *showUnprintableOctal = 0;
    QAction *showUnprintableHexadecimal = 0;
516
    formatMenu.setTitle(tr("Change Display Format..."));
517 518 519 520 521 522 523 524 525 526 527 528 529 530
    showUnprintableUnicode =
        formatMenu.addAction(tr("Treat All Characters as Printable"));
    showUnprintableUnicode->setCheckable(true);
    showUnprintableUnicode->setChecked(unprintableBase == 0);
    showUnprintableOctal =
        formatMenu.addAction(tr("Show Unprintable Characters as Octal"));
    showUnprintableOctal->setCheckable(true);
    showUnprintableOctal->setChecked(unprintableBase == 8);
    showUnprintableHexadecimal =
        formatMenu.addAction(tr("Show Unprintable Characters as Hexadecimal"));
    showUnprintableHexadecimal->setCheckable(true);
    showUnprintableHexadecimal->setChecked(unprintableBase == 16);
    if (idx.isValid() /*&& !alternativeFormats.isEmpty() */) {
        const QString spacer = QLatin1String("     ");
531
        formatMenu.addSeparator();
532
        QAction *dummy = formatMenu.addAction(
533
            tr("Change Display for Object Named \"%1\":").arg(mi0.data().toString()));
534
        dummy->setEnabled(false);
535 536 537 538 539
        clearIndividualFormatAction
            = formatMenu.addAction(spacer + tr("Use Display Format Based on Type"));
        //clearIndividualFormatAction->setEnabled(individualFormat != -1);
        clearIndividualFormatAction->setCheckable(true);
        clearIndividualFormatAction->setChecked(effectiveIndividualFormat == -1);
540
        for (int i = 0; i != alternativeFormats.size(); ++i) {
541
            const QString format = spacer + alternativeFormats.at(i);
542 543
            QAction *act = new QAction(format, &formatMenu);
            act->setCheckable(true);
544
            if (i == effectiveIndividualFormat)
545 546
                act->setChecked(true);
            formatMenu.addAction(act);
547
            individualFormatActions.append(act);
hjk's avatar
hjk committed
548
        }
549 550
        formatMenu.addSeparator();
        dummy = formatMenu.addAction(
551
            tr("Change Display for Type \"%1\":").arg(type));
552
        dummy->setEnabled(false);
553 554 555 556 557
        clearTypeFormatAction = formatMenu.addAction(spacer + tr("Automatic"));
        //clearTypeFormatAction->setEnabled(typeFormat != -1);
        //clearTypeFormatAction->setEnabled(individualFormat != -1);
        clearTypeFormatAction->setCheckable(true);
        clearTypeFormatAction->setChecked(typeFormat == -1);
558
        for (int i = 0; i != alternativeFormats.size(); ++i) {
559
            const QString format = spacer + alternativeFormats.at(i);
560 561
            QAction *act = new QAction(format, &formatMenu);
            act->setCheckable(true);
562 563
            //act->setEnabled(individualFormat != -1);
            if (i == typeFormat)
564 565
                act->setChecked(true);
            formatMenu.addAction(act);
566
            typeFormatActions.append(act);
567
        }
568
    } else {
569 570 571
        QAction *dummy = formatMenu.addAction(
            tr("Change Display for Type or Item..."));
        dummy->setEnabled(false);
572 573
    }

hjk's avatar
hjk committed
574 575
    const bool actionsEnabled = engine->debuggerActionsEnabled();
    const unsigned engineCapabilities = engine->debuggerCapabilities();
576
    const bool canHandleWatches = engineCapabilities & AddWatcherCapability;
hjk's avatar
hjk committed
577
    const DebuggerState state = engine->state();
578
    const bool canInsertWatches = (state==InferiorStopOk) || ((state==InferiorRunOk) && engine->acceptsWatchesWhileRunning());
579

580
    QMenu menu;
581
    QAction *actInsertNewWatchItem = menu.addAction(tr("Insert New Watch Item"));
582
    actInsertNewWatchItem->setEnabled(canHandleWatches && canInsertWatches);
583
    QAction *actSelectWidgetToWatch = menu.addAction(tr("Select Widget to Watch"));
584
    actSelectWidgetToWatch->setEnabled(canHandleWatches && (engine->canWatchWidgets()));
585

586
    // Offer to open address pointed to or variable address.
587
    const bool createPointerActions = pointerValue && pointerValue != address;
588

589
    menu.addSeparator();
590

hjk's avatar
hjk committed
591 592
    QAction *actSetWatchpointAtVariableAddress = 0;
    QAction *actSetWatchpointAtPointerValue = 0;
593
    const bool canSetWatchpoint = engineCapabilities & WatchpointCapability;
594
    if (canSetWatchpoint && address) {
hjk's avatar
hjk committed
595
        actSetWatchpointAtVariableAddress =
596
            new QAction(tr("Add Watchpoint at Object's Address (0x%1)")
597
                .arg(address, 0, 16), &menu);
hjk's avatar
hjk committed
598
        actSetWatchpointAtVariableAddress->
599
            setChecked(mi0.data(LocalsIsWatchpointAtAddressRole).toBool());
600
        if (createPointerActions) {
hjk's avatar
hjk committed
601
            actSetWatchpointAtPointerValue =
602
                new QAction(tr("Add Watchpoint at Referenced Address (0x%1)")
603
                    .arg(pointerValue, 0, 16), &menu);
hjk's avatar
hjk committed
604 605
            actSetWatchpointAtPointerValue->setCheckable(true);
            actSetWatchpointAtPointerValue->
606
                setChecked(mi0.data(LocalsIsWatchpointAtPointerValueRole).toBool());
607
        }
608
    } else {
hjk's avatar
hjk committed
609
        actSetWatchpointAtVariableAddress =
hjk's avatar
hjk committed
610
            new QAction(tr("Add Watchpoint"), &menu);
hjk's avatar
hjk committed
611
        actSetWatchpointAtVariableAddress->setEnabled(false);
612
    }
hjk's avatar
hjk committed
613
    actSetWatchpointAtVariableAddress->setToolTip(
614
        tr("Setting a watchpoint on an address will cause the program "
hjk's avatar
hjk committed
615
           "to stop when the data at the address it modified."));
616

617
    QAction *actWatchExpression = new QAction(addWatchActionText(exp), &menu);
618
    actWatchExpression->setEnabled(canHandleWatches && !exp.isEmpty());
619

620
    // Can remove watch if engine can handle it or session engine.
621
    QAction *actRemoveWatchExpression = new QAction(removeWatchActionText(exp), &menu);
622 623
    actRemoveWatchExpression->setEnabled(
        (canHandleWatches || state == DebuggerNotReady) && !exp.isEmpty());
624
    QAction *actRemoveWatches = new QAction(tr("Remove All Watch Items"), &menu);
625
    actRemoveWatches->setEnabled(!WatchHandler::watcherNames().isEmpty());
626 627 628

    if (m_type == LocalsType)
        menu.addAction(actWatchExpression);
629
    else {
630
        menu.addAction(actRemoveWatchExpression);
631 632
        menu.addAction(actRemoveWatches);
    }
633

634 635 636 637 638
    QMenu memoryMenu;
    memoryMenu.setTitle(tr("Open Memory Editor..."));
    QAction *actOpenMemoryEditAtVariableAddress = new QAction(&memoryMenu);
    QAction *actOpenMemoryEditAtPointerValue = new QAction(&memoryMenu);
    QAction *actOpenMemoryEditor = new QAction(&memoryMenu);
639 640
    QAction *actOpenMemoryViewAtVariableAddress = new QAction(&memoryMenu);
    QAction *actOpenMemoryViewAtPointerValue = new QAction(&memoryMenu);
641 642 643 644 645 646
    if (engineCapabilities & ShowMemoryCapability) {
        actOpenMemoryEditor->setText(tr("Open Memory Editor..."));
        if (address) {
            actOpenMemoryEditAtVariableAddress->setText(
                tr("Open Memory Editor at Object's Address (0x%1)")
                    .arg(address, 0, 16));
647 648 649
            actOpenMemoryViewAtVariableAddress->setText(
                    tr("Open Memory View at Object's Address (0x%1)")
                        .arg(address, 0, 16));
650 651 652 653
        } else {
            actOpenMemoryEditAtVariableAddress->setText(
                tr("Open Memory Editor at Object's Address"));
            actOpenMemoryEditAtVariableAddress->setEnabled(false);
654 655 656
            actOpenMemoryViewAtVariableAddress->setText(
                    tr("Open Memory View at Object's Address"));
            actOpenMemoryViewAtVariableAddress->setEnabled(false);
657 658 659 660 661
        }
        if (createPointerActions) {
            actOpenMemoryEditAtPointerValue->setText(
                tr("Open Memory Editor at Referenced Address (0x%1)")
                    .arg(pointerValue, 0, 16));
662 663 664
            actOpenMemoryViewAtPointerValue->setText(
                tr("Open Memory View at Referenced Address (0x%1)")
                    .arg(pointerValue, 0, 16));
665 666 667 668
        } else {
            actOpenMemoryEditAtPointerValue->setText(
                tr("Open Memory Editor at Referenced Address"));
            actOpenMemoryEditAtPointerValue->setEnabled(false);
669 670 671
            actOpenMemoryViewAtPointerValue->setText(
                tr("Open Memory View at Referenced Address"));
            actOpenMemoryViewAtPointerValue->setEnabled(false);
672
        }
673 674
        memoryMenu.addAction(actOpenMemoryViewAtVariableAddress);
        memoryMenu.addAction(actOpenMemoryViewAtPointerValue);
675 676 677 678 679 680 681
        memoryMenu.addAction(actOpenMemoryEditAtVariableAddress);
        memoryMenu.addAction(actOpenMemoryEditAtPointerValue);
        memoryMenu.addAction(actOpenMemoryEditor);
    } else {
        memoryMenu.setEnabled(false);
    }

682
    QAction *actCopy = new QAction(tr("Copy Contents to Clipboard"), &menu);
683 684 685
    QAction *actCopyValue = new QAction(tr("Copy Value to Clipboard"), &menu);
    actCopyValue->setEnabled(idx.isValid());

686

687 688
    menu.addAction(actInsertNewWatchItem);
    menu.addAction(actSelectWidgetToWatch);
689
    menu.addMenu(&formatMenu);
690
    menu.addMenu(&memoryMenu);
hjk's avatar
hjk committed
691 692 693
    menu.addAction(actSetWatchpointAtVariableAddress);
    if (actSetWatchpointAtPointerValue)
        menu.addAction(actSetWatchpointAtPointerValue);
694 695
    menu.addAction(actCopy);
    menu.addAction(actCopyValue);
696
    menu.addSeparator();
697

hjk's avatar
hjk committed
698 699 700 701 702 703
    menu.addAction(debuggerCore()->action(UseDebuggingHelpers));
    menu.addAction(debuggerCore()->action(UseToolTipsInLocalsView));
    menu.addAction(debuggerCore()->action(AutoDerefPointers));
    menu.addAction(debuggerCore()->action(ShowStdNamespace));
    menu.addAction(debuggerCore()->action(ShowQtNamespace));
    menu.addAction(debuggerCore()->action(SortStructMembers));
704

705
    QAction *actAdjustColumnWidths =
706
        menu.addAction(tr("Adjust Column Widths to Contents"));
707
    menu.addAction(debuggerCore()->action(AlwaysAdjustLocalsColumnWidths));
708
    menu.addSeparator();
709

710 711 712
    QAction *actClearCodeModelSnapshot
        = new QAction(tr("Refresh Code Model Snapshot"), &menu);
    actClearCodeModelSnapshot->setEnabled(actionsEnabled
hjk's avatar
hjk committed
713
        && debuggerCore()->action(UseCodeModel)->isChecked());
714 715 716 717 718
    menu.addAction(actClearCodeModelSnapshot);
    QAction *actShowInEditor
        = new QAction(tr("Show View Contents in Editor"), &menu);
    actShowInEditor->setEnabled(actionsEnabled);
    menu.addAction(actShowInEditor);
hjk's avatar
hjk committed
719
    menu.addAction(debuggerCore()->action(SettingsDialog));
con's avatar
con committed
720

721 722 723 724
    QAction *actCloseEditorToolTips = new QAction(tr("Close Editor Tooltips"), &menu);
    actCloseEditorToolTips->setEnabled(DebuggerToolTipManager::instance()->hasToolTips());
    menu.addAction(actCloseEditorToolTips);

con's avatar
con committed
725
    QAction *act = menu.exec(ev->globalPos());
726 727
    if (act == 0)
        return;
con's avatar
con committed
728

729
    if (act == actAdjustColumnWidths) {
con's avatar
con committed
730
        resizeColumnsToContents();
731
    } else if (act == actInsertNewWatchItem) {
732 733 734 735 736 737 738
        bool ok;
        QString newExp = QInputDialog::getText(this, tr("Enter watch expression"),
                                   tr("Expression:"), QLineEdit::Normal,
                                   QString(), &ok);
        if (ok && !newExp.isEmpty()) {
            watchExpression(newExp);
        }
739
    } else if (act == actOpenMemoryEditAtVariableAddress) {
740
        addVariableMemoryView(currentEngine(), false, mi0, false, ev->globalPos(), this);
741
    } else if (act == actOpenMemoryEditAtPointerValue) {
742
        addVariableMemoryView(currentEngine(), false, mi0, true, ev->globalPos(), this);
743
    } else if (act == actOpenMemoryEditor) {
744
        AddressDialog dialog;
745
        if (dialog.exec() == QDialog::Accepted)
746
            currentEngine()->openMemoryView(dialog.address(), false, MemoryMarkupList(), QPoint());
747
    } else if (act == actOpenMemoryViewAtVariableAddress) {
748
        addVariableMemoryView(currentEngine(), true, mi0, false, ev->globalPos(), this);
749
    } else if (act == actOpenMemoryViewAtPointerValue) {
750
        addVariableMemoryView(currentEngine(), true, mi0, true, ev->globalPos(), this);
hjk's avatar
hjk committed
751
    } else if (act == actSetWatchpointAtVariableAddress) {
752
        setWatchpoint(address, size);
hjk's avatar
hjk committed
753
    } else if (act == actSetWatchpointAtPointerValue) {
754
        setWatchpoint(pointerValue, 1);
755
    } else if (act == actSelectWidgetToWatch) {
756 757
        grabMouse(Qt::CrossCursor);
        m_grabbing = true;
758 759 760 761
    } else if (act == actWatchExpression) {
        watchExpression(exp);
    } else if (act == actRemoveWatchExpression) {
        removeWatchExpression(exp);
762 763 764 765
    } else if (act == actCopy) {
        copyToClipboard(DebuggerTreeViewToolTipWidget::treeModelClipboardContents(model()));
    } else if (act == actCopyValue) {
        copyToClipboard(mi1.data().toString());
766 767
    } else if (act == actRemoveWatches) {
        currentEngine()->watchHandler()->clearWatches();
768
    } else if (act == actClearCodeModelSnapshot) {
769
        debuggerCore()->clearCppCodeModelSnapshot();
770
    } else if (act == clearTypeFormatAction) {
771
        setModelData(LocalsTypeFormatRole, -1, mi1);
772
    } else if (act == clearIndividualFormatAction) {
773
        setModelData(LocalsIndividualFormatRole, -1, mi1);
774
    } else if (act == actShowInEditor) {
775
        QString contents = handler->editorContents();
776
        debuggerCore()->openTextEditor(tr("Locals & Watchers"), contents);
777 778 779 780 781 782
    } else if (act == showUnprintableUnicode) {
        handler->setUnprintableBase(0);
    } else if (act == showUnprintableOctal) {
        handler->setUnprintableBase(8);
    } else if (act == showUnprintableHexadecimal) {
        handler->setUnprintableBase(16);
783 784
    } else if (act == actCloseEditorToolTips) {
        DebuggerToolTipManager::instance()->closeAllToolTips();
785
    } else {
hjk's avatar
hjk committed
786
        for (int i = 0; i != typeFormatActions.size(); ++i) {
787
            if (act == typeFormatActions.at(i))
788
                setModelData(LocalsTypeFormatRole, i, mi1);
hjk's avatar
hjk committed
789 790 791
        }
        for (int i = 0; i != individualFormatActions.size(); ++i) {
            if (act == individualFormatActions.at(i))
792
                setModelData(LocalsIndividualFormatRole, i, mi1);
793
        }
794
    }
con's avatar
con committed
795 796 797 798 799 800 801 802 803 804 805 806
}

void WatchWindow::resizeColumnsToContents()
{
    resizeColumnToContents(0);
    resizeColumnToContents(1);
}

void WatchWindow::setAlwaysResizeColumnsToContents(bool on)
{
    if (!header())
        return;
807
    QHeaderView::ResizeMode mode = on
con's avatar
con committed
808 809 810 811 812
        ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
    header()->setResizeMode(0, mode);
    header()->setResizeMode(1, mode);
}

813 814 815 816 817 818
bool WatchWindow::event(QEvent *ev)
{
    if (m_grabbing && ev->type() == QEvent::MouseButtonPress) {
        QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
        m_grabbing = false;
        releaseMouse();
819
        currentEngine()->watchPoint(mapToGlobal(mev->pos()));
820 821 822 823
    }
    return QTreeView::event(ev);
}

con's avatar
con committed
824 825
void WatchWindow::editItem(const QModelIndex &idx)
{
826
    Q_UNUSED(idx) // FIXME
con's avatar
con committed
827 828
}

829
void WatchWindow::setModel(QAbstractItemModel *model)
con's avatar
con committed
830
{
831
    QTreeView::setModel(model);
con's avatar
con committed
832 833

    setRootIsDecorated(true);
834 835 836 837 838 839 840
    if (header()) {
        setAlwaysResizeColumnsToContents(
            debuggerCore()->boolSetting(AlwaysAdjustLocalsColumnWidths));
        header()->setDefaultAlignment(Qt::AlignLeft);
        if (m_type != LocalsType)
            header()->hide();
    }
841

842