1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtSCriptTools module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qscriptbreakpointswidget_p.h"
41#include "qscriptbreakpointswidgetinterface_p_p.h"
42#include "qscriptbreakpointsmodel_p.h"
43#include "qscriptdebuggerscriptsmodel_p.h"
44
45#include <QtCore/qdebug.h>
46#include <QtWidgets/qaction.h>
47#include <QtWidgets/qcompleter.h>
48#include <QtWidgets/qheaderview.h>
49#include <QtWidgets/qlineedit.h>
50#include <QtWidgets/qmessagebox.h>
51#include <QtWidgets/qtoolbar.h>
52#include <QtWidgets/qtoolbutton.h>
53#include <QtWidgets/qtreeview.h>
54#include <QtWidgets/qboxlayout.h>
55#include <QtWidgets/qstyleditemdelegate.h>
56#include <QtGui/qevent.h>
57#include <QtScript/qscriptengine.h>
58
59QT_BEGIN_NAMESPACE
60
61class QScriptNewBreakpointWidget : public QWidget
62{
63 Q_OBJECT
64public:
65 QScriptNewBreakpointWidget(QWidget *parent = 0)
66 : QWidget(parent) {
67 QString system = QLatin1String("win");
68 QHBoxLayout *hboxLayout = new QHBoxLayout(this);
69#ifdef Q_OS_MAC
70 system = QLatin1String("mac");
71#else
72 hboxLayout->setSpacing(6);
73 hboxLayout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
74#endif
75
76 toolClose = new QToolButton(this);
77 toolClose->setIcon(QIcon(QString::fromUtf8(str: ":/qt/scripttools/debugging/images/%1/closetab.png").arg(a: system)));
78 toolClose->setAutoRaise(true);
79 toolClose->setText(tr(s: "Close"));
80 hboxLayout->addWidget(toolClose);
81
82 fileNameEdit = new QLineEdit();
83 setFocusProxy(fileNameEdit);
84 QRegExp locationRegExp(QString::fromLatin1(str: ".+:[0-9]+"));
85 QRegExpValidator *validator = new QRegExpValidator(locationRegExp, fileNameEdit);
86 fileNameEdit->setValidator(validator);
87 hboxLayout->addWidget(fileNameEdit);
88
89 toolOk = new QToolButton(this);
90 toolOk->setIcon(QIcon(QString::fromUtf8(str: ":/qt/scripttools/debugging/images/%1/plus.png").arg(a: system)));
91 toolOk->setAutoRaise(true);
92 toolOk->setEnabled(false);
93 hboxLayout->addWidget(toolOk);
94
95 QObject::connect(sender: toolClose, SIGNAL(clicked()), receiver: this, SLOT(hide()));
96 QObject::connect(sender: toolOk, SIGNAL(clicked()), receiver: this, SLOT(onOkClicked()));
97 QObject::connect(sender: fileNameEdit, SIGNAL(textChanged(QString)),
98 receiver: this, SLOT(onTextChanged()));
99 QObject::connect(sender: fileNameEdit, SIGNAL(returnPressed()),
100 receiver: this, SLOT(onOkClicked()));
101 }
102
103 void setCompleter(QCompleter *comp)
104 { fileNameEdit->setCompleter(comp); }
105
106Q_SIGNALS:
107 void newBreakpointRequest(const QString &fileName, int lineNumber);
108
109protected:
110 void keyPressEvent(QKeyEvent *e)
111 {
112 if (e->key() == Qt::Key_Escape)
113 hide();
114 else
115 QWidget::keyPressEvent(event: e);
116 }
117
118private Q_SLOTS:
119 void onOkClicked()
120 {
121 QString location = fileNameEdit->text();
122 fileNameEdit->clear();
123 QString fileName = location.left(n: location.lastIndexOf(c: QLatin1Char(':')));
124 int lineNumber = location.mid(position: fileName.length()+1).toInt();
125 emit newBreakpointRequest(fileName, lineNumber);
126 }
127
128 void onTextChanged()
129 {
130 toolOk->setEnabled(fileNameEdit->hasAcceptableInput());
131 }
132
133private:
134 QLineEdit *fileNameEdit;
135 QToolButton *toolClose;
136 QToolButton *toolOk;
137};
138
139
140
141class QScriptBreakpointsWidgetPrivate
142 : public QScriptBreakpointsWidgetInterfacePrivate
143{
144 Q_DECLARE_PUBLIC(QScriptBreakpointsWidget)
145public:
146 QScriptBreakpointsWidgetPrivate();
147 ~QScriptBreakpointsWidgetPrivate();
148
149 void _q_newBreakpoint();
150 void _q_deleteBreakpoint();
151 void _q_onCurrentChanged(const QModelIndex &index);
152 void _q_onNewBreakpointRequest(const QString &fileName, int lineNumber);
153
154 static QPixmap pixmap(const QString &path)
155 {
156 static QString prefix = QString::fromLatin1(str: ":/qt/scripttools/debugging/images/");
157 return QPixmap(prefix + path);
158 }
159
160 QTreeView *view;
161 QScriptNewBreakpointWidget *newBreakpointWidget;
162 QAction *deleteBreakpointAction;
163 QScriptDebuggerScriptsModel *scriptsModel;
164};
165
166QScriptBreakpointsWidgetPrivate::QScriptBreakpointsWidgetPrivate()
167{
168}
169
170QScriptBreakpointsWidgetPrivate::~QScriptBreakpointsWidgetPrivate()
171{
172}
173
174void QScriptBreakpointsWidgetPrivate::_q_newBreakpoint()
175{
176 newBreakpointWidget->show();
177 newBreakpointWidget->setFocus(Qt::OtherFocusReason);
178}
179
180void QScriptBreakpointsWidgetPrivate::_q_deleteBreakpoint()
181{
182 Q_Q(QScriptBreakpointsWidget);
183 QModelIndex index = view->currentIndex();
184 if (index.isValid()) {
185 int id = q->breakpointsModel()->breakpointIdAt(row: index.row());
186 q->breakpointsModel()->deleteBreakpoint(id);
187 }
188}
189
190void QScriptBreakpointsWidgetPrivate::_q_onCurrentChanged(const QModelIndex &index)
191{
192 deleteBreakpointAction->setEnabled(index.isValid());
193}
194
195void QScriptBreakpointsWidgetPrivate::_q_onNewBreakpointRequest(const QString &fileName, int lineNumber)
196{
197 QScriptBreakpointData data(fileName, lineNumber);
198 q_func()->breakpointsModel()->setBreakpoint(data);
199}
200
201class QScriptBreakpointsItemDelegate : public QStyledItemDelegate
202{
203 Q_OBJECT
204public:
205 QScriptBreakpointsItemDelegate(QObject *parent = 0)
206 : QStyledItemDelegate(parent) {}
207
208 QWidget *createEditor(QWidget *parent,
209 const QStyleOptionViewItem &option,
210 const QModelIndex &index) const
211 {
212 QWidget *editor = QStyledItemDelegate::createEditor(parent, option, index);
213 if (index.column() == 2) {
214 // condition
215 QLineEdit *le = qobject_cast<QLineEdit*>(object: editor);
216 if (le) {
217 QObject::connect(sender: le, SIGNAL(textEdited(QString)),
218 receiver: this, SLOT(validateInput(QString)));
219 }
220 }
221 return editor;
222 }
223
224 bool eventFilter(QObject *editor, QEvent *event)
225 {
226 if (QLineEdit *le = qobject_cast<QLineEdit*>(object: editor)) {
227 if (event->type() == QEvent::KeyPress) {
228 int key = static_cast<QKeyEvent*>(event)->key();
229 if ((key == Qt::Key_Enter) || (key == Qt::Key_Return)) {
230 if (QScriptEngine::checkSyntax(program: le->text()).state() != QScriptSyntaxCheckResult::Valid) {
231 // ignore when script contains syntax error
232 return true;
233 }
234 }
235 }
236 }
237 return QStyledItemDelegate::eventFilter(object: editor, event);
238 }
239
240 void setModelData(QWidget *editor, QAbstractItemModel *model,
241 const QModelIndex &index) const
242 {
243 if (index.column() == 2) {
244 // check that the syntax is OK
245 QString condition = qobject_cast<QLineEdit*>(object: editor)->text();
246 if (QScriptEngine::checkSyntax(program: condition).state() != QScriptSyntaxCheckResult::Valid)
247 return;
248 }
249 QStyledItemDelegate::setModelData(editor, model, index);
250 }
251
252private Q_SLOTS:
253 void validateInput(const QString &text)
254 {
255 QWidget *editor = qobject_cast<QWidget*>(o: sender());
256 QPalette pal = editor->palette();
257 QColor col;
258 bool ok = (QScriptEngine::checkSyntax(program: text).state() == QScriptSyntaxCheckResult::Valid);
259 if (ok) {
260 col = Qt::white;
261 } else {
262 QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(
263 program: text + QLatin1Char('\n'));
264 if (result.state() == QScriptSyntaxCheckResult::Intermediate)
265 col = QColor(255, 240, 192);
266 else
267 col = QColor(255, 102, 102);
268 }
269 pal.setColor(acg: QPalette::Active, acr: QPalette::Base, acolor: col);
270 editor->setPalette(pal);
271 }
272};
273
274QScriptBreakpointsWidget::QScriptBreakpointsWidget(QWidget *parent)
275 : QScriptBreakpointsWidgetInterface(*new QScriptBreakpointsWidgetPrivate, parent, {})
276{
277 Q_D(QScriptBreakpointsWidget);
278 d->view = new QTreeView();
279// d->view->setEditTriggers(QAbstractItemView::NoEditTriggers);
280 d->view->setEditTriggers(QAbstractItemView::AllEditTriggers);
281// d->view->setAlternatingRowColors(true);
282 d->view->setRootIsDecorated(false);
283 d->view->setSelectionBehavior(QAbstractItemView::SelectRows);
284// d->view->header()->hide();
285// d->view->header()->setDefaultAlignment(Qt::AlignLeft);
286// d->view->header()->setResizeMode(QHeaderView::ResizeToContents);
287 d->view->setItemDelegate(new QScriptBreakpointsItemDelegate(this));
288
289 d->newBreakpointWidget = new QScriptNewBreakpointWidget();
290 d->newBreakpointWidget->hide();
291 QObject::connect(sender: d->newBreakpointWidget, SIGNAL(newBreakpointRequest(QString,int)),
292 receiver: this, SLOT(_q_onNewBreakpointRequest(QString,int)));
293
294 QIcon newBreakpointIcon;
295 newBreakpointIcon.addPixmap(pixmap: d->pixmap(path: QString::fromLatin1(str: "new.png")), mode: QIcon::Normal);
296 QAction *newBreakpointAction = new QAction(newBreakpointIcon, tr(s: "New"), this);
297 QObject::connect(sender: newBreakpointAction, SIGNAL(triggered()),
298 receiver: this, SLOT(_q_newBreakpoint()));
299
300 QIcon deleteBreakpointIcon;
301 deleteBreakpointIcon.addPixmap(pixmap: d->pixmap(path: QString::fromLatin1(str: "delete.png")), mode: QIcon::Normal);
302 d->deleteBreakpointAction = new QAction(deleteBreakpointIcon, tr(s: "Delete"), this);
303 d->deleteBreakpointAction->setEnabled(false);
304 QObject::connect(sender: d->deleteBreakpointAction, SIGNAL(triggered()),
305 receiver: this, SLOT(_q_deleteBreakpoint()));
306
307#ifndef QT_NO_TOOLBAR
308 QToolBar *toolBar = new QToolBar();
309 toolBar->addAction(action: newBreakpointAction);
310 toolBar->addAction(action: d->deleteBreakpointAction);
311#endif
312
313 QVBoxLayout *vbox = new QVBoxLayout(this);
314 vbox->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
315#ifndef QT_NO_TOOLBAR
316 vbox->addWidget(toolBar);
317#endif
318 vbox->addWidget(d->newBreakpointWidget);
319 vbox->addWidget(d->view);
320}
321
322QScriptBreakpointsWidget::~QScriptBreakpointsWidget()
323{
324}
325
326/*!
327 \reimp
328*/
329QScriptBreakpointsModel *QScriptBreakpointsWidget::breakpointsModel() const
330{
331 Q_D(const QScriptBreakpointsWidget);
332 return qobject_cast<QScriptBreakpointsModel*>(object: d->view->model());
333}
334
335/*!
336 \reimp
337*/
338void QScriptBreakpointsWidget::setBreakpointsModel(QScriptBreakpointsModel *model)
339{
340 Q_D(QScriptBreakpointsWidget);
341 d->view->setModel(model);
342 d->view->header()->resizeSection(logicalIndex: 0, size: 50);
343 QObject::connect(sender: d->view->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
344 receiver: this, SLOT(_q_onCurrentChanged(QModelIndex)));
345}
346
347/*!
348 \reimp
349*/
350QScriptDebuggerScriptsModel *QScriptBreakpointsWidget::scriptsModel() const
351{
352 Q_D(const QScriptBreakpointsWidget);
353 return d->scriptsModel;
354}
355
356/*!
357 \reimp
358*/
359void QScriptBreakpointsWidget::setScriptsModel(QScriptDebuggerScriptsModel *model)
360{
361 Q_D(QScriptBreakpointsWidget);
362 d->scriptsModel = model;
363 QCompleter *completer = new QCompleter(model, this);
364 completer->setCompletionRole(Qt::DisplayRole);
365 d->newBreakpointWidget->setCompleter(completer);
366}
367
368/*!
369 \reimp
370*/
371void QScriptBreakpointsWidget::keyPressEvent(QKeyEvent *e)
372{
373 Q_D(QScriptBreakpointsWidget);
374 if (e->key() == Qt::Key_Delete) {
375 QModelIndex index = d->view->currentIndex();
376 if (!index.isValid())
377 return;
378 int id = breakpointsModel()->breakpointIdAt(row: index.row());
379 breakpointsModel()->deleteBreakpoint(id);
380 }
381}
382
383QT_END_NAMESPACE
384
385#include "qscriptbreakpointswidget.moc"
386
387#include "moc_qscriptbreakpointswidget_p.cpp"
388

source code of qtscript/src/scripttools/debugging/qscriptbreakpointswidget.cpp