PageRenderTime 45ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/quassel-0.7.3/src/uisupport/tabcompleter.cpp

#
C++ | 204 lines | 143 code | 32 blank | 29 comment | 41 complexity | c9850d3554b18e898c8f2668f6349354 MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0
  1. /***************************************************************************
  2. * Copyright (C) 2005-09 by the Quassel Project *
  3. * devel@quassel-irc.org *
  4. * *
  5. * This program is free software; you can redistribute it and/or modify *
  6. * it under the terms of the GNU General Public License as published by *
  7. * the Free Software Foundation; either version 2 of the License, or *
  8. * (at your option) version 3. *
  9. * *
  10. * This program is distributed in the hope that it will be useful, *
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  13. * GNU General Public License for more details. *
  14. * *
  15. * You should have received a copy of the GNU General Public License *
  16. * along with this program; if not, write to the *
  17. * Free Software Foundation, Inc., *
  18. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
  19. ***************************************************************************/
  20. #include "tabcompleter.h"
  21. #include "buffermodel.h"
  22. #include "client.h"
  23. #include "ircchannel.h"
  24. #include "ircuser.h"
  25. #include "multilineedit.h"
  26. #include "network.h"
  27. #include "networkmodel.h"
  28. #include "uisettings.h"
  29. #include <QRegExp>
  30. const Network *TabCompleter::_currentNetwork;
  31. BufferId TabCompleter::_currentBufferId;
  32. QString TabCompleter::_currentBufferName;
  33. TabCompleter::Type TabCompleter::_completionType;
  34. TabCompleter::TabCompleter(MultiLineEdit *_lineEdit)
  35. : QObject(_lineEdit),
  36. _lineEdit(_lineEdit),
  37. _enabled(false),
  38. _nickSuffix(": ")
  39. {
  40. _lineEdit->installEventFilter(this);
  41. }
  42. void TabCompleter::buildCompletionList() {
  43. // ensure a safe state in case we return early.
  44. _completionMap.clear();
  45. _nextCompletion = _completionMap.begin();
  46. // this is the first time tab is pressed -> build up the completion list and it's iterator
  47. QModelIndex currentIndex = Client::bufferModel()->currentIndex();
  48. _currentBufferId = currentIndex.data(NetworkModel::BufferIdRole).value<BufferId>();
  49. if(!_currentBufferId.isValid())
  50. return;
  51. NetworkId networkId = currentIndex.data(NetworkModel::NetworkIdRole).value<NetworkId>();
  52. _currentBufferName = currentIndex.sibling(currentIndex.row(), 0).data().toString();
  53. _currentNetwork = Client::network(networkId);
  54. if(!_currentNetwork)
  55. return;
  56. QString tabAbbrev = _lineEdit->text().left(_lineEdit->cursorPosition()).section(QRegExp("[^#\\w\\d-_\\[\\]{}|`^.\\\\]"),-1,-1);
  57. QRegExp regex(QString("^[-_\\[\\]{}|`^.\\\\]*").append(QRegExp::escape(tabAbbrev)), Qt::CaseInsensitive);
  58. // channel completion - add all channels of the current network to the map
  59. if(tabAbbrev.startsWith('#')) {
  60. _completionType = ChannelTab;
  61. foreach(IrcChannel *ircChannel, _currentNetwork->ircChannels()) {
  62. if(regex.indexIn(ircChannel->name()) > -1)
  63. _completionMap[ircChannel->name()] = ircChannel->name();
  64. }
  65. } else {
  66. // user completion
  67. _completionType = UserTab;
  68. switch(static_cast<BufferInfo::Type>(currentIndex.data(NetworkModel::BufferTypeRole).toInt())) {
  69. case BufferInfo::ChannelBuffer:
  70. { // scope is needed for local var declaration
  71. IrcChannel *channel = _currentNetwork->ircChannel(_currentBufferName);
  72. if(!channel)
  73. return;
  74. foreach(IrcUser *ircUser, channel->ircUsers()) {
  75. if(regex.indexIn(ircUser->nick()) > -1)
  76. _completionMap[ircUser->nick().toLower()] = ircUser->nick();
  77. }
  78. }
  79. break;
  80. case BufferInfo::QueryBuffer:
  81. if(regex.indexIn(_currentBufferName) > -1)
  82. _completionMap[_currentBufferName.toLower()] = _currentBufferName;
  83. case BufferInfo::StatusBuffer:
  84. if(!_currentNetwork->myNick().isEmpty() && regex.indexIn(_currentNetwork->myNick()) > -1)
  85. _completionMap[_currentNetwork->myNick().toLower()] = _currentNetwork->myNick();
  86. break;
  87. default:
  88. return;
  89. }
  90. }
  91. _nextCompletion = _completionMap.begin();
  92. _lastCompletionLength = tabAbbrev.length();
  93. }
  94. void TabCompleter::complete() {
  95. TabCompletionSettings s;
  96. _nickSuffix = s.completionSuffix();
  97. if(!_enabled) {
  98. buildCompletionList();
  99. _enabled = true;
  100. }
  101. if (_nextCompletion != _completionMap.end()) {
  102. // clear previous completion
  103. for (int i = 0; i < _lastCompletionLength; i++) {
  104. _lineEdit->backspace();
  105. }
  106. // insert completion
  107. _lineEdit->insert(*_nextCompletion);
  108. // remember charcount to delete next time and advance to next completion
  109. _lastCompletionLength = _nextCompletion->length();
  110. _nextCompletion++;
  111. // we're completing the first word of the line
  112. if(_completionType == UserTab && _lineEdit->cursorPosition() == _lastCompletionLength) {
  113. _lineEdit->insert(_nickSuffix);
  114. _lastCompletionLength += _nickSuffix.length();
  115. }
  116. // we're at the end of the list -> start over again
  117. } else {
  118. if(!_completionMap.isEmpty()) {
  119. _nextCompletion = _completionMap.begin();
  120. complete();
  121. }
  122. }
  123. }
  124. void TabCompleter::reset() {
  125. _enabled = false;
  126. }
  127. bool TabCompleter::eventFilter(QObject *obj, QEvent *event) {
  128. if(obj != _lineEdit || event->type() != QEvent::KeyPress)
  129. return QObject::eventFilter(obj, event);
  130. QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
  131. if(keyEvent->key() == Qt::Key_Tab) {
  132. complete();
  133. return true;
  134. } else {
  135. reset();
  136. return false;
  137. }
  138. }
  139. // this determines the sort order
  140. bool TabCompleter::CompletionKey::operator<(const CompletionKey &other) const {
  141. switch(_completionType) {
  142. case UserTab:
  143. {
  144. IrcUser *thisUser = _currentNetwork->ircUser(this->contents);
  145. if(thisUser && _currentNetwork->isMe(thisUser))
  146. return false;
  147. IrcUser *thatUser = _currentNetwork->ircUser(other.contents);
  148. if(thatUser && _currentNetwork->isMe(thatUser))
  149. return true;
  150. if(!thisUser || !thatUser)
  151. return QString::localeAwareCompare(this->contents, other.contents) < 0;
  152. QDateTime thisSpokenTo = thisUser->lastSpokenTo(_currentBufferId);
  153. QDateTime thatSpokenTo = thatUser->lastSpokenTo(_currentBufferId);
  154. if(thisSpokenTo.isValid() || thatSpokenTo.isValid())
  155. return thisSpokenTo > thatSpokenTo;
  156. QDateTime thisTime = thisUser->lastChannelActivity(_currentBufferId);
  157. QDateTime thatTime = thatUser->lastChannelActivity(_currentBufferId);
  158. if(thisTime.isValid() || thatTime.isValid())
  159. return thisTime > thatTime;
  160. }
  161. break;
  162. case ChannelTab:
  163. if(QString::compare(_currentBufferName, this->contents, Qt::CaseInsensitive) == 0)
  164. return true;
  165. if(QString::compare(_currentBufferName, other.contents, Qt::CaseInsensitive) == 0)
  166. return false;
  167. break;
  168. default:
  169. break;
  170. }
  171. return QString::localeAwareCompare(this->contents, other.contents) < 0;
  172. }