summaryrefslogtreecommitdiffstats
path: root/src/scripttools/debugging/qscriptdebuggerconsolewidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/scripttools/debugging/qscriptdebuggerconsolewidget.cpp')
-rw-r--r--src/scripttools/debugging/qscriptdebuggerconsolewidget.cpp444
1 files changed, 444 insertions, 0 deletions
diff --git a/src/scripttools/debugging/qscriptdebuggerconsolewidget.cpp b/src/scripttools/debugging/qscriptdebuggerconsolewidget.cpp
new file mode 100644
index 0000000..479487b
--- /dev/null
+++ b/src/scripttools/debugging/qscriptdebuggerconsolewidget.cpp
@@ -0,0 +1,444 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtSCriptTools module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qscriptdebuggerconsolewidget_p.h"
+#include "qscriptdebuggerconsolewidgetinterface_p_p.h"
+#include "qscriptdebuggerconsolehistorianinterface_p.h"
+#include "qscriptcompletionproviderinterface_p.h"
+#include "qscriptcompletiontaskinterface_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtGui/qplaintextedit.h>
+#include <QtGui/qlabel.h>
+#include <QtGui/qlineedit.h>
+#include <QtGui/qlistview.h>
+#include <QtGui/qscrollbar.h>
+#include <QtGui/qboxlayout.h>
+#include <QtGui/qcompleter.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+class PromptLabel : public QLabel
+{
+public:
+ PromptLabel(QWidget *parent = 0)
+ : QLabel(parent)
+ {
+ setFrameShape(QFrame::NoFrame);
+ setIndent(2);
+ setMargin(2);
+ setSizePolicy(QSizePolicy::Minimum, sizePolicy().verticalPolicy());
+ setAlignment(Qt::AlignHCenter);
+#ifndef QT_NO_STYLE_STYLESHEET
+ setStyleSheet(QLatin1String("background: white;"));
+#endif
+ }
+
+ QSize sizeHint() const {
+ QFontMetrics fm(font());
+ return fm.size(0, text()) + QSize(8, 0);
+ }
+};
+
+class InputEdit : public QLineEdit
+{
+public:
+ InputEdit(QWidget *parent = 0)
+ : QLineEdit(parent)
+ {
+ setFrame(false);
+ setSizePolicy(QSizePolicy::MinimumExpanding, sizePolicy().verticalPolicy());
+ }
+};
+
+class CommandLine : public QWidget
+{
+ Q_OBJECT
+public:
+ CommandLine(QWidget *parent = 0)
+ : QWidget(parent)
+ {
+ promptLabel = new PromptLabel();
+ inputEdit = new InputEdit();
+ QHBoxLayout *hbox = new QHBoxLayout(this);
+ hbox->setSpacing(0);
+ hbox->setMargin(0);
+ hbox->addWidget(promptLabel);
+ hbox->addWidget(inputEdit);
+
+ QObject::connect(inputEdit, SIGNAL(returnPressed()),
+ this, SLOT(onReturnPressed()));
+ QObject::connect(inputEdit, SIGNAL(textEdited(QString)),
+ this, SIGNAL(lineEdited(QString)));
+
+ setFocusProxy(inputEdit);
+ }
+
+ QString prompt() const
+ {
+ return promptLabel->text();
+ }
+ void setPrompt(const QString &prompt)
+ {
+ promptLabel->setText(prompt);
+ }
+
+ QString input() const
+ {
+ return inputEdit->text();
+ }
+ void setInput(const QString &input)
+ {
+ inputEdit->setText(input);
+ }
+
+ int cursorPosition() const
+ {
+ return inputEdit->cursorPosition();
+ }
+ void setCursorPosition(int position)
+ {
+ inputEdit->setCursorPosition(position);
+ }
+
+ QWidget *editor() const
+ {
+ return inputEdit;
+ }
+
+Q_SIGNALS:
+ void lineEntered(const QString &contents);
+ void lineEdited(const QString &contents);
+
+private Q_SLOTS:
+ void onReturnPressed()
+ {
+ QString text = inputEdit->text();
+ inputEdit->clear();
+ emit lineEntered(text);
+ }
+
+private:
+ PromptLabel *promptLabel;
+ InputEdit *inputEdit;
+};
+
+class OutputEdit : public QPlainTextEdit
+{
+public:
+ OutputEdit(QWidget *parent = 0)
+ : QPlainTextEdit(parent)
+ {
+ setFrameShape(QFrame::NoFrame);
+ setReadOnly(true);
+// ### there's no context menu when the edit can't have focus,
+// even though you can select text in it.
+// setFocusPolicy(Qt::NoFocus);
+ setMaximumBlockCount(2500);
+ }
+
+ void scrollToBottom()
+ {
+ QScrollBar *bar = verticalScrollBar();
+ bar->setValue(bar->maximum());
+ }
+
+ int charactersPerLine() const
+ {
+ QFontMetrics fm(font());
+ return width() / fm.maxWidth();
+ }
+};
+
+} // namespace
+
+class QScriptDebuggerConsoleWidgetPrivate
+ : public QScriptDebuggerConsoleWidgetInterfacePrivate
+{
+ Q_DECLARE_PUBLIC(QScriptDebuggerConsoleWidget)
+public:
+ QScriptDebuggerConsoleWidgetPrivate();
+ ~QScriptDebuggerConsoleWidgetPrivate();
+
+ // private slots
+ void _q_onLineEntered(const QString &contents);
+ void _q_onLineEdited(const QString &contents);
+ void _q_onCompletionTaskFinished();
+
+ CommandLine *commandLine;
+ OutputEdit *outputEdit;
+ int historyIndex;
+ QString newInput;
+};
+
+QScriptDebuggerConsoleWidgetPrivate::QScriptDebuggerConsoleWidgetPrivate()
+{
+ historyIndex = -1;
+}
+
+QScriptDebuggerConsoleWidgetPrivate::~QScriptDebuggerConsoleWidgetPrivate()
+{
+}
+
+void QScriptDebuggerConsoleWidgetPrivate::_q_onLineEntered(const QString &contents)
+{
+ Q_Q(QScriptDebuggerConsoleWidget);
+ outputEdit->appendPlainText(QString::fromLatin1("%0 %1").arg(commandLine->prompt()).arg(contents));
+ outputEdit->scrollToBottom();
+ historyIndex = -1;
+ newInput.clear();
+ emit q->lineEntered(contents);
+}
+
+void QScriptDebuggerConsoleWidgetPrivate::_q_onLineEdited(const QString &contents)
+{
+ if (historyIndex != -1) {
+ // ### try to get the bash behavior...
+#if 0
+ historian->changeHistoryAt(historyIndex, contents);
+#endif
+ } else {
+ newInput = contents;
+ }
+}
+
+static bool lengthLessThan(const QString &s1, const QString &s2)
+{
+ return s1.length() < s2.length();
+}
+
+// input must be sorted by length already
+static QString longestCommonPrefix(const QStringList &lst)
+{
+ QString result = lst.last();
+ for (int i = lst.size() - 2; (i >= 0) && !result.isEmpty(); --i) {
+ const QString &s = lst.at(i);
+ int j = 0;
+ for ( ; (j < qMin(s.length(), result.length())) && (s.at(j) == result.at(j)); ++j)
+ ;
+ result = result.left(j);
+ }
+ return result;
+}
+
+void QScriptDebuggerConsoleWidgetPrivate::_q_onCompletionTaskFinished()
+{
+ QScriptCompletionTaskInterface *task = 0;
+ task = qobject_cast<QScriptCompletionTaskInterface*>(q_func()->sender());
+ if (task->resultCount() == 1) {
+ QString completion = task->resultAt(0);
+ completion.append(task->appendix());
+ QString tmp = commandLine->input();
+ tmp.remove(task->position(), task->length());
+ tmp.insert(task->position(), completion);
+ commandLine->setInput(tmp);
+ } else if (task->resultCount() > 1) {
+ {
+ QStringList lst;
+ for (int i = 0; i < task->resultCount(); ++i)
+ lst.append(task->resultAt(i).mid(task->length()));
+ qSort(lst.begin(), lst.end(), lengthLessThan);
+ QString lcp = longestCommonPrefix(lst);
+ if (!lcp.isEmpty()) {
+ QString tmp = commandLine->input();
+ tmp.insert(task->position() + task->length(), lcp);
+ commandLine->setInput(tmp);
+ }
+ }
+
+ outputEdit->appendPlainText(QString::fromLatin1("%0 %1")
+ .arg(commandLine->prompt()).arg(commandLine->input()));
+ int maxLength = 0;
+ for (int i = 0; i < task->resultCount(); ++i)
+ maxLength = qMax(maxLength, task->resultAt(i).length());
+ Q_ASSERT(maxLength > 0);
+ int tab = 8;
+ int columns = qMax(1, outputEdit->charactersPerLine() / (maxLength + tab));
+ QString msg;
+ for (int i = 0; i < task->resultCount(); ++i) {
+ if (i != 0) {
+ if ((i % columns) == 0) {
+ outputEdit->appendPlainText(msg);
+ msg.clear();
+ } else {
+ int pad = maxLength + tab - (msg.length() % (maxLength + tab));
+ msg.append(QString(pad, QLatin1Char(' ')));
+ }
+ }
+ msg.append(task->resultAt(i));
+ }
+ if (!msg.isEmpty())
+ outputEdit->appendPlainText(msg);
+ outputEdit->scrollToBottom();
+ }
+ task->deleteLater();
+}
+
+QScriptDebuggerConsoleWidget::QScriptDebuggerConsoleWidget(QWidget *parent)
+ : QScriptDebuggerConsoleWidgetInterface(*new QScriptDebuggerConsoleWidgetPrivate, parent, 0)
+{
+ Q_D(QScriptDebuggerConsoleWidget);
+ d->commandLine = new CommandLine();
+ d->commandLine->setPrompt(QString::fromLatin1("qsdb>"));
+ d->outputEdit = new OutputEdit();
+ QVBoxLayout *vbox = new QVBoxLayout(this);
+ vbox->setSpacing(0);
+ vbox->setMargin(0);
+ vbox->addWidget(d->outputEdit);
+ vbox->addWidget(d->commandLine);
+
+#if 0
+ QString sheet = QString::fromLatin1("background-color: black;"
+ "color: aquamarine;"
+ "font-size: 14px;"
+ "font-family: \"Monospace\"");
+#endif
+#ifndef QT_NO_STYLE_STYLESHEET
+ QString sheet = QString::fromLatin1("font-size: 14px; font-family: \"Monospace\";");
+ setStyleSheet(sheet);
+#endif
+
+ QObject::connect(d->commandLine, SIGNAL(lineEntered(QString)),
+ this, SLOT(_q_onLineEntered(QString)));
+ QObject::connect(d->commandLine, SIGNAL(lineEdited(QString)),
+ this, SLOT(_q_onLineEdited(QString)));
+}
+
+QScriptDebuggerConsoleWidget::~QScriptDebuggerConsoleWidget()
+{
+}
+
+void QScriptDebuggerConsoleWidget::message(
+ QtMsgType type, const QString &text, const QString &fileName,
+ int lineNumber, int columnNumber, const QVariant &/*data*/)
+{
+ Q_D(QScriptDebuggerConsoleWidget);
+ QString msg;
+ if (!fileName.isEmpty() || (lineNumber != -1)) {
+ if (!fileName.isEmpty())
+ msg.append(fileName);
+ else
+ msg.append(QLatin1String("<noname>"));
+ if (lineNumber != -1) {
+ msg.append(QLatin1Char(':'));
+ msg.append(QString::number(lineNumber));
+ if (columnNumber != -1) {
+ msg.append(QLatin1Char(':'));
+ msg.append(QString::number(columnNumber));
+ }
+ }
+ msg.append(QLatin1String(": "));
+ }
+ msg.append(text);
+ QTextCharFormat oldFmt = d->outputEdit->currentCharFormat();
+ QTextCharFormat fmt(oldFmt);
+ if (type == QtCriticalMsg) {
+ fmt.setForeground(Qt::red);
+ d->outputEdit->setCurrentCharFormat(fmt);
+ }
+ d->outputEdit->appendPlainText(msg);
+ d->outputEdit->setCurrentCharFormat(oldFmt);
+ d->outputEdit->scrollToBottom();
+}
+
+void QScriptDebuggerConsoleWidget::setLineContinuationMode(bool enabled)
+{
+ Q_D(QScriptDebuggerConsoleWidget);
+ QString prompt = enabled
+ ? QString::fromLatin1("....")
+ : QString::fromLatin1("qsdb>");
+ d->commandLine->setPrompt(prompt);
+}
+
+void QScriptDebuggerConsoleWidget::clear()
+{
+ Q_D(QScriptDebuggerConsoleWidget);
+ d->outputEdit->clear();
+}
+
+void QScriptDebuggerConsoleWidget::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QScriptDebuggerConsoleWidget);
+ if (event->key() == Qt::Key_Up) {
+ if (d->historyIndex+1 == d->historian->historyCount())
+ return;
+ QString cmd = d->historian->historyAt(++d->historyIndex);
+ d->commandLine->setInput(cmd);
+ } else if (event->key() == Qt::Key_Down) {
+ if (d->historyIndex == -1) {
+ // nothing to do
+ } else if (d->historyIndex == 0) {
+ d->commandLine->setInput(d->newInput);
+ --d->historyIndex;
+ } else {
+ QString cmd = d->historian->historyAt(--d->historyIndex);
+ d->commandLine->setInput(cmd);
+ }
+ } else if (event->key() == Qt::Key_Tab) {
+ QScriptCompletionTaskInterface *task = 0;
+ task = d->completionProvider->createCompletionTask(
+ d->commandLine->input(), d->commandLine->cursorPosition(),
+ /*frameIndex=*/-1, // current frame
+ QScriptCompletionProviderInterface::ConsoleCommandCompletion);
+ QObject::connect(task, SIGNAL(finished()),
+ this, SLOT(_q_onCompletionTaskFinished()));
+ task->start();
+ } else {
+ QScriptDebuggerConsoleWidgetInterface::keyPressEvent(event);
+ }
+}
+
+bool QScriptDebuggerConsoleWidget::focusNextPrevChild(bool b)
+{
+ Q_D(QScriptDebuggerConsoleWidget);
+ if (d->outputEdit->hasFocus())
+ return QScriptDebuggerConsoleWidgetInterface::focusNextPrevChild(b);
+ else
+ return false;
+}
+
+QT_END_NAMESPACE
+
+#include "qscriptdebuggerconsolewidget.moc"
+
+#include "moc_qscriptdebuggerconsolewidget_p.cpp"