QTfrontend/ui/widget/SmartLineEdit.cpp
branchhedgeroid
changeset 6224 42b256eca362
parent 6218 999215ca87d7
child 6616 f77bb02b669f
equal deleted inserted replaced
6055:88cfcd9161d3 6224:42b256eca362
       
     1 /*
       
     2  * Hedgewars, a free turn based strategy game
       
     3  * Copyright (c) 2006-2007 Igor Ulyanov <iulyanov@gmail.com>
       
     4  * Copyright (c) 2007-2011 Andrey Korotaev <unC0Rr@gmail.com>
       
     5  *
       
     6  * This program is free software; you can redistribute it and/or modify
       
     7  * it under the terms of the GNU General Public License as published by
       
     8  * the Free Software Foundation; version 2 of the License
       
     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 Free Software
       
    17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
       
    18  */
       
    19 
       
    20 /**
       
    21  * @file
       
    22  * @brief SmartLineEdit class implementation
       
    23  */
       
    24 
       
    25 #include "SmartLineEdit.h"
       
    26 
       
    27 SmartLineEdit::SmartLineEdit(QWidget * parent, int maxHistorySize)
       
    28 : HistoryLineEdit(parent, maxHistorySize)
       
    29 {
       
    30     m_whitespace = QRegExp("\\s");
       
    31 
       
    32     m_cmds  = new QStringList();
       
    33     m_nicks = new QStringList();
       
    34     m_sorted_nicks = new QMap<QString, QString>();
       
    35 
       
    36     resetAutoCompletionStatus();
       
    37 
       
    38     // reset autocompletion status when cursor is moved or content is changed
       
    39     connect(this, SIGNAL(cursorPositionChanged(int, int)),
       
    40             this, SLOT(resetAutoCompletionStatus()));
       
    41     connect(this, SIGNAL(textChanged(const QString&)),
       
    42             this, SLOT(resetAutoCompletionStatus()));
       
    43 }
       
    44 
       
    45 
       
    46 SmartLineEdit::~SmartLineEdit()
       
    47 {
       
    48     delete m_cmds;
       
    49     delete m_nicks;
       
    50     delete m_sorted_nicks;
       
    51 }
       
    52 
       
    53 
       
    54 void SmartLineEdit::addCommands(const QStringList & commands)
       
    55 {
       
    56     m_cmds->append(commands);
       
    57 }
       
    58 
       
    59 
       
    60 void SmartLineEdit::removeCommands(const QStringList & commands)
       
    61 {
       
    62     foreach (const QString & cmd, commands)
       
    63     {
       
    64         m_cmds->removeAll(cmd);
       
    65     }
       
    66 }
       
    67 
       
    68 
       
    69 void SmartLineEdit::addNickname(const QString & name)
       
    70 {
       
    71     m_sorted_nicks->insert(name.toLower(), name);
       
    72     m_nicks->append(name);
       
    73 }
       
    74 
       
    75 
       
    76 void SmartLineEdit::removeNickname(const QString & name)
       
    77 {
       
    78     m_sorted_nicks->remove(name.toLower());
       
    79     m_nicks->removeAll(name);
       
    80 }
       
    81 
       
    82 
       
    83 void SmartLineEdit::reset()
       
    84 {
       
    85     // forget keywords
       
    86     m_cmds->clear();
       
    87     m_sorted_nicks->clear();
       
    88     m_nicks->clear();
       
    89     resetAutoCompletionStatus();
       
    90 
       
    91     // forget history
       
    92     HistoryLineEdit::reset();
       
    93 }
       
    94 
       
    95 
       
    96 bool SmartLineEdit::event(QEvent * event)
       
    97 {
       
    98     // we only want special treatment for key press events
       
    99     if (event->type() == QEvent::KeyPress)
       
   100     {
       
   101         QKeyEvent * keyEvent = static_cast<QKeyEvent*>(event);
       
   102 
       
   103         // TAB key pressed and any useful chars in the matchMe -> let's process those
       
   104         if ((keyEvent->key() == Qt::Key_Tab) && (!text().trimmed().isEmpty()))
       
   105         {
       
   106             keyPressEvent(keyEvent);
       
   107             if (event->isAccepted())
       
   108                 return true;
       
   109         }
       
   110     }
       
   111     return HistoryLineEdit::event(event);
       
   112 }
       
   113 
       
   114 void SmartLineEdit::keyPressEvent(QKeyEvent * event)
       
   115 {
       
   116     int key = event->key(); // retrieve pressed key
       
   117 
       
   118     // auto-complete on pressed TAB (except for whitespace-only contents)
       
   119     if ((key == Qt::Key_Tab) && (!text().trimmed().isEmpty()))
       
   120     {
       
   121         autoComplete();
       
   122         event->accept();
       
   123     }
       
   124     // clear contents on pressed ESC
       
   125     else if ((event->modifiers() == Qt::NoModifier) && (key == Qt::Key_Escape))
       
   126     {
       
   127         clear();
       
   128         event->accept();
       
   129     }
       
   130     // otherwise forward keys to parent
       
   131     else
       
   132         HistoryLineEdit::keyPressEvent(event);
       
   133 }
       
   134 
       
   135 
       
   136 void SmartLineEdit::autoComplete()
       
   137 {
       
   138     QString match = "";
       
   139     bool isNick = false;
       
   140     QString matchMe = text();
       
   141     QString prefix = "";
       
   142     QString postfix = "";
       
   143     bool isFirstWord;
       
   144 
       
   145     // we are trying to rematch, so use the data from earlier
       
   146     if (m_hasJustMatched)
       
   147     {
       
   148         // restore values from earlier auto-completion
       
   149         matchMe = m_beforeMatch;
       
   150         prefix = m_prefix;
       
   151         postfix = m_postfix;
       
   152         isFirstWord = prefix.isEmpty();
       
   153     }
       
   154     else
       
   155     {
       
   156         m_cmds->sort();
       
   157         m_nicks = new QStringList(m_sorted_nicks->values());
       
   158 
       
   159         int cp = cursorPosition();
       
   160 
       
   161         // cursor is not in or at end/beginning of word
       
   162         if ((cp = matchMe.length()) || (QString(matchMe.at(cp)).contains(m_whitespace)))
       
   163             if ((cp < 1) || (QString(matchMe.at(cp-1)).contains(m_whitespace)))
       
   164                 return;
       
   165 
       
   166         // crop matchMe at cursor position
       
   167         prefix  = matchMe.left (cp);
       
   168         postfix = matchMe.right(matchMe.length()-cp);
       
   169 
       
   170         matchMe = "";
       
   171 
       
   172 
       
   173         // use the whole word the curser is on for matching
       
   174         int prefixLen = prefix.lastIndexOf(m_whitespace) + 1;
       
   175         int preWordLen = prefix.length() - prefixLen;
       
   176         int postWordLen = postfix.indexOf(m_whitespace);
       
   177         int postfixLen = 0;
       
   178         if (postWordLen < 0)
       
   179             postWordLen = postfix.length();
       
   180         else
       
   181             postfixLen = postfix.length() - (postWordLen + 1);
       
   182 
       
   183         matchMe = prefix.right(preWordLen) + postfix.left(postWordLen);
       
   184         prefix  = prefix.left(prefixLen);
       
   185         postfix = postfix.right(postfixLen);
       
   186 
       
   187 
       
   188         isFirstWord = prefix.isEmpty(); // true if first word
       
   189     }
       
   190 
       
   191 
       
   192     if (isFirstWord)
       
   193     {
       
   194         // find matching commands
       
   195         foreach (const QString & cmd, *m_cmds)
       
   196         {
       
   197             if (cmd.startsWith(matchMe, Qt::CaseInsensitive))
       
   198             {
       
   199                 match = cmd;
       
   200 
       
   201                 // move match to end so next time new matches will be preferred
       
   202                 m_cmds->removeAll(cmd);
       
   203                 m_cmds->append(cmd);
       
   204 
       
   205                 break;
       
   206             }
       
   207         }
       
   208     }
       
   209 
       
   210     if (match.isEmpty())
       
   211     {
       
   212         // find matching nicks
       
   213         foreach (const QString & nick, *m_nicks)
       
   214         {
       
   215             if (nick.startsWith(matchMe, Qt::CaseInsensitive))
       
   216             {
       
   217                 match = nick;
       
   218                 isNick = true;
       
   219 
       
   220                 // move match to end so next time new matches will be prefered
       
   221                 m_nicks->removeAll(nick);
       
   222                 m_nicks->append(nick);
       
   223 
       
   224                 break;
       
   225             }
       
   226         }
       
   227     }
       
   228 
       
   229     // we found a single match?
       
   230     if (!match.isEmpty())
       
   231     {
       
   232         // replace last word with match
       
   233         // and append ':' if a name at the beginning of the matchMe got completed
       
   234         // also add a space at the very end to ease further typing
       
   235         QString addAfter = ((isNick && isFirstWord)?": ":" ");
       
   236         match = prefix + match + addAfter;
       
   237         setText(match + postfix);
       
   238 
       
   239         setCursorPosition(match.length());
       
   240 
       
   241         // save values for for the case a rematch is requested
       
   242         m_beforeMatch = matchMe;
       
   243         m_hasJustMatched = true;
       
   244         m_prefix = prefix;
       
   245         m_postfix = postfix;
       
   246     }
       
   247 }
       
   248 
       
   249 void SmartLineEdit::resetAutoCompletionStatus()
       
   250 {
       
   251     m_beforeMatch = "";
       
   252     m_hasJustMatched = false;
       
   253     m_prefix = "";
       
   254     m_postfix = "";
       
   255 }
       
   256