watchwindow.cpp 18.5 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11
12
13
14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18
19
20
21
22
23
** Alternatively, 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.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
31
#include "watchwindow.h"

32
#include "debuggeractions.h"
33
34
#include "debuggerconstants.h"
#include "debuggerengine.h"
35
#include "debuggerdialogs.h"
36
#include "watchhandler.h"
37

38
#include <utils/qtcassert.h>
39
#include <utils/savedaction.h>
40

con's avatar
con committed
41
42
43
44
45
#include <QtCore/QDebug>
#include <QtCore/QTimer>

#include <QtGui/QAction>
#include <QtGui/QContextMenuEvent>
46
47
#include <QtGui/QDialog>
#include <QtGui/QHBoxLayout>
con's avatar
con committed
48
#include <QtGui/QHeaderView>
49
#include <QtGui/QItemDelegate>
50
#include <QtGui/QLabel>
con's avatar
con committed
51
52
53
54
#include <QtGui/QLineEdit>
#include <QtGui/QMenu>
#include <QtGui/QResizeEvent>

55
using namespace Debugger;
con's avatar
con committed
56
57
using namespace Debugger::Internal;

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/////////////////////////////////////////////////////////////////////
//
// WatchDelegate
//
/////////////////////////////////////////////////////////////////////

class WatchDelegate : public QItemDelegate
{
public:
    WatchDelegate(QObject *parent) : QItemDelegate(parent) {}

    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
        const QModelIndex &) const
    {
        return new QLineEdit(parent);
    }

    void setEditorData(QWidget *editor, const QModelIndex &index) const
    {
        QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor);
        QTC_ASSERT(lineEdit, return);
79
        if (index.column() == 1)
80
            lineEdit->setText(index.data(Qt::DisplayRole).toString());
81
        else
82
            lineEdit->setText(index.data(LocalsExpressionRole).toString());
83
84
    }

85
    void setModelData(QWidget *editor, QAbstractItemModel *model,
86
87
        const QModelIndex &index) const
    {
88
        //qDebug() << "SET MODEL DATA";
89
90
        QLineEdit *lineEdit = qobject_cast<QLineEdit*>(editor);
        QTC_ASSERT(lineEdit, return);
91
92
        const QString value = lineEdit->text();
        const QString exp = index.data(LocalsExpressionRole).toString();
93
        model->setData(index, value, Qt::EditRole);
94
        if (index.column() == 1) {
hjk's avatar
hjk committed
95
            // The value column.
96
            model->setData(index, QString(exp + '=' + value), RequestAssignValueRole);
hjk's avatar
hjk committed
97
        } else if (index.column() == 2) {
hjk's avatar
hjk committed
98
            // The type column.
99
            model->setData(index, QString(exp + '=' + value), RequestAssignTypeRole);
100
        } else if (index.column() == 0) {
hjk's avatar
hjk committed
101
            // The watcher name column.
hjk's avatar
hjk committed
102
            theDebuggerAction(RemoveWatchExpression)->trigger(exp);
hjk's avatar
hjk committed
103
            theDebuggerAction(WatchExpression)->trigger(value);
104
105
106
107
108
109
110
111
112
113
114
        }
    }

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


con's avatar
con committed
115
116
117
118
119
120
/////////////////////////////////////////////////////////////////////
//
// WatchWindow
//
/////////////////////////////////////////////////////////////////////

121
122
123
124
WatchWindow::WatchWindow(Type type, QWidget *parent)
  : QTreeView(parent),
    m_alwaysResizeColumnsToContents(true),
    m_type(type)
con's avatar
con committed
125
{
126
127
    m_grabbing = false;

128
    QAction *act = theDebuggerAction(UseAlternatingRowColors);
129
    setFrameStyle(QFrame::NoFrame);
130
    setAttribute(Qt::WA_MacShowFocusRect, false);
con's avatar
con committed
131
    setWindowTitle(tr("Locals and Watchers"));
132
    setAlternatingRowColors(act->isChecked());
con's avatar
con committed
133
134
    setIndentation(indentation() * 9/10);
    setUniformRowHeights(true);
135
    setItemDelegate(new WatchDelegate(this));
136
137
138
    setDragEnabled(true);
    setAcceptDrops(true);
    setDropIndicatorShown(true);
con's avatar
con committed
139

140
141
    connect(act, SIGNAL(toggled(bool)),
        this, SLOT(setAlternatingRowColorsHelper(bool)));
142
143
144
145
    connect(this, SIGNAL(expanded(QModelIndex)),
        this, SLOT(expandNode(QModelIndex)));
    connect(this, SIGNAL(collapsed(QModelIndex)),
        this, SLOT(collapseNode(QModelIndex)));
146
147
148
149
}

void WatchWindow::expandNode(const QModelIndex &idx)
{
150
    setModelData(LocalsExpandedRole, true, idx);
151
152
153
154
}

void WatchWindow::collapseNode(const QModelIndex &idx)
{
155
    setModelData(LocalsExpandedRole, false, idx);
con's avatar
con committed
156
157
}

158
159
void WatchWindow::keyPressEvent(QKeyEvent *ev)
{
hjk's avatar
hjk committed
160
    if (ev->key() == Qt::Key_Delete && m_type == WatchersType) {
161
162
        QModelIndex idx = currentIndex();
        QModelIndex idx1 = idx.sibling(idx.row(), 0);
163
        QString exp = idx1.data().toString();
hjk's avatar
hjk committed
164
        theDebuggerAction(RemoveWatchExpression)->trigger(exp);
165
166
167
    } else if (ev->key() == Qt::Key_Return
            && ev->modifiers() == Qt::ControlModifier
            && m_type == LocalsType) {
hjk's avatar
hjk committed
168
169
170
171
        QModelIndex idx = currentIndex();
        QModelIndex idx1 = idx.sibling(idx.row(), 0);
        QString exp = model()->data(idx1).toString();
        theDebuggerAction(WatchExpression)->trigger(exp);
172
173
174
175
    }
    QTreeView::keyPressEvent(ev);
}

176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
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")) {
        theDebuggerAction(WatchExpression)->trigger(ev->mimeData()->text());
        //ev->acceptProposedAction();
        ev->setDropAction(Qt::CopyAction);
        ev->accept();
    }
    //QTreeView::dropEvent(ev);
}

con's avatar
con committed
205
206
void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
{
207
208
209
210
    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);
211
212
213
214
    const quint64 address = mi0.data(LocalsAddressRole).toULongLong();
    const quint64 pointerValue = mi0.data(LocalsPointerValueRole).toULongLong();
    const QString exp = mi0.data(LocalsExpressionRole).toString();
    const QString type = mi2.data().toString();
215

216
    const QStringList alternativeFormats =
217
        mi0.data(LocalsTypeFormatListRole).toStringList();
218
    const int typeFormat =
219
        mi0.data(LocalsTypeFormatRole).toInt();
220
    const int individualFormat =
221
        mi0.data(LocalsIndividualFormatRole).toInt();
222
223
    const int effectiveIndividualFormat =
        individualFormat == -1 ? typeFormat : individualFormat;
224

225
    QMenu typeFormatMenu;
226
    QList<QAction *> typeFormatActions;
227
    QAction *clearTypeFormatAction = 0;
228
    if (idx.isValid()) {
229
        typeFormatMenu.setTitle(
230
            tr("Change Format for Type \"%1\"").arg(type));
231
232
        if (alternativeFormats.isEmpty()) {
            typeFormatMenu.setEnabled(false);
233
        } else {
234
235
236
237
238
            clearTypeFormatAction = typeFormatMenu.addAction(tr("Clear"));
            clearTypeFormatAction->setEnabled(typeFormat != -1);
            clearTypeFormatAction->setCheckable(true);
            clearTypeFormatAction->setChecked(typeFormat == -1);
            typeFormatMenu.addSeparator();
239
240
241
242
243
244
245
246
            for (int i = 0; i != alternativeFormats.size(); ++i) {
                const QString format = alternativeFormats.at(i);
                QAction *act = new QAction(format, &typeFormatMenu);
                act->setCheckable(true);
                if (i == typeFormat)
                    act->setChecked(true);
                typeFormatMenu.addAction(act);
                typeFormatActions.append(act);
hjk's avatar
hjk committed
247
248
249
250
251
252
253
254
255
256
            }
        }
    } else {
        typeFormatMenu.setTitle(tr("Change Format for Type"));
        typeFormatMenu.setEnabled(false);
    }

    QMenu individualFormatMenu;
    QList<QAction *> individualFormatActions;
    QAction *clearIndividualFormatAction = 0;
257
    if (idx.isValid() && address) {
hjk's avatar
hjk committed
258
        individualFormatMenu.setTitle(
259
            tr("Change Format for Object at 0x%1").arg(address, 0, 16));
hjk's avatar
hjk committed
260
261
262
263
264
        if (alternativeFormats.isEmpty()) {
            individualFormatMenu.setEnabled(false);
        } else {
            clearIndividualFormatAction = individualFormatMenu.addAction(tr("Clear"));
            clearIndividualFormatAction->setEnabled(individualFormat != -1);
265
266
            clearIndividualFormatAction->setCheckable(true);
            clearIndividualFormatAction->setChecked(individualFormat == -1);
hjk's avatar
hjk committed
267
268
269
270
            individualFormatMenu.addSeparator();
            for (int i = 0; i != alternativeFormats.size(); ++i) {
                const QString format = alternativeFormats.at(i);
                QAction *act = new QAction(format, &individualFormatMenu);
271
272
273
274
275
276
                act->setCheckable(true);
                if (i == effectiveIndividualFormat)
                    act->setChecked(true);
                individualFormatMenu.addAction(act);
                individualFormatActions.append(act);
            }
277
        }
278
    } else {
279
        individualFormatMenu.setTitle(tr("Change Format for Object"));
280
        individualFormatMenu.setEnabled(false);
281
282
    }

283
284
    const bool actionsEnabled = modelData(EngineActionsEnabledRole).toBool();
    const unsigned engineCapabilities = modelData(EngineCapabilitiesRole).toUInt();
285
286
    const bool canHandleWatches = actionsEnabled && (engineCapabilities & AddWatcherCapability);

287
    QMenu menu;
288
    QAction *actInsertNewWatchItem = menu.addAction(tr("Insert New Watch Item"));
289
    actInsertNewWatchItem->setEnabled(canHandleWatches);
290
    QAction *actSelectWidgetToWatch = menu.addAction(tr("Select Widget to Watch"));
291
    actSelectWidgetToWatch->setEnabled(canHandleWatches);
292

293
294
295
    QAction *actOpenMemoryEditAtVariableAddress = 0;
    QAction *actOpenMemoryEditAtPointerValue = 0;
    QAction *actOpenMemoryEditor =
296
        new QAction(tr("Open Memory Editor..."), &menu);
297
    const bool canShowMemory = engineCapabilities & ShowMemoryCapability;
298
299
300
    actOpenMemoryEditor->setEnabled(actionsEnabled && canShowMemory);

    // Offer to open address pointed to or variable address.
301
    const bool createPointerActions = pointerValue && pointerValue != address;
302
303
304

    if (canShowMemory && address)
        actOpenMemoryEditAtVariableAddress =
305
            new QAction(tr("Open Memory Editor at Object's Address (0x%1)").arg(address, 0, 16), &menu);
306
307
    if (createPointerActions)
        actOpenMemoryEditAtPointerValue =
308
            new QAction(tr("Open Memory Editor at Referenced Address (0x%1)").arg(pointerValue, 0, 16), &menu);
309
    menu.addSeparator();
310

311
    QAction *actSetWatchPointAtVariableAddress = 0;
312
    QAction *actSetWatchPointAtPointerValue = 0;
313
    const bool canSetWatchpoint = engineCapabilities & WatchpointCapability;
314
315
    if (canSetWatchpoint && address) {
        actSetWatchPointAtVariableAddress =
316
            new QAction(tr("Break on Changes at Object's Address (0x%1)").arg(address, 0, 16), &menu);
317
        actSetWatchPointAtVariableAddress->setCheckable(true);
318
319
        actSetWatchPointAtVariableAddress->
            setChecked(mi0.data(LocalsIsWatchpointAtAddressRole).toBool());
320
        if (createPointerActions) {
321
            actSetWatchPointAtPointerValue =
322
                new QAction(tr("Break on Changes at Referenced Address (0x%1)").arg(pointerValue, 0, 16), &menu);
323
            actSetWatchPointAtPointerValue->setCheckable(true);
324
325
            actSetWatchPointAtPointerValue->
                setChecked(mi0.data(LocalsIsWatchpointAtPointerValueRole).toBool());
326
        }
327
    } else {
328
        actSetWatchPointAtVariableAddress =
329
            new QAction(tr("Break on Changing Contents"), &menu);
330
        actSetWatchPointAtVariableAddress->setEnabled(false);
331
332
    }

333
    QAction *actWatchOrRemove;
334
    if (m_type == LocalsType) {
335
        actWatchOrRemove = theDebuggerAction(WatchExpression)->updatedAction(exp);
336
        actWatchOrRemove->setEnabled(canHandleWatches);
337
338
339
340
341
342
    } else {
        actWatchOrRemove = theDebuggerAction(RemoveWatchExpression)->updatedAction(exp);
        // Also for the case where the user cleared the expression.
        actWatchOrRemove->setEnabled(true);
    }
    menu.addAction(actWatchOrRemove);
343

344
345
    menu.addAction(actInsertNewWatchItem);
    menu.addAction(actSelectWidgetToWatch);
346
347
    menu.addMenu(&typeFormatMenu);
    menu.addMenu(&individualFormatMenu);
348
349
350
351
352
353
354
355
    if (actOpenMemoryEditAtVariableAddress)
        menu.addAction(actOpenMemoryEditAtVariableAddress);
    if (actOpenMemoryEditAtPointerValue)
        menu.addAction(actOpenMemoryEditAtPointerValue);
    menu.addAction(actOpenMemoryEditor);
    menu.addAction(actSetWatchPointAtVariableAddress);
    if (actSetWatchPointAtPointerValue)
        menu.addAction(actSetWatchPointAtPointerValue);
356
    menu.addSeparator();
357

358
359
    menu.addAction(theDebuggerAction(RecheckDebuggingHelpers));
    menu.addAction(theDebuggerAction(UseDebuggingHelpers));
360
361
362
363
    QAction *actClearCodeModelSnapshot
        = new QAction(tr("Refresh Code Model Snapshot"), &menu);
    actClearCodeModelSnapshot->setEnabled(actionsEnabled
        && theDebuggerAction(UseCodeModel)->isChecked());
364
    menu.addAction(actClearCodeModelSnapshot);
365
    menu.addSeparator();
366
    menu.addAction(theDebuggerAction(UseToolTipsInLocalsView));
367

368
    menu.addAction(theDebuggerAction(AutoDerefPointers));
369
370
    menu.addAction(theDebuggerAction(ShowStdNamespace));
    menu.addAction(theDebuggerAction(ShowQtNamespace));
371

372
    QAction *actAdjustColumnWidths =
373
        menu.addAction(tr("Adjust Column Widths to Contents"));
374
    QAction *actAlwaysAdjustColumnWidth =
375
        menu.addAction(tr("Always Adjust Column Widths to Contents"));
376
377
378
    actAlwaysAdjustColumnWidth->setCheckable(true);
    actAlwaysAdjustColumnWidth->setChecked(m_alwaysResizeColumnsToContents);

379
    menu.addSeparator();
380
    menu.addAction(theDebuggerAction(SettingsDialog));
con's avatar
con committed
381
382

    QAction *act = menu.exec(ev->globalPos());
383
384
    if (act == 0)
        return;
con's avatar
con committed
385

386
    if (act == actAdjustColumnWidths) {
con's avatar
con committed
387
        resizeColumnsToContents();
388
    } else if (act == actAlwaysAdjustColumnWidth) {
con's avatar
con committed
389
        setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
390
    } else if (act == actInsertNewWatchItem) {
391
392
        theDebuggerAction(WatchExpression)
            ->trigger(WatchHandler::watcherEditPlaceHolder());
393
    } else if (act == actOpenMemoryEditAtVariableAddress) {
394
        setModelData(RequestShowMemoryRole, address);
395
    } else if (act == actOpenMemoryEditAtPointerValue) {
396
        setModelData(RequestShowMemoryRole, pointerValue);
397
    } else if (act == actOpenMemoryEditor) {
398
        AddressDialog dialog;
399
400
        if (dialog.exec() == QDialog::Accepted)
            setModelData(RequestShowMemoryRole, dialog.address());
401
    } else if (act == actSetWatchPointAtVariableAddress) {
402
        setModelData(RequestToggleWatchRole, address);
403
    } else if (act == actSetWatchPointAtPointerValue) {
404
        setModelData(RequestToggleWatchRole, pointerValue);
405
    } else if (act == actSelectWidgetToWatch) {
406
407
        grabMouse(Qt::CrossCursor);
        m_grabbing = true;
408
    } else if (act == actClearCodeModelSnapshot) {
409
        setModelData(RequestClearCppCodeModelSnapshotRole);
410
    } else if (act == clearTypeFormatAction) {
411
        setModelData(LocalsTypeFormatRole, -1, mi1);
412
    } else if (act == clearIndividualFormatAction) {
413
        setModelData(LocalsIndividualFormatRole, -1, mi1);
414
    } else {
hjk's avatar
hjk committed
415
        for (int i = 0; i != typeFormatActions.size(); ++i) {
416
            if (act == typeFormatActions.at(i))
417
                setModelData(LocalsTypeFormatRole, 1, mi1);
hjk's avatar
hjk committed
418
419
420
        }
        for (int i = 0; i != individualFormatActions.size(); ++i) {
            if (act == individualFormatActions.at(i))
421
                setModelData(LocalsIndividualFormatRole, 1, mi1);
422
        }
423
    }
con's avatar
con committed
424
425
426
427
428
429
430
431
432
433
434
435
436
}

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

void WatchWindow::setAlwaysResizeColumnsToContents(bool on)
{
    if (!header())
        return;
    m_alwaysResizeColumnsToContents = on;
437
    QHeaderView::ResizeMode mode = on
con's avatar
con committed
438
439
440
441
442
        ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
    header()->setResizeMode(0, mode);
    header()->setResizeMode(1, mode);
}

443
444
445
446
447
448
449
450
451
452
453
bool WatchWindow::event(QEvent *ev)
{
    if (m_grabbing && ev->type() == QEvent::MouseButtonPress) {
        QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
        m_grabbing = false;
        releaseMouse();
        theDebuggerAction(WatchPoint)->trigger(mapToGlobal(mev->pos()));
    }
    return QTreeView::event(ev);
}

con's avatar
con committed
454
455
void WatchWindow::editItem(const QModelIndex &idx)
{
456
    Q_UNUSED(idx) // FIXME
con's avatar
con committed
457
458
}

459
void WatchWindow::setModel(QAbstractItemModel *model)
con's avatar
con committed
460
{
461
    QTreeView::setModel(model);
con's avatar
con committed
462
463
464
465
466
467

    setRootIsDecorated(true);
    header()->setDefaultAlignment(Qt::AlignLeft);
    header()->setResizeMode(QHeaderView::ResizeToContents);
    if (m_type != LocalsType)
        header()->hide();
468

469
470
471
472
473
474
475
476
477
478
    connect(model, SIGNAL(layoutChanged()),
        this, SLOT(resetHelper()));
    connect(model, SIGNAL(enableUpdates(bool)),
        this, SLOT(setUpdatesEnabled(bool)));
}

void WatchWindow::setUpdatesEnabled(bool enable)
{
    //qDebug() << "ENABLING UPDATES: " << enable;
    QTreeView::setUpdatesEnabled(enable);
479
480
481
482
483
}

void WatchWindow::resetHelper()
{
    resetHelper(model()->index(0, 0));
con's avatar
con committed
484
485
}

486
void WatchWindow::resetHelper(const QModelIndex &idx)
con's avatar
con committed
487
{
488
    if (idx.data(LocalsExpandedRole).toBool()) {
489
        //qDebug() << "EXPANDING " << model()->data(idx, INameRole);
con's avatar
con committed
490
491
492
        expand(idx);
        for (int i = 0, n = model()->rowCount(idx); i != n; ++i) {
            QModelIndex idx1 = model()->index(i, 0, idx);
493
            resetHelper(idx1);
con's avatar
con committed
494
        }
495
496
497
    } else {
        //qDebug() << "COLLAPSING " << model()->data(idx, INameRole);
        collapse(idx);
con's avatar
con committed
498
499
    }
}
500
501
502
503
504
505
506
507
508
509
510
511
512
513

void WatchWindow::setModelData
    (int role, const QVariant &value, const QModelIndex &index)
{
    QTC_ASSERT(model(), return);
    model()->setData(index, value, role);
}

QVariant WatchWindow::modelData(int role, const QModelIndex &index)
{
    QTC_ASSERT(model(), return QVariant());
    return model()->data(index, role);
}