source: trunk/src/scripttools/debugging/qscriptedit.cpp@ 1168

Last change on this file since 1168 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 14.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtSCriptTools module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
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.
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** 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.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qscriptedit_p.h"
43#include "qscriptsyntaxhighlighter_p.h"
44
45#include <QtGui/qpainter.h>
46#include <QtGui/qicon.h>
47#include <QtGui/qboxlayout.h>
48#include <QtGui/qlabel.h>
49#include <QtGui/qlineedit.h>
50#include <QtGui/qmenu.h>
51#include <QtGui/qaction.h>
52#include <QtGui/qwidgetaction.h>
53#include <QtCore/qdebug.h>
54
55QT_BEGIN_NAMESPACE
56
57class QScriptEditExtraArea : public QWidget
58{
59public:
60 QScriptEditExtraArea(QScriptEdit *edit)
61 : QWidget(edit)
62 {
63 setMouseTracking(true);
64 }
65
66 QSize sizeHint() const {
67 return QSize(editor()->extraAreaWidth(), 0);
68 }
69
70protected:
71 void paintEvent(QPaintEvent *event)
72 {
73 editor()->extraAreaPaintEvent(event);
74 }
75 void mousePressEvent(QMouseEvent *event)
76 {
77 editor()->extraAreaMouseEvent(event);
78 }
79 void mouseMoveEvent(QMouseEvent *event)
80 {
81 editor()->extraAreaMouseEvent(event);
82 }
83 void mouseReleaseEvent(QMouseEvent *event)
84 {
85 editor()->extraAreaMouseEvent(event);
86 }
87 bool event(QEvent *event)
88 {
89 if (editor()->extraAreaEvent(event))
90 return true;
91 return QWidget::event(event);
92 }
93
94private:
95 QScriptEdit *editor() const
96 {
97 return qobject_cast<QScriptEdit*>(parent());
98 }
99};
100
101
102
103QScriptEdit::QScriptEdit(QWidget *parent)
104 : QPlainTextEdit(parent)
105{
106 m_baseLineNumber = 1;
107 m_executionLineNumber = -1;
108
109 m_extraArea = new QScriptEditExtraArea(this);
110
111 QObject::connect(this, SIGNAL(blockCountChanged(int)),
112 this, SLOT(updateExtraAreaWidth()));
113 QObject::connect(this, SIGNAL(updateRequest(QRect,int)),
114 this, SLOT(updateExtraArea(QRect,int)));
115 QObject::connect(this, SIGNAL(cursorPositionChanged()),
116 this, SLOT(highlightCurrentLine()));
117
118 updateExtraAreaWidth();
119
120#ifndef QT_NO_SYNTAXHIGHLIGHTER
121 (void) new QScriptSyntaxHighlighter(document());
122#endif
123}
124
125QScriptEdit::~QScriptEdit()
126{
127}
128
129int QScriptEdit::baseLineNumber() const
130{
131 return m_baseLineNumber;
132}
133
134void QScriptEdit::setBaseLineNumber(int base)
135{
136 m_baseLineNumber = base;
137 m_extraArea->update();
138}
139
140int QScriptEdit::executionLineNumber() const
141{
142 return m_executionLineNumber;
143}
144
145void QScriptEdit::setExecutionLineNumber(int lineNumber, bool error)
146{
147 m_executionLineNumber = lineNumber;
148 m_executionLineNumberHasError = error;
149 m_extraArea->update();
150 updateExtraSelections();
151 gotoLine(lineNumber);
152}
153
154void QScriptEdit::setExecutableLineNumbers(const QSet<int> &lineNumbers)
155{
156 m_executableLineNumbers = lineNumbers;
157}
158
159bool QScriptEdit::isExecutableLine(int lineNumber) const
160{
161#if 0 // ### enable me once we have information about the script again
162 return m_executableLineNumbers.contains(lineNumber);
163#else
164 Q_UNUSED(lineNumber);
165 return true;
166#endif
167}
168
169int QScriptEdit::currentLineNumber() const
170{
171 return textCursor().blockNumber() + m_baseLineNumber;
172}
173
174void QScriptEdit::gotoLine(int lineNumber)
175{
176 int blockNumber = lineNumber - m_baseLineNumber;
177 const QTextBlock &block = document()->findBlockByNumber(blockNumber);
178 if (block.isValid()) {
179 setTextCursor(QTextCursor(block));
180 centerCursor();
181 }
182}
183
184void QScriptEdit::setBreakpoint(int lineNumber)
185{
186 m_breakpoints[lineNumber] = BreakpointData();
187 m_extraArea->update();
188}
189
190void QScriptEdit::setBreakpointEnabled(int lineNumber, bool enable)
191{
192 m_breakpoints[lineNumber].enabled = enable;
193 m_extraArea->update();
194}
195
196void QScriptEdit::deleteBreakpoint(int lineNumber)
197{
198 m_breakpoints.remove(lineNumber);
199 m_extraArea->update();
200}
201
202void QScriptEdit::paintEvent(QPaintEvent *e)
203{
204 QPlainTextEdit::paintEvent(e);
205}
206
207void QScriptEdit::resizeEvent(QResizeEvent *e)
208{
209 QPlainTextEdit::resizeEvent(e);
210
211 QRect cr = contentsRect();
212 int x = isLeftToRight() ? cr.left() : cr.left() + cr.width() - extraAreaWidth();
213 m_extraArea->setGeometry(QRect(x, cr.top(), extraAreaWidth(), cr.height()));
214}
215
216void QScriptEdit::updateExtraAreaWidth()
217{
218 if (isLeftToRight())
219 setViewportMargins(extraAreaWidth(), 0, 0, 0);
220 else
221 setViewportMargins(0, 0, extraAreaWidth(), 0);
222}
223
224void QScriptEdit::updateExtraArea(const QRect &rect, int dy)
225{
226 if (dy)
227 m_extraArea->scroll(0, dy);
228 else
229 m_extraArea->update(0, rect.y(), m_extraArea->width(), rect.height());
230
231 if (rect.contains(viewport()->rect()))
232 updateExtraAreaWidth();
233}
234
235void QScriptEdit::highlightCurrentLine()
236{
237 updateExtraSelections();
238}
239
240void QScriptEdit::updateExtraSelections()
241{
242 QList<QTextEdit::ExtraSelection> extraSelections;
243
244 {
245 QTextEdit::ExtraSelection selection;
246 QColor lineColor = QColor(Qt::yellow).lighter(160);
247 selection.format.setBackground(lineColor);
248 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
249 selection.cursor = textCursor();
250 selection.cursor.clearSelection();
251 extraSelections.append(selection);
252 }
253 if (m_executionLineNumber != -1) {
254 QTextEdit::ExtraSelection selection;
255 QColor lineColor;
256 if (m_executionLineNumberHasError)
257 lineColor = QColor(Qt::red);
258 else
259 lineColor = QColor(Qt::green).lighter(160);
260 selection.format.setBackground(lineColor);
261 selection.format.setProperty(QTextFormat::FullWidthSelection, true);
262 int blockNumber = m_executionLineNumber - m_baseLineNumber;
263 selection.cursor = QTextCursor(document()->findBlockByNumber(blockNumber));
264 selection.cursor.clearSelection();
265 extraSelections.append(selection);
266 }
267
268 setExtraSelections(extraSelections);
269}
270
271int QScriptEdit::extraAreaWidth() const
272{
273 int space = 0;
274 const QFontMetrics fm(fontMetrics());
275
276 int digits = 1;
277 int max = qMax(1, blockCount() + m_baseLineNumber);
278 while (max >= 10) {
279 max /= 10;
280 ++digits;
281 }
282 space += fm.width(QLatin1Char('9')) * digits;
283
284 int markWidth = fm.lineSpacing();
285 space += markWidth;
286
287 space += 4;
288
289 return space;
290}
291
292void QScriptEdit::extraAreaPaintEvent(QPaintEvent *e)
293{
294 QRect rect = e->rect();
295 QPalette pal = palette();
296 pal.setCurrentColorGroup(QPalette::Active);
297 QPainter painter(m_extraArea);
298 painter.fillRect(rect, Qt::lightGray);
299 const QFontMetrics fm(fontMetrics());
300
301 int markWidth = fm.lineSpacing();
302 int extraAreaWidth = m_extraArea->width();
303
304 QLinearGradient gradient(QPointF(extraAreaWidth - 10, 0), QPointF(extraAreaWidth, 0));
305 gradient.setColorAt(0, pal.color(QPalette::Background));
306 gradient.setColorAt(1, pal.color(QPalette::Base));
307 painter.fillRect(rect, gradient);
308
309 QLinearGradient gradient2(QPointF(0, 0), QPointF(markWidth, 0));
310 gradient2.setColorAt(0, pal.color(QPalette::Dark));
311 gradient2.setColorAt(1, pal.color(QPalette::Background));
312 painter.fillRect(rect.intersected(QRect(rect.x(), rect.y(), markWidth, rect.height())), gradient2);
313
314 painter.setPen(QPen(pal.color(QPalette::Background), 2));
315 if (isLeftToRight())
316 painter.drawLine(rect.x() + extraAreaWidth-1, rect.top(), rect.x() + extraAreaWidth-1, rect.bottom());
317 else
318 painter.drawLine(rect.x(), rect.top(), rect.x(), rect.bottom());
319 painter.setRenderHint(QPainter::Antialiasing);
320
321 QTextBlock block = firstVisibleBlock();
322 int blockNumber = block.blockNumber();
323 qreal top = blockBoundingGeometry(block).translated(contentOffset()).top();
324 qreal bottom = top + blockBoundingRect(block).height();
325
326 QString imagesPath = QString::fromLatin1(":/qt/scripttools/debugging/images");
327 QString imageExt;
328// SVGs don't work on all platforms, even when QT_NO_SVG is not defined, so disable SVG usage for now.
329// #ifndef QT_NO_SVG
330#if 0
331 imageExt = QString::fromLatin1("svg");
332#else
333 imageExt = QString::fromLatin1("png");
334#endif
335
336 while (block.isValid() && top <= rect.bottom()) {
337 if (block.isVisible() && bottom >= rect.top()) {
338
339 int lineNumber = blockNumber + m_baseLineNumber;
340 if (m_breakpoints.contains(lineNumber)) {
341 int radius = fm.lineSpacing() - 1;
342 QRect r(rect.x(), (int)top, radius, radius);
343 QIcon icon(m_breakpoints[lineNumber].enabled
344 ? QString::fromLatin1("%0/breakpoint.%1").arg(imagesPath).arg(imageExt)
345 : QString::fromLatin1("%0/d_breakpoint.%1").arg(imagesPath).arg(imageExt));
346 icon.paint(&painter, r, Qt::AlignCenter);
347 }
348 if (m_executionLineNumber == lineNumber) {
349 int radius = fm.lineSpacing() - 1;
350 QRect r(rect.x(), (int)top, radius, radius);
351 QIcon icon(QString::fromLatin1("%0/location.%1").arg(imagesPath).arg(imageExt));
352 icon.paint(&painter, r, Qt::AlignCenter);
353 }
354
355 if (!isExecutableLine(lineNumber))
356 painter.setPen(pal.color(QPalette::Mid));
357 else
358 painter.setPen(QColor(Qt::darkCyan));
359 QString number = QString::number(lineNumber);
360 painter.drawText(rect.x() + markWidth, (int)top, rect.x() + extraAreaWidth - markWidth - 4,
361 fm.height(), Qt::AlignRight, number);
362 }
363
364 block = block.next();
365 top = bottom;
366 bottom = top + blockBoundingRect(block).height();
367 ++blockNumber;
368 }
369}
370
371void QScriptEdit::extraAreaMouseEvent(QMouseEvent *e)
372{
373 QTextCursor cursor = cursorForPosition(QPoint(0, e->pos().y()));
374 cursor.setPosition(cursor.block().position());
375
376 QFontMetrics fm(font());
377 int markWidth = fm.lineSpacing();
378
379 if (e->type() == QEvent::MouseMove && e->buttons() == 0) { // mouse tracking
380 bool hand = (e->pos().x() <= markWidth);
381 int lineNumber = cursor.blockNumber() + m_baseLineNumber;
382 hand = hand && isExecutableLine(lineNumber);
383#ifndef QT_NO_CURSOR
384 if (hand != (m_extraArea->cursor().shape() == Qt::PointingHandCursor))
385 m_extraArea->setCursor(hand ? Qt::PointingHandCursor : Qt::ArrowCursor);
386#endif
387 }
388
389 if (e->type() == QEvent::MouseButtonPress) {
390 if (e->button() == Qt::LeftButton) {
391 int lineNumber = cursor.blockNumber() + m_baseLineNumber;
392 bool executable = isExecutableLine(lineNumber);
393 if ((e->pos().x() <= markWidth) && executable)
394 m_extraAreaToggleBlockNumber = cursor.blockNumber();
395 else
396 m_extraAreaToggleBlockNumber = -1;
397 }
398 } else if (e->type() == QEvent::MouseButtonRelease) {
399 if (e->button() == Qt::LeftButton) {
400 if ((m_extraAreaToggleBlockNumber != -1) && (e->pos().x() <= markWidth)) {
401 int lineNumber = m_extraAreaToggleBlockNumber + m_baseLineNumber;
402 bool on = !m_breakpoints.contains(lineNumber);
403 emit breakpointToggleRequest(lineNumber, on);
404 }
405 } else if (e->button() == Qt::RightButton) {
406 int lineNumber = cursor.blockNumber() + m_baseLineNumber;
407 if (!isExecutableLine(lineNumber))
408 return;
409 bool has = m_breakpoints.contains(lineNumber);
410 QMenu *popup = new QMenu();
411 QAction *toggleAct = new QAction(tr("Toggle Breakpoint"), popup);
412 popup->addAction(toggleAct);
413 QAction *disableAct = new QAction(tr("Disable Breakpoint"), popup);
414 QAction *enableAct = new QAction(tr("Enable Breakpoint"), popup);
415 QWidget *conditionWidget = new QWidget();
416 {
417 QHBoxLayout *hbox = new QHBoxLayout(conditionWidget);
418 hbox->addWidget(new QLabel(tr("Breakpoint Condition:")));
419 hbox->addWidget(new QLineEdit());
420 }
421// QWidgetAction *conditionAct = new QWidgetAction(popup);
422// conditionAct->setDefaultWidget(conditionWidget);
423 if (has) {
424 popup->addSeparator();
425 popup->addAction(m_breakpoints[lineNumber].enabled ? disableAct : enableAct);
426// popup->addAction(conditionAct);
427 }
428 QAction *ret = popup->exec(e->globalPos());
429 if (ret) {
430 if (ret == toggleAct) {
431 emit breakpointToggleRequest(lineNumber, !has);
432 } else if (ret == disableAct) {
433 emit breakpointEnableRequest(lineNumber, false);
434 } else if (ret == enableAct) {
435 emit breakpointEnableRequest(lineNumber, true);
436 }// else if (ret == conditionAct) {
437 //}
438 }
439 popup->deleteLater();
440 }
441 }
442}
443
444bool QScriptEdit::extraAreaEvent(QEvent *e)
445{
446 if (e->type() == QEvent::ToolTip) {
447 // ### show the breakpoint's condition, if any
448 return true;
449 }
450 return false;
451}
452
453QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.