watchwindow.cpp 18.8 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.
102
103
            model->setData(index, exp, RequestRemoveWatchExpressionRole);
            model->setData(index, value, RequestWatchExpressionRole);
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();
164
        removeWatchExpression(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
        QModelIndex idx = currentIndex();
        QModelIndex idx1 = idx.sibling(idx.row(), 0);
        QString exp = model()->data(idx1).toString();
171
        watchExpression(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
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")) {
197
        watchExpression(ev->mimeData()->text());
198
199
200
201
202
203
204
        //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
317
            new QAction(tr("Break on Changes at Object's Address (0x%1)")
                .arg(address, 0, 16), &menu);
318
        actSetWatchPointAtVariableAddress->setCheckable(true);
319
320
        actSetWatchPointAtVariableAddress->
            setChecked(mi0.data(LocalsIsWatchpointAtAddressRole).toBool());
321
        if (createPointerActions) {
322
            actSetWatchPointAtPointerValue =
323
324
                new QAction(tr("Break on Changes at Referenced Address (0x%1)")
                    .arg(pointerValue, 0, 16), &menu);
325
            actSetWatchPointAtPointerValue->setCheckable(true);
326
327
            actSetWatchPointAtPointerValue->
                setChecked(mi0.data(LocalsIsWatchpointAtPointerValueRole).toBool());
328
        }
329
    } else {
330
        actSetWatchPointAtVariableAddress =
331
            new QAction(tr("Break on Changing Contents"), &menu);
332
        actSetWatchPointAtVariableAddress->setEnabled(false);
333
334
    }

335
336
337
338
339
340
341
342
343
344
345
    QAction *actWatchExpression =
        new QAction(tr("Watch Expression \"%1\"").arg(exp), &menu);
    actWatchExpression->setEnabled(canHandleWatches);

    QAction *actRemoveWatchExpression =
        new QAction(tr("Remove Watch Expression \"%1\"").arg(exp), &menu);

    if (m_type == LocalsType)
        menu.addAction(actWatchExpression);
    else
        menu.addAction(actRemoveWatchExpression);
346

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

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

371
    menu.addAction(theDebuggerAction(AutoDerefPointers));
372
373
    menu.addAction(theDebuggerAction(ShowStdNamespace));
    menu.addAction(theDebuggerAction(ShowQtNamespace));
374

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

382
    menu.addSeparator();
383
    menu.addAction(theDebuggerAction(SettingsDialog));
con's avatar
con committed
384
385

    QAction *act = menu.exec(ev->globalPos());
386
387
    if (act == 0)
        return;
con's avatar
con committed
388

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

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

void WatchWindow::setAlwaysResizeColumnsToContents(bool on)
{
    if (!header())
        return;
    m_alwaysResizeColumnsToContents = on;
443
    QHeaderView::ResizeMode mode = on
con's avatar
con committed
444
445
446
447
448
        ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
    header()->setResizeMode(0, mode);
    header()->setResizeMode(1, mode);
}

449
450
451
452
453
454
bool WatchWindow::event(QEvent *ev)
{
    if (m_grabbing && ev->type() == QEvent::MouseButtonPress) {
        QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
        m_grabbing = false;
        releaseMouse();
455
        setModelData(RequestWatchPointRole, mapToGlobal(mev->pos()));
456
457
458
459
    }
    return QTreeView::event(ev);
}

con's avatar
con committed
460
461
void WatchWindow::editItem(const QModelIndex &idx)
{
462
    Q_UNUSED(idx) // FIXME
con's avatar
con committed
463
464
}

465
void WatchWindow::setModel(QAbstractItemModel *model)
con's avatar
con committed
466
{
467
    QTreeView::setModel(model);
con's avatar
con committed
468
469
470
471
472
473

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

475
476
477
478
479
480
481
482
483
484
    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);
485
486
487
488
489
}

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

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

507
508
509
510
511
512
513
514
515
516
void WatchWindow::watchExpression(const QString &exp)
{
    setModelData(RequestWatchExpressionRole, exp);
}

void WatchWindow::removeWatchExpression(const QString &exp)
{
    setModelData(RequestRemoveWatchExpressionRole, exp);
}

517
518
519
520
521
522
523
524
525
526
527
528
529
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);
}