File indexing completed on 2024-05-12 04:37:38
0001 /* 0002 SPDX-FileCopyrightText: 1999 John Birch <jbb@kdevelop.org> 0003 SPDX-FileCopyrightText: 2006 Vladimir Prus <ghost@cs.msu.su> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "variablewidget.h" 0009 0010 #include <QApplication> 0011 #include <QAction> 0012 #include <QActionGroup> 0013 #include <QClipboard> 0014 #include <QContextMenuEvent> 0015 #include <QMenu> 0016 #include <QVBoxLayout> 0017 0018 #include <KConfigGroup> 0019 #include <KHistoryComboBox> 0020 #include <KLocalizedString> 0021 #include <KSharedConfig> 0022 0023 #include "../util/treemodel.h" 0024 #include "../../interfaces/icore.h" 0025 #include <interfaces/idebugcontroller.h> 0026 #include "../interfaces/ivariablecontroller.h" 0027 #include "variablecollection.h" 0028 #include "variablesortmodel.h" 0029 #include <debug.h> 0030 0031 /** The variables widget is passive, and is invoked by the rest of the 0032 code via two main Q_SLOTS: 0033 - slotDbgStatus 0034 - slotCurrentFrame 0035 0036 The first is received the program status changes and the second is 0037 received after current frame in the debugger can possibly changes. 0038 0039 The widget has a list item for each frame/thread combination, with 0040 variables as children. However, at each moment only one item is shown. 0041 When handling the slotCurrentFrame, we check if variables for the 0042 current frame are available. If yes, we simply show the corresponding item. 0043 Otherwise, we fetch the new data from debugger. 0044 0045 Fetching the data is done by emitting the produceVariablesInfo signal. 0046 In response, we get slotParametersReady and slotLocalsReady signal, 0047 in that order. 0048 0049 The data is parsed and changed variables are highlighted. After that, 0050 we 'trim' variable items that were not reported by gdb -- that is, gone 0051 out of scope. 0052 */ 0053 0054 // ************************************************************************** 0055 // ************************************************************************** 0056 // ************************************************************************** 0057 0058 namespace { 0059 0060 constexpr const char* autoResizeColumnsKey = "autoResizeColumns"; 0061 0062 auto variablesViewConfigGroup() 0063 { 0064 return KSharedConfig::openConfig()->group("Variables View"); 0065 } 0066 0067 } 0068 0069 namespace KDevelop 0070 { 0071 0072 VariableCollection *variableCollection() 0073 { 0074 return ICore::self()->debugController()->variableCollection(); 0075 } 0076 0077 VariableWidget::VariableWidget(IDebugController* controller, QWidget *parent) 0078 : QWidget(parent), m_variablesRoot(controller->variableCollection()->root()) 0079 { 0080 //setWindowIcon(QIcon::fromTheme("math_brace")); 0081 setWindowIcon(QIcon::fromTheme(QStringLiteral("debugger"), windowIcon())); 0082 setWindowTitle(i18n("Debugger Variables")); 0083 0084 m_varTree = new VariableTree(controller, this, new VariableSortProxyModel(this)); 0085 setFocusProxy(m_varTree); 0086 0087 m_watchVarEditor = new KHistoryComboBox( this ); 0088 0089 auto *topLayout = new QVBoxLayout(this); 0090 topLayout->addWidget(m_varTree, 10); 0091 topLayout->addWidget(m_watchVarEditor); 0092 topLayout->setContentsMargins(0, 0, 0, 0); 0093 0094 connect(m_watchVarEditor, QOverload<const QString&>::of(&KHistoryComboBox::returnPressed), 0095 this, &VariableWidget::slotAddWatch); 0096 0097 const bool autoResizeColumns = variablesViewConfigGroup().readEntry(autoResizeColumnsKey, true); 0098 m_varTree->setAutoResizeColumns(autoResizeColumns); 0099 0100 auto* const autoResizeColumnsAction = new QAction(i18nc("@option:check", "Auto-resize columns on click"), this); 0101 autoResizeColumnsAction->setIcon(QIcon::fromTheme(QStringLiteral("resizecol"))); 0102 autoResizeColumnsAction->setCheckable(true); 0103 autoResizeColumnsAction->setChecked(autoResizeColumns); 0104 connect(autoResizeColumnsAction, &QAction::triggered, this, [this](bool on) { 0105 m_varTree->setAutoResizeColumns(on); 0106 variablesViewConfigGroup().writeEntry(autoResizeColumnsKey, on); 0107 }); 0108 addAction(autoResizeColumnsAction); 0109 0110 //TODO 0111 //connect(plugin, SIGNAL(raiseVariableViews()), this, SIGNAL(requestRaise())); 0112 0113 // Setup help items. 0114 0115 setWhatsThis( i18n( 0116 "<b>Variable tree</b>" 0117 "The variable tree allows you to see the values of local " 0118 "variables and arbitrary expressions.<br />" 0119 "Local variables are displayed automatically and are updated " 0120 "as you step through your program. " 0121 "For each expression you enter, you can either evaluate it once, " 0122 "or \"watch\" it (make it auto-updated). Expressions that are not " 0123 "auto-updated can be updated manually from the context menu. " 0124 "Expressions can be renamed to more descriptive names by clicking " 0125 "on the name column.<br />" 0126 "To change the value of a variable or an expression, " 0127 "click on the value.<br />")); 0128 0129 m_watchVarEditor->setWhatsThis( 0130 i18n("<b>Expression entry</b>" 0131 "Type in expression to watch.")); 0132 0133 } 0134 0135 void VariableWidget::slotAddWatch(const QString &expression) 0136 { 0137 if (!expression.isEmpty()) 0138 { 0139 m_watchVarEditor->addToHistory(expression); 0140 qCDebug(DEBUGGER) << "Trying to add watch"; 0141 Variable* v = m_variablesRoot->watches()->add(expression); 0142 if (v) { 0143 /* For watches on structures, we really do want them to be shown 0144 expanded. Except maybe for structure with custom pretty printing, 0145 but will handle that later. 0146 FIXME: it does not actually works now. 0147 */ 0148 //QModelIndex index = variableCollection()->indexForItem(v, 0); 0149 //varTree_->setExpanded(index, true); 0150 } 0151 m_watchVarEditor->clearEditText(); 0152 } 0153 } 0154 0155 void VariableWidget::hideEvent(QHideEvent* e) 0156 { 0157 QWidget::hideEvent(e); 0158 variableCollection()->variableWidgetHidden(); 0159 } 0160 0161 void VariableWidget::showEvent(QShowEvent* e) 0162 { 0163 QWidget::showEvent(e); 0164 variableCollection()->variableWidgetShown(); 0165 } 0166 0167 // ************************************************************************** 0168 // ************************************************************************** 0169 // ************************************************************************** 0170 0171 VariableTree::VariableTree(IDebugController* controller, VariableWidget* parent, QSortFilterProxyModel* proxy) 0172 : AsyncTreeView(*controller->variableCollection(), parent) 0173 , m_proxy(proxy) 0174 #if 0 0175 , 0176 activePopup_(0), 0177 toggleWatch_(0) 0178 #endif 0179 { 0180 setRootIsDecorated(true); 0181 setAllColumnsShowFocus(true); 0182 0183 // setting proxy model 0184 m_proxy->setSourceModel(&treeModel()); 0185 setModel(m_proxy); 0186 setSortingEnabled(true); 0187 sortByColumn(VariableCollection::NameColumn, Qt::AscendingOrder); 0188 0189 QModelIndex index = controller->variableCollection()->indexForItem( 0190 controller->variableCollection()->watches(), 0); 0191 setExpanded(index, true); 0192 0193 setupActions(); 0194 } 0195 0196 VariableTree::~VariableTree() 0197 { 0198 } 0199 0200 void VariableTree::setupActions() 0201 { 0202 // TODO decorate this properly to make nice menu title 0203 m_contextMenuTitle = new QAction(this); 0204 m_contextMenuTitle->setEnabled(false); 0205 0206 // make Format menu action group 0207 m_formatMenu = new QMenu(i18n("&Format"), this); 0208 auto *ag= new QActionGroup(m_formatMenu); 0209 0210 QAction* act; 0211 0212 act = new QAction(i18n("&Natural"), ag); 0213 act->setData(Variable::Natural); 0214 act->setShortcut(Qt::Key_N); 0215 m_formatMenu->addAction(act); 0216 0217 act = new QAction(i18n("&Binary"), ag); 0218 act->setData(Variable::Binary); 0219 act->setShortcut(Qt::Key_B); 0220 m_formatMenu->addAction(act); 0221 0222 act = new QAction(i18n("&Octal"), ag); 0223 act->setData(Variable::Octal); 0224 act->setShortcut(Qt::Key_O); 0225 m_formatMenu->addAction(act); 0226 0227 act = new QAction(i18n("&Decimal"), ag); 0228 act->setData(Variable::Decimal); 0229 act->setShortcut(Qt::Key_D); 0230 m_formatMenu->addAction(act); 0231 0232 act = new QAction(i18n("&Hexadecimal"), ag); 0233 act->setData(Variable::Hexadecimal); 0234 act->setShortcut(Qt::Key_H); 0235 m_formatMenu->addAction(act); 0236 0237 const auto formatMenuActions = m_formatMenu->actions(); 0238 for (QAction* act : formatMenuActions) { 0239 act->setCheckable(true); 0240 act->setShortcutContext(Qt::WidgetWithChildrenShortcut); 0241 const int id = act->data().toInt(); 0242 connect(act, &QAction::triggered, this, [this, id](){ changeVariableFormat(id); }); 0243 addAction(act); 0244 } 0245 0246 m_watchDelete = new QAction( 0247 QIcon::fromTheme(QStringLiteral("edit-delete")), i18n( "Remove Watch Variable" ), this); 0248 0249 m_watchDelete->setShortcut(Qt::Key_Delete); 0250 m_watchDelete->setShortcutContext(Qt::WidgetWithChildrenShortcut); 0251 addAction(m_watchDelete); 0252 connect(m_watchDelete, &QAction::triggered, this, &VariableTree::watchDelete); 0253 0254 m_copyVariableValue = new QAction(i18n("&Copy Value"), this); 0255 m_copyVariableValue->setShortcutContext(Qt::WidgetWithChildrenShortcut); 0256 m_copyVariableValue->setShortcut(QKeySequence::Copy); 0257 connect(m_copyVariableValue, &QAction::triggered, this, &VariableTree::copyVariableValue); 0258 0259 m_stopOnChange = new QAction(i18n("&Stop on Change"), this); 0260 connect(m_stopOnChange, &QAction::triggered, this, &VariableTree::stopOnChange); 0261 } 0262 0263 Variable* VariableTree::selectedVariable() const 0264 { 0265 if (selectionModel()->selectedRows().isEmpty()) return nullptr; 0266 auto item = selectionModel()->currentIndex().data(TreeModel::ItemRole).value<TreeItem*>(); 0267 if (!item) return nullptr; 0268 return qobject_cast<Variable*>(item); 0269 } 0270 0271 void VariableTree::contextMenuEvent(QContextMenuEvent* event) 0272 { 0273 if (!selectedVariable()) return; 0274 0275 // set up menu 0276 QMenu contextMenu(this->parentWidget()); 0277 m_contextMenuTitle->setText(selectedVariable()->expression()); 0278 contextMenu.addAction(m_contextMenuTitle); 0279 0280 if(selectedVariable()->canSetFormat()) 0281 contextMenu.addMenu(m_formatMenu); 0282 0283 const auto formatMenuActions = m_formatMenu->actions(); 0284 for (QAction* act : formatMenuActions) { 0285 if(act->data().toInt()==selectedVariable()->format()) 0286 act->setChecked(true); 0287 } 0288 0289 if (qobject_cast<Watches*>(selectedVariable()->parent())) { 0290 contextMenu.addAction(m_watchDelete); 0291 } 0292 0293 contextMenu.addSeparator(); 0294 contextMenu.addAction(m_copyVariableValue); 0295 contextMenu.addAction(m_stopOnChange); 0296 0297 contextMenu.exec(event->globalPos()); 0298 } 0299 0300 QModelIndex VariableTree::mapViewIndexToTreeModelIndex(const QModelIndex& viewIndex) const 0301 { 0302 return m_proxy->mapToSource(viewIndex); 0303 } 0304 0305 void VariableTree::changeVariableFormat(int format) 0306 { 0307 if (!selectedVariable()) return; 0308 selectedVariable()->setFormat(static_cast<Variable::format_t>(format)); 0309 } 0310 0311 void VariableTree::watchDelete() 0312 { 0313 if (!selectedVariable()) return; 0314 if (!qobject_cast<Watches*>(selectedVariable()->parent())) return; 0315 selectedVariable()->die(); 0316 } 0317 0318 void VariableTree::copyVariableValue() 0319 { 0320 if (!selectedVariable()) return; 0321 QApplication::clipboard()->setText(selectedVariable()->value()); 0322 } 0323 0324 void VariableTree::stopOnChange() 0325 { 0326 if (!selectedVariable()) return; 0327 IDebugSession *session = ICore::self()->debugController()->currentSession(); 0328 if (session && session->state() != IDebugSession::NotStartedState && session->state() != IDebugSession::EndedState) { 0329 session->variableController()->addWatchpoint(selectedVariable()); 0330 } 0331 } 0332 } 0333 0334 #include "moc_variablewidget.cpp"