source: branches/4.5.1/src/scripttools/debugging/qscriptdebuggerconsolewidget.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: 13.6 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 "qscriptdebuggerconsolewidget_p.h"
43#include "qscriptdebuggerconsolewidgetinterface_p_p.h"
44#include "qscriptdebuggerconsolehistorianinterface_p.h"
45#include "qscriptcompletionproviderinterface_p.h"
46#include "qscriptcompletiontaskinterface_p.h"
47
48#include <QtCore/qdebug.h>
49#include <QtGui/qplaintextedit.h>
50#include <QtGui/qlabel.h>
51#include <QtGui/qlineedit.h>
52#include <QtGui/qlistview.h>
53#include <QtGui/qscrollbar.h>
54#include <QtGui/qboxlayout.h>
55#include <QtGui/qcompleter.h>
56
57QT_BEGIN_NAMESPACE
58
59namespace {
60
61class PromptLabel : public QLabel
62{
63public:
64 PromptLabel(QWidget *parent = 0)
65 : QLabel(parent)
66 {
67 setFrameShape(QFrame::NoFrame);
68 setIndent(2);
69 setMargin(2);
70 setSizePolicy(QSizePolicy::Minimum, sizePolicy().verticalPolicy());
71 setAlignment(Qt::AlignHCenter);
72#ifndef QT_NO_STYLE_STYLESHEET
73 setStyleSheet(QLatin1String("background: white;"));
74#endif
75 }
76
77 QSize sizeHint() const {
78 QFontMetrics fm(font());
79 return fm.size(0, text()) + QSize(8, 0);
80 }
81};
82
83class InputEdit : public QLineEdit
84{
85public:
86 InputEdit(QWidget *parent = 0)
87 : QLineEdit(parent)
88 {
89 setFrame(false);
90 setSizePolicy(QSizePolicy::MinimumExpanding, sizePolicy().verticalPolicy());
91 }
92};
93
94class CommandLine : public QWidget
95{
96 Q_OBJECT
97public:
98 CommandLine(QWidget *parent = 0)
99 : QWidget(parent)
100 {
101 promptLabel = new PromptLabel();
102 inputEdit = new InputEdit();
103 QHBoxLayout *hbox = new QHBoxLayout(this);
104 hbox->setSpacing(0);
105 hbox->setMargin(0);
106 hbox->addWidget(promptLabel);
107 hbox->addWidget(inputEdit);
108
109 QObject::connect(inputEdit, SIGNAL(returnPressed()),
110 this, SLOT(onReturnPressed()));
111 QObject::connect(inputEdit, SIGNAL(textEdited(QString)),
112 this, SIGNAL(lineEdited(QString)));
113
114 setFocusProxy(inputEdit);
115 }
116
117 QString prompt() const
118 {
119 return promptLabel->text();
120 }
121 void setPrompt(const QString &prompt)
122 {
123 promptLabel->setText(prompt);
124 }
125
126 QString input() const
127 {
128 return inputEdit->text();
129 }
130 void setInput(const QString &input)
131 {
132 inputEdit->setText(input);
133 }
134
135 int cursorPosition() const
136 {
137 return inputEdit->cursorPosition();
138 }
139 void setCursorPosition(int position)
140 {
141 inputEdit->setCursorPosition(position);
142 }
143
144 QWidget *editor() const
145 {
146 return inputEdit;
147 }
148
149Q_SIGNALS:
150 void lineEntered(const QString &contents);
151 void lineEdited(const QString &contents);
152
153private Q_SLOTS:
154 void onReturnPressed()
155 {
156 QString text = inputEdit->text();
157 inputEdit->clear();
158 emit lineEntered(text);
159 }
160
161private:
162 PromptLabel *promptLabel;
163 InputEdit *inputEdit;
164};
165
166class OutputEdit : public QPlainTextEdit
167{
168public:
169 OutputEdit(QWidget *parent = 0)
170 : QPlainTextEdit(parent)
171 {
172 setFrameShape(QFrame::NoFrame);
173 setReadOnly(true);
174// ### there's no context menu when the edit can't have focus,
175// even though you can select text in it.
176// setFocusPolicy(Qt::NoFocus);
177 setMaximumBlockCount(2500);
178 }
179
180 void scrollToBottom()
181 {
182 QScrollBar *bar = verticalScrollBar();
183 bar->setValue(bar->maximum());
184 }
185
186 int charactersPerLine() const
187 {
188 QFontMetrics fm(font());
189 return width() / fm.maxWidth();
190 }
191};
192
193} // namespace
194
195class QScriptDebuggerConsoleWidgetPrivate
196 : public QScriptDebuggerConsoleWidgetInterfacePrivate
197{
198 Q_DECLARE_PUBLIC(QScriptDebuggerConsoleWidget)
199public:
200 QScriptDebuggerConsoleWidgetPrivate();
201 ~QScriptDebuggerConsoleWidgetPrivate();
202
203 // private slots
204 void _q_onLineEntered(const QString &contents);
205 void _q_onLineEdited(const QString &contents);
206 void _q_onCompletionTaskFinished();
207
208 CommandLine *commandLine;
209 OutputEdit *outputEdit;
210 int historyIndex;
211 QString newInput;
212};
213
214QScriptDebuggerConsoleWidgetPrivate::QScriptDebuggerConsoleWidgetPrivate()
215{
216 historyIndex = -1;
217}
218
219QScriptDebuggerConsoleWidgetPrivate::~QScriptDebuggerConsoleWidgetPrivate()
220{
221}
222
223void QScriptDebuggerConsoleWidgetPrivate::_q_onLineEntered(const QString &contents)
224{
225 Q_Q(QScriptDebuggerConsoleWidget);
226 outputEdit->appendPlainText(QString::fromLatin1("%0 %1").arg(commandLine->prompt()).arg(contents));
227 outputEdit->scrollToBottom();
228 historyIndex = -1;
229 newInput.clear();
230 emit q->lineEntered(contents);
231}
232
233void QScriptDebuggerConsoleWidgetPrivate::_q_onLineEdited(const QString &contents)
234{
235 if (historyIndex != -1) {
236 // ### try to get the bash behavior...
237#if 0
238 historian->changeHistoryAt(historyIndex, contents);
239#endif
240 } else {
241 newInput = contents;
242 }
243}
244
245static bool lengthLessThan(const QString &s1, const QString &s2)
246{
247 return s1.length() < s2.length();
248}
249
250// input must be sorted by length already
251static QString longestCommonPrefix(const QStringList &lst)
252{
253 QString result = lst.last();
254 for (int i = lst.size() - 2; (i >= 0) && !result.isEmpty(); --i) {
255 const QString &s = lst.at(i);
256 int j = 0;
257 for ( ; (j < qMin(s.length(), result.length())) && (s.at(j) == result.at(j)); ++j)
258 ;
259 result = result.left(j);
260 }
261 return result;
262}
263
264void QScriptDebuggerConsoleWidgetPrivate::_q_onCompletionTaskFinished()
265{
266 QScriptCompletionTaskInterface *task = 0;
267 task = qobject_cast<QScriptCompletionTaskInterface*>(q_func()->sender());
268 if (task->resultCount() == 1) {
269 QString completion = task->resultAt(0);
270 completion.append(task->appendix());
271 QString tmp = commandLine->input();
272 tmp.remove(task->position(), task->length());
273 tmp.insert(task->position(), completion);
274 commandLine->setInput(tmp);
275 } else if (task->resultCount() > 1) {
276 {
277 QStringList lst;
278 for (int i = 0; i < task->resultCount(); ++i)
279 lst.append(task->resultAt(i).mid(task->length()));
280 qSort(lst.begin(), lst.end(), lengthLessThan);
281 QString lcp = longestCommonPrefix(lst);
282 if (!lcp.isEmpty()) {
283 QString tmp = commandLine->input();
284 tmp.insert(task->position() + task->length(), lcp);
285 commandLine->setInput(tmp);
286 }
287 }
288
289 outputEdit->appendPlainText(QString::fromLatin1("%0 %1")
290 .arg(commandLine->prompt()).arg(commandLine->input()));
291 int maxLength = 0;
292 for (int i = 0; i < task->resultCount(); ++i)
293 maxLength = qMax(maxLength, task->resultAt(i).length());
294 Q_ASSERT(maxLength > 0);
295 int tab = 8;
296 int columns = qMax(1, outputEdit->charactersPerLine() / (maxLength + tab));
297 QString msg;
298 for (int i = 0; i < task->resultCount(); ++i) {
299 if (i != 0) {
300 if ((i % columns) == 0) {
301 outputEdit->appendPlainText(msg);
302 msg.clear();
303 } else {
304 int pad = maxLength + tab - (msg.length() % (maxLength + tab));
305 msg.append(QString(pad, QLatin1Char(' ')));
306 }
307 }
308 msg.append(task->resultAt(i));
309 }
310 if (!msg.isEmpty())
311 outputEdit->appendPlainText(msg);
312 outputEdit->scrollToBottom();
313 }
314 task->deleteLater();
315}
316
317QScriptDebuggerConsoleWidget::QScriptDebuggerConsoleWidget(QWidget *parent)
318 : QScriptDebuggerConsoleWidgetInterface(*new QScriptDebuggerConsoleWidgetPrivate, parent, 0)
319{
320 Q_D(QScriptDebuggerConsoleWidget);
321 d->commandLine = new CommandLine();
322 d->commandLine->setPrompt(QString::fromLatin1("qsdb>"));
323 d->outputEdit = new OutputEdit();
324 QVBoxLayout *vbox = new QVBoxLayout(this);
325 vbox->setSpacing(0);
326 vbox->setMargin(0);
327 vbox->addWidget(d->outputEdit);
328 vbox->addWidget(d->commandLine);
329
330#if 0
331 QString sheet = QString::fromLatin1("background-color: black;"
332 "color: aquamarine;"
333 "font-size: 14px;"
334 "font-family: \"Monospace\"");
335#endif
336#ifndef QT_NO_STYLE_STYLESHEET
337 QString sheet = QString::fromLatin1("font-size: 14px; font-family: \"Monospace\";");
338 setStyleSheet(sheet);
339#endif
340
341 QObject::connect(d->commandLine, SIGNAL(lineEntered(QString)),
342 this, SLOT(_q_onLineEntered(QString)));
343 QObject::connect(d->commandLine, SIGNAL(lineEdited(QString)),
344 this, SLOT(_q_onLineEdited(QString)));
345}
346
347QScriptDebuggerConsoleWidget::~QScriptDebuggerConsoleWidget()
348{
349}
350
351void QScriptDebuggerConsoleWidget::message(
352 QtMsgType type, const QString &text, const QString &fileName,
353 int lineNumber, int columnNumber, const QVariant &/*data*/)
354{
355 Q_D(QScriptDebuggerConsoleWidget);
356 QString msg;
357 if (!fileName.isEmpty() || (lineNumber != -1)) {
358 if (!fileName.isEmpty())
359 msg.append(fileName);
360 else
361 msg.append(QLatin1String("<noname>"));
362 if (lineNumber != -1) {
363 msg.append(QLatin1Char(':'));
364 msg.append(QString::number(lineNumber));
365 if (columnNumber != -1) {
366 msg.append(QLatin1Char(':'));
367 msg.append(QString::number(columnNumber));
368 }
369 }
370 msg.append(QLatin1String(": "));
371 }
372 msg.append(text);
373 QTextCharFormat oldFmt = d->outputEdit->currentCharFormat();
374 QTextCharFormat fmt(oldFmt);
375 if (type == QtCriticalMsg) {
376 fmt.setForeground(Qt::red);
377 d->outputEdit->setCurrentCharFormat(fmt);
378 }
379 d->outputEdit->appendPlainText(msg);
380 d->outputEdit->setCurrentCharFormat(oldFmt);
381 d->outputEdit->scrollToBottom();
382}
383
384void QScriptDebuggerConsoleWidget::setLineContinuationMode(bool enabled)
385{
386 Q_D(QScriptDebuggerConsoleWidget);
387 QString prompt = enabled
388 ? QString::fromLatin1("....")
389 : QString::fromLatin1("qsdb>");
390 d->commandLine->setPrompt(prompt);
391}
392
393void QScriptDebuggerConsoleWidget::clear()
394{
395 Q_D(QScriptDebuggerConsoleWidget);
396 d->outputEdit->clear();
397}
398
399void QScriptDebuggerConsoleWidget::keyPressEvent(QKeyEvent *event)
400{
401 Q_D(QScriptDebuggerConsoleWidget);
402 if (event->key() == Qt::Key_Up) {
403 if (d->historyIndex+1 == d->historian->historyCount())
404 return;
405 QString cmd = d->historian->historyAt(++d->historyIndex);
406 d->commandLine->setInput(cmd);
407 } else if (event->key() == Qt::Key_Down) {
408 if (d->historyIndex == -1) {
409 // nothing to do
410 } else if (d->historyIndex == 0) {
411 d->commandLine->setInput(d->newInput);
412 --d->historyIndex;
413 } else {
414 QString cmd = d->historian->historyAt(--d->historyIndex);
415 d->commandLine->setInput(cmd);
416 }
417 } else if (event->key() == Qt::Key_Tab) {
418 QScriptCompletionTaskInterface *task = 0;
419 task = d->completionProvider->createCompletionTask(
420 d->commandLine->input(), d->commandLine->cursorPosition(),
421 /*frameIndex=*/-1, // current frame
422 QScriptCompletionProviderInterface::ConsoleCommandCompletion);
423 QObject::connect(task, SIGNAL(finished()),
424 this, SLOT(_q_onCompletionTaskFinished()));
425 task->start();
426 } else {
427 QScriptDebuggerConsoleWidgetInterface::keyPressEvent(event);
428 }
429}
430
431bool QScriptDebuggerConsoleWidget::focusNextPrevChild(bool b)
432{
433 Q_D(QScriptDebuggerConsoleWidget);
434 if (d->outputEdit->hasFocus())
435 return QScriptDebuggerConsoleWidgetInterface::focusNextPrevChild(b);
436 else
437 return false;
438}
439
440QT_END_NAMESPACE
441
442#include "qscriptdebuggerconsolewidget.moc"
443
444#include "moc_qscriptdebuggerconsolewidget_p.cpp"
Note: See TracBrowser for help on using the repository browser.