watchwindow.cpp 13.1 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

    menu.addSeparator();
276
    menu.addAction(theDebuggerAction(UseToolTipsInLocalsView));
277
278
279
280
281
282
283
    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);

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

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

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

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);
}

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

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

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

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

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

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