watchwindow.cpp 16.4 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
#include "watchwindow.h"
31
#include "watchhandler.h"
con's avatar
con committed
32

33
#include "breakpoint.h"
hjk's avatar
hjk committed
34
#include "breakhandler.h"
35
#include "debuggeractions.h"
36
#include "debuggeragents.h"
37
#include "debuggerdialogs.h"
38
#include "debuggermanager.h"
39
#include "idebuggerengine.h"
40

41
#include <utils/qtcassert.h>
42
#include <utils/savedaction.h>
43

con's avatar
con committed
44
45
46
47
48
#include <QtCore/QDebug>
#include <QtCore/QTimer>

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

58
using namespace Debugger;
con's avatar
con committed
59
60
using namespace Debugger::Internal;

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/////////////////////////////////////////////////////////////////////
//
// 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);
82
        if (index.column() == 1)
83
84
85
            lineEdit->setText(index.model()->data(index, Qt::DisplayRole).toString());
        else
            lineEdit->setText(index.model()->data(index, ExpressionRole).toString());
86
87
    }

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

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


con's avatar
con committed
118
119
120
121
122
123
/////////////////////////////////////////////////////////////////////
//
// WatchWindow
//
/////////////////////////////////////////////////////////////////////

124
125
126
WatchWindow::WatchWindow(Type type, DebuggerManager *manager, QWidget *parent)
    : QTreeView(parent), m_alwaysResizeColumnsToContents(true), m_type(type),
        m_manager(manager)
con's avatar
con committed
127
{
128
129
    m_grabbing = false;

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

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

void WatchWindow::expandNode(const QModelIndex &idx)
{
hjk's avatar
hjk committed
152
    model()->setData(idx, true, ExpandedRole);
153
154
155
156
}

void WatchWindow::collapseNode(const QModelIndex &idx)
{
hjk's avatar
hjk committed
157
    model()->setData(idx, false, ExpandedRole);
con's avatar
con committed
158
159
}

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

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
205
206
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
207
208
void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
{
209
210
211
212
213
214
215
    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);
    const QString addr = model()->data(mi0, AddressRole).toString();
    const QString exp = model()->data(mi0, ExpressionRole).toString();
    const QString type = model()->data(mi2).toString();
216

217
    const QStringList alternativeFormats =
218
        model()->data(mi0, TypeFormatListRole).toStringList();
219
220
221
222
223
224
    const int typeFormat =
        qMax(int(DecimalFormat), model()->data(mi0, TypeFormatRole).toInt());
    const int individualFormat =
        model()->data(mi0, IndividualFormatRole).toInt();
    const int effectiveIndividualFormat =
        individualFormat == -1 ? typeFormat : individualFormat;
225

226
    QMenu typeFormatMenu;
227
    QList<QAction *> typeFormatActions;
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
234
235
236
237
238
239
240
241
        } else {
            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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
            }
        }
    } else {
        typeFormatMenu.setTitle(tr("Change Format for Type"));
        typeFormatMenu.setEnabled(false);
    }

    QMenu individualFormatMenu;
    QList<QAction *> individualFormatActions;
    QAction *clearIndividualFormatAction = 0;
    if (idx.isValid() && !addr.isEmpty()) {
        individualFormatMenu.setTitle(
            tr("Change Format for Object at %1").arg(addr));
        if (alternativeFormats.isEmpty()) {
            individualFormatMenu.setEnabled(false);
        } else {
            clearIndividualFormatAction = individualFormatMenu.addAction(tr("Clear"));
            clearIndividualFormatAction->setEnabled(individualFormat != -1);
            individualFormatMenu.addSeparator();
            for (int i = 0; i != alternativeFormats.size(); ++i) {
                const QString format = alternativeFormats.at(i);
                QAction *act = new QAction(format, &individualFormatMenu);
264
265
266
267
268
269
                act->setCheckable(true);
                if (i == effectiveIndividualFormat)
                    act->setChecked(true);
                individualFormatMenu.addAction(act);
                individualFormatActions.append(act);
            }
270
        }
271
    } else {
272
        individualFormatMenu.setTitle(tr("Change Format for Object"));
273
        individualFormatMenu.setEnabled(false);
274
275
    }

con's avatar
con committed
276
    QMenu menu;
277

278
279
280
281
    const bool actionsEnabled = m_manager->debuggerActionsEnabled();
    const unsigned engineCapabilities = m_manager->debuggerCapabilities();
    const bool canHandleWatches = actionsEnabled && (engineCapabilities & AddWatcherCapability);

282
    QAction *actInsertNewWatchItem = menu.addAction(tr("Insert New Watch Item"));
283
    actInsertNewWatchItem->setEnabled(canHandleWatches);
284
    QAction *actSelectWidgetToWatch = menu.addAction(tr("Select Widget to Watch"));
285
    actSelectWidgetToWatch->setEnabled(canHandleWatches);
286

287
    const QString address = model()->data(mi0, AddressRole).toString();
288
    QAction *actWatchKnownMemory = 0;
289
290
    QAction *actWatchUnknownMemory =
        new QAction(tr("Open Memory Editor..."), &menu);
291
292
    const bool canShowMemory = engineCapabilities & ShowMemoryCapability;
    actWatchUnknownMemory->setEnabled(actionsEnabled && canShowMemory);
293

294
    if (canShowMemory && !address.isEmpty())
295
296
        actWatchKnownMemory =
            new QAction(tr("Open Memory Editor at %1").arg(address), &menu);
297
    menu.addSeparator();
298

299
300
301
302
303
304
305
306
307
308
309
    QAction *actSetWatchpoint = 0;
    const bool canSetWatchpoint = engineCapabilities & WatchpointCapability;
    if (canSetWatchpoint && !address.isEmpty()) {
        actSetWatchpoint =
            new QAction(tr("Break on changing %1").arg(address), &menu);
    } else {
        actSetWatchpoint =
            new QAction(tr("Break on changing contents"), &menu);
        actSetWatchpoint->setEnabled(false);
    }

310
    QAction *actWatchOrRemove;
311
    if (m_type == LocalsType) {
312
        actWatchOrRemove = theDebuggerAction(WatchExpression)->updatedAction(exp);
313
        actWatchOrRemove->setEnabled(canHandleWatches);
314
315
316
317
318
319
    } else {
        actWatchOrRemove = theDebuggerAction(RemoveWatchExpression)->updatedAction(exp);
        // Also for the case where the user cleared the expression.
        actWatchOrRemove->setEnabled(true);
    }
    menu.addAction(actWatchOrRemove);
320

321
322
    menu.addAction(actInsertNewWatchItem);
    menu.addAction(actSelectWidgetToWatch);
323
324
    menu.addMenu(&typeFormatMenu);
    menu.addMenu(&individualFormatMenu);
325
326
327
    if (actWatchKnownMemory)
        menu.addAction(actWatchKnownMemory);
    menu.addAction(actWatchUnknownMemory);
328
    menu.addAction(actSetWatchpoint);
329
    menu.addSeparator();
330

331
332
    menu.addAction(theDebuggerAction(RecheckDebuggingHelpers));
    menu.addAction(theDebuggerAction(UseDebuggingHelpers));
333
334
335
336
    QAction *actClearCodeModelSnapshot
        = new QAction(tr("Refresh Code Model Snapshot"), &menu);
    actClearCodeModelSnapshot->setEnabled(actionsEnabled
        && theDebuggerAction(UseCodeModel)->isChecked());
337
    menu.addAction(actClearCodeModelSnapshot);
338
    menu.addSeparator();
339
    menu.addAction(theDebuggerAction(UseToolTipsInLocalsView));
340

341
    menu.addAction(theDebuggerAction(AutoDerefPointers));
342
343
    menu.addAction(theDebuggerAction(ShowStdNamespace));
    menu.addAction(theDebuggerAction(ShowQtNamespace));
344

345
    QAction *actAdjustColumnWidths =
346
        menu.addAction(tr("Adjust Column Widths to Contents"));
347
    QAction *actAlwaysAdjustColumnWidth =
348
        menu.addAction(tr("Always Adjust Column Widths to Contents"));
349
350
351
    actAlwaysAdjustColumnWidth->setCheckable(true);
    actAlwaysAdjustColumnWidth->setChecked(m_alwaysResizeColumnsToContents);

352
    menu.addSeparator();
353
    menu.addAction(theDebuggerAction(SettingsDialog));
con's avatar
con committed
354
355
356

    QAction *act = menu.exec(ev->globalPos());

357
    if (act == actAdjustColumnWidths) {
con's avatar
con committed
358
        resizeColumnsToContents();
359
    } else if (act == actAlwaysAdjustColumnWidth) {
con's avatar
con committed
360
        setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
361
    } else if (act == actInsertNewWatchItem) {
362
363
        theDebuggerAction(WatchExpression)
            ->trigger(WatchHandler::watcherEditPlaceHolder());
364
    } else if (actWatchKnownMemory != 0 && act == actWatchKnownMemory) {
365
        (void) new MemoryViewAgent(m_manager, address);
366
367
368
369
370
    } else if (actWatchUnknownMemory != 0 && act == actWatchUnknownMemory) {
        AddressDialog dialog;
        if (dialog.exec() == QDialog::Accepted) {
            (void) new MemoryViewAgent(m_manager, dialog.address());
        }
371
372
373
374
    } else if (act == actSetWatchpoint) {
        BreakpointData *data = new BreakpointData;
        data->type = BreakpointData::WatchpointType;
        data->address = address.toLatin1();
hjk's avatar
hjk committed
375
376
        m_manager->breakHandler()->appendBreakpoint(data);
        m_manager->attemptBreakpointSynchronization();
377
    } else if (act == actSelectWidgetToWatch) {
378
379
        grabMouse(Qt::CrossCursor);
        m_grabbing = true;
380
381
    } else if (act == actClearCodeModelSnapshot) {
        m_manager->clearCppCodeModelSnapshot();
382
383
    } else if (clearIndividualFormatAction && act == clearIndividualFormatAction) {
        model()->setData(mi1, -1, IndividualFormatRole);
384
    } else {
hjk's avatar
hjk committed
385
        for (int i = 0; i != typeFormatActions.size(); ++i) {
386
387
            if (act == typeFormatActions.at(i))
                model()->setData(mi1, i, TypeFormatRole);
hjk's avatar
hjk committed
388
389
390
        }
        for (int i = 0; i != individualFormatActions.size(); ++i) {
            if (act == individualFormatActions.at(i))
391
392
                model()->setData(mi1, i, IndividualFormatRole);
        }
393
    }
con's avatar
con committed
394
395
396
397
398
399
400
401
402
403
404
405
406
}

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

void WatchWindow::setAlwaysResizeColumnsToContents(bool on)
{
    if (!header())
        return;
    m_alwaysResizeColumnsToContents = on;
407
    QHeaderView::ResizeMode mode = on
con's avatar
con committed
408
409
410
411
412
        ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
    header()->setResizeMode(0, mode);
    header()->setResizeMode(1, mode);
}

413
414
415
416
417
418
419
420
421
422
423
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
424
425
void WatchWindow::editItem(const QModelIndex &idx)
{
426
    Q_UNUSED(idx) // FIXME
con's avatar
con committed
427
428
}

429
void WatchWindow::setModel(QAbstractItemModel *model)
con's avatar
con committed
430
{
431
    QTreeView::setModel(model);
con's avatar
con committed
432
433
434
435
436
437

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

439
440
441
442
443
444
445
446
447
448
    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);
449
450
451
452
453
}

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

456
void WatchWindow::resetHelper(const QModelIndex &idx)
con's avatar
con committed
457
{
458
    if (model()->data(idx, ExpandedRole).toBool()) {
459
        //qDebug() << "EXPANDING " << model()->data(idx, INameRole);
con's avatar
con committed
460
461
462
        expand(idx);
        for (int i = 0, n = model()->rowCount(idx); i != n; ++i) {
            QModelIndex idx1 = model()->index(i, 0, idx);
463
            resetHelper(idx1);
con's avatar
con committed
464
        }
465
466
467
    } else {
        //qDebug() << "COLLAPSING " << model()->data(idx, INameRole);
        collapse(idx);
con's avatar
con committed
468
469
    }
}