File: variablewidget.cpp

package info (click to toggle)
kdevelop 4%3A24.12.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 71,888 kB
  • sloc: cpp: 290,869; python: 3,626; javascript: 3,518; sh: 1,316; ansic: 703; xml: 401; php: 95; lisp: 66; makefile: 31; sed: 12
file content (334 lines) | stat: -rw-r--r-- 11,231 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
/*
    SPDX-FileCopyrightText: 1999 John Birch <jbb@kdevelop.org>
    SPDX-FileCopyrightText: 2006 Vladimir Prus <ghost@cs.msu.su>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "variablewidget.h"

#include <QApplication>
#include <QAction>
#include <QActionGroup>
#include <QClipboard>
#include <QContextMenuEvent>
#include <QMenu>
#include <QVBoxLayout>

#include <KConfigGroup>
#include <KHistoryComboBox>
#include <KLocalizedString>
#include <KSharedConfig>

#include "../util/treemodel.h"
#include "../../interfaces/icore.h"
#include <interfaces/idebugcontroller.h>
#include "../interfaces/ivariablecontroller.h"
#include "variablecollection.h"
#include "variablesortmodel.h"
#include <debug.h>

/** The variables widget is passive, and is invoked by the rest of the
    code via two main Q_SLOTS:
    - slotDbgStatus
    - slotCurrentFrame

    The first is received the program status changes and the second is
    received after current frame in the debugger can possibly changes.

    The widget has a list item for each frame/thread combination, with
    variables as children. However, at each moment only one item is shown.
    When handling the slotCurrentFrame, we check if variables for the
    current frame are available. If yes, we simply show the corresponding item.
    Otherwise, we fetch the new data from debugger.

    Fetching the data is done by emitting the produceVariablesInfo signal.
    In response, we get slotParametersReady and slotLocalsReady signal,
    in that order.

    The data is parsed and changed variables are highlighted. After that,
    we 'trim' variable items that were not reported by gdb -- that is, gone
    out of scope.
*/

// **************************************************************************
// **************************************************************************
// **************************************************************************

namespace {

constexpr const char* autoResizeColumnsKey = "autoResizeColumns";

auto variablesViewConfigGroup()
{
    return KSharedConfig::openConfig()->group(QStringLiteral("Variables View"));
}

}

namespace KDevelop
{

VariableCollection *variableCollection()
{
    return ICore::self()->debugController()->variableCollection();
}

VariableWidget::VariableWidget(IDebugController* controller, QWidget *parent)
: QWidget(parent), m_variablesRoot(controller->variableCollection()->root())
{
  //setWindowIcon(QIcon::fromTheme("math_brace"));
    setWindowIcon(QIcon::fromTheme(QStringLiteral("debugger"), windowIcon()));
    setWindowTitle(i18n("Debugger Variables"));

    m_varTree = new VariableTree(controller, this, new VariableSortProxyModel(this));
    setFocusProxy(m_varTree);

    m_watchVarEditor = new KHistoryComboBox( this );

    auto *topLayout = new QVBoxLayout(this);
    topLayout->addWidget(m_varTree, 10);
    topLayout->addWidget(m_watchVarEditor);
    topLayout->setContentsMargins(0, 0, 0, 0);

    connect(m_watchVarEditor, QOverload<const QString&>::of(&KHistoryComboBox::returnPressed),
            this, &VariableWidget::slotAddWatch);

    const bool autoResizeColumns = variablesViewConfigGroup().readEntry(autoResizeColumnsKey, true);
    m_varTree->setAutoResizeColumns(autoResizeColumns);

    auto* const autoResizeColumnsAction = new QAction(i18nc("@option:check", "Auto-resize columns on click"), this);
    autoResizeColumnsAction->setIcon(QIcon::fromTheme(QStringLiteral("resizecol")));
    autoResizeColumnsAction->setCheckable(true);
    autoResizeColumnsAction->setChecked(autoResizeColumns);
    connect(autoResizeColumnsAction, &QAction::triggered, this, [this](bool on) {
        m_varTree->setAutoResizeColumns(on);
        variablesViewConfigGroup().writeEntry(autoResizeColumnsKey, on);
    });
    addAction(autoResizeColumnsAction);

    //TODO
    //connect(plugin, SIGNAL(raiseVariableViews()), this, SIGNAL(requestRaise()));

    // Setup help items.

    setWhatsThis( i18n(
        "<b>Variable tree</b>"
        "The variable tree allows you to see the values of local "
        "variables and arbitrary expressions.<br />"
        "Local variables are displayed automatically and are updated "
        "as you step through your program. "
        "For each expression you enter, you can either evaluate it once, "
        "or \"watch\" it (make it auto-updated). Expressions that are not "
        "auto-updated can be updated manually from the context menu. "
        "Expressions can be renamed to more descriptive names by clicking "
        "on the name column.<br />"
        "To change the value of a variable or an expression, "
        "click on the value.<br />"));

    m_watchVarEditor->setWhatsThis(
                    i18n("<b>Expression entry</b>"
                         "Type in expression to watch."));

}

void VariableWidget::slotAddWatch(const QString &expression)
{
    if (!expression.isEmpty())
    {
        m_watchVarEditor->addToHistory(expression);
        qCDebug(DEBUGGER) << "Trying to add watch";
        Variable* v = m_variablesRoot->watches()->add(expression);
        if (v) {
            /* For watches on structures, we really do want them to be shown
            expanded.  Except maybe for structure with custom pretty printing,
            but will handle that later.
            FIXME: it does not actually works now.
            */
            //QModelIndex index = variableCollection()->indexForItem(v, 0);
            //varTree_->setExpanded(index, true);
        }
        m_watchVarEditor->clearEditText();
    }
}

void VariableWidget::hideEvent(QHideEvent* e)
{
    QWidget::hideEvent(e);
    variableCollection()->variableWidgetHidden();
}

void VariableWidget::showEvent(QShowEvent* e)
{
    QWidget::showEvent(e);
    variableCollection()->variableWidgetShown();
}

// **************************************************************************
// **************************************************************************
// **************************************************************************

VariableTree::VariableTree(IDebugController* controller, VariableWidget* parent, QSortFilterProxyModel* proxy)
    : AsyncTreeView(*controller->variableCollection(), parent)
    , m_proxy(proxy)
#if 0
,
      activePopup_(0),
      toggleWatch_(0)
#endif
{
    setRootIsDecorated(true);
    setAllColumnsShowFocus(true);

    // setting proxy model
    m_proxy->setSourceModel(&treeModel());
    setModel(m_proxy);
    setSortingEnabled(true);
    sortByColumn(VariableCollection::NameColumn, Qt::AscendingOrder);

    QModelIndex index = controller->variableCollection()->indexForItem(
        controller->variableCollection()->watches(), 0);
    setExpanded(index, true);

    setupActions();
}

VariableTree::~VariableTree()
{
}

void VariableTree::setupActions()
{
    // TODO decorate this properly to make nice menu title
    m_contextMenuTitle = new QAction(this);
    m_contextMenuTitle->setEnabled(false);

    // make Format menu action group
    m_formatMenu = new QMenu(i18n("&Format"), this);
    auto *ag= new QActionGroup(m_formatMenu);

    QAction* act;

    act = new QAction(i18n("&Natural"), ag);
    act->setData(Variable::Natural);
    act->setShortcut(Qt::Key_N);
    m_formatMenu->addAction(act);

    act = new QAction(i18n("&Binary"), ag);
    act->setData(Variable::Binary);
    act->setShortcut(Qt::Key_B);
    m_formatMenu->addAction(act);

    act = new QAction(i18n("&Octal"), ag);
    act->setData(Variable::Octal);
    act->setShortcut(Qt::Key_O);
    m_formatMenu->addAction(act);

    act = new QAction(i18n("&Decimal"), ag);
    act->setData(Variable::Decimal);
    act->setShortcut(Qt::Key_D);
    m_formatMenu->addAction(act);

    act = new QAction(i18n("&Hexadecimal"), ag);
    act->setData(Variable::Hexadecimal);
    act->setShortcut(Qt::Key_H);
    m_formatMenu->addAction(act);

    const auto formatMenuActions = m_formatMenu->actions();
    for (QAction* act : formatMenuActions) {
        act->setCheckable(true);
        act->setShortcutContext(Qt::WidgetWithChildrenShortcut);
        const int id = act->data().toInt();
        connect(act, &QAction::triggered, this, [this, id](){ changeVariableFormat(id); });
        addAction(act);
    }

    m_watchDelete = new QAction(
        QIcon::fromTheme(QStringLiteral("edit-delete")), i18n( "Remove Watch Variable" ), this);

    m_watchDelete->setShortcut(Qt::Key_Delete);
    m_watchDelete->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    addAction(m_watchDelete);
    connect(m_watchDelete, &QAction::triggered, this, &VariableTree::watchDelete);

    m_copyVariableValue = new QAction(i18n("&Copy Value"), this);
    m_copyVariableValue->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    m_copyVariableValue->setShortcut(QKeySequence::Copy);
    connect(m_copyVariableValue, &QAction::triggered, this, &VariableTree::copyVariableValue);

    m_stopOnChange = new QAction(i18n("&Stop on Change"), this);
    connect(m_stopOnChange, &QAction::triggered, this, &VariableTree::stopOnChange);
}

Variable* VariableTree::selectedVariable() const
{
    if (selectionModel()->selectedRows().isEmpty()) return nullptr;
    auto item = selectionModel()->currentIndex().data(TreeModel::ItemRole).value<TreeItem*>();
    if (!item) return nullptr;
    return qobject_cast<Variable*>(item);
}

void VariableTree::contextMenuEvent(QContextMenuEvent* event)
{
    if (!selectedVariable()) return;

    // set up menu
    QMenu contextMenu(this->parentWidget());
    m_contextMenuTitle->setText(selectedVariable()->expression());
    contextMenu.addAction(m_contextMenuTitle);

    if(selectedVariable()->canSetFormat())
        contextMenu.addMenu(m_formatMenu);

    const auto formatMenuActions = m_formatMenu->actions();
    for (QAction* act : formatMenuActions) {
        if(act->data().toInt()==selectedVariable()->format())
            act->setChecked(true);
    }

    if (qobject_cast<Watches*>(selectedVariable()->parent())) {
        contextMenu.addAction(m_watchDelete);
    }

    contextMenu.addSeparator();
    contextMenu.addAction(m_copyVariableValue);
    contextMenu.addAction(m_stopOnChange);

    contextMenu.exec(event->globalPos());
}

QModelIndex VariableTree::mapViewIndexToTreeModelIndex(const QModelIndex& viewIndex) const
{
    return m_proxy->mapToSource(viewIndex);
}

void VariableTree::changeVariableFormat(int format)
{
    if (!selectedVariable()) return;
    selectedVariable()->setFormat(static_cast<Variable::format_t>(format));
}

void VariableTree::watchDelete()
{
    if (!selectedVariable()) return;
    if (!qobject_cast<Watches*>(selectedVariable()->parent())) return;
    selectedVariable()->die();
}

void VariableTree::copyVariableValue()
{
    if (!selectedVariable()) return;
    QApplication::clipboard()->setText(selectedVariable()->value());
}

void VariableTree::stopOnChange()
{
    if (!selectedVariable()) return;
    IDebugSession *session = ICore::self()->debugController()->currentSession();
    if (session && session->state() != IDebugSession::NotStartedState && session->state() != IDebugSession::EndedState) {
        session->variableController()->addWatchpoint(selectedVariable());
    }
}
}

#include "moc_variablewidget.cpp"