This introduces the new MultiLineEdit that basically enhances the old InputLine widget by being
able to expand to show more than one line. All non-editing related things have been moved out of the
MultiLineEdit to ease reuse in other widgets. For example, InputWidget-specific keypresses are now handled
in InputWidget rather than MultiLineEdit.
Closes #209, closes #213, fixes #386. Thanks to Squider for input (I saw your patch too late, but I did refactor
things anyway...)
#include "chatviewsearchcontroller.h"
#include "client.h"
#include "iconloader.h"
-#include "inputline.h"
+#include "multilineedit.h"
#include "qtui.h"
#include "settings.h"
// Intercept copy key presses
if(keyEvent == QKeySequence::Copy) {
- InputLine *inputLine = qobject_cast<InputLine *>(watched);
+ MultiLineEdit *inputLine = qobject_cast<MultiLineEdit *>(watched);
if(!inputLine)
return false;
if(inputLine->hasSelectedText())
#include "action.h"
#include "actioncollection.h"
+#include "bufferview.h"
#include "client.h"
#include "iconloader.h"
#include "ircuser.h"
#include "networkmodel.h"
#include "qtui.h"
#include "qtuisettings.h"
+#include "tabcompleter.h"
InputWidget::InputWidget(QWidget *parent)
: AbstractItemView(parent),
_networkId(0)
{
ui.setupUi(this);
- connect(ui.inputEdit, SIGNAL(sendText(QString)), this, SLOT(sendText(QString)));
+ connect(ui.inputEdit, SIGNAL(textEntered(QString)), this, SLOT(sendText(QString)));
connect(ui.ownNick, SIGNAL(activated(QString)), this, SLOT(changeNick(QString)));
+
+ layout()->setAlignment(ui.ownNick, Qt::AlignBottom);
+ layout()->setAlignment(ui.inputEdit, Qt::AlignBottom);
+
setFocusProxy(ui.inputEdit);
+ ui.ownNick->setFocusProxy(ui.inputEdit);
ui.ownNick->setSizeAdjustPolicy(QComboBox::AdjustToContents);
ui.ownNick->installEventFilter(new MouseWheelFilter(this));
ui.inputEdit->installEventFilter(new JumpKeyHandler(this));
+ ui.inputEdit->setMinHeight(1);
+ ui.inputEdit->setMaxHeight(5);
+ ui.inputEdit->setMode(MultiLineEdit::MultiLine);
+
+ new TabCompleter(ui.inputEdit);
+
QtUiStyleSettings s("Fonts");
s.notify("InputLine", this, SLOT(setCustomFont(QVariant)));
setCustomFont(s.value("InputLine", QFont()));
ui.inputEdit->setCustomFont(font);
}
+bool InputWidget::eventFilter(QObject *watched, QEvent *event) {
+ if(event->type() != QEvent::KeyPress)
+ return false;
+
+ // keys from BufferView should be sent to (and focus) the input line
+ BufferView *view = qobject_cast<BufferView *>(watched);
+ if(view) {
+ QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
+ if(keyEvent->text().length() == 1 && !(keyEvent->modifiers() & (Qt::ControlModifier ^ Qt::AltModifier)) ) { // normal key press
+ QChar c = keyEvent->text().at(0);
+ if(c.isLetterOrNumber() || c.isSpace() || c.isPunct() || c.isSymbol()) {
+ setFocus();
+ QCoreApplication::sendEvent(inputLine(), keyEvent);
+ return true;
+ } else
+ return false;
+ }
+ }
+ return false;
+}
+
+void InputWidget::keyPressEvent(QKeyEvent * event) {
+ if(event->matches(QKeySequence::Find)) {
+ QAction *act = GraphicalUi::actionCollection()->action("ToggleSearchBar");
+ if(act) {
+ act->toggle();
+ return;
+ }
+ }
+ AbstractItemView::keyPressEvent(event);
+}
+
void InputWidget::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) {
Q_UNUSED(previous)
NetworkId networkId = current.data(NetworkModel::NetworkIdRole).value<NetworkId>();
#include "identity.h"
#include "network.h"
-class InputLine;
+class MultiLineEdit;
class InputWidget : public AbstractItemView {
Q_OBJECT
const Network *currentNetwork() const;
- inline InputLine* inputLine() const { return ui.inputEdit; }
+ inline MultiLineEdit* inputLine() const { return ui.inputEdit; }
+
+protected:
+ virtual bool eventFilter(QObject *watched, QEvent *event);
+ virtual void keyPressEvent(QKeyEvent * event);
protected slots:
virtual void currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
#include "flatproxymodel.h"
#include "iconloader.h"
#include "inputwidget.h"
-#include "inputline.h"
#include "irclistmodel.h"
#include "ircconnectionwizard.h"
#include "jumpkeyhandler.h"
//create the view and initialize it's filter
BufferView *view = new BufferView(dock);
view->setFilteredModel(Client::bufferModel(), config);
- view->installEventFilter(_inputWidget->inputLine()); // for key presses
+ view->installEventFilter(_inputWidget); // for key presses
view->show();
Client::bufferModel()->synchronizeView(view);
<rect>
<x>0</x>
<y>0</y>
- <width>761</width>
- <height>194</height>
+ <width>585</width>
+ <height>52</height>
</rect>
</property>
<property name="sizePolicy">
<property name="windowTitle">
<string>Form</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <property name="spacing">
- <number>-1</number>
- </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin">
<number>0</number>
</property>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <widget class="QComboBox" name="ownNick"/>
- </item>
- <item>
- <widget class="InputLine" name="inputEdit">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="verticalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOff</enum>
- </property>
- <property name="horizontalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOff</enum>
- </property>
- <property name="lineWrapMode">
- <enum>QTextEdit::NoWrap</enum>
- </property>
- </widget>
- </item>
- </layout>
+ <widget class="QComboBox" name="ownNick"/>
+ </item>
+ <item>
+ <widget class="MultiLineEdit" name="inputEdit">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="verticalScrollBarPolicy">
+ <enum>Qt::ScrollBarAsNeeded</enum>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAsNeeded</enum>
+ </property>
+ <property name="lineWrapMode">
+ <enum>QTextEdit::NoWrap</enum>
+ </property>
+ </widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
- <class>InputLine</class>
+ <class>MultiLineEdit</class>
<extends>QTextEdit</extends>
- <header>inputline.h</header>
+ <header>multilineedit.h</header>
</customwidget>
</customwidgets>
<resources/>
graphicalui.cpp
icon.cpp
iconloader.cpp
- inputline.cpp
+ multilineedit.cpp
networkmodelcontroller.cpp
nickview.cpp
nickviewfilter.cpp
fontselector.h
graphicalui.h
iconloader.h
- inputline.h
+ multilineedit.h
networkmodelcontroller.h
nickview.h
nickviewfilter.h
+++ /dev/null
-/***************************************************************************
- * Copyright (C) 2005/06 by the Quassel Project *
- * devel@quassel-irc.org *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) version 3. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write to the *
- * Free Software Foundation, Inc., *
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
- ***************************************************************************/
-
-#include <QApplication>
-#include <QMenu>
-#include <QMessageBox>
-
-#include "bufferview.h"
-#include "graphicalui.h"
-#include "inputline.h"
-#include "tabcompleter.h"
-
-const int leftMargin = 3;
-
-InputLine::InputLine(QWidget *parent)
- :
-#ifdef HAVE_KDE
- KTextEdit(parent),
-#else
- QTextEdit(parent),
-#endif
- idx(0),
- tabCompleter(new TabCompleter(this))
-{
- // Make the QTextEdit look like a QLineEdit
-#if QT_VERSION >= 0x040500
- document()->setDocumentMargin(0); // new in Qt 4.5 and we really don't want it here
-#endif
- setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- setAcceptRichText(false);
- setLineWrapMode(NoWrap);
-#ifdef HAVE_KDE
- enableFindReplace(false);
-#endif
- resetLine();
-
- connect(this, SIGNAL(textChanged()), this, SLOT(on_textChanged()));
- connect(this, SIGNAL(returnPressed()), this, SLOT(on_returnPressed()));
- connect(this, SIGNAL(textChanged(QString)), this, SLOT(on_textChanged(QString)));
-}
-
-InputLine::~InputLine() {
-}
-
-void InputLine::setCustomFont(const QFont &font) {
- setFont(font);
-}
-
-QSize InputLine::sizeHint() const {
- // use the style to determine a decent size
- QFontMetrics fm(font());
- int h = fm.lineSpacing() + 2 * frameWidth();
- QStyleOptionFrameV2 opt;
- opt.initFrom(this);
- opt.rect = QRect(0, 0, 100, h);
- opt.lineWidth = lineWidth();
- opt.midLineWidth = midLineWidth();
- opt.state |= QStyle::State_Sunken;
- QSize s = style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(100, h).expandedTo(QApplication::globalStrut()), this);
- return s;
-}
-
-QSize InputLine::minimumSizeHint() const {
- return sizeHint();
-}
-
-bool InputLine::eventFilter(QObject *watched, QEvent *event) {
- if(event->type() != QEvent::KeyPress)
- return false;
-
- // keys from BufferView should be sent to (and focus) the input line
- BufferView *view = qobject_cast<BufferView *>(watched);
- if(view) {
- QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
- if(keyEvent->text().length() == 1 && !(keyEvent->modifiers() & (Qt::ControlModifier ^ Qt::AltModifier)) ) { // normal key press
- QChar c = keyEvent->text().at(0);
- if(c.isLetterOrNumber() || c.isSpace() || c.isPunct() || c.isSymbol()) {
- setFocus();
- keyPressEvent(keyEvent);
- return true;
- } else
- return false;
- }
- }
- return false;
-}
-
-void InputLine::keyPressEvent(QKeyEvent * event) {
- if(event->matches(QKeySequence::Find)) {
- QAction *act = GraphicalUi::actionCollection()->action("ToggleSearchBar");
- if(act) {
- act->toggle();
- event->accept();
- return;
- }
- }
-
- switch(event->key()) {
- case Qt::Key_Up:
- event->accept();
-
- addToHistory(text(), true);
-
- if(idx > 0) {
- idx--;
- showHistoryEntry();
- }
-
- break;
-
- case Qt::Key_Down:
- event->accept();
-
- addToHistory(text(), true);
-
- if(idx < history.count()) {
- idx++;
- if(idx < history.count() || tempHistory.contains(idx)) // tempHistory might have an entry for idx == history.count() + 1
- showHistoryEntry();
- else
- resetLine(); // equals clear() in this case
- } else {
- addToHistory(text());
- resetLine();
- }
-
- break;
-
- case Qt::Key_Return:
- case Qt::Key_Enter:
- case Qt::Key_Select:
- event->accept();
- emit returnPressed();
- break;
-
- default:
- QTextEdit::keyPressEvent(event);
- }
-}
-
-bool InputLine::addToHistory(const QString &text, bool temporary) {
- if(text.isEmpty())
- return false;
-
- Q_ASSERT(0 <= idx && idx <= history.count());
-
- if(temporary) {
- // if an entry of the history is changed, we remember it and show it again at this
- // position until a line was actually sent
- // sent lines get appended to the history
- if(history.isEmpty() || text != history[idx - (int)(idx == history.count())]) {
- tempHistory[idx] = text;
- return true;
- }
- } else {
- if(history.isEmpty() || text != history.last()) {
- history << text;
- tempHistory.clear();
- return true;
- }
- }
- return false;
-}
-
-void InputLine::on_returnPressed() {
- if(!text().isEmpty()) {
- addToHistory(text());
- emit sendText(text());
- resetLine();
- }
-}
-
-void InputLine::on_textChanged(QString newText) {
- QStringList lineSeparators;
- lineSeparators << QString("\r\n")
- << QString('\n')
- << QString('\r');
-
- QString lineSep;
- foreach(QString separator, lineSeparators) {
- if(newText.contains(separator)) {
- lineSep = separator;
- break;
- }
- }
-
- if(lineSep.isEmpty())
- return;
-
- QStringList lines = newText.split(lineSep, QString::SkipEmptyParts);
-
- if(lines.count() >= 4) {
- QString msg = tr("Do you really want to paste %n lines?", "", lines.count());
- msg += "<p>";
- for(int i = 0; i < 3; i++) {
- msg += lines[i].left(40);
- if(lines[i].count() > 40)
- msg += "...";
- msg += "<br />";
- }
- msg += "...</p>";
- QMessageBox question(QMessageBox::NoIcon, tr("Paste Protection"), msg, QMessageBox::Yes|QMessageBox::No);
- question.setDefaultButton(QMessageBox::No);
-#ifdef Q_WS_MAC
- question.setWindowFlags(question.windowFlags() | Qt::Sheet); // Qt::Sheet is not ignored on other platforms as it should :/
-#endif
- if(question.exec() == QMessageBox::No) {
- clear();
- return;
- }
- }
-
- foreach(QString line, lines) {
- if(!line.isEmpty()) {
- resetLine();
- insert(line);
- emit returnPressed();
- }
- }
-
-// if(newText.contains(lineSep)) {
-// clear();
-// QString line = newText.section(lineSep, 0, 0);
-// QString remainder = newText.section(lineSep, 1);
-// insert(line);
-// emit returnPressed();
-// insert(remainder);
-// }
-}
-
-void InputLine::resetLine() {
- // every time the InputLine is cleared we also reset history index
- idx = history.count();
- clear();
- QTextBlockFormat format = textCursor().blockFormat();
- format.setLeftMargin(leftMargin); // we want a little space between the frame and the contents
- textCursor().setBlockFormat(format);
-}
-
-void InputLine::showHistoryEntry() {
- // if the user changed the history, display the changed line
- setPlainText(tempHistory.contains(idx) ? tempHistory[idx] : history[idx]);
- QTextCursor cursor = textCursor();
- QTextBlockFormat format = cursor.blockFormat();
- format.setLeftMargin(leftMargin); // we want a little space between the frame and the contents
- cursor.setBlockFormat(format);
- cursor.movePosition(QTextCursor::End);
- setTextCursor(cursor);
-}
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005/06 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QApplication>
+#include <QMenu>
+#include <QMessageBox>
+#include <QScrollBar>
+
+#include "bufferview.h"
+#include "graphicalui.h"
+#include "multilineedit.h"
+#include "tabcompleter.h"
+
+const int leftMargin = 3;
+
+MultiLineEdit::MultiLineEdit(QWidget *parent)
+ :
+#ifdef HAVE_KDE
+ KTextEdit(parent),
+#else
+ QTextEdit(parent),
+#endif
+ idx(0),
+ _mode(SingleLine),
+ _wrapMode(QTextOption::NoWrap),
+ _numLines(1),
+ _minHeight(1),
+ _maxHeight(5),
+ _scrollBarsEnabled(true),
+ _lastDocumentHeight(-1)
+{
+#if QT_VERSION >= 0x040500
+ document()->setDocumentMargin(0); // new in Qt 4.5 and we really don't want it here
+#endif
+
+ setAcceptRichText(false);
+ setWordWrapMode(QTextOption::NoWrap);
+#ifdef HAVE_KDE
+ enableFindReplace(false);
+#endif
+
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+ setMode(SingleLine);
+
+ reset();
+
+ connect(this, SIGNAL(textChanged()), this, SLOT(on_textChanged()));
+}
+
+MultiLineEdit::~MultiLineEdit() {
+}
+
+void MultiLineEdit::setCustomFont(const QFont &font) {
+ setFont(font);
+ updateGeometry();
+}
+
+void MultiLineEdit::setMode(Mode mode) {
+ if(mode == _mode)
+ return;
+
+ _mode = mode;
+}
+
+void MultiLineEdit::setWrapMode(QTextOption::WrapMode wrapMode) {
+ if(_wrapMode == wrapMode)
+ return;
+
+ _wrapMode = wrapMode;
+ setWordWrapMode(wrapMode);
+}
+
+void MultiLineEdit::setMinHeight(int lines) {
+ if(lines == _minHeight)
+ return;
+
+ _minHeight = lines;
+ updateGeometry();
+}
+
+void MultiLineEdit::setMaxHeight(int lines) {
+ if(lines == _maxHeight)
+ return;
+
+ _maxHeight = lines;
+ updateGeometry();
+}
+
+void MultiLineEdit::enableScrollBars(bool enable) {
+ if(_scrollBarsEnabled == enable)
+ return;
+
+ _scrollBarsEnabled = enable;
+ if(enable && numLines() > 1) {
+ // the vertical scrollbar must be enabled/disabled manually;
+ // ScrollBarAsNeeded leads to flicker because of the dynamic widget resize
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ } else {
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ }
+ updateScrollBars();
+}
+
+void MultiLineEdit::updateScrollBars() {
+ QFontMetrics fm(font());
+ int _maxPixelHeight = fm.lineSpacing() * _maxHeight;
+ if(_scrollBarsEnabled && document()->size().height() > _maxPixelHeight)
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ else
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+}
+
+void MultiLineEdit::resizeEvent(QResizeEvent *event) {
+ updateScrollBars();
+ QTextEdit::resizeEvent(event);
+}
+
+QSize MultiLineEdit::sizeHint() const {
+ QFontMetrics fm(font());
+ int _minPixelHeight = fm.lineSpacing() * _minHeight;
+ int _maxPixelHeight = fm.lineSpacing() * _maxHeight;
+
+ // use the style to determine a decent size
+ int h = qMin(qMax((int)document()->size().height(), _minPixelHeight), _maxPixelHeight) + 2 * frameWidth();
+ QStyleOptionFrameV2 opt;
+ opt.initFrom(this);
+ opt.rect = QRect(0, 0, 100, h);
+ opt.lineWidth = lineWidth();
+ opt.midLineWidth = midLineWidth();
+ opt.state |= QStyle::State_Sunken;
+ QSize s = style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(100, h).expandedTo(QApplication::globalStrut()), this);
+ return s;
+}
+
+QSize MultiLineEdit::minimumSizeHint() const {
+ return sizeHint();
+}
+
+void MultiLineEdit::historyMoveBack() {
+ addToHistory(text(), true);
+
+ if(idx > 0) {
+ idx--;
+ showHistoryEntry();
+ }
+}
+
+void MultiLineEdit::historyMoveForward() {
+ addToHistory(text(), true);
+
+ if(idx < history.count()) {
+ idx++;
+ if(idx < history.count() || tempHistory.contains(idx)) // tempHistory might have an entry for idx == history.count() + 1
+ showHistoryEntry();
+ else
+ reset(); // equals clear() in this case
+ }
+}
+
+bool MultiLineEdit::addToHistory(const QString &text, bool temporary) {
+ if(text.isEmpty())
+ return false;
+
+ Q_ASSERT(0 <= idx && idx <= history.count());
+
+ if(temporary) {
+ // if an entry of the history is changed, we remember it and show it again at this
+ // position until a line was actually sent
+ // sent lines get appended to the history
+ if(history.isEmpty() || text != history[idx - (int)(idx == history.count())]) {
+ tempHistory[idx] = text;
+ return true;
+ }
+ } else {
+ if(history.isEmpty() || text != history.last()) {
+ history << text;
+ tempHistory.clear();
+ return true;
+ }
+ }
+ return false;
+}
+
+void MultiLineEdit::keyPressEvent(QKeyEvent *event) {
+ if(event == QKeySequence::InsertLineSeparator) {
+ if(_mode == SingleLine)
+ return;
+#ifdef HAVE_KDE
+ KTextEdit::keyPressEvent(event);
+#else
+ QTextEdit::keyPressEvent(event);
+#endif
+ return;
+ }
+
+ switch(event->key()) {
+ case Qt::Key_Up: {
+ event->accept();
+ if(!(event->modifiers() & Qt::ControlModifier)) {
+ int pos = textCursor().position();
+ moveCursor(QTextCursor::Up);
+ if(pos == textCursor().position()) // already on top line -> history
+ historyMoveBack();
+ } else
+ historyMoveBack();
+ break;
+ }
+
+ case Qt::Key_Down: {
+ event->accept();
+ if(!(event->modifiers() & Qt::ControlModifier)) {
+ int pos = textCursor().position();
+ moveCursor(QTextCursor::Down);
+ if(pos == textCursor().position()) // already on bottom line -> history
+ historyMoveForward();
+ } else
+ historyMoveForward();
+ break;
+ }
+
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ case Qt::Key_Select:
+ event->accept();
+ on_returnPressed();
+ break;
+
+ // We don't want to have the tab key react if no completer is installed
+ case Qt::Key_Tab:
+ event->accept();
+ break;
+
+ default:
+#ifdef HAVE_KDE
+ KTextEdit::keyPressEvent(event);
+#else
+ QTextEdit::keyPressEvent(event);
+#endif
+ }
+}
+
+void MultiLineEdit::on_returnPressed() {
+ if(!text().isEmpty()) {
+ foreach(const QString &line, text().split('\n', QString::SkipEmptyParts)) {
+ if(line.isEmpty())
+ continue;
+ addToHistory(line);
+ emit textEntered(line);
+ }
+ reset();
+ tempHistory.clear();
+ }
+}
+
+void MultiLineEdit::on_textChanged() {
+ QString newText = text();
+ newText.replace("\r\n", "\n");
+ newText.replace('\r', '\n');
+ if(_mode == SingleLine)
+ newText.replace('\n', ' ');
+
+ if(document()->size().height() != _lastDocumentHeight) {
+ _lastDocumentHeight = document()->size().height();
+ on_documentHeightChanged(_lastDocumentHeight);
+ }
+ updateGeometry();
+}
+
+void MultiLineEdit::on_documentHeightChanged(qreal) {
+ updateScrollBars();
+}
+
+void MultiLineEdit::reset() {
+ // every time the MultiLineEdit is cleared we also reset history index
+ idx = history.count();
+ clear();
+ QTextBlockFormat format = textCursor().blockFormat();
+ format.setLeftMargin(leftMargin); // we want a little space between the frame and the contents
+ textCursor().setBlockFormat(format);
+}
+
+void MultiLineEdit::showHistoryEntry() {
+ // if the user changed the history, display the changed line
+ setPlainText(tempHistory.contains(idx) ? tempHistory[idx] : history[idx]);
+ QTextCursor cursor = textCursor();
+ QTextBlockFormat format = cursor.blockFormat();
+ format.setLeftMargin(leftMargin); // we want a little space between the frame and the contents
+ cursor.setBlockFormat(format);
+ cursor.movePosition(QTextCursor::End);
+ setTextCursor(cursor);
+}
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
-#ifndef INPUTLINE_H_
-#define INPUTLINE_H_
+#ifndef MULTILINEEDIT_H_
+#define MULTILINEEDIT_H_
-#include <QHash>
#include <QKeyEvent>
+#include <QHash>
#include <QTextEdit>
#ifdef HAVE_KDE
-#include <KDE/KTextEdit>
+# include <KDE/KTextEdit>
#endif
+class QKeyEvent;
class TabCompleter;
-class InputLine : public
+class MultiLineEdit : public
#ifdef HAVE_KDE
KTextEdit
#else
Q_OBJECT
public:
- InputLine(QWidget *parent = 0);
- ~InputLine();
+ enum Mode {
+ SingleLine,
+ MultiLine
+ };
+
+ MultiLineEdit(QWidget *parent = 0);
+ ~MultiLineEdit();
void setCustomFont(const QFont &); // should be used instead setFont(), so we can set our size correctly
virtual QSize sizeHint() const;
virtual QSize minimumSizeHint() const;
+public slots:
+ void setMode(Mode mode);
+ void setWrapMode(QTextOption::WrapMode = QTextOption::NoWrap);
+ void setMinHeight(int numLines);
+ void setMaxHeight(int numLines);
+ void enableScrollBars(bool enable = true);
+
+signals:
+ void textEntered(const QString &text);
+
protected:
virtual void keyPressEvent(QKeyEvent * event);
- virtual bool eventFilter(QObject *watched, QEvent *event);
+ virtual void resizeEvent(QResizeEvent *event);
private slots:
void on_returnPressed();
- void on_textChanged(QString newText);
-
- // Needed to emulate the signal that QLineEdit has
- inline void on_textChanged() { emit textChanged(toPlainText()); };
+ void on_textChanged();
+ void on_documentHeightChanged(qreal height);
bool addToHistory(const QString &text, bool temporary = false);
-
-signals:
- void sendText(QString text);
-
- // QTextEdit does not provide this signal, so we manually emit it in keyPressEvent()
- void returnPressed();
- void textChanged(QString newText);
+ void historyMoveForward();
+ void historyMoveBack();
private:
QStringList history;
QHash<int, QString> tempHistory;
qint32 idx;
- TabCompleter *tabCompleter;
+ Mode _mode;
+ QTextOption::WrapMode _wrapMode;
+ int _numLines;
+ int _minHeight;
+ int _maxHeight;
+ bool _scrollBarsEnabled;
- int bindModifier;
- int jumpModifier;
+ QSize _sizeHint;
+ qreal _lastDocumentHeight;
- void resetLine();
+ void reset();
void showHistoryEntry();
+ void updateScrollBars();
+
+ inline int numLines() const { return _numLines; }
+ void setNumLines(int);
};
#endif
#include "tabcompleter.h"
-#include "inputline.h"
-#include "client.h"
#include "buffermodel.h"
-#include "networkmodel.h"
-#include "network.h"
+#include "client.h"
#include "ircchannel.h"
#include "ircuser.h"
+#include "multilineedit.h"
+#include "network.h"
+#include "networkmodel.h"
#include "uisettings.h"
#include <QRegExp>
const Network *TabCompleter::_currentNetwork;
BufferId TabCompleter::_currentBufferId;
-TabCompleter::TabCompleter(InputLine *inputLine_)
- : QObject(inputLine_),
- inputLine(inputLine_),
- enabled(false),
- nickSuffix(": ")
+TabCompleter::TabCompleter(MultiLineEdit *_lineEdit)
+ : QObject(_lineEdit),
+ _lineEdit(_lineEdit),
+ _enabled(false),
+ _nickSuffix(": ")
{
- inputLine->installEventFilter(this);
+ _lineEdit->installEventFilter(this);
}
void TabCompleter::buildCompletionList() {
// ensure a safe state in case we return early.
- completionMap.clear();
- nextCompletion = completionMap.begin();
+ _completionMap.clear();
+ _nextCompletion = _completionMap.begin();
// this is the first time tab is pressed -> build up the completion list and it's iterator
QModelIndex currentIndex = Client::bufferModel()->currentIndex();
if(!_currentNetwork)
return;
- QString tabAbbrev = inputLine->text().left(inputLine->cursorPosition()).section(' ',-1,-1);
+ QString tabAbbrev = _lineEdit->text().left(_lineEdit->cursorPosition()).section(' ',-1,-1);
QRegExp regex(QString("^[^a-zA-Z]*").append(QRegExp::escape(tabAbbrev)), Qt::CaseInsensitive);
switch(static_cast<BufferInfo::Type>(currentIndex.data(NetworkModel::BufferTypeRole).toInt())) {
return;
foreach(IrcUser *ircUser, channel->ircUsers()) {
if(regex.indexIn(ircUser->nick()) > -1)
- completionMap[ircUser->nick().toLower()] = ircUser->nick();
+ _completionMap[ircUser->nick().toLower()] = ircUser->nick();
}
}
break;
case BufferInfo::QueryBuffer:
if(regex.indexIn(bufferName) > -1)
- completionMap[bufferName.toLower()] = bufferName;
+ _completionMap[bufferName.toLower()] = bufferName;
case BufferInfo::StatusBuffer:
if(!_currentNetwork->myNick().isEmpty() && regex.indexIn(_currentNetwork->myNick()) > -1)
- completionMap[_currentNetwork->myNick().toLower()] = _currentNetwork->myNick();
+ _completionMap[_currentNetwork->myNick().toLower()] = _currentNetwork->myNick();
break;
default:
return;
}
- nextCompletion = completionMap.begin();
- lastCompletionLength = tabAbbrev.length();
+ _nextCompletion = _completionMap.begin();
+ _lastCompletionLength = tabAbbrev.length();
}
void TabCompleter::complete() {
TabCompletionSettings s;
- nickSuffix = s.completionSuffix();
+ _nickSuffix = s.completionSuffix();
- if(!enabled) {
+ if(!_enabled) {
buildCompletionList();
- enabled = true;
+ _enabled = true;
}
- if (nextCompletion != completionMap.end()) {
+ if (_nextCompletion != _completionMap.end()) {
// clear previous completion
- for (int i = 0; i < lastCompletionLength; i++) {
- inputLine->backspace();
+ for (int i = 0; i < _lastCompletionLength; i++) {
+ _lineEdit->backspace();
}
// insert completion
- inputLine->insert(*nextCompletion);
+ _lineEdit->insert(*_nextCompletion);
// remember charcount to delete next time and advance to next completion
- lastCompletionLength = nextCompletion->length();
- nextCompletion++;
+ _lastCompletionLength = _nextCompletion->length();
+ _nextCompletion++;
// we're completing the first word of the line
- if(inputLine->cursorPosition() == lastCompletionLength) {
- inputLine->insert(nickSuffix);
- lastCompletionLength += nickSuffix.length();
+ if(_lineEdit->cursorPosition() == _lastCompletionLength) {
+ _lineEdit->insert(_nickSuffix);
+ _lastCompletionLength += _nickSuffix.length();
}
// we're at the end of the list -> start over again
} else {
- if(!completionMap.isEmpty()) {
- nextCompletion = completionMap.begin();
+ if(!_completionMap.isEmpty()) {
+ _nextCompletion = _completionMap.begin();
complete();
}
}
}
void TabCompleter::reset() {
- enabled = false;
+ _enabled = false;
}
bool TabCompleter::eventFilter(QObject *obj, QEvent *event) {
- if(obj != inputLine || event->type() != QEvent::KeyPress)
+ if(obj != _lineEdit || event->type() != QEvent::KeyPress)
return QObject::eventFilter(obj, event);
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
#include "types.h"
-class InputLine;
+class MultiLineEdit;
class IrcUser;
class Network;
Q_OBJECT
public:
- TabCompleter(InputLine *inputLine_);
+ explicit TabCompleter(MultiLineEdit *inputLine_);
void reset();
void complete();
QString nick;
};
- QPointer<InputLine> inputLine;
- bool enabled;
- QString nickSuffix;
+ QPointer<MultiLineEdit> _lineEdit;
+ bool _enabled;
+ QString _nickSuffix;
static const Network *_currentNetwork;
static BufferId _currentBufferId;
- QMap<CompletionKey, QString> completionMap;
+ QMap<CompletionKey, QString> _completionMap;
// QStringList completionTemplates;
- QMap<CompletionKey, QString>::Iterator nextCompletion;
- int lastCompletionLength;
+ QMap<CompletionKey, QString>::Iterator _nextCompletion;
+ int _lastCompletionLength;
void buildCompletionList();