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