From 17f2d2e8a589322e28b4e67ec6539acb3e78e2a4 Mon Sep 17 00:00:00 2001 From: csoler Date: Wed, 6 Mar 2013 19:41:08 +0000 Subject: [PATCH] Patch from AC to enable completion over chat lobby participants git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@6193 b45a01b8-16f6-495d-af2f-9b41ad6348cc --- retroshare-gui/src/gui/chat/ChatWidget.cpp | 157 +++++++++++++++++++-- retroshare-gui/src/gui/chat/ChatWidget.h | 4 + 2 files changed, 147 insertions(+), 14 deletions(-) diff --git a/retroshare-gui/src/gui/chat/ChatWidget.cpp b/retroshare-gui/src/gui/chat/ChatWidget.cpp index f89cf48d2..ad6bf02bf 100644 --- a/retroshare-gui/src/gui/chat/ChatWidget.cpp +++ b/retroshare-gui/src/gui/chat/ChatWidget.cpp @@ -233,23 +233,34 @@ bool ChatWidget::eventFilter(QObject *obj, QEvent *event) updateStatusTyping(); QKeyEvent *keyEvent = static_cast(event); - if (keyEvent && (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return)) { - // Enter pressed - if (Settings->getChatSendMessageWithCtrlReturn()) { - if (keyEvent->modifiers() & Qt::ControlModifier) { - // send message with Ctrl+Enter - sendChat(); + if (keyEvent) { + if (isChatLobby) { + if (keyEvent->key() == Qt::Key_Tab) { + completeNickname((bool)(keyEvent->modifiers() & Qt::ShiftModifier)); return true; // eat event } - } else { - if (keyEvent->modifiers() & Qt::ControlModifier) { - // insert return - ui->chatTextEdit->textCursor().insertText("\n"); - } else { - // send message with Enter - sendChat(); + else { + completionWord.clear(); + } + } + if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) { + // Enter pressed + if (Settings->getChatSendMessageWithCtrlReturn()) { + if (keyEvent->modifiers() & Qt::ControlModifier) { + // send message with Ctrl+Enter + sendChat(); + return true; // eat event + } + } else { + if (keyEvent->modifiers() & Qt::ControlModifier) { + // insert return + ui->chatTextEdit->textCursor().insertText("\n"); + } else { + // send message with Enter + sendChat(); + } + return true; // eat event } - return true; // eat event } } } @@ -266,6 +277,124 @@ bool ChatWidget::eventFilter(QObject *obj, QEvent *event) return QWidget::eventFilter(obj, event); } +/** + * @brief Utility function for completeNickname. + */ +static bool caseInsensitiveCompare(QString a, QString b) +{ + return a.toLower() < b.toLower(); +} + +/** + * @brief Completes nickname based on previous characters. + * @param reverse true to list nicknames in reverse alphabetical order. + */ +void ChatWidget::completeNickname(bool reverse) +{ + // Find lobby we belong to + const ChatLobbyInfo *lobby = NULL; + std::list lobbies; + rsMsgs->getChatLobbyList(lobbies); + + std::list::const_iterator lobbyIt; + for (lobbyIt = lobbies.begin(); lobbyIt != lobbies.end(); ++lobbyIt) { + std::string vpid; + if (rsMsgs->getVirtualPeerId(lobbyIt->lobby_id, vpid)) { + if (vpid == peerId) { + lobby = &*lobbyIt; + break; + } + } + } + + if (!lobby) + return; + + QTextCursor cursor = ui->chatTextEdit->textCursor(); + + // Do nothing if there is a selection + if (cursor.anchor() != cursor.position()) + return; + + // We want to complete the word typed by the user. This is easy. + // But also, if there are two participants whose name start with the word + // the user typed, the first press on should show the first + // participant’s name, and the second press on should show the + // second participant’s name. + + cursor.beginEditBlock(); + if (!completionWord.isEmpty()) { + cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, cursor.position() - completionPosition); + } + else { + cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor); + completionPosition = cursor.position(); + } + if (cursor.selectedText() == ": ") { + cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor); + } + bool firstWord = (cursor.position() == 0); + QString word = cursor.selectedText(); + if (word.endsWith(": ")) { + word.chop(2); + } + + if (word.length() > 0) { + // Sort participants list + std::list participants; + for ( std::map::const_iterator it = lobby->nick_names.begin(); + it != lobby->nick_names.end(); + it++) { + participants.push_front(QString::fromUtf8(it->first.c_str())); + } + participants.sort(caseInsensitiveCompare); + + // Search for a participant nickname that starts with the previous word + std::list::const_iterator it, start, end; + int delta; + bool skippedParticipant = false; + if (reverse) { + // Note: at the moment reverse completion doesn’t work because + // shift-tab selects the previous widget + start = participants.end(); + --start; + end = participants.begin(); + --end; + delta = -1; + } + else { + start = participants.begin(); + end = participants.end(); + delta = 1; + } + do { + for (it = start; it != end; (delta == 1) ? ++it : --it) { + QString participant = *it; + if (participant.startsWith(word, Qt::CaseInsensitive)) { + if (!completionWord.isEmpty() && !skippedParticipant) { + // This participant nicknaem was completed with ; + // skip it and find the next one that starts with + // completionWord + word = completionWord; + skippedParticipant = true; + continue; + } + skippedParticipant = false; + cursor.insertText(participant); + if (firstWord) { + cursor.insertText(QString(": ")); + } + break; + } + } + } while (skippedParticipant); + if (completionWord.isEmpty()) { + completionWord = word; + } + } + cursor.endEditBlock(); +} + void ChatWidget::addToolsAction(QAction *action) { ui->pushtoolsButton->menu()->addAction(action); diff --git a/retroshare-gui/src/gui/chat/ChatWidget.h b/retroshare-gui/src/gui/chat/ChatWidget.h index f8361db65..39d28c29f 100644 --- a/retroshare-gui/src/gui/chat/ChatWidget.h +++ b/retroshare-gui/src/gui/chat/ChatWidget.h @@ -135,9 +135,13 @@ private: void setColorAndFont(); void processSettings(bool load); + void completeNickname(bool reverse); + std::string peerId; QString title; QString name; + QString completionWord; + int completionPosition; QColor currentColor; QFont currentFont;