1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtSCriptTools module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
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.
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.
28 ** GNU General Public License Usage
29 ** Alternatively, this file may be used under the terms of the GNU
30 ** General Public License version 3.0 as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL included in the
32 ** packaging of this file. Please review the following information to
33 ** ensure the GNU General Public License version 3.0 requirements will be
34 ** met: http://www.gnu.org/copyleft/gpl.html.
36 ** If you have questions regarding the use of this file, please contact
37 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "qscriptdebuggerlocalswidget_p.h"
43 #include "qscriptdebuggerlocalswidgetinterface_p_p.h"
44 #include "qscriptdebuggerlocalsmodel_p.h"
45 #include "qscriptcompletionproviderinterface_p.h"
46 #include "qscriptcompletiontaskinterface_p.h"
48 #include <QtCore/qdebug.h>
49 #include <QtGui/qheaderview.h>
50 #include <QtGui/qcompleter.h>
51 #include <QtGui/qstringlistmodel.h>
52 #include <QtGui/qtreeview.h>
53 #include <QtGui/qboxlayout.h>
54 #include <QtGui/qsortfilterproxymodel.h>
55 #include <QtGui/qlineedit.h>
56 #include <QtGui/qstyleditemdelegate.h>
57 #include <QtGui/qevent.h>
58 #include <QtGui/qmessagebox.h>
59 #include <QtScript/qscriptengine.h>
65 class CustomProxyModel
: public QSortFilterProxyModel
68 CustomProxyModel(QObject
*parent
= 0)
69 : QSortFilterProxyModel(parent
) {}
71 bool hasChildren(const QModelIndex
&parent
) const
73 QModelIndex sourceParent
= mapToSource(parent
);
74 if (parent
.isValid() && !sourceParent
.isValid())
76 return sourceModel()->hasChildren(sourceParent
);
82 class QScriptDebuggerLocalsWidgetPrivate
83 : public QScriptDebuggerLocalsWidgetInterfacePrivate
85 Q_DECLARE_PUBLIC(QScriptDebuggerLocalsWidget
)
87 QScriptDebuggerLocalsWidgetPrivate();
88 ~QScriptDebuggerLocalsWidgetPrivate();
90 void complete(QLineEdit
*le
);
93 void _q_onCompletionTaskFinished();
94 void _q_insertCompletion(const QString
&text
);
95 void _q_expandIndex(const QModelIndex
&index
);
98 QPointer
<QLineEdit
> completingEditor
;
99 QCompleter
*completer
;
100 CustomProxyModel
*proxy
;
103 QScriptDebuggerLocalsWidgetPrivate::QScriptDebuggerLocalsWidgetPrivate()
105 completingEditor
= 0;
110 QScriptDebuggerLocalsWidgetPrivate::~QScriptDebuggerLocalsWidgetPrivate()
114 void QScriptDebuggerLocalsWidgetPrivate::complete(QLineEdit
*le
)
116 Q_Q(QScriptDebuggerLocalsWidget
);
117 QScriptCompletionTaskInterface
*task
= 0;
118 // ### need to pass the current frame #
119 task
= completionProvider
->createCompletionTask(
120 le
->text(), le
->cursorPosition(),
121 q
->localsModel()->frameIndex(), /*options=*/0);
122 QObject::connect(task
, SIGNAL(finished()),
123 q
, SLOT(_q_onCompletionTaskFinished()));
124 completingEditor
= le
;
128 void QScriptDebuggerLocalsWidgetPrivate::_q_onCompletionTaskFinished()
130 Q_Q(QScriptDebuggerLocalsWidget
);
131 QScriptCompletionTaskInterface
*task
= 0;
132 task
= qobject_cast
<QScriptCompletionTaskInterface
*>(q_func()->sender());
133 if (!completingEditor
) {
138 if (task
->resultCount() == 1) {
139 // do the completion right away
140 QString completion
= task
->resultAt(0);
141 completion
.append(task
->appendix());
142 QString tmp
= completingEditor
->text();
143 tmp
.remove(task
->position(), task
->length());
144 tmp
.insert(task
->position(), completion
);
145 completingEditor
->setText(tmp
);
146 completingEditor
= 0;
147 } else if (task
->resultCount() > 1) {
150 completer
= new QCompleter(q
);
151 completer
->setCompletionMode(QCompleter::PopupCompletion
);
152 completer
->setCaseSensitivity(Qt::CaseSensitive
);
153 completer
->setWrapAround(false);
154 QObject::connect(completer
, SIGNAL(activated(QString
)),
155 q
, SLOT(_q_insertCompletion(QString
)));
157 QStringListModel
*model
= qobject_cast
<QStringListModel
*>(completer
->model());
159 model
= new QStringListModel(q
);
160 completer
->setModel(model
);
163 for (int i
= 0; i
< task
->resultCount(); ++i
)
164 strings
.append(task
->resultAt(i
));
165 model
->setStringList(strings
);
166 QString prefix
= completingEditor
->text().mid(task
->position(), task
->length());
167 completer
->setCompletionPrefix(prefix
);
168 completingEditor
->setCompleter(completer
);
169 // we want to handle the insertion ourselves
170 QObject::disconnect(completer
, 0, completingEditor
, 0);
171 completer
->complete();
176 void QScriptDebuggerLocalsWidgetPrivate::_q_insertCompletion(const QString
&text
)
178 Q_ASSERT(completingEditor
!= 0);
179 QString tmp
= completingEditor
->text();
180 tmp
.insert(completingEditor
->cursorPosition(), text
.mid(completer
->completionPrefix().length()));
181 completingEditor
->setText(tmp
);
182 completingEditor
= 0;
185 void QScriptDebuggerLocalsWidgetPrivate::_q_expandIndex(const QModelIndex
&index
)
187 view
->expand(proxy
->mapFromSource(index
));
190 class QScriptDebuggerLocalsItemDelegate
191 : public QStyledItemDelegate
195 QScriptDebuggerLocalsItemDelegate(QObject
*parent
= 0);
197 QWidget
*createEditor(QWidget
*parent
, const QStyleOptionViewItem
&option
, const QModelIndex
&index
) const;
198 void setModelData(QWidget
*editor
, QAbstractItemModel
*model
, const QModelIndex
&index
) const;
199 void paint(QPainter
*painter
, const QStyleOptionViewItem
&option
, const QModelIndex
&index
) const;
201 bool eventFilter(QObject
*watched
, QEvent
*event
);
204 void validateInput(const QString
&text
)
206 QWidget
*editor
= qobject_cast
<QWidget
*>(sender());
207 QPalette pal
= editor
->palette();
209 bool ok
= (QScriptEngine::checkSyntax(text
).state() == QScriptSyntaxCheckResult::Valid
);
213 QScriptSyntaxCheckResult result
= QScriptEngine::checkSyntax(
214 text
+ QLatin1Char('\n'));
215 if (result
.state() == QScriptSyntaxCheckResult::Intermediate
)
216 col
= QColor(255, 240, 192);
218 col
= QColor(255, 102, 102);
220 pal
.setColor(QPalette::Active
, QPalette::Base
, col
);
221 editor
->setPalette(pal
);
225 static const QWidget
*widget(const QStyleOptionViewItem
&option
)
227 if (const QStyleOptionViewItemV3
*v3
= qstyleoption_cast
<const QStyleOptionViewItemV3
*>(&option
))
233 QScriptDebuggerLocalsItemDelegate::QScriptDebuggerLocalsItemDelegate(
235 : QStyledItemDelegate(parent
)
239 QWidget
*QScriptDebuggerLocalsItemDelegate::createEditor(
240 QWidget
*parent
, const QStyleOptionViewItem
&option
,
241 const QModelIndex
&index
) const
243 QWidget
*editor
= QStyledItemDelegate::createEditor(parent
, option
, index
);
244 if (index
.column() == 1) {
246 QLineEdit
*le
= qobject_cast
<QLineEdit
*>(editor
);
248 QObject::connect(le
, SIGNAL(textEdited(QString
)),
249 this, SLOT(validateInput(QString
)));
255 bool QScriptDebuggerLocalsItemDelegate::eventFilter(QObject
*watched
, QEvent
*event
)
257 QLineEdit
*le
= qobject_cast
<QLineEdit
*>(watched
);
259 return QStyledItemDelegate::eventFilter(watched
, event
);
261 QScriptDebuggerLocalsWidget
*localsWidget
= qobject_cast
<QScriptDebuggerLocalsWidget
*>(parent());
262 Q_ASSERT(localsWidget
!= 0);
263 QScriptDebuggerLocalsWidgetPrivate
*lvp
=
264 reinterpret_cast<QScriptDebuggerLocalsWidgetPrivate
*>(
265 QScriptDebuggerLocalsWidgetPrivate::get(localsWidget
));
267 if ((event
->type() == QEvent::FocusIn
) && lvp
->completingEditor
) {
268 // because QLineEdit insists on being difficult...
272 if (event
->type() != QEvent::KeyPress
)
273 return QStyledItemDelegate::eventFilter(watched
, event
);
274 QKeyEvent
*ke
= static_cast<QKeyEvent
*>(event
);
275 if ((ke
->key() == Qt::Key_Enter
) || (ke
->key() == Qt::Key_Return
)) {
276 if (QScriptEngine::checkSyntax(le
->text()).state() != QScriptSyntaxCheckResult::Valid
) {
277 // ignore when script contains syntax error
281 if (ke
->key() != Qt::Key_Tab
)
282 return QStyledItemDelegate::eventFilter(watched
, event
);
284 // trigger completion
289 void QScriptDebuggerLocalsItemDelegate::setModelData(
290 QWidget
*editor
, QAbstractItemModel
*model
,
291 const QModelIndex
&index
) const
293 if (index
.column() == 1) {
294 // check that the syntax is OK
295 QString expression
= qobject_cast
<QLineEdit
*>(editor
)->text();
296 if (QScriptEngine::checkSyntax(expression
).state() != QScriptSyntaxCheckResult::Valid
)
299 QStyledItemDelegate::setModelData(editor
, model
, index
);
302 void QScriptDebuggerLocalsItemDelegate::paint(QPainter
*painter
, const QStyleOptionViewItem
&option
,
303 const QModelIndex
&index
) const
305 QStyledItemDelegate::paint(painter
, option
, index
);
307 QModelIndex parent
= index
.parent();
308 if (parent
.isValid()) {
309 QStyledItemDelegate::paint(painter
, option
, index
);
311 // this is a top-level item.
312 const QTreeView
*view
= qobject_cast
<const QTreeView
*>(widget(option
));
315 QStyleOptionButton buttonOption
;
317 buttonOption
.state
= option
.state
;
319 buttonOption
.state
|= QStyle::State_Raised
;
321 buttonOption
.state
&= ~QStyle::State_HasFocus
;
323 buttonOption
.rect
= option
.rect
;
324 buttonOption
.palette
= option
.palette
;
325 buttonOption
.features
= QStyleOptionButton::None
;
326 view
->style()->drawControl(QStyle::CE_PushButton
, &buttonOption
, painter
, view
);
328 QStyleOption branchOption
;
329 static const int i
= 9; // ### hardcoded in qcommonstyle.cpp
330 QRect r
= option
.rect
;
331 branchOption
.rect
= QRect(r
.left() + i
/2, r
.top() + (r
.height() - i
)/2, i
, i
);
332 branchOption
.palette
= option
.palette
;
333 branchOption
.state
= QStyle::State_Children
;
335 if (view
->isExpanded(index
))
336 branchOption
.state
|= QStyle::State_Open
;
338 view
->style()->drawPrimitive(QStyle::PE_IndicatorBranch
, &branchOption
, painter
, view
);
341 QRect textrect
= QRect(r
.left() + i
*2, r
.top(), r
.width() - ((5*i
)/2), r
.height());
342 QString text
= elidedText(option
.fontMetrics
, textrect
.width(), Qt::ElideMiddle
,
343 index
.data(Qt::DisplayRole
).toString());
344 view
->style()->drawItemText(painter
, textrect
, Qt::AlignCenter
,
345 option
.palette
, view
->isEnabled(), text
);
350 QScriptDebuggerLocalsWidget::QScriptDebuggerLocalsWidget(QWidget
*parent
)
351 : QScriptDebuggerLocalsWidgetInterface(*new QScriptDebuggerLocalsWidgetPrivate
, parent
, 0)
353 Q_D(QScriptDebuggerLocalsWidget
);
354 d
->view
= new QTreeView();
355 d
->view
->setItemDelegate(new QScriptDebuggerLocalsItemDelegate(this));
356 d
->view
->setEditTriggers(QAbstractItemView::DoubleClicked
);
357 // d->view->setEditTriggers(QAbstractItemView::NoEditTriggers);
358 d
->view
->setEditTriggers(QAbstractItemView::DoubleClicked
| QAbstractItemView::SelectedClicked
);
359 d
->view
->setAlternatingRowColors(true);
360 d
->view
->setSelectionBehavior(QAbstractItemView::SelectRows
);
361 d
->view
->setSortingEnabled(true);
362 d
->view
->header()->setDefaultAlignment(Qt::AlignLeft
);
363 // d->view->header()->setSortIndicatorShown(true);
364 // d->view->header()->setResizeMode(QHeaderView::ResizeToContents);
366 QVBoxLayout
*vbox
= new QVBoxLayout(this);
368 vbox
->addWidget(d
->view
);
371 QScriptDebuggerLocalsWidget::~QScriptDebuggerLocalsWidget()
378 QScriptDebuggerLocalsModel
*QScriptDebuggerLocalsWidget::localsModel() const
380 Q_D(const QScriptDebuggerLocalsWidget
);
383 return qobject_cast
<QScriptDebuggerLocalsModel
*>(d
->proxy
->sourceModel());
389 void QScriptDebuggerLocalsWidget::setLocalsModel(QScriptDebuggerLocalsModel
*model
)
391 Q_D(QScriptDebuggerLocalsWidget
);
393 QObject::disconnect(localsModel(), 0, d
->view
, 0);
396 QObject::connect(model
, SIGNAL(scopeObjectAvailable(QModelIndex
)),
397 this, SLOT(_q_expandIndex(QModelIndex
)));
400 d
->proxy
= new CustomProxyModel(this);
401 d
->view
->sortByColumn(0, Qt::AscendingOrder
);
403 d
->proxy
->setSourceModel(model
);
404 d
->view
->setModel(d
->proxy
);
410 void QScriptDebuggerLocalsWidget::expand(const QModelIndex
&index
)
412 Q_D(QScriptDebuggerLocalsWidget
);
413 d
->view
->expand(index
);
414 d
->view
->setFirstColumnSpanned(index
.row(), QModelIndex(), true);
419 #include "qscriptdebuggerlocalswidget.moc"
421 #include "moc_qscriptdebuggerlocalswidget_p.cpp"