diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/beingmanager.cpp | 61 | ||||
-rw-r--r-- | src/beingmanager.h | 12 | ||||
-rw-r--r-- | src/gui/chat.cpp | 178 | ||||
-rw-r--r-- | src/gui/chat.h | 25 | ||||
-rw-r--r-- | src/gui/textdialog.cpp | 4 | ||||
-rw-r--r-- | src/gui/widgets/chattab.cpp | 6 | ||||
-rw-r--r-- | src/gui/widgets/chattab.h | 7 | ||||
-rw-r--r-- | src/gui/widgets/textfield.cpp | 137 | ||||
-rw-r--r-- | src/gui/widgets/textfield.h | 68 | ||||
-rw-r--r-- | src/utils/stringutils.cpp | 37 | ||||
-rw-r--r-- | src/utils/stringutils.h | 4 |
11 files changed, 318 insertions, 221 deletions
diff --git a/src/beingmanager.cpp b/src/beingmanager.cpp index 931e4579..d7045684 100644 --- a/src/beingmanager.cpp +++ b/src/beingmanager.cpp @@ -53,8 +53,48 @@ class FindBeingFunctor Being::Type type; } beingFinder; +class PlayerNamesLister : public AutoCompleteLister +{ + void getAutoCompleteList(std::vector<std::string>& names) const + { + Beings::iterator i = beingManager->mBeings.begin(); + names.clear(); + + while (i != beingManager->mBeings.end()) + { + Being *being = (*i); + if (being->getType() == Being::PLAYER && being->getName() != "") + names.push_back(being->getName()); + + ++i; + } + } +}; + +class PlayerNPCNamesLister : public AutoCompleteLister +{ + void getAutoCompleteList(std::vector<std::string>& names) const + { + Beings::iterator i = beingManager->mBeings.begin(); + names.clear(); + + while (i != beingManager->mBeings.end()) + { + Being *being = (*i); + if ((being->getType() == Being::PLAYER + || being->getType() == Being::NPC) + && being->getName() != "") + names.push_back(being->getName()); + + ++i; + } + } +}; + BeingManager::BeingManager() { + mPlayerNames = new PlayerNamesLister; + mPlayerNPCNames = new PlayerNPCNamesLister; } BeingManager::~BeingManager() @@ -263,23 +303,14 @@ bool BeingManager::hasBeing(Being *being) const return false; } -void BeingManager::getPlayerNames(std::vector<std::string> &names, - bool npcNames) +AutoCompleteLister *BeingManager::getPlayerNameLister() { - Beings::iterator i = mBeings.begin(); - names.clear(); + return mPlayerNames; +} - while (i != mBeings.end()) - { - Being *being = (*i); - if ((being->getType() == Being::PLAYER - || (being->getType() == Being::NPC && npcNames)) - && being->getName() != "") - { - names.push_back(being->getName()); - } - ++i; - } +AutoCompleteLister *BeingManager::getPlayerNPCNameLister() +{ + return mPlayerNPCNames; } void BeingManager::updatePlayerNames() diff --git a/src/beingmanager.h b/src/beingmanager.h index 7fd63afe..f2f8eb6d 100644 --- a/src/beingmanager.h +++ b/src/beingmanager.h @@ -24,6 +24,8 @@ #include "being.h" +#include "gui/widgets/textfield.h" + class LocalPlayer; class Map; @@ -122,12 +124,18 @@ class BeingManager */ void clear(); - void getPlayerNames(std::vector<std::string> &names, - bool npcNames); + AutoCompleteLister *getPlayerNameLister(); + + AutoCompleteLister *getPlayerNPCNameLister(); void updatePlayerNames(); protected: + friend class PlayerNamesLister; + friend class PlayerNPCNamesLister; + + AutoCompleteLister *mPlayerNames; + AutoCompleteLister *mPlayerNPCNames; Beings mBeings; Map *mMap; }; diff --git a/src/gui/chat.cpp b/src/gui/chat.cpp index f87a8a3d..6d900e98 100644 --- a/src/gui/chat.cpp +++ b/src/gui/chat.cpp @@ -72,9 +72,21 @@ class ChatInput : public TextField, public gcn::FocusListener } }; +class ChatAutoComplete : public AutoCompleteLister +{ + void getAutoCompleteList(std::vector<std::string> &list) const + { + ChatTab *tab = static_cast<ChatTab*>(chatWindow->mChatTabs + ->getSelectedTab()); + + return tab->getAutoCompleteList(list); + } +}; ChatWindow::ChatWindow(): Window(_("Chat")), + mHistory(new TextHistory()), + mAutoComplete(new ChatAutoComplete), mTmpVisible(false) { setWindowName("Chat"); @@ -104,9 +116,8 @@ ChatWindow::ChatWindow(): loadWindowState(); - // Add key listener to chat input to be able to respond to up/down - mChatInput->addKeyListener(this); - mCurHist = mHistory.end(); + mChatInput->setHistory(mHistory); + mChatInput->setAutoComplete(mAutoComplete); mReturnToggles = config.getValue("ReturnToggles", "0") == "1"; @@ -119,6 +130,8 @@ ChatWindow::~ChatWindow() delete mRecorder; removeAllWhispers(); delete mItemLinkHandler; + delete mHistory; + delete mAutoComplete; } void ChatWindow::resetToDefaultSize() @@ -173,14 +186,6 @@ void ChatWindow::action(const gcn::ActionEvent &event) if (!message.empty()) { - // If message different from previous, put it in the history - if (mHistory.empty() || message != mHistory.back()) - { - mHistory.push_back(message); - } - // Reset history iterator - mCurHist = mHistory.end(); - // Send the message to the server chatInput(message); @@ -366,48 +371,6 @@ void ChatWindow::mouseDragged(gcn::MouseEvent &event) } } - -void ChatWindow::keyPressed(gcn::KeyEvent &event) -{ - if (event.getKey().getValue() == Key::DOWN) - { - if (mCurHist != mHistory.end()) - { - // Move forward through the history - HistoryIterator prevHist = mCurHist++; - - if (mCurHist != mHistory.end()) - { - mChatInput->setText(*mCurHist); - mChatInput->setCaretPosition(mChatInput->getText().length()); - } - else - { - mChatInput->setText(""); - mCurHist = prevHist; - } - } - else if (mChatInput->getText() != "") - { - mChatInput->setText(""); - } - } - else if (event.getKey().getValue() == Key::UP && - mCurHist != mHistory.begin() && mHistory.size() > 0) - { - // Move backward through the history - mCurHist--; - mChatInput->setText(*mCurHist); - mChatInput->setCaretPosition(mChatInput->getText().length()); - } - else if (event.getKey().getValue() == Key::TAB && - mChatInput->getText() != "") - { - autoComplete(); - return; - } -} - void ChatWindow::addInputText(const std::string &text) { const int caretPos = mChatInput->getCaretPosition(); @@ -516,112 +479,3 @@ ChatTab *ChatWindow::addWhisperTab(const std::string &nick, bool switchTo) return ret; } - -void ChatWindow::autoComplete() -{ - int caretPos = mChatInput->getCaretPosition(); - int startName = 0; - const std::string inputText = mChatInput->getText(); - std::string name = inputText.substr(0, caretPos); - std::string newName(""); - - for (int f = caretPos - 1; f > -1; f --) - { - if (isWordSeparator(inputText[f])) - { - startName = f + 1; - name = inputText.substr(f + 1, caretPos - f); - break; - } - } - - if (caretPos - 1 + 1 == startName) - return; - - - ChatTab *cTab = static_cast<ChatTab*>(mChatTabs->getSelectedTab()); - std::vector<std::string> nameList; - cTab->getAutoCompleteList(nameList); - newName = autoComplete(nameList, name); - - if (newName == "") - { - beingManager->getPlayerNames(nameList, true); - newName = autoComplete(nameList, name); - } - if (newName == "") - { - newName = autoCompleteHistory(name); - } - - if (newName != "") - { - if(inputText[0] == '@' || inputText[0] == '/') - newName = "\"" + newName + "\""; - - mChatInput->setText(inputText.substr(0, startName) + newName - + inputText.substr(caretPos, inputText.length() - caretPos)); - - if (startName > 0) - mChatInput->setCaretPosition(caretPos - name.length() + newName.length() + 1); - else - mChatInput->setCaretPosition(caretPos - name.length() + newName.length()); - } -} - -std::string ChatWindow::autoComplete(std::vector<std::string> &names, - std::string partName) const -{ - std::vector<std::string>::iterator i = names.begin(); - toLower(partName); - std::string newName(""); - - while (i != names.end()) - { - if (!i->empty()) - { - std::string name = *i; - toLower(name); - - std::string::size_type pos = name.find(partName, 0); - if (pos == 0) - { - if (newName != "") - { - toLower(newName); - newName = findSameSubstring(name, newName); - } - else - { - newName = *i; - } - } - } - ++i; - } - - return newName; -} - -std::string ChatWindow::autoCompleteHistory(std::string partName) -{ - History::iterator i = mHistory.begin(); - std::vector<std::string> nameList; - - while (i != mHistory.end()) - { - std::string line = *i; - unsigned int f = 0; - while (f < line.length() && !isWordSeparator(line.at(f))) - { - f++; - } - line = line.substr(0, f); - if (line != "") - { - nameList.push_back(line); - } - ++i; - } - return autoComplete(nameList, partName); -} diff --git a/src/gui/chat.h b/src/gui/chat.h index b0d91556..1c673556 100644 --- a/src/gui/chat.h +++ b/src/gui/chat.h @@ -23,6 +23,7 @@ #define CHAT_H #include "gui/widgets/window.h" +#include "gui/widgets/textfield.h" #include <guichan/actionlistener.hpp> #include <guichan/keylistener.hpp> @@ -61,8 +62,7 @@ struct CHATLOG * \ingroup Interface */ class ChatWindow : public Window, - public gcn::ActionListener, - public gcn::KeyListener + public gcn::ActionListener { public: /** @@ -131,9 +131,6 @@ class ChatWindow : public Window, */ void chatInput(const std::string &msg); - /** Called when key is pressed */ - void keyPressed(gcn::KeyEvent &event); - /** Add the given text to the chat input. */ void addInputText(const std::string &text); @@ -143,11 +140,9 @@ class ChatWindow : public Window, /** Override to reset mTmpVisible */ void setVisible(bool visible); - void mousePressed(gcn::MouseEvent &event); void mouseDragged(gcn::MouseEvent &event); - /** * Scrolls the chat window * @@ -177,7 +172,7 @@ class ChatWindow : public Window, protected: friend class ChatTab; friend class WhisperTab; - friend class TextField; + friend class ChatAutoComplete; /** Remove the given tab from the window */ void removeTab(ChatTab *tab); @@ -189,13 +184,6 @@ class ChatWindow : public Window, void removeAllWhispers(); - void autoComplete(); - - std::string autoCompleteHistory(std::string partName); - - std::string autoComplete(std::vector<std::string> &names, - std::string partName) const; - /** Used for showing item popup on clicking links **/ ItemLinkHandler *mItemLinkHandler; Recorder *mRecorder; @@ -203,6 +191,9 @@ class ChatWindow : public Window, /** Input box for typing chat messages. */ ChatInput *mChatInput; + TextHistory *mHistory; + AutoCompleteLister *mAutoComplete; + private: bool mTmpVisible; @@ -213,10 +204,6 @@ class ChatWindow : public Window, /** Manage whisper tabs */ TabMap mWhispers; - typedef std::list<std::string> History; - typedef History::iterator HistoryIterator; - History mHistory; /**< Command history. */ - HistoryIterator mCurHist; /**< History iterator. */ bool mReturnToggles; /**< Marks whether <Return> toggles the chat log or not */ }; diff --git a/src/gui/textdialog.cpp b/src/gui/textdialog.cpp index 28792a0b..d9728357 100644 --- a/src/gui/textdialog.cpp +++ b/src/gui/textdialog.cpp @@ -21,6 +21,8 @@ #include "gui/textdialog.h" +#include "beingmanager.h" + #include "gui/widgets/button.h" #include "gui/widgets/label.h" #include "gui/widgets/textfield.h" @@ -40,7 +42,7 @@ TextDialog::TextDialog(const std::string &title, const std::string &msg, // In TextField the escape key will either cause autoComplete or lose focus mTextField = new TextField("", ! autoCompleteEnabled); if (autoCompleteEnabled) - mTextField->setAutoComplete(true); + mTextField->setAutoComplete(beingManager->getPlayerNameLister()); mTextField->addActionListener(this); diff --git a/src/gui/widgets/chattab.cpp b/src/gui/widgets/chattab.cpp index 39ea6887..8bf57508 100644 --- a/src/gui/widgets/chattab.cpp +++ b/src/gui/widgets/chattab.cpp @@ -21,6 +21,7 @@ #include "gui/widgets/chattab.h" +#include "beingmanager.h" #include "commandhandler.h" #include "configuration.h" #include "localplayer.h" @@ -275,6 +276,11 @@ void ChatTab::handleCommand(const std::string &msg) commandHandler->handleCommand(msg, this); } +void ChatTab::getAutoCompleteList(std::vector<std::string> &names) const +{ + beingManager->getPlayerNPCNameLister()->getAutoCompleteList(names); +} + void ChatTab::addRow(std::string &line) { std::string::size_type idx = 0; diff --git a/src/gui/widgets/chattab.h b/src/gui/widgets/chattab.h index 7fd3931e..c2dfa1c1 100644 --- a/src/gui/widgets/chattab.h +++ b/src/gui/widgets/chattab.h @@ -25,6 +25,7 @@ #include "gui/chat.h" #include "gui/widgets/tab.h" +#include "gui/widgets/textfield.h" class BrowserBox; class Recorder; @@ -45,7 +46,7 @@ enum /** * A tab for the chat window. This is special to ease chat handling. */ -class ChatTab : public Tab +class ChatTab : public Tab, public AutoCompleteLister { public: /** @@ -111,6 +112,8 @@ class ChatTab : public Tab const std::string &args) { return false; } + void getAutoCompleteList(std::vector<std::string> &names) const; + protected: friend class ChatWindow; friend class WhisperWindow; @@ -121,8 +124,6 @@ class ChatTab : public Tab virtual void handleCommand(const std::string &msg); - virtual void getAutoCompleteList(std::vector<std::string>&) const {} - void addRow(std::string &line); ScrollArea *mScrollArea; diff --git a/src/gui/widgets/textfield.cpp b/src/gui/widgets/textfield.cpp index 60a1f57f..3e02be98 100644 --- a/src/gui/widgets/textfield.cpp +++ b/src/gui/widgets/textfield.cpp @@ -25,7 +25,6 @@ #include "configuration.h" #include "graphics.h" -#include "gui/chat.h" #include "gui/palette.h" #include "gui/sdlinput.h" #include "gui/theme.h" @@ -34,6 +33,7 @@ #include "utils/copynpaste.h" #include "utils/dtor.h" +#include "utils/stringutils.h" #include <guichan/font.hpp> @@ -46,7 +46,8 @@ ImageRect TextField::skin; TextField::TextField(const std::string &text, bool loseFocusOnTab): gcn::TextField(text), mNumeric(false), - mAutoComplete(false) + mAutoComplete(NULL), + mHistory(NULL) { setFrameSize(2); @@ -212,6 +213,42 @@ void TextField::keyPressed(gcn::KeyEvent &keyEvent) } } break; + case Key::UP: + { + if (mHistory && !mHistory->atBegining() && !mHistory->empty()) + { + // Move backward through the history + mHistory->current--; + setText(*mHistory->current); + setCaretPosition(getText().length()); + } + } break; + + case Key::DOWN: + { + if (mHistory && !mHistory->atEnd()) + { + // Move forward through the history + TextHistoryIterator prevHist = mHistory->current++; + + if (!mHistory->atEnd()) + { + setText(*mHistory->current); + setCaretPosition(getText().length()); + } + else + { + setText(""); + mHistory->current = prevHist; + } + } + else if (getText() != "") + { + // Always clear (easy access to useful function) + setText(""); + } + } break; + case Key::DELETE: { unsigned sz = mText.size(); @@ -237,6 +274,19 @@ void TextField::keyPressed(gcn::KeyEvent &keyEvent) } break; case Key::ENTER: + if (mHistory) + { + mHistory->toEnd(); + + // If the input is different from previous, put it in the history + if (mHistory->empty() || !mHistory->matchesEntry(getText())) + { + mHistory->addEntry(getText()); + } + + mHistory->toEnd(); + } + distributeActionEvent(); break; @@ -249,17 +299,7 @@ void TextField::keyPressed(gcn::KeyEvent &keyEvent) break; case Key::TAB: - if (mAutoComplete && mText.size() > 0) - { - std::vector<std::string> names; - beingManager->getPlayerNames(names, false); - std::string newName = chatWindow->autoComplete(names, mText); - if (newName != "") - { - setText(newName); - setCaretPosition(mText.size()); - } - } + autoComplete(); if (mLoseFocusOnTab) return; break; @@ -273,6 +313,77 @@ void TextField::keyPressed(gcn::KeyEvent &keyEvent) fixScroll(); } +void TextField::autoComplete() +{ + if (mAutoComplete && mText.size() > 0) + { + int caretPos = getCaretPosition(); + int startName = 0; + const std::string inputText = getText(); + std::string name = inputText.substr(0, caretPos); + std::string newName(""); + + for (int f = caretPos - 1; f > -1; f --) + { + if (isWordSeparator(inputText[f])) + { + startName = f + 1; + name = inputText.substr(f + 1, caretPos - f); + break; + } + } + + if (caretPos - 1 + 1 != startName) + return; + + + std::vector<std::string> nameList; + mAutoComplete->getAutoCompleteList(nameList); + newName = autocomplete(nameList, name); + + if (newName == "" && mHistory) + { + + TextHistoryIterator i = mHistory->history.begin(); + std::vector<std::string> nameList; + + while (i != mHistory->history.end()) + { + std::string line = *i; + unsigned int f = 0; + while (f < line.length() && !isWordSeparator(line.at(f))) + { + f++; + } + line = line.substr(0, f); + if (line != "") + { + nameList.push_back(line); + } + ++i; + } + + newName = autocomplete(nameList, name); + } + + if (newName != "") + { + if(inputText[0] == '@' || inputText[0] == '/') + newName = "\"" + newName + "\""; + + setText(inputText.substr(0, startName) + newName + + inputText.substr(caretPos, inputText.length() + - caretPos)); + + if (startName > 0) + setCaretPosition(caretPos - name.length() + newName.length() + + 1); + else + setCaretPosition(caretPos - name.length() + newName.length()); + } + } +} + void TextField::handlePaste() { std::string text = getText(); diff --git a/src/gui/widgets/textfield.h b/src/gui/widgets/textfield.h index 1e6df9d6..6e50f1e9 100644 --- a/src/gui/widgets/textfield.h +++ b/src/gui/widgets/textfield.h @@ -24,9 +24,48 @@ #include <guichan/widgets/textfield.hpp> +#include <vector> + class ImageRect; class TextField; +typedef std::list<std::string> TextHistoryList; +typedef TextHistoryList::iterator TextHistoryIterator; + +struct TextHistory { + TextHistoryList history; /**< Command history. */ + TextHistoryIterator current; /**< History iterator. */ + + TextHistory() + { current = history.end(); } + + bool empty() const + { return history.empty(); } + + bool atBegining() const + { return current == history.begin(); } + + bool atEnd() const + { return current == history.end(); } + + void toBegining() + { current = history.begin(); } + + void toEnd() + { current = history.end(); } + + void addEntry(const std::string &text) + { history.push_back(text); } + + bool matchesEntry(const std::string &text) + { return (*current) == text; } +}; + +class AutoCompleteLister { +public: + virtual void getAutoCompleteList(std::vector<std::string>&) const {} +}; + /** * A text field. * @@ -91,16 +130,32 @@ class TextField : public gcn::TextField int getValue() const; /** - * Set if the tabulator key causes auto complete + * Sets the TextField's source of autocomplete. Passing null will + * disable autocomplete. + */ + void setAutoComplete(AutoCompleteLister *lister) + { mAutoComplete = lister; } + + /** + * Returns the TextField's source of autocomplete. */ - void setAutoComplete(bool b ) {mAutoComplete = b;} + AutoCompleteLister *getAutoComplete() const + { return mAutoComplete; } /** - * Returns if the tabulator key causes auto complete + * Sets the TextField's source of input history. */ - bool getAutoComplete() {return mAutoComplete;} + void setHistory(TextHistory *history) + { mHistory = history; } + + /** + * Returns the TextField's source of input history. + */ + TextHistory *getHistory() const + { return mHistory; } private: + void autoComplete(); void handlePaste(); static int instances; @@ -110,7 +165,10 @@ class TextField : public gcn::TextField int mMinimum; int mMaximum; bool mLoseFocusOnTab; - bool mAutoComplete; + + AutoCompleteLister *mAutoComplete; + + TextHistory *mHistory; /**< Text history. */ }; #endif diff --git a/src/utils/stringutils.cpp b/src/utils/stringutils.cpp index 9fe3de14..445427fe 100644 --- a/src/utils/stringutils.cpp +++ b/src/utils/stringutils.cpp @@ -174,4 +174,39 @@ const char* getSafeUtf8String(std::string text) memcpy(buf, text.c_str(), text.size()); memset(buf + text.size(), 0, UTF8_MAX_SIZE); return buf; -}
\ No newline at end of file +} + +std::string autocomplete(std::vector<std::string> &candidates, + std::string base) +{ + std::vector<std::string>::iterator i = candidates.begin(); + toLower(base); + std::string newName(""); + + while (i != candidates.end()) + { + if (!i->empty()) + { + std::string name = *i; + toLower(name); + + std::string::size_type pos = name.find(base, 0); + if (pos == 0) + { + if (newName != "") + { + toLower(newName); + newName = findSameSubstring(name, newName); + } + else + { + newName = *i; + } + } + } + + ++i; + } + + return newName; +} diff --git a/src/utils/stringutils.h b/src/utils/stringutils.h index ec82e240..5f1f05f0 100644 --- a/src/utils/stringutils.h +++ b/src/utils/stringutils.h @@ -24,6 +24,7 @@ #include <string> #include <sstream> +#include <vector> /** * Trims spaces off the end and the beginning of the given string. @@ -125,4 +126,7 @@ const std::string findSameSubstring(const std::string &str1, const std::string & const char* getSafeUtf8String(std::string text); +std::string autocomplete(std::vector<std::string> &candidates, + std::string base); + #endif // UTILS_STRINGUTILS_H |