breakwindow.cpp 17.1 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 "breakwindow.h"

32
#include "debuggeractions.h"
33
#include "debuggerconstants.h"
34
#include "ui_breakpoint.h"
con's avatar
con committed
35
36
#include "ui_breakcondition.h"

37
#include <utils/pathchooser.h>
38
#include <utils/qtcassert.h>
39
#include <utils/savedaction.h>
40

41
42
43
44
45
46
47
#include <QtCore/QDebug>

#include <QtGui/QAction>
#include <QtGui/QHeaderView>
#include <QtGui/QKeyEvent>
#include <QtGui/QMenu>
#include <QtGui/QResizeEvent>
48
#include <QtGui/QItemSelectionModel>
49
50
#include <QtGui/QToolButton>
#include <QtGui/QTreeView>
51
#include <QtGui/QIntValidator>
con's avatar
con committed
52
53


54
55
namespace Debugger {
namespace Internal {
con's avatar
con committed
56

57
58
59
60
61
62
63
64
65
66
///////////////////////////////////////////////////////////////////////
//
// BreakpointDialog
//
///////////////////////////////////////////////////////////////////////

class BreakpointDialog : public QDialog, public Ui::BreakpointDialog
{
    Q_OBJECT
public:
67
    explicit BreakpointDialog(QWidget *parent, BreakpointData *data)
68
69
70
71
72
73
74
      : QDialog(parent)
    {
        setupUi(this);
        comboBoxType->insertItem(0, tr("File and Line Number"));
        comboBoxType->insertItem(1, tr("Function Name"));
        comboBoxType->insertItem(2, tr("Function \"main()\""));
        comboBoxType->insertItem(3, tr("Address"));
75
76
        pathChooserFileName->lineEdit()->setText(data->fileName);
        pathChooserFileName->setExpectedKind(Utils::PathChooser::File);
77
78
79
80
81
82
83
84
85
86
87
88
89
90
        lineEditLineNumber->setText(QByteArray::number(data->lineNumber));
        lineEditFunction->setText(data->funcName);
        lineEditCondition->setText(data->condition);
        lineEditIgnoreCount->setText(QByteArray::number(data->ignoreCount));
        checkBoxUseFullPath->setChecked(data->useFullPath);
        if (data->address)
            lineEditAddress->setText("0x" + QByteArray::number(data->address, 16));
        int initialType = 0;
        if (!data->funcName.isEmpty())
            initialType = lineEditFunction->text() == "main" ? 2 : 1;
        if (data->address)
            initialType = 3;
        typeChanged(initialType);
        connect(comboBoxType, SIGNAL(activated(int)), SLOT(typeChanged(int)));
91
92
93
94
95
96
97
98
99
    }

public slots:
    void typeChanged(int index)
    {
        const bool isLineVisible = index == 0;
        const bool isFunctionVisible = index == 1;
        const bool isAddressVisible = index == 3;
        labelFileName->setEnabled(isLineVisible);
100
        pathChooserFileName->setEnabled(isLineVisible);
101
102
        labelLineNumber->setEnabled(isLineVisible);
        lineEditLineNumber->setEnabled(isLineVisible);
103
104
        labelUseFullPath->setEnabled(isLineVisible);
        checkBoxUseFullPath->setEnabled(isLineVisible);
105
106
107
108
109
110
111
112
113
114
        labelFunction->setEnabled(isFunctionVisible);
        lineEditFunction->setEnabled(isFunctionVisible);
        labelAddress->setEnabled(isAddressVisible);
        lineEditAddress->setEnabled(isAddressVisible);
        if (index == 2)
            lineEditFunction->setText("main");
    }

};

115
116
117
118
119
120
///////////////////////////////////////////////////////////////////////
//
// BreakWindow
//
///////////////////////////////////////////////////////////////////////

121
122
BreakWindow::BreakWindow(QWidget *parent)
  : QTreeView(parent)
con's avatar
con committed
123
{
124
125
    m_alwaysResizeColumnsToContents = false;

126
    QAction *act = theDebuggerAction(UseAlternatingRowColors);
127
    setFrameStyle(QFrame::NoFrame);
128
    setAttribute(Qt::WA_MacShowFocusRect, false);
con's avatar
con committed
129
    setWindowTitle(tr("Breakpoints"));
130
    setWindowIcon(QIcon(QLatin1String(":/debugger/images/debugger_breakpoints.png")));
131
    setAlternatingRowColors(act->isChecked());
con's avatar
con committed
132
133
    setRootIsDecorated(false);
    setIconSize(QSize(10, 10));
134
    setSelectionMode(QAbstractItemView::ExtendedSelection);
con's avatar
con committed
135
136
137

    connect(this, SIGNAL(activated(QModelIndex)),
        this, SLOT(rowActivated(QModelIndex)));
138
139
    connect(act, SIGNAL(toggled(bool)),
        this, SLOT(setAlternatingRowColorsHelper(bool)));
140
141
142
143
    connect(theDebuggerAction(UseAddressInBreakpointsView), SIGNAL(toggled(bool)),
        this, SLOT(showAddressColumn(bool)));
}

144
145
146
147
BreakWindow::~BreakWindow()
{
}

148
149
void BreakWindow::showAddressColumn(bool on)
{
150
    setColumnHidden(7, !on);
con's avatar
con committed
151
152
}

153
154
155
static QModelIndexList normalizeIndexes(const QModelIndexList &list)
{
    QModelIndexList res;
156
157
158
    foreach (const QModelIndex &index, list)
        if (index.column() == 0)
            res.append(index);
159
160
161
    return res;
}

162
void BreakWindow::keyPressEvent(QKeyEvent *ev)
con's avatar
con committed
163
{
164
165
166
167
168
169
170
171
    if (ev->key() == Qt::Key_Delete) {
        QItemSelectionModel *sm = selectionModel();
        QTC_ASSERT(sm, return);
        QModelIndexList si = sm->selectedIndexes();
        if (si.isEmpty())
            si.append(currentIndex().sibling(currentIndex().row(), 0));
        deleteBreakpoints(normalizeIndexes(si));
    }
172
    QTreeView::keyPressEvent(ev);
con's avatar
con committed
173
174
}

175
void BreakWindow::resizeEvent(QResizeEvent *ev)
con's avatar
con committed
176
{
177
    QTreeView::resizeEvent(ev);
con's avatar
con committed
178
179
}

180
181
182
void BreakWindow::mouseDoubleClickEvent(QMouseEvent *ev)
{
    QModelIndex indexUnderMouse = indexAt(ev->pos());
183
    if (indexUnderMouse.isValid() && indexUnderMouse.column() >= 4)
184
        editBreakpoints(QModelIndexList() << indexUnderMouse);
185
    QTreeView::mouseDoubleClickEvent(ev);
186
187
}

con's avatar
con committed
188
189
190
void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
{
    QMenu menu;
191
192
193
194
195
196
197
198
    QItemSelectionModel *sm = selectionModel();
    QTC_ASSERT(sm, return);
    QModelIndexList si = sm->selectedIndexes();
    QModelIndex indexUnderMouse = indexAt(ev->pos());
    if (si.isEmpty() && indexUnderMouse.isValid())
        si.append(indexUnderMouse.sibling(indexUnderMouse.row(), 0));
    si = normalizeIndexes(si);

199
200
201
    const int rowCount = model()->rowCount();
    const unsigned engineCapabilities =
        model()->data(QModelIndex(), EngineCapabilitiesRole).toUInt();
202

203
    QAction *deleteAction = new QAction(tr("Delete Breakpoint"), &menu);
204
205
    deleteAction->setEnabled(si.size() > 0);

206
    QAction *deleteAllAction = new QAction(tr("Delete All Breakpoints"), &menu);
207
    deleteAllAction->setEnabled(model()->rowCount() > 0);
208

209
    // Delete by file: Find indices of breakpoints of the same file.
210
211
212
    QAction *deleteByFileAction = 0;
    QList<int> breakPointsOfFile;
    if (indexUnderMouse.isValid()) {
213
        const QModelIndex index = indexUnderMouse.sibling(indexUnderMouse.row(), 2);
214
        const QString file = model()->data(index).toString();
215
216
        if (!file.isEmpty()) {
            for (int i = 0; i < rowCount; i++)
217
                if (model()->data(model()->index(i, 2)).toString() == file)
218
219
                    breakPointsOfFile.push_back(i);
            if (breakPointsOfFile.size() > 1) {
220
221
                deleteByFileAction =
                    new QAction(tr("Delete Breakpoints of \"%1\"").arg(file), &menu);
222
223
224
225
226
                deleteByFileAction->setEnabled(true);
            }
        }
    }
    if (!deleteByFileAction) {
227
        deleteByFileAction = new QAction(tr("Delete Breakpoints of File"), &menu);
228
229
        deleteByFileAction->setEnabled(false);
    }
230

231
232
233
234
235
    QAction *adjustColumnAction =
        new QAction(tr("Adjust Column Widths to Contents"), &menu);

    QAction *alwaysAdjustAction =
        new QAction(tr("Always Adjust Column Widths to Contents"), &menu);
236

237
238
    alwaysAdjustAction->setCheckable(true);
    alwaysAdjustAction->setChecked(m_alwaysResizeColumnsToContents);
239

240
241
242
    QAction *editBreakpointAction =
        new QAction(tr("Edit Breakpoint..."), &menu);
    editBreakpointAction->setEnabled(si.size() > 0);
243

244
    int threadId = model()->data(QModelIndex(), CurrentThreadIdRole).toInt();
245
246
247
248
249
250
    QString associateTitle = threadId == -1
        ?  tr("Associate Breakpoint With All Threads")
        :  tr("Associate Breakpoint With Thread %1").arg(threadId);
    QAction *associateBreakpointAction = new QAction(associateTitle, &menu);
    associateBreakpointAction->setEnabled(si.size() > 0);

251
252
253
    QAction *synchronizeAction =
        new QAction(tr("Synchronize Breakpoints"), &menu);
    synchronizeAction->setEnabled(
254
        model()->data(QModelIndex(), EngineActionsEnabledRole).toBool());
255

hjk's avatar
hjk committed
256
    QModelIndex idx0 = (si.size() ? si.front() : QModelIndex());
257
    QModelIndex idx2 = idx0.sibling(idx0.row(), 2);
258
259
    bool enabled = si.isEmpty()
        || idx0.data(BreakpointEnabledRole).toBool();
260
261
262
263
264
265
266
267

    const QString str5 = si.size() > 1
        ? enabled
            ? tr("Disable Selected Breakpoints")
            : tr("Enable Selected Breakpoints")
        : enabled
            ? tr("Disable Breakpoint")
            : tr("Enable Breakpoint");
268
269
270
    QAction *toggleEnabledAction = new QAction(str5, &menu);
    toggleEnabledAction->setEnabled(si.size() > 0);

271
272
    const bool fullpath = si.isEmpty()
        || idx2.data(BreakpointEnabledRole).toBool();
273
    const QString str6 = fullpath ? tr("Use Short Path") : tr("Use Full Path");
274
275
276
    QAction *pathAction = new QAction(str6, &menu);
    pathAction->setEnabled(si.size() > 0);

277
    QAction *addBreakpointAction =
278
        new QAction(tr("Add Breakpoint..."), this);
279
280
281
282
    QAction *breakAtThrowAction =
        new QAction(tr("Set Breakpoint at \"throw\""), this);
    QAction *breakAtCatchAction =
        new QAction(tr("Set Breakpoint at \"catch\""), this);
283

284
    menu.addAction(addBreakpointAction);
285
    menu.addAction(deleteAction);
286
    menu.addAction(editBreakpointAction);
287
    menu.addAction(associateBreakpointAction);
288
289
290
291
292
    menu.addAction(toggleEnabledAction);
    menu.addAction(pathAction);
    menu.addSeparator();
    menu.addAction(deleteAllAction);
    menu.addAction(deleteByFileAction);
293
    menu.addSeparator();
294
    menu.addAction(synchronizeAction);
295
    if (engineCapabilities & BreakOnThrowAndCatchCapability) {
296
        menu.addSeparator();
297
298
299
        menu.addAction(breakAtThrowAction);
        menu.addAction(breakAtCatchAction);
    }
300
    menu.addSeparator();
301
302
    menu.addAction(theDebuggerAction(UseToolTipsInBreakpointsView));
    menu.addAction(theDebuggerAction(UseAddressInBreakpointsView));
303
304
305
    menu.addAction(adjustColumnAction);
    menu.addAction(alwaysAdjustAction);
    menu.addSeparator();
306
    menu.addAction(theDebuggerAction(SettingsDialog));
con's avatar
con committed
307
308
309

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

310
    if (act == deleteAction) {
311
        deleteBreakpoints(si);
312
313
314
315
316
317
318
319
    } else if (act == deleteAllAction) {
        QList<int> allRows;
        for (int i = 0; i < rowCount; i++)
            allRows.push_back(i);
        deleteBreakpoints(allRows);
    }  else if (act == deleteByFileAction)
        deleteBreakpoints(breakPointsOfFile);
    else if (act == adjustColumnAction)
con's avatar
con committed
320
        resizeColumnsToContents();
321
    else if (act == alwaysAdjustAction)
con's avatar
con committed
322
        setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
323
    else if (act == editBreakpointAction)
324
        editBreakpoints(si);
325
326
    else if (act == associateBreakpointAction)
        associateBreakpoint(si, threadId);
327
    else if (act == synchronizeAction)
328
        setModelData(RequestSynchronizeBreakpointsRole);
329
    else if (act == toggleEnabledAction)
330
        setBreakpointsEnabled(si, !enabled);
331
    else if (act == pathAction)
332
        setBreakpointsFullPath(si, !fullpath);
333
334
    else if (act == addBreakpointAction)
        addBreakpoint();
335
    else if (act == breakAtThrowAction)
336
        setModelData(RequestBreakByFunctionRole, QLatin1String(BreakpointData::throwFunction));
337
    else if (act == breakAtCatchAction)
338
        setModelData(RequestBreakByFunctionRole, QLatin1String(BreakpointData::catchFunction));
339
340
341
342
}

void BreakWindow::setBreakpointsEnabled(const QModelIndexList &list, bool enabled)
{
343
344
345
    foreach (const QModelIndex &index, list)
        setModelData(BreakpointEnabledRole, enabled, index);
    setModelData(RequestSynchronizeBreakpointsRole);
346
347
348
349
}

void BreakWindow::setBreakpointsFullPath(const QModelIndexList &list, bool fullpath)
{
350
351
352
    foreach (const QModelIndex &index, list)
        setModelData(BreakpointUseFullPathRole, fullpath, index);
    setModelData(RequestSynchronizeBreakpointsRole);
con's avatar
con committed
353
354
}

355
void BreakWindow::deleteBreakpoints(const QModelIndexList &indexes)
con's avatar
con committed
356
{
357
358
    QTC_ASSERT(!indexes.isEmpty(), return);
    QList<int> list;
359
360
    foreach (const QModelIndex &index, indexes)
        list.append(index.row());
361
362
    deleteBreakpoints(list);
}
363

364
365
366
367
368
void BreakWindow::deleteBreakpoints(QList<int> list)
{
    if (list.empty())
        return;
    const int firstRow = list.front();
369
370
    qSort(list.begin(), list.end());
    for (int i = list.size(); --i >= 0; )
371
        setModelData(RequestRemoveBreakpointByIndexRole, list.at(i));
372

373
    const int row = qMin(firstRow, model()->rowCount() - 1);
374
375
    if (row >= 0)
        setCurrentIndex(model()->index(row, 0));
376
    setModelData(RequestSynchronizeBreakpointsRole);
con's avatar
con committed
377
378
}

379
bool BreakWindow::editBreakpoint(BreakpointData *data)
380
{
381
    BreakpointDialog dialog(this, data);
382
    if (dialog.exec() == QDialog::Rejected)
383
384
        return false;
    bool ok = false;
385
386
    data->lineNumber = dialog.lineEditLineNumber->text().toInt();
    data->useFullPath = dialog.checkBoxUseFullPath->isChecked();
387
388
389
390
391
392
    data->address = dialog.lineEditAddress->text().toULongLong(&ok, 0);
    data->funcName = dialog.lineEditFunction->text();
    data->fileName = dialog.pathChooserFileName->lineEdit()->text();
    data->condition = dialog.lineEditCondition->text().toUtf8();
    data->ignoreCount = dialog.lineEditIgnoreCount->text().toInt();
    data->threadSpec = dialog.lineEditThreadSpec->text().toUtf8();
393
394
395
396
397
398
399
400
401
402
    return true;
}

void BreakWindow::addBreakpoint()
{
    BreakpointData *data = new BreakpointData();
    if (editBreakpoint(data))
        setModelData(RequestBreakpointRole, QVariant::fromValue(data));
    else
        delete data;
403
404
405
}

void BreakWindow::editBreakpoints(const QModelIndexList &list)
con's avatar
con committed
406
{
407
408
409
410
411
412
413
414
415
    if (list.size() == 1) {
        QVariant var = model()->data(list.at(0), BreakpointRole);
        BreakpointData *data = (BreakpointData *)var.toULongLong();
        if (editBreakpoint(data))
            data->reinsertBreakpoint();
        return;
    }

    // This allows to change properties of multiple breakpoints at a time.
con's avatar
con committed
416
417
418
419
    QDialog dlg(this);
    Ui::BreakCondition ui;
    ui.setupUi(&dlg);

420
421
    QTC_ASSERT(!list.isEmpty(), return);
    QModelIndex idx = list.front();
422
    dlg.setWindowTitle(tr("Edit Breakpoint Properties"));
423
424
    ui.lineEditFunction->hide();
    ui.labelFunction->hide();
425
    QAbstractItemModel *m = model();
426
    ui.lineEditCondition->setText(
427
        m->data(idx, BreakpointConditionRole).toString());
428
429
    ui.lineEditIgnoreCount->setValidator(
        new QIntValidator(0, 2147483647, ui.lineEditIgnoreCount));
430
    ui.lineEditIgnoreCount->setText(
431
        m->data(idx, BreakpointIgnoreCountRole).toString());
432
    ui.lineEditThreadSpec->setText(
433
        m->data(idx, BreakpointThreadSpecRole).toString());
con's avatar
con committed
434
435
436
437

    if (dlg.exec() == QDialog::Rejected)
        return;

438
    foreach (const QModelIndex &idx, list) {
439
        //m->setData(idx.sibling(idx.row(), 1), ui.lineEditFunction->text());
440
        //m->setData(idx.sibling(idx.row(), 2), ui.pathChooserFileName->text());
441
        //m->setData(idx.sibling(idx.row(), 3), ui.lineEditLineNumber->text());
442
443
444
        m->setData(idx, ui.lineEditCondition->text(), BreakpointConditionRole);
        m->setData(idx, ui.lineEditIgnoreCount->text(), BreakpointIgnoreCountRole);
        m->setData(idx, ui.lineEditThreadSpec->text(), BreakpointThreadSpecRole);
445
    }
446
    setModelData(RequestSynchronizeBreakpointsRole);
con's avatar
con committed
447
448
}

449
450
451
452
453
void BreakWindow::associateBreakpoint(const QModelIndexList &list, int threadId)
{
    QString str;
    if (threadId != -1)
        str = QString::number(threadId);
454
455
456
    foreach (const QModelIndex &index, list)
        setModelData(BreakpointThreadSpecRole, str, index);
    setModelData(RequestSynchronizeBreakpointsRole);
457
458
}

con's avatar
con committed
459
460
void BreakWindow::resizeColumnsToContents()
{
461
462
    for (int i = model()->columnCount(); --i >= 0; )
        resizeColumnToContents(i);
con's avatar
con committed
463
464
465
466
467
}

void BreakWindow::setAlwaysResizeColumnsToContents(bool on)
{
    m_alwaysResizeColumnsToContents = on;
468
    QHeaderView::ResizeMode mode = on
con's avatar
con committed
469
        ? QHeaderView::ResizeToContents : QHeaderView::Interactive;
470
471
    for (int i = model()->columnCount(); --i >= 0; )
        header()->setResizeMode(i, mode);
con's avatar
con committed
472
473
}

474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
void BreakWindow::rowActivated(const QModelIndex &index)
{
    setModelData(RequestActivateBreakpointRole, index.row());
}

void BreakWindow::setModelData
    (int role, const QVariant &value, const QModelIndex &index)
{
    QTC_ASSERT(model(), return);
    model()->setData(index, value, role);
}


} // namespace Internal
} // namespace Debugger
489
490

#include "breakwindow.moc"