watchwindow.cpp 13 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2009 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 "debuggeractions.h"
34
#include "debuggeragents.h"
35
#include "debuggerdialogs.h"
36
#include "debuggermanager.h"
37

38
39
#include <utils/qtcassert.h>

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

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

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

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

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

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


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

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

126
    QAction *act = theDebuggerAction(UseAlternatingRowColors);
con's avatar
con committed
127
    setWindowTitle(tr("Locals and Watchers"));
128
    setAlternatingRowColors(act->isChecked());
con's avatar
con committed
129
130
    setIndentation(indentation() * 9/10);
    setUniformRowHeights(true);
131
    setItemDelegate(new WatchDelegate(this));
132
133
134
    setDragEnabled(true);
    setAcceptDrops(true);
    setDropIndicatorShown(true);
con's avatar
con committed
135

136
137
    connect(act, SIGNAL(toggled(bool)),
        this, SLOT(setAlternatingRowColorsHelper(bool)));
hjk's avatar
hjk committed
138
139
140
141
142
143
144
145
146
147
148
149
150
151
    connect(this, SIGNAL(expanded(QModelIndex)), 
        this, SLOT(expandNode(QModelIndex))); 
    connect(this, SIGNAL(collapsed(QModelIndex)), 
        this, SLOT(collapseNode(QModelIndex))); 
} 
 
void WatchWindow::expandNode(const QModelIndex &idx) 
{ 
    model()->setData(idx, true, ExpandedRole);
} 
 
void WatchWindow::collapseNode(const QModelIndex &idx) 
{ 
    model()->setData(idx, false, ExpandedRole);
con's avatar
con committed
152
153
}

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

172
173
174
175
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
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
201
202
void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
{
203
204
205
206
    QModelIndex idx = indexAt(ev->pos());
    QModelIndex mi0 = idx.sibling(idx.row(), 0);
    QModelIndex mi1 = idx.sibling(idx.row(), 1);
    QModelIndex mi2 = idx.sibling(idx.row(), 2);
hjk's avatar
hjk committed
207
    QString exp = model()->data(mi0, ExpressionRole).toString();
208
209
210
211
212
213
214
215
216
    QString type = model()->data(mi2).toString();

    QStringList alternativeFormats = 
        model()->data(mi0, TypeFormatListRole).toStringList();
    int typeFormat = 
        model()->data(mi0, TypeFormatRole).toInt();
    int individualFormat = 
        model()->data(mi0, IndividualFormatRole).toInt();

217
218
    QMenu typeFormatMenu;
    QMenu individualFormatMenu;
219
220
    QList<QAction *> typeFormatActions;
    QList<QAction *> individualFormatActions;
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
    if (idx.isValid()) {
        typeFormatMenu.setTitle(tr("Change format for type '%1'").arg(type));
        individualFormatMenu.setTitle(tr("Change format for expression '%1'").arg(exp));
        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);
            act = new QAction(format, &individualFormatMenu);
            act->setCheckable(true);
            if (i == individualFormat)
                act->setChecked(true);
            individualFormatMenu.addAction(act);
            individualFormatActions.append(act);
        }
    } else {
        typeFormatMenu.setTitle(tr("Change format for type"));
        typeFormatMenu.setEnabled(false);
        individualFormatMenu.setTitle(tr("Change format for expression"));
        individualFormatMenu.setEnabled(false);
244
245
    }

con's avatar
con committed
246
    QMenu menu;
247
248
249
250
    //QAction *actWatchExpressionInWindow
    // = theDebuggerAction(WatchExpressionInWindow);
    //menu.addAction(actWatchExpressionInWindow);

251
252
    QAction *actInsertNewWatchItem = menu.addAction(tr("Insert new watch item"));
    QAction *actSelectWidgetToWatch = menu.addAction(tr("Select widget to watch"));
253

254
    const QString address = model()->data(mi0, AddressRole).toString();
255
    QAction *actWatchKnownMemory = 0;
256
257
258
    QAction *actWatchUnknownMemory = new QAction(tr("Open memory editor..."), &menu);;
    if (!address.isEmpty())
        actWatchKnownMemory = new QAction(tr("Open memory editor at %1").arg(address), &menu);
259
    menu.addSeparator();
260

261
262
263
    int atype = (m_type == LocalsType) ? WatchExpression : RemoveWatchExpression;
    menu.addAction(theDebuggerAction(atype)->updatedAction(exp));

264
265
    menu.addAction(actInsertNewWatchItem);
    menu.addAction(actSelectWidgetToWatch);
266
267
    menu.addMenu(&typeFormatMenu);
    menu.addMenu(&individualFormatMenu);
268
269
270
    if (actWatchKnownMemory)
        menu.addAction(actWatchKnownMemory);
    menu.addAction(actWatchUnknownMemory);
271
    menu.addSeparator();
272
273
    menu.addAction(theDebuggerAction(RecheckDebuggingHelpers));
    menu.addAction(theDebuggerAction(UseDebuggingHelpers));
274
275
276
277
278
279
280
281
282

    menu.addSeparator();
    QAction *actAdjustColumnWidths =
        menu.addAction(tr("Adjust column widths to contents"));
    QAction *actAlwaysAdjustColumnWidth =
        menu.addAction(tr("Always adjust column widths to contents"));
    actAlwaysAdjustColumnWidth->setCheckable(true);
    actAlwaysAdjustColumnWidth->setChecked(m_alwaysResizeColumnsToContents);

283
    menu.addSeparator();
284
    menu.addAction(theDebuggerAction(SettingsDialog));
con's avatar
con committed
285
286
287

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

288
    if (act == actAdjustColumnWidths) {
con's avatar
con committed
289
        resizeColumnsToContents();
290
    } else if (act == actAlwaysAdjustColumnWidth) {
con's avatar
con committed
291
        setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
292
    } else if (act == actInsertNewWatchItem) {
293
294
        theDebuggerAction(WatchExpression)
            ->trigger(WatchHandler::watcherEditPlaceHolder());
295
    } else if (actWatchKnownMemory != 0 && act == actWatchKnownMemory) {
296
        (void) new MemoryViewAgent(m_manager, address);
297
298
299
300
301
    } else if (actWatchUnknownMemory != 0 && act == actWatchUnknownMemory) {
        AddressDialog dialog;
        if (dialog.exec() == QDialog::Accepted) {
            (void) new MemoryViewAgent(m_manager, dialog.address());
        }
302
    } else if (act == actSelectWidgetToWatch) {
303
304
        grabMouse(Qt::CrossCursor);
        m_grabbing = true;
305
306
307
308
309
310
311
    } else { 
        for (int i = 0; i != alternativeFormats.size(); ++i) {
            if (act == typeFormatActions.at(i))
                model()->setData(mi1, i, TypeFormatRole);
            else if (act == individualFormatActions.at(i))
                model()->setData(mi1, i, IndividualFormatRole);
        }
312
    }
con's avatar
con committed
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
}

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

void WatchWindow::setAlwaysResizeColumnsToContents(bool on)
{
    if (!header())
        return;
    m_alwaysResizeColumnsToContents = on;
    QHeaderView::ResizeMode mode = on 
        ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
    header()->setResizeMode(0, mode);
    header()->setResizeMode(1, mode);
}

332
333
334
335
336
337
338
339
340
341
342
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
343
344
void WatchWindow::editItem(const QModelIndex &idx)
{
345
    Q_UNUSED(idx) // FIXME
con's avatar
con committed
346
347
}

348
void WatchWindow::setModel(QAbstractItemModel *model)
con's avatar
con committed
349
{
350
    QTreeView::setModel(model);
con's avatar
con committed
351
352
353
354
355
356

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

358
359
360
361
362
363
364
365
366
367
    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);
368
369
370
371
372
}

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

375
void WatchWindow::resetHelper(const QModelIndex &idx)
con's avatar
con committed
376
{
377
    if (model()->data(idx, ExpandedRole).toBool()) {
378
        //qDebug() << "EXPANDING " << model()->data(idx, INameRole);
con's avatar
con committed
379
380
381
        expand(idx);
        for (int i = 0, n = model()->rowCount(idx); i != n; ++i) {
            QModelIndex idx1 = model()->index(i, 0, idx);
382
            resetHelper(idx1);
con's avatar
con committed
383
        }
384
385
386
    } else {
        //qDebug() << "COLLAPSING " << model()->data(idx, INameRole);
        collapse(idx);
con's avatar
con committed
387
388
    }
}