source: branches/4.5.1/src/scripttools/debugging/qscriptenginedebugger.cpp@ 559

Last change on this file since 559 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 28.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtSCriptTools module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qscriptenginedebugger.h"
43#include "qscriptdebugger_p.h"
44#include "qscriptenginedebuggerfrontend_p.h"
45#include "qscriptdebuggerconsolewidget_p.h"
46#include "qscriptdebuggerstackwidget_p.h"
47#include "qscriptdebuggerscriptswidget_p.h"
48#include "qscriptdebuggerlocalswidget_p.h"
49#include "qscriptdebuggercodewidget_p.h"
50#include "qscriptdebuggercodefinderwidget_p.h"
51#include "qscriptbreakpointswidget_p.h"
52#include "qscriptdebugoutputwidget_p.h"
53#include "qscripterrorlogwidget_p.h"
54#include "qscriptdebuggerwidgetfactoryinterface_p.h"
55#include <private/qobject_p.h>
56
57#include <QtCore/qsettings.h>
58#include <QtGui/qapplication.h>
59#include <QtGui/qdockwidget.h>
60#include <QtGui/qmainwindow.h>
61#include <QtGui/qmenu.h>
62#include <QtGui/qmenubar.h>
63#include <QtGui/qtoolbar.h>
64#include <QtGui/qboxlayout.h>
65
66class QtScriptDebuggerResourceInitializer
67{
68public:
69 QtScriptDebuggerResourceInitializer() {
70 Q_INIT_RESOURCE(scripttools_debugging);
71 }
72};
73
74QT_BEGIN_NAMESPACE
75
76/*!
77 \since 4.5
78 \class QScriptEngineDebugger
79
80 \brief The QScriptEngineDebugger class provides a QScriptEngine debugger.
81
82 \ingroup scripttools
83 \mainclass
84
85 The QScriptEngineDebugger class provides a debugger that can be
86 embedded into Qt applications that use Qt Script. The debugger
87 enables the application user to inspect the state of the script
88 environment and control script execution.
89
90 To attach the debugger to a script engine, call the attachTo()
91 function.
92
93 \snippet doc/src/snippets/code/src.scripttools.qscriptenginedebugger.cpp 0
94
95 Once the debugger has been attached to a script engine, you can
96 proceed to evaluate scripts as usual, e.g. by calling
97 QScriptEngine::evaluate(). The debugger will be triggered when an
98 uncaught exception occurs, or when a \c{debugger} statement is
99 encountered in a script. It is also possible to interrupt script
100 evaluation at an arbitrary time by triggering the InterruptAction.
101 For instance, to start the debugger when script evaluation starts,
102 you trigger the action before you begin to \l{QScriptEngine::}{evaluate()}
103 the script.
104
105 \snippet doc/src/snippets/scriptdebugger.cpp 2
106
107 By default, the \l{standardWindow()}{standard debugger window} is shown when
108 evaluation is suspended. This can be changed by calling the
109 setAutoShowStandardWindow() function.
110
111 The debugger defines a set of \l{DebuggerAction}{actions} that are
112 available, such as stopping execution or printing the contents of a
113 variable. It also provides a set of widgets (components) that
114 display the information available from the debugger and that trigger
115 the actions on request. The actions available are identified by the
116 DebuggerAction enum, and the widgets are identified by the
117 DebuggerWidget enum.
118
119 Access to the individual debugger widgets is provided by the
120 widget() function. This makes it possible to arrange the widgets in
121 a custom manner. Similarly, the action() function provides access
122 to the various debugger actions.
123
124 The createStandardToolBar() function creates a standard toolbar, and the
125 createStandardMenu() function creates a standard menu; these functions can
126 be useful if you are creating a custom debugger configuration.
127
128 The evaluationSuspended() signal is emitted when the debugger has
129 suspended script evaluation and entered interactive mode, i.e., the
130 mode in which it accepts input from the user. The
131 evaluationResumed() signal is emitted when script evaluation is
132 resumed, i.e, when execution control is given back to the script
133 engine.
134
135 When calling QScriptEngine::evaluate() it is useful to pass a
136 descriptive script name (file name) as second argument, as this is
137 the name that will be displayed by the debugger in the
138 ScriptsWidget; if a name is not passed, the script will be labelled
139 "anonymous".
140
141 When evaluation is suspended, the debugger will also suspend the
142 event loop of the script. In the following snippet, the call to
143 QScriptEngine::evaluate() causes the debugger to be triggered, and
144 the function call does not return until the user has finished
145 interacting with the debugger.
146
147 \snippet doc/src/snippets/code/src.scripttools.qscriptenginedebugger.cpp 1
148
149 When the Qt Script debugger is running, the C++ application itself
150 is not "frozen". This means that it is possible that more scripts
151 are evaluated, even though the debugger has suspended evaluation of
152 the \bold{current} script evaluation. For example, a C++ timer might
153 trigger that causes a script function to be called, or the user
154 might click on a button in the main application user interface whose
155 clicked() signal is connected to a script function. This kind of
156 nested evaluation is permitted. The debugger will enter interactive
157 mode for the new script if an exception is thrown or a breakpoint is
158 reached. Note that it will not stop when encountering \c{debugger}
159 statements. \omit The effects are similar to those achieved by
160 typing a program into the debugger's console and evaluating
161 it. \endomit
162
163 Nested evaluation requires some thought when deciding
164 how the debugger is presented to the user; for example, whether a
165 modal dialog is suitable, or whether some parts of the main
166 application user interface should be disabled while the debugger is
167 running. \omit Seems unfinished somehow \endomit
168
169 Debugging inside of a \l{QWidget::paintEvent()}{paintEvent}() is
170 currently not supported. If you need to debug painting-related
171 script code, that code should be evaluated outside of the C++
172 paintEvent(), e.g. by rendering to an image, like the Context2D and
173 Tetrix QtScript examples do. This will make the code safe for
174 debugging.
175
176 The debugger adds some special properties to the script engine:
177 \c{__FILE__} holds the name of the script in which the current
178 evaluation occurs, and \c{__LINE__} holds the current line
179 number. These are useful when doing print()-style debugging (the
180 messages appear in the debugger's debug output widget).
181
182 The \l{Qt Script Debugger Manual} describes how to use the debugger.
183 The \l{Context2D Example}{Context2D example} shows how to integrate
184 the debugger in applications.
185
186 \sa QScriptEngine, {Context2D Example}
187*/
188
189/*!
190 \enum QScriptEngineDebugger::DebuggerWidget
191
192 This enum decides the widget that the widget() function should
193 retrieve. We treat these widgets in more detail in the \l{Qt
194 Script Debugger Manual}.
195
196 \value ConsoleWidget Provides a command-line interface to the debugger.
197 \value StackWidget Shows a backtrace of the script's execution state.
198 \value ScriptsWidget Displays a list of currently loaded scripts.
199 \value LocalsWidget Shows the local variables of the current stack frame.
200 \value CodeWidget Displays the code of the current script.
201 \value CodeFinderWidget Provides a widget that can search for text in the script shown in the
202 CodeWidget.
203 \value BreakpointsWidget Shows breakpoints that have been set.
204 \value DebugOutputWidget Contains output from the \c print() script function.
205 \value ErrorLogWidget Shows error messages that have been generated.
206*/
207
208/*!
209 \enum QScriptEngineDebugger::DebuggerAction
210
211 This enum specifies the action that the action() function should
212 retrieve. The actions retrieved can be connected to any slot and
213 connected to any widget. Please see the \l{Qt Script Debugger Manual}'s
214 \l{Console Command Reference} for a detailed description of these actions.
215
216 \value InterruptAction Suspends script execution as soon as the next script statement is reached.
217 \value ContinueAction Gives the execution control back to the script engine.
218 \value StepIntoAction Performs a step action.
219 \value StepOverAction Performs a next action.
220 \value StepOutAction Executes the script until the current function returns.
221 \value RunToCursorAction Continues execution to the selected line (which contains the cursor) in the CodeWidget.
222 \value RunToNewScriptAction Returns control to the script engine until a new script is executed.
223 \value ToggleBreakpointAction Toggles a breakpoint at the selected line in the CodeWidget.
224 \value ClearDebugOutputAction Clears the contents of the DebugOutputWidget.
225 \value ClearErrorLogAction Clears the contents of the ErrorLogWidget.
226 \value ClearConsoleAction Clears the contents of the ConsoleWidget.
227 \value FindInScriptAction Displays the CodeFinderWidget.
228 \value FindNextInScriptAction Finds next occurrence in the CodeWidget.
229 \value FindPreviousInScriptAction Finds previous occurrence in the CodeWidget.
230 \value GoToLineAction Shows the "Go to Line" dialog.
231*/
232
233class QScriptEngineDebuggerPrivate
234 : public QObjectPrivate,
235 public QScriptDebuggerWidgetFactoryInterface
236{
237 Q_DECLARE_PUBLIC(QScriptEngineDebugger)
238public:
239 QScriptEngineDebuggerPrivate();
240 ~QScriptEngineDebuggerPrivate();
241
242 QScriptDebugOutputWidgetInterface *createDebugOutputWidget();
243 QScriptDebuggerConsoleWidgetInterface *createConsoleWidget();
244 QScriptErrorLogWidgetInterface *createErrorLogWidget();
245 QScriptDebuggerCodeFinderWidgetInterface *createCodeFinderWidget();
246
247 // private slots
248 void _q_showStandardWindow();
249
250 void createDebugger();
251
252 QScriptDebugger *debugger;
253 QScriptEngineDebuggerFrontend *frontend;
254 QMainWindow *standardWindow;
255 bool autoShow;
256
257 static QtScriptDebuggerResourceInitializer resourceInitializer;
258};
259
260namespace {
261
262class WidgetClosedNotifier : public QObject
263{
264 Q_OBJECT
265public:
266 WidgetClosedNotifier(QWidget *w, QObject *parent = 0)
267 : QObject(parent), widget(w)
268 {
269 w->installEventFilter(this);
270 }
271
272 bool eventFilter(QObject *watched, QEvent *e)
273 {
274 if (watched != widget)
275 return false;
276 if (e->type() != QEvent::Close)
277 return false;
278 emit widgetClosed();
279 return true;
280 }
281
282Q_SIGNALS:
283 void widgetClosed();
284
285private:
286 QWidget *widget;
287};
288
289} // namespace
290
291QtScriptDebuggerResourceInitializer QScriptEngineDebuggerPrivate::resourceInitializer;
292
293QScriptEngineDebuggerPrivate::QScriptEngineDebuggerPrivate()
294{
295 debugger = 0;
296 frontend = 0;
297 standardWindow = 0;
298 autoShow = true;
299}
300
301QScriptEngineDebuggerPrivate::~QScriptEngineDebuggerPrivate()
302{
303 delete debugger;
304 delete frontend;
305 if (standardWindow) {
306 QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
307 QByteArray geometry = standardWindow->saveGeometry();
308 settings.setValue(QLatin1String("Qt/scripttools/debugging/mainWindowGeometry"), geometry);
309 QByteArray state = standardWindow->saveState();
310 settings.setValue(QLatin1String("Qt/scripttools/debugging/mainWindowState"), state);
311 if (standardWindow->parent() == 0)
312 delete standardWindow;
313 }
314}
315
316QScriptDebugOutputWidgetInterface *QScriptEngineDebuggerPrivate::createDebugOutputWidget()
317{
318 return new QScriptDebugOutputWidget();
319}
320
321QScriptDebuggerConsoleWidgetInterface *QScriptEngineDebuggerPrivate::createConsoleWidget()
322{
323 return new QScriptDebuggerConsoleWidget();
324}
325
326QScriptErrorLogWidgetInterface *QScriptEngineDebuggerPrivate::createErrorLogWidget()
327{
328 return new QScriptErrorLogWidget();
329}
330
331QScriptDebuggerCodeFinderWidgetInterface *QScriptEngineDebuggerPrivate::createCodeFinderWidget()
332{
333 return new QScriptDebuggerCodeFinderWidget();
334}
335
336void QScriptEngineDebuggerPrivate::_q_showStandardWindow()
337{
338 Q_Q(QScriptEngineDebugger);
339 (void)q->standardWindow(); // ensure it's created
340 standardWindow->show();
341}
342
343void QScriptEngineDebuggerPrivate::createDebugger()
344{
345 Q_Q(QScriptEngineDebugger);
346 if (!debugger) {
347 debugger = new QScriptDebugger();
348 debugger->setWidgetFactory(this);
349 QObject::connect(debugger, SIGNAL(started()),
350 q, SIGNAL(evaluationResumed()));
351 QObject::connect(debugger, SIGNAL(stopped()),
352 q, SIGNAL(evaluationSuspended()));
353 if (autoShow) {
354 QObject::connect(q, SIGNAL(evaluationSuspended()),
355 q, SLOT(_q_showStandardWindow()));
356 }
357 }
358}
359
360/*!
361 Constructs a new QScriptEngineDebugger object with the given \a
362 parent.
363
364 To attach a QScriptEngine to the debugger, use attachTo()
365 function.
366
367*/
368QScriptEngineDebugger::QScriptEngineDebugger(QObject *parent)
369 : QObject(*new QScriptEngineDebuggerPrivate, parent)
370{
371}
372
373/*!
374 Destroys this QScriptEngineDebugger.
375*/
376QScriptEngineDebugger::~QScriptEngineDebugger()
377{
378}
379
380/*!
381 Attaches to the given \a engine.
382
383 The debugger will install a custom agent (using
384 QScriptEngine::setAgent()) to monitor the engine. While the debugger
385 is attached, you should not change the agent; however, if you do
386 have to perform additional monitoring, you must set a proxy agent
387 that forwards all events to the debugger's agent.
388
389 \sa detach()
390*/
391void QScriptEngineDebugger::attachTo(QScriptEngine *engine)
392{
393 Q_D(QScriptEngineDebugger);
394 if (!engine) {
395 detach();
396 return;
397 }
398 d->createDebugger();
399 if (!d->frontend)
400 d->frontend = new QScriptEngineDebuggerFrontend();
401 d->frontend->attachTo(engine);
402 d->debugger->setFrontend(d->frontend);
403}
404
405/*!
406 Detaches from the current script engine, if any.
407
408 \sa attachTo()
409*/
410void QScriptEngineDebugger::detach()
411{
412 Q_D(QScriptEngineDebugger);
413 if (d->frontend)
414 d->frontend->detach();
415 if (d->debugger)
416 d->debugger->setFrontend(0);
417}
418
419/*!
420
421 Returns a pointer to the instance of the specified standard \a
422 widget. The widgets available are defined by the DebuggerWidget
423 enum.
424
425 A main window containing all widgets is returned by
426 standardWindow(). If you do not want to use this window, you can
427 fetch the individual widgets with this function. For instance, the
428 code example below shows how to set up a layout containing a
429 \l{QScriptEngineDebugger::CodeWidget}{code window} and a
430 \l{QScriptEngineDebugger::StackWidget}{stack widget}.
431
432 \snippet doc/src/snippets/scriptdebugger.cpp 0
433
434 Note that you need to set setAutoShowStandardWindow() to false; if
435 not, the standard window will be shown regardless.
436
437 \sa action(), standardWindow(), setAutoShowStandardWindow()
438*/
439QWidget *QScriptEngineDebugger::widget(DebuggerWidget widget) const
440{
441 Q_D(const QScriptEngineDebugger);
442 const_cast<QScriptEngineDebuggerPrivate*>(d)->createDebugger();
443 switch (widget) {
444 case ConsoleWidget: {
445 QScriptDebuggerConsoleWidgetInterface *w = d->debugger->consoleWidget();
446 if (!w) {
447 w = new QScriptDebuggerConsoleWidget();
448 d->debugger->setConsoleWidget(w);
449 }
450 return w;
451 }
452 case StackWidget: {
453 QScriptDebuggerStackWidgetInterface *w = d->debugger->stackWidget();
454 if (!w) {
455 w = new QScriptDebuggerStackWidget();
456 d->debugger->setStackWidget(w);
457 }
458 return w;
459 }
460 case ScriptsWidget: {
461 QScriptDebuggerScriptsWidgetInterface *w = d->debugger->scriptsWidget();
462 if (!w) {
463 w = new QScriptDebuggerScriptsWidget();
464 d->debugger->setScriptsWidget(w);
465 }
466 return w;
467 }
468 case LocalsWidget: {
469 QScriptDebuggerLocalsWidgetInterface *w = d->debugger->localsWidget();
470 if (!w) {
471 w = new QScriptDebuggerLocalsWidget();
472 d->debugger->setLocalsWidget(w);
473 }
474 return w;
475 }
476 case CodeWidget: {
477 QScriptDebuggerCodeWidgetInterface *w = d->debugger->codeWidget();
478 if (!w) {
479 w = new QScriptDebuggerCodeWidget();
480 d->debugger->setCodeWidget(w);
481 }
482 return w;
483 }
484 case CodeFinderWidget: {
485 QScriptDebuggerCodeFinderWidgetInterface *w = d->debugger->codeFinderWidget();
486 if (!w) {
487 w = new QScriptDebuggerCodeFinderWidget();
488 d->debugger->setCodeFinderWidget(w);
489 }
490 return w;
491 }
492 case BreakpointsWidget: {
493 QScriptBreakpointsWidgetInterface *w = d->debugger->breakpointsWidget();
494 if (!w) {
495 w = new QScriptBreakpointsWidget();
496 d->debugger->setBreakpointsWidget(w);
497 }
498 return w;
499 }
500 case DebugOutputWidget: {
501 QScriptDebugOutputWidgetInterface *w = d->debugger->debugOutputWidget();
502 if (!w) {
503 w = new QScriptDebugOutputWidget();
504 d->debugger->setDebugOutputWidget(w);
505 }
506 return w;
507 }
508 case ErrorLogWidget: {
509 QScriptErrorLogWidgetInterface *w = d->debugger->errorLogWidget();
510 if (!w) {
511 w = new QScriptErrorLogWidget();
512 d->debugger->setErrorLogWidget(w);
513 }
514 return w;
515 }
516 }
517 return 0;
518}
519
520/*!
521 Returns a pointer to the specified \a action. The actions available
522 are given by the DebuggerAction enum.
523
524 With this function, you can add the actions to your own widgets,
525 toolbars, and menus. It is also convenient if you, for example,
526 wish to spice things up with your own groovy icons. The code
527 example below shows how to add actions to a QToolBar.
528
529 \snippet doc/src/snippets/scriptdebugger.cpp 1
530
531 Note that QScriptEngineDebugger has already added the actions to
532 its \l{DebuggerWidget}{standard widgets} and \l{standardWindow()}{standard window}.
533
534 \sa widget(), createStandardMenu(), createStandardToolBar(), standardWindow()
535*/
536QAction *QScriptEngineDebugger::action(DebuggerAction action) const
537{
538 Q_D(const QScriptEngineDebugger);
539 QScriptEngineDebugger *that = const_cast<QScriptEngineDebugger*>(this);
540 that->d_func()->createDebugger();
541 switch (action) {
542 case InterruptAction:
543 return d->debugger->interruptAction(that);
544 case ContinueAction:
545 return d->debugger->continueAction(that);
546 case StepIntoAction:
547 return d->debugger->stepIntoAction(that);
548 case StepOverAction:
549 return d->debugger->stepOverAction(that);
550 case StepOutAction:
551 return d->debugger->stepOutAction(that);
552 case RunToCursorAction:
553 return d->debugger->runToCursorAction(that);
554 case RunToNewScriptAction:
555 return d->debugger->runToNewScriptAction(that);
556 case ToggleBreakpointAction:
557 return d->debugger->toggleBreakpointAction(that);
558 case ClearDebugOutputAction:
559 return d->debugger->clearDebugOutputAction(that);
560 case ClearErrorLogAction:
561 return d->debugger->clearErrorLogAction(that);
562 case ClearConsoleAction: