changeset 13603:3a1ba08ad392

Further development of IRC backend.
author Jacob Dawid <jacob.dawid@googlemail.com>
date Mon, 15 Aug 2011 13:10:38 +0200
parents cfd231a4286a
children 269ef479ecbf
files gui/src/IRCWidget.cpp gui/src/IRCWidget.h gui/src/qirc/IRCClientImpl.cpp gui/src/qirc/IRCClientImpl.h gui/src/qirc/IRCClientInterface.h
diffstat 5 files changed, 178 insertions(+), 58 deletions(-) [+]
line wrap: on
line diff
--- a/gui/src/IRCWidget.cpp
+++ b/gui/src/IRCWidget.cpp
@@ -24,8 +24,109 @@
 #include <QLabel>
 #include <QSettings>
 #include <QInputDialog>
+#include <QKeyEvent>
+#include <QScrollBar>
 #include "IRCClientImpl.h"
 
+ChatMessageTextEdit::ChatMessageTextEdit (QWidget *parent)
+  : QTextEdit (parent), m_completer (0)
+{
+  setMaximumHeight (50);
+  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+}
+
+ChatMessageTextEdit::~ChatMessageTextEdit ()
+{
+}
+
+void
+ChatMessageTextEdit::setCompleter (QCompleter *completer)
+{
+  if (m_completer)
+    QObject::disconnect (m_completer, 0, this, 0);
+
+  m_completer = completer;
+
+  if (!m_completer)
+    return;
+
+  m_completer->setWidget (this);
+  m_completer->setCompletionMode (QCompleter::InlineCompletion);
+  m_completer->setCaseSensitivity (Qt::CaseInsensitive);
+  QObject::connect (m_completer, SIGNAL (activated (QString)),
+                    this, SLOT (insertCompletion (QString)));
+}
+
+QCompleter *
+ChatMessageTextEdit::completer () const
+{
+  return m_completer;
+}
+
+void
+ChatMessageTextEdit::insertCompletion(const QString& completion)
+{
+
+  if (m_completer->widget() != this)
+    return;
+  QTextCursor tc = textCursor();
+  int extra = completion.length() - m_completer->completionPrefix().length();
+  tc.movePosition(QTextCursor::Left);
+  tc.movePosition(QTextCursor::EndOfWord);
+  tc.insertText(completion.right(extra));
+  setTextCursor(tc);
+}
+
+QString
+ChatMessageTextEdit::textUnderCursor () const
+{
+  QTextCursor tc = textCursor ();
+  tc.select (QTextCursor::WordUnderCursor);
+  return tc.selectedText ();
+}
+
+void
+ChatMessageTextEdit::focusInEvent (QFocusEvent *e)
+{
+  if (m_completer)
+    m_completer->setWidget (this);
+  QTextEdit::focusInEvent (e);
+}
+
+void
+ChatMessageTextEdit::keyPressEvent (QKeyEvent *keyPressEvent)
+{
+  if (m_completer) {
+    switch (keyPressEvent->key ()) {
+    case Qt::Key_Enter:
+    case Qt::Key_Return:
+      if (! (keyPressEvent->modifiers () & Qt::ShiftModifier))
+        {
+          emit sendMessage (toPlainText ());
+          setText ("");
+        }
+      else
+        {
+          QTextEdit::keyPressEvent (keyPressEvent);
+        }
+      break;
+    case Qt::Key_Escape:
+    case Qt::Key_Tab:
+    case Qt::Key_Backtab:
+      keyPressEvent->ignore ();
+      return;
+    default:
+      QTextEdit::keyPressEvent(keyPressEvent);
+      break;
+      }
+
+    QString completionPrefix = textUnderCursor ();
+    if (completionPrefix != m_completer->completionPrefix ())
+      m_completer->setCompletionPrefix(completionPrefix);
+    m_completer->complete ();
+   }
+}
+
 IRCWidget::IRCWidget (QWidget * parent):
 QWidget (parent)
 {
@@ -54,21 +155,21 @@
   m_nickButton = new QPushButton (bottomWidget);
   m_nickButton->setStatusTip (tr ((char *) "Click here to change your nick."));
   m_nickButton->setText (m_initialNick);
-  m_inputLine = new QLineEdit (bottomWidget);
-  m_inputLine->setStatusTip (tr ((char *) "Enter your message here."));
+  m_chatMessageTextEdit = new ChatMessageTextEdit (bottomWidget);
+  m_chatMessageTextEdit->setStatusTip (tr ((char *) "Enter your message here."));
 
   bottomLayout->addWidget (m_nickButton);
   bottomLayout->addWidget (new QLabel (":", this));
-  bottomLayout->addWidget (m_inputLine);
+  bottomLayout->addWidget (m_chatMessageTextEdit);
   bottomLayout->setMargin (0);
   bottomWidget->setLayout (bottomLayout);
 
   m_nickButton->setEnabled (false);
-  m_inputLine->setEnabled (false);
+  m_chatMessageTextEdit->setEnabled (false);
 
   //m_chatWindow->setFocusProxy (m_inputLine);
-  this->setFocusProxy (m_inputLine);
-  m_nickButton->setFocusProxy (m_inputLine);
+  this->setFocusProxy (m_chatMessageTextEdit);
+  m_nickButton->setFocusProxy (m_chatMessageTextEdit);
 
   QFont font;
   font.setFamily ("Courier");
@@ -96,9 +197,12 @@
   connect (m_ircClientInterface, SIGNAL (userNicknameChanged (QString)),
            this, SLOT (handleUserNicknameChanged (QString)));
 
-  connect (m_nickButton, SIGNAL (clicked ()), this, SLOT (nickPopup ()));
-  connect (m_inputLine, SIGNAL (returnPressed ()), this,
-	   SLOT (sendInputLine ()));
+  connect (m_nickButton, SIGNAL (clicked ()), this, SLOT (showChangeUserNickPopup ()));
+  connect (m_chatMessageTextEdit, SIGNAL (sendMessage (QString)),
+           this, SLOT (sendMessage (QString)));
+
+  m_chatMessageTextEdit->setCompleter
+      (new QCompleter (m_ircClientInterface->ircChannelProxy ("#octave")->userListModel (), this));
 
   if (connectOnStartup)
     connectToServer ();
@@ -178,7 +282,7 @@
 }
 
 void
-IRCWidget::nickPopup ()
+IRCWidget::showChangeUserNickPopup ()
 {
   bool ok;
   QString newNick =
@@ -238,13 +342,6 @@
 }
 
 void
-IRCWidget::sendInputLine ()
-{
-  sendMessage (m_inputLine->text ());
-  m_inputLine->setText ("");
-}
-
-void
 IRCWidget::handleLoggedIn (const QString &nick)
 {
   m_chatWindow->
@@ -252,9 +349,9 @@
             ("<i><font color=\"#00AA00\"><b>Successfully logged in as %1.</b></font></i>").
             arg (nick));
   m_nickButton->setEnabled (true);
-  m_inputLine->setEnabled (true);
+  m_chatMessageTextEdit->setEnabled (true);
   m_chatWindow->setEnabled (true);
-  m_inputLine->setFocus ();
+  m_chatMessageTextEdit->setFocus ();
 
 
   if (m_autoIdentification)
@@ -268,27 +365,18 @@
 IRCWidget::handleNickChange (const QString &oldNick, const QString &newNick)
 {
   m_chatWindow->append (QString ("%1 is now known as %2.").arg (oldNick).arg (newNick));
-  m_nickList.removeAll (QString (oldNick));
-  m_nickList.append (QString (newNick));
-  updateNickCompleter ();
 }
 
 void
 IRCWidget::handleUserJoined (const QString &nick, const QString &channel)
 {
-  m_chatWindow->append (QString ("<i>%1 has joined %2.</i>").arg (nick).
-                        arg (channel));
-  m_nickList.append (QString (nick));
-  updateNickCompleter ();
+  m_chatWindow->append (QString ("<i>%1 has joined %2.</i>").arg (nick).arg (channel));
 }
 
 void
 IRCWidget::handleUserQuit (const QString &nick, const QString &reason)
 {
-  m_chatWindow->append (QString ("<i>%1 has quit.(%2).</i>").arg (nick).
-                        arg (reason));
-  m_nickList.removeAll (QString (nick));
-  updateNickCompleter ();
+  m_chatWindow->append (QString ("<i>%1 has quit.(%2).</i>").arg (nick).arg (reason));
 }
 
 void
@@ -298,11 +386,3 @@
   QSettings *settings = ResourceManager::instance ()->settings ();
   settings->setValue ("IRCNick", nick);
 }
-
-void
-IRCWidget::updateNickCompleter ()
-{
-  QCompleter *completer = new QCompleter (m_nickList, this);
-  completer->setCompletionMode (QCompleter::InlineCompletion);
-  m_inputLine->setCompleter (completer);
-}
--- a/gui/src/IRCWidget.h
+++ b/gui/src/IRCWidget.h
@@ -26,7 +26,34 @@
 #include <QCompleter>
 #include "IRCClientInterface.h"
 
-class IRCWidget:public QWidget
+class ChatMessageTextEdit : public QTextEdit
+{
+  Q_OBJECT
+public:
+  explicit ChatMessageTextEdit(QWidget *parent = 0);
+  ~ChatMessageTextEdit();
+
+  void setCompleter(QCompleter *m_completer);
+  QCompleter *completer() const;
+
+signals:
+  void sendMessage (const QString& message);
+
+protected:
+  void keyPressEvent(QKeyEvent *e);
+  void focusInEvent(QFocusEvent *e);
+
+private slots:
+  void insertCompletion(const QString &completion);
+
+private:
+  QString textUnderCursor() const;
+
+private:
+  QCompleter *m_completer;
+};
+
+class IRCWidget : public QWidget
 {
 Q_OBJECT public:
   explicit IRCWidget (QWidget * parent);
@@ -47,23 +74,20 @@
   void handleUserQuit (const QString& nick, const QString& reason);
   void handleUserNicknameChanged (const QString& nick);
 
-  void nickPopup ();
+  void showChangeUserNickPopup ();
   void sendMessage (QString);
-  void sendInputLine ();
-  void updateNickCompleter ();
 
 private:
   IRCClientInterface *m_ircClientInterface;
   IRCChannelProxyInterface *m_octaveChannel;
   QTextEdit *m_chatWindow;
   QPushButton *m_nickButton;
-  QLineEdit *m_inputLine;
+  ChatMessageTextEdit *m_chatMessageTextEdit;
 
   QString m_initialNick;
   bool m_autoIdentification;
   QString m_nickServPassword;
   QString m_settingsFile;
-  QStringList m_nickList;
 };
 
 #endif // IRCWIDGET_H
--- a/gui/src/qirc/IRCClientImpl.cpp
+++ b/gui/src/qirc/IRCClientImpl.cpp
@@ -20,6 +20,9 @@
 
 IRCServerMessage::IRCServerMessage (const QString& serverMessage)
 {
+  if (serverMessage.isEmpty ())
+    return;
+
   int position = 0;
   QString buffer;
 
@@ -31,50 +34,55 @@
   // a prefix. A prefix has the format:
   // :nick!user@host
   // followed by a space character.
-  if (serverMessage.startsWith(":"))
+  if (serverMessage.startsWith (":"))
     {
       position++;
-      while ((serverMessage.at (position) != '!') && !serverMessage.at (position).isSpace ())
+      while ((position < serverMessage.size ())
+             &&(serverMessage.at (position) != '!')
+             && !serverMessage.at (position).isSpace ())
         {
           buffer.append (serverMessage.at (position));
           position++;
         }
-      m_nick = buffer, buffer.clear(), position++;
+      m_nick = buffer, buffer.clear (), position++;
 
       // If it belongs to the prefix, it must be concatenanted neatlessly without
       // any spaces.
       if (!serverMessage.at (position - 1).isSpace ())
         {
-          while (serverMessage.at (position) != '@')
+          while ((position < serverMessage.size ())
+                 && serverMessage.at (position) != '@')
             {
               buffer.append (serverMessage.at (position));
               position++;
             }
-          m_user = buffer, buffer.clear(), position++;
+          m_user = buffer, buffer.clear (), position++;
         }
 
       // If it belongs to the prefix, it must be concatenanted neatlessly without
       // any spaces.
       if (!serverMessage.at (position - 1).isSpace ())
         {
-          while (serverMessage.at (position) != ' ')
+          while ((position < serverMessage.size ())
+                 && serverMessage.at (position) != ' ')
             {
               buffer.append (serverMessage.at (position));
               position++;
             }
-          m_host = buffer, buffer.clear(), position++;
+          m_host = buffer, buffer.clear (), position++;
         }
     }
 
   // The next part is the command. The command can either be numeric
   // or a written command.
-  while (!serverMessage.at (position).isSpace ())
+  while ((position < serverMessage.size ())
+         && !serverMessage.at (position).isSpace ())
     {
       buffer.append (serverMessage.at (position));
       position++;
     }
-  m_command = buffer.toUpper(), buffer.clear(), position++;
-  m_codeNumber = m_command.toInt(&m_isNumeric);
+  m_command = buffer.toUpper (), buffer.clear (), position++;
+  m_codeNumber = m_command.toInt (&m_isNumeric);
 
   // Next: a list of parameters. If any of these parameters
   // starts with a colon, we have to read everything that follows
@@ -158,6 +166,13 @@
 }
 
 void
+IRCChannelProxy::setNickList (const QStringList &nickList)
+{
+  m_userList = nickList;
+  m_userListModel.setStringList (nickList);
+}
+
+void
 IRCChannelProxy::sendMessage (const QString& message)
 {
   QStringList arguments;
@@ -330,7 +345,6 @@
 void
 IRCClientImpl::handleIncomingLine (const QString &line)
 {
-  //emit debugMessage (QString (">>>recv: \"%1\"").arg (line));
   if (m_connected && !line.isEmpty())
     {
       IRCServerMessage ircServerMessage(line);
@@ -372,8 +386,9 @@
               case IRCReply::Topic:
                 break;
               case IRCReply::NameReply:
-                emit debugMessage (QString ("LINKME: (NameReply) \'%1\'").arg (ircServerMessage.parameter(2)));
-                //m_nickList = event->getParam (3).split (QRegExp ("\\s+"), QString::SkipEmptyParts);
+                QString channel = ircServerMessage.parameter (2);
+                QString nickList = ircServerMessage.parameter (3);
+                ircChannelProxy (channel)->setNickList (nickList.split (QRegExp ("\\s+"), QString::SkipEmptyParts));
                 break;
             }
         }
@@ -454,7 +469,6 @@
 void
 IRCClientImpl::sendLine (const QString &line)
 {
-  //emit debugMessage (QString (">>>send: \"%1\"").arg (line));
   if (m_connected)
     m_tcpSocket.write ((line + "\r\n").toStdString ().c_str ());
 }
--- a/gui/src/qirc/IRCClientImpl.h
+++ b/gui/src/qirc/IRCClientImpl.h
@@ -262,6 +262,7 @@
   QStringListModel *userListModel ();
   QString channelName ();
 
+  void setNickList (const QStringList &nickList);
   void sendMessage (const QString& message);
   void sendJoinRequest ();
   void leave (const QString &reason);
--- a/gui/src/qirc/IRCClientInterface.h
+++ b/gui/src/qirc/IRCClientInterface.h
@@ -36,6 +36,7 @@
   virtual QTextDocument *conversationModel () = 0;
   virtual QStringListModel *userListModel () = 0;
   virtual QString channelName () = 0;
+  virtual void setNickList (const QStringList& nickList) = 0;
   virtual void sendMessage (const QString& message) = 0;
   virtual void sendJoinRequest () = 0;
   virtual void leave (const QString& reason) = 0;