watchwindow.cpp 16.3 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"
34
#include "debuggeractions.h"
35
#include "debuggeragents.h"
36
#include "debuggerdialogs.h"
37
#include "debuggermanager.h"
38
#include "idebuggerengine.h"
39

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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
205
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
206
207
void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
{
208
209
210
211
212
213
214
    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();
215

216
    const QStringList alternativeFormats =
217
        model()->data(mi0, TypeFormatListRole).toStringList();
218
219
220
221
222
223
    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;
224

225
    QMenu typeFormatMenu;
226
    QList<QAction *> typeFormatActions;
227
    if (idx.isValid()) {
228
        typeFormatMenu.setTitle(
229
            tr("Change Format for Type \"%1\"").arg(type));
230
231
        if (alternativeFormats.isEmpty()) {
            typeFormatMenu.setEnabled(false);
232
233
234
235
236
237
238
239
240
        } 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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
            }
        }
    } 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);
263
264
265
266
267
268
                act->setCheckable(true);
                if (i == effectiveIndividualFormat)
                    act->setChecked(true);
                individualFormatMenu.addAction(act);
                individualFormatActions.append(act);
            }
269
        }
270
    } else {
271
        individualFormatMenu.setTitle(tr("Change Format for Object"));
272
        individualFormatMenu.setEnabled(false);
273
274
    }

con's avatar
con committed
275
    QMenu menu;
276

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

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

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

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

298
299
300
301
302
303
304
305
306
307
308
    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);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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