diff options
-rw-r--r-- | src/plugins/debugger/consolewindow.cpp | 381 | ||||
-rw-r--r-- | src/plugins/debugger/consolewindow.h | 76 | ||||
-rw-r--r-- | src/plugins/debugger/debugger.pro | 2 | ||||
-rw-r--r-- | src/plugins/debugger/debuggerconstants.h | 1 | ||||
-rw-r--r-- | src/plugins/debugger/debuggerplugin.cpp | 20 |
5 files changed, 478 insertions, 2 deletions
diff --git a/src/plugins/debugger/consolewindow.cpp b/src/plugins/debugger/consolewindow.cpp new file mode 100644 index 0000000000..8169e7ba2b --- /dev/null +++ b/src/plugins/debugger/consolewindow.cpp @@ -0,0 +1,381 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "consolewindow.h" + +#include "debuggeractions.h" +#include "debuggerconstants.h" + +#include <QtCore/QDebug> +#include <QtCore/QFile> +#include <QtCore/QTime> + +#include <QtGui/QAction> +#include <QtGui/QHBoxLayout> +#include <QtGui/QVBoxLayout> +#include <QtGui/QKeyEvent> +#include <QtGui/QLabel> +#include <QtGui/QLineEdit> +#include <QtGui/QMenu> +#include <QtGui/QSpacerItem> +#include <QtGui/QSplitter> +#include <QtGui/QSyntaxHighlighter> +#include <QtGui/QTextBlock> +#include <QtGui/QPlainTextEdit> +#include <QtGui/QFileDialog> +#include <QtGui/QMessageBox> + +#include <aggregation/aggregate.h> +#include <coreplugin/findplaceholder.h> +#include <coreplugin/minisplitter.h> +#include <find/basetextfind.h> + +#include <utils/savedaction.h> + +using namespace Find; + +namespace Debugger { +namespace Internal { + +static QChar charForChannel(int channel) +{ + switch (channel) { + case LogDebug: return 'd'; + case LogWarning: return 'w'; + case LogError: return 'e'; + case LogInput: return '<'; + case LogOutput: return '>'; + case LogStatus: return 's'; + case LogTime: return 't'; + case LogMisc: + default: return ' '; + } +} + +static LogChannel channelForChar(QChar c) +{ + switch (c.unicode()) { + case 'd': return LogDebug; + case 'w': return LogWarning; + case 'e': return LogError; + case '<': return LogInput; + case '>': return LogOutput; + case 's': return LogStatus; + case 't': return LogTime; + default: return LogMisc; + } +} + + +///////////////////////////////////////////////////////////////////// +// +// ConsoleHighlighter +// +///////////////////////////////////////////////////////////////////// + +class ConsoleHighlighter : public QSyntaxHighlighter +{ +public: + ConsoleHighlighter(QPlainTextEdit *parent) + : QSyntaxHighlighter(parent->document()), m_parent(parent) + {} + +private: + void highlightBlock(const QString &text) + { + QTextCharFormat format; + switch (channelForChar(text.isEmpty() ? QChar() : text.at(0))) { + case LogInput: + format.setForeground(Qt::blue); + setFormat(1, text.size(), format); + break; + case LogStatus: + format.setForeground(Qt::darkGreen); + setFormat(1, text.size(), format); + break; + case LogWarning: + format.setForeground(Qt::darkYellow); + setFormat(1, text.size(), format); + break; + case LogError: + format.setForeground(Qt::red); + setFormat(1, text.size(), format); + break; + case LogTime: + format.setForeground(Qt::darkRed); + setFormat(1, text.size(), format); + break; + default: + break; + } + QColor base = m_parent->palette().color(QPalette::Base); + format.setForeground(base); + format.setFontPointSize(1); + setFormat(0, 1, format); +/* + if (text.size() > 3 && text.at(2) == QLatin1Char(':')) { + QTextCharFormat format; + format.setForeground(Qt::darkRed); + setFormat(1, text.size(), format); + } +*/ + } + + QPlainTextEdit *m_parent; +}; + +///////////////////////////////////////////////////////////////////// +// +// DebbuggerPane base class +// +///////////////////////////////////////////////////////////////////// + +// FIXME: Code duplication with FakeVim +class History +{ +public: + History() : m_index(0) {} + void append(const QString &item) { + m_items.removeAll(item); + m_items.append(item); m_index = m_items.size() - 1; + } + void down() { m_index = qMin(m_index + 1, m_items.size()); } + void up() { m_index = qMax(m_index - 1, 0); } + //void clear() { m_items.clear(); m_index = 0; } + void restart() { m_index = m_items.size(); } + QString current() const { return m_items.value(m_index, QString()); } + QStringList items() const { return m_items; } +private: + QStringList m_items; + int m_index; +}; + +class Console : public QPlainTextEdit +{ + Q_OBJECT + +public: + Console(QWidget *parent) + : QPlainTextEdit(parent) + { + setMaximumBlockCount(100000); + setFrameStyle(QFrame::NoFrame); + m_clearContentsAction = new QAction(this); + m_clearContentsAction->setText(tr("Clear Contents")); + m_clearContentsAction->setEnabled(true); + connect(m_clearContentsAction, SIGNAL(triggered(bool)), + parent, SLOT(clearContents())); + + m_saveContentsAction = new QAction(this); + m_saveContentsAction->setText(tr("Save Contents")); + m_saveContentsAction->setEnabled(true); + connect(m_saveContentsAction, SIGNAL(triggered()), this, SLOT(saveContents())); + } + + void contextMenuEvent(QContextMenuEvent *ev) + { + theDebuggerAction(ExecuteCommand)->setData(textCursor().block().text()); + QMenu *menu = createStandardContextMenu(); + menu->addAction(m_clearContentsAction); + menu->addAction(m_saveContentsAction); // X11 clipboard is unreliable for long texts + menu->addAction(theDebuggerAction(ExecuteCommand)); + menu->addAction(theDebuggerAction(LogTimeStamps)); + menu->addAction(theDebuggerAction(VerboseLog)); + menu->addSeparator(); + menu->addAction(theDebuggerAction(SettingsDialog)); + menu->exec(ev->globalPos()); + delete menu; + } + + void keyPressEvent(QKeyEvent *ev) + { + if (ev->key() == Qt::Key_Return) { + if (ev->modifiers() == 0) { + QString cmd = textCursor().block().text(); + if (cmd.isEmpty()) + cmd = m_history.current(); + QString cleanCmd; + foreach (QChar c, cmd) + if (c.unicode() >= 32 && c.unicode() < 128) + cleanCmd.append(c); + if (!cleanCmd.isEmpty()) { + theDebuggerAction(ExecuteCommand)->trigger(cleanCmd); + m_history.append(cleanCmd); + } + } + QPlainTextEdit::keyPressEvent(ev); + } else if (ev->key() == Qt::Key_Up) { + m_history.up(); + } else if (ev->key() == Qt::Key_Down) { + m_history.down(); + } else { + QPlainTextEdit::keyPressEvent(ev); + } + } + + void mouseDoubleClickEvent(QMouseEvent *ev) + { + QString line = cursorForPosition(ev->pos()).block().text(); + int n = 0; + + // cut time string + if (line.size() > 18 && line.at(0) == '[') + line = line.mid(18); + //qDebug() << line; + + for (int i = 0; i != line.size(); ++i) { + QChar c = line.at(i); + if (!c.isDigit()) + break; + n = 10 * n + c.unicode() - '0'; + } + //emit commandSelected(n); + } + + +private slots: + void saveContents(); + +private: + QAction *m_clearContentsAction; + QAction *m_saveContentsAction; + History m_history; +}; + +void Console::saveContents() +{ + while (true) { + const QString fileName = QFileDialog::getSaveFileName(this, tr("Log File")); + if (fileName.isEmpty()) + break; + QFile file(fileName); + if (file.open(QIODevice::WriteOnly|QIODevice::Text|QIODevice::Truncate)) { + file.write(toPlainText().toUtf8()); + file.close(); + break; + } else { + QMessageBox::warning(this, tr("Write Failure"), + tr("Unable to write log contents to '%1': %2"). + arg(fileName, file.errorString())); + } + } +} + + + +///////////////////////////////////////////////////////////////////// +// +// ConsoleWindow +// +///////////////////////////////////////////////////////////////////// + +ConsoleWindow::ConsoleWindow(QWidget *parent) + : QWidget(parent) +{ + setWindowTitle(tr("Console")); + + m_console = new Console(this); + m_console->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget(m_console); + layout->addWidget(new Core::FindToolBarPlaceHolder(this)); + setLayout(layout); + + Aggregation::Aggregate *aggregate = new Aggregation::Aggregate; + aggregate->add(m_console); + aggregate->add(new BaseTextFind(m_console)); + + //connect(m_console, SIGNAL(statusMessageRequested(QString,int)), + // this, SIGNAL(statusMessageRequested(QString,int))); +}; + +void ConsoleWindow::showOutput(int channel, const QString &output) +{ + if (output.isEmpty()) + return; + //QTextCursor oldCursor = m_console->textCursor(); + //QTextCursor cursor = oldCursor; + //cursor.movePosition(QTextCursor::End); + //bool atEnd = oldCursor.position() == cursor.position(); + + foreach (QString line, output.split('\n')) { + // FIXME: QTextEdit asserts on really long lines... + const int n = 30000; + if (line.size() > n) { + line.truncate(n); + line += QLatin1String(" [...] <cut off>"); + } + m_console->appendPlainText(charForChannel(channel) + line + "\n"); + } + QTextCursor cursor = m_console->textCursor(); + cursor.movePosition(QTextCursor::End); + //if (atEnd) { + m_console->setTextCursor(cursor); + m_console->ensureCursorVisible(); + //} +} + +void ConsoleWindow::showInput(int channel, const QString &input) +{ + Q_UNUSED(channel) + m_console->appendPlainText(input); + QTextCursor cursor = m_console->textCursor(); + cursor.movePosition(QTextCursor::End); + m_console->setTextCursor(cursor); + m_console->ensureCursorVisible(); +} + +void ConsoleWindow::clearContents() +{ + m_console->clear(); +} + +void ConsoleWindow::setCursor(const QCursor &cursor) +{ + m_console->viewport()->setCursor(cursor); + QWidget::setCursor(cursor); +} + +QString ConsoleWindow::combinedContents() const +{ + return m_console->toPlainText(); +} + +QString ConsoleWindow::inputContents() const +{ + return m_console->toPlainText(); +} + +} // namespace Internal +} // namespace Debugger + +#include "consolewindow.moc" diff --git a/src/plugins/debugger/consolewindow.h b/src/plugins/debugger/consolewindow.h new file mode 100644 index 0000000000..8403e00ead --- /dev/null +++ b/src/plugins/debugger/consolewindow.h @@ -0,0 +1,76 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DEBUGGER_CONSOLEWINDOW_H +#define DEBUGGER_CONSOLEWINDOW_H + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE +class QCursor; +QT_END_NAMESPACE + +namespace Debugger { +namespace Internal { + +class Console; + +class ConsoleWindow : public QWidget +{ + Q_OBJECT + +public: + explicit ConsoleWindow(QWidget *parent = 0); + + void setCursor(const QCursor &cursor); + + QString combinedContents() const; + QString inputContents() const; + + static QString logTimeStamp(); + +public slots: + void clearContents(); + void showOutput(int channel, const QString &output); + void showInput(int channel, const QString &input); + +signals: + void showPage(); + void statusMessageRequested(const QString &msg, int); + +private: + Console *m_console; // combined input/output +}; + + +} // namespace Internal +} // namespace Debugger + +#endif // DEBUGGER_CONSOLEWINDOW_H + diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro index bc95ba52fc..eae0dc3770 100644 --- a/src/plugins/debugger/debugger.pro +++ b/src/plugins/debugger/debugger.pro @@ -19,6 +19,7 @@ QT += gui \ HEADERS += breakhandler.h \ breakwindow.h \ breakpoint.h \ + consolewindow.h \ debuggeragents.h \ debuggeractions.h \ debuggerconstants.h \ @@ -56,6 +57,7 @@ HEADERS += breakhandler.h \ SOURCES += breakhandler.cpp \ breakwindow.cpp \ breakpoint.cpp \ + consolewindow.cpp \ debuggeragents.cpp \ debuggeractions.cpp \ debuggerdialogs.cpp \ diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h index 7850f36cbd..0459b932c7 100644 --- a/src/plugins/debugger/debuggerconstants.h +++ b/src/plugins/debugger/debuggerconstants.h @@ -65,6 +65,7 @@ const char * const DEBUGGER_COMMON_SETTINGS_CATEGORY_ICON = // dock widget names const char * const DOCKWIDGET_BREAK = "Debugger.Docks.Break"; +const char * const DOCKWIDGET_CONSOLE = "Debugger.Docks.Console"; const char * const DOCKWIDGET_MODULES = "Debugger.Docks.Modules"; const char * const DOCKWIDGET_REGISTER = "Debugger.Docks.Register"; const char * const DOCKWIDGET_OUTPUT = "Debugger.Docks.Output"; diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index beb66390ea..def20df07c 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -43,6 +43,7 @@ #include "debuggeruiswitcher.h" #include "breakwindow.h" +#include "consolewindow.h" #include "moduleswindow.h" #include "registerwindow.h" #include "snapshotwindow.h" @@ -993,6 +994,7 @@ public: QComboBox *m_threadBox; QDockWidget *m_breakDock; + QDockWidget *m_consoleDock; QDockWidget *m_modulesDock; QDockWidget *m_outputDock; QDockWidget *m_registerDock; @@ -1006,6 +1008,7 @@ public: DebuggerActions m_actions; BreakWindow *m_breakWindow; + ConsoleWindow *m_consoleWindow; QTreeView *m_returnWindow; QTreeView *m_localsWindow; QTreeView *m_watchersWindow; @@ -1039,6 +1042,7 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(DebuggerPlugin *plugin) m_threadBox = 0; m_breakDock = 0; + m_consoleDock = 0; m_modulesDock = 0; m_outputDock = 0; m_registerDock = 0; @@ -1104,6 +1108,8 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er m_breakWindow = new BreakWindow; m_breakWindow->setObjectName(QLatin1String("CppDebugBreakpoints")); + m_consoleWindow = new ConsoleWindow; + m_consoleWindow->setObjectName(QLatin1String("CppDebugConsole")); m_modulesWindow = new ModulesWindow; m_modulesWindow->setObjectName(QLatin1String("CppDebugModules")); m_outputWindow = new DebuggerOutputWindow; @@ -1266,6 +1272,11 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er // Dock widgets m_breakDock = m_uiSwitcher->createDockWidget(CppLanguage, m_breakWindow); m_breakDock->setObjectName(QString(DOCKWIDGET_BREAK)); + + m_consoleDock = m_uiSwitcher->createDockWidget(CppLanguage, m_consoleWindow, + Qt::TopDockWidgetArea); + m_consoleDock->setObjectName(QString(DOCKWIDGET_OUTPUT)); + m_modulesDock = m_uiSwitcher->createDockWidget(CppLanguage, m_modulesWindow, Qt::TopDockWidgetArea); m_modulesDock->setObjectName(QString(DOCKWIDGET_MODULES)); @@ -1281,6 +1292,7 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er m_outputDock = m_uiSwitcher->createDockWidget(AnyLanguage, m_outputWindow, Qt::TopDockWidgetArea); m_outputDock->setObjectName(QString(DOCKWIDGET_OUTPUT)); + m_snapshotDock = m_uiSwitcher->createDockWidget(CppLanguage, m_snapshotWindow); m_snapshotDock->setObjectName(QString(DOCKWIDGET_SNAPSHOTS)); @@ -2065,9 +2077,10 @@ void DebuggerPluginPrivate::fontSettingsChanged { int size = settings.fontZoom() * settings.fontSize() / 100; changeFontSize(m_breakWindow, size); + changeFontSize(m_outputWindow, size); changeFontSize(m_localsWindow, size); changeFontSize(m_modulesWindow, size); - changeFontSize(m_outputWindow, size); + changeFontSize(m_consoleWindow, size); changeFontSize(m_registerWindow, size); changeFontSize(m_returnWindow, size); changeFontSize(m_sourceFilesWindow, size); @@ -2104,6 +2117,7 @@ void DebuggerPluginPrivate::setBusyCursor(bool busy) m_busy = busy; QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor); m_breakWindow->setCursor(cursor); + m_consoleWindow->setCursor(cursor); m_localsWindow->setCursor(cursor); m_modulesWindow->setCursor(cursor); m_outputWindow->setCursor(cursor); @@ -2131,7 +2145,7 @@ void DebuggerPluginPrivate::setSimpleDockWidgetArrangement } foreach (QDockWidget *dockWidget, dockWidgets) { - if (dockWidget == m_outputDock) { + if (dockWidget == m_outputDock || dockWidget == m_consoleDock) { mw->addDockWidget(Qt::TopDockWidgetArea, dockWidget); } else { mw->addDockWidget(Qt::BottomDockWidgetArea, dockWidget); @@ -2660,6 +2674,7 @@ void DebuggerPlugin::showMessage(const QString &msg, int channel, int timeout) { //qDebug() << "PLUGIN OUTPUT: " << channel << msg; DebuggerOutputWindow *ow = d->m_outputWindow; + ConsoleWindow *cw = d->m_consoleWindow; QTC_ASSERT(ow, return); switch (channel) { case StatusBar: @@ -2679,6 +2694,7 @@ void DebuggerPlugin::showMessage(const QString &msg, int channel, int timeout) break; default: ow->showOutput(channel, msg); + cw->showOutput(channel, msg); break; } } |