Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/Shell.cc @ 6950

Last change on this file since 6950 was 6536, checked in by rgrieder, 15 years ago

Merged revisions 6430-6440 from the gamestate branch to the trunk.
This adds keybindings merging functionality.

(from log of r6437)
When running development builds, the keybinder will merge the local file and the one from the data folder.
Catch: if you want to remove a binding, you'll have to write "NoBinding" (not case sensitive) to override the default command

The keybind command already does that for you though.

  • Property svn:eol-style set to native
File size: 15.0 KB
RevLine 
[1505]1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
[6105]25 *      Reto Grieder
[1505]26 *
27 */
28
29#include "Shell.h"
[3196]30
31#include "util/OutputHandler.h"
[6105]32#include "util/StringUtils.h"
33#include "util/SubString.h"
[1505]34#include "CommandExecutor.h"
35#include "CoreIncludes.h"
[6536]36#include "ConfigFileManager.h"
[1505]37#include "ConfigValueIncludes.h"
38#include "ConsoleCommand.h"
39
40namespace orxonox
41{
[1747]42    SetConsoleCommandShortcut(OutputHandler, log);
43    SetConsoleCommandShortcut(OutputHandler, error);
44    SetConsoleCommandShortcut(OutputHandler, warning);
45    SetConsoleCommandShortcut(OutputHandler, info);
46    SetConsoleCommandShortcut(OutputHandler, debug);
47
[6417]48    Shell::Shell(const std::string& consoleName, bool bScrollable)
[6105]49        : OutputListener(consoleName)
50        , inputBuffer_(new InputBuffer())
51        , consoleName_(consoleName)
52        , bScrollable_(bScrollable)
[1505]53    {
54        RegisterRootObject(Shell);
55
56        this->scrollPosition_ = 0;
57        this->maxHistoryLength_ = 100;
58        this->historyPosition_ = 0;
59        this->historyOffset_ = 0;
[6105]60        this->bFinishedLastLine_ = true;
[1505]61
[6105]62        this->clearOutput();
[1755]63        this->configureInputBuffer();
[1505]64
[6417]65        // Specify file for the command history
66        ConfigFileManager::getInstance().setFilename(ConfigFileType::CommandHistory, "commandHistory.ini");
[3280]67
[6417]68        // Use a stringstream object to buffer the output
[6105]69        this->outputStream_ = &this->outputBuffer_;
70
[1505]71        this->setConfigValues();
[1792]72
[6105]73        // Get the previous output and add it to the Shell
74        for (OutputHandler::OutputVectorIterator it = OutputHandler::getInstance().getOutputVectorBegin();
75            it != OutputHandler::getInstance().getOutputVectorEnd(); ++it)
76        {
77            if (it->first <= this->getSoftDebugLevel())
78            {
79                this->outputBuffer_ << it->second;
80                this->outputChanged(it->first);
81            }
82        }
83
84        // Register the shell as output listener
85        OutputHandler::getInstance().registerOutputListener(this);
[1505]86    }
87
[1755]88    Shell::~Shell()
89    {
[6105]90        OutputHandler::getInstance().unregisterOutputListener(this);
91        this->inputBuffer_->destroy();
[1755]92    }
93
[1505]94    void Shell::setConfigValues()
95    {
[6105]96        SetConfigValue(maxHistoryLength_, 100)
[3280]97            .callback(this, &Shell::commandHistoryLengthChanged);
[6105]98        SetConfigValue(historyOffset_, 0)
[3280]99            .callback(this, &Shell::commandHistoryOffsetChanged);
[6417]100        setConfigValueGeneric(this, &commandHistory_, ConfigFileType::CommandHistory, "Shell", "commandHistory_", std::vector<std::string>());
[6105]101
102#ifdef ORXONOX_RELEASE
103        const unsigned int defaultLevel = 1;
104#else
105        const unsigned int defaultLevel = 3;
106#endif
[6417]107        setConfigValueGeneric(this, &softDebugLevel_, ConfigFileType::Settings, "OutputHandler", "softDebugLevel" + this->consoleName_, defaultLevel)
[6105]108            .description("The maximal level of debug output shown in the Shell");
109        this->setSoftDebugLevel(this->softDebugLevel_);
[1747]110    }
[1505]111
[1747]112    void Shell::commandHistoryOffsetChanged()
113    {
[1505]114        if (this->historyOffset_ >= this->maxHistoryLength_)
115            this->historyOffset_ = 0;
[1747]116    }
[1505]117
[1747]118    void Shell::commandHistoryLengthChanged()
119    {
120        this->commandHistoryOffsetChanged();
121
[1505]122        while (this->commandHistory_.size() > this->maxHistoryLength_)
123        {
124            unsigned int index = this->commandHistory_.size() - 1;
125            this->commandHistory_.erase(this->commandHistory_.begin() + index);
126            ModifyConfigValue(commandHistory_, remove, index);
127        }
128    }
129
[1755]130    void Shell::configureInputBuffer()
[1505]131    {
132        this->inputBuffer_->registerListener(this, &Shell::inputChanged, true);
[6105]133        this->inputBuffer_->registerListener(this, &Shell::execute,         '\r',   false);
134        this->inputBuffer_->registerListener(this, &Shell::execute,         '\n',   false);
135        this->inputBuffer_->registerListener(this, &Shell::hintAndComplete, '\t',   true);
136        this->inputBuffer_->registerListener(this, &Shell::backspace,       '\b',   true);
137        this->inputBuffer_->registerListener(this, &Shell::backspace,       '\177', true);
138        this->inputBuffer_->registerListener(this, &Shell::exit,            '\033', true); // escape
139        this->inputBuffer_->registerListener(this, &Shell::deleteChar,      KeyCode::Delete);
140        this->inputBuffer_->registerListener(this, &Shell::cursorRight,     KeyCode::Right);
141        this->inputBuffer_->registerListener(this, &Shell::cursorLeft,      KeyCode::Left);
142        this->inputBuffer_->registerListener(this, &Shell::cursorEnd,       KeyCode::End);
143        this->inputBuffer_->registerListener(this, &Shell::cursorHome,      KeyCode::Home);
144        this->inputBuffer_->registerListener(this, &Shell::historyUp,       KeyCode::Up);
145        this->inputBuffer_->registerListener(this, &Shell::historyDown,     KeyCode::Down);
146        if (this->bScrollable_)
147        {
148            this->inputBuffer_->registerListener(this, &Shell::scrollUp,    KeyCode::PageUp);
149            this->inputBuffer_->registerListener(this, &Shell::scrollDown,  KeyCode::PageDown);
150        }
151        else
152        {
153            this->inputBuffer_->registerListener(this, &Shell::historySearchUp,   KeyCode::PageUp);
154            this->inputBuffer_->registerListener(this, &Shell::historySearchDown, KeyCode::PageDown);
155        }
[1505]156    }
157
[6105]158    /*
[1505]159    void Shell::history()
160    {
161        Shell& instance = Shell::getInstance();
162
[3300]163        for (unsigned int i = instance.historyOffset_; i < instance.commandHistory_.size(); ++i)
[6417]164            instance.addOutput(instance.commandHistory_[i] + '\n', -1);
[3300]165        for (unsigned int i =  0; i < instance.historyOffset_; ++i)
[6417]166            instance.addOutput(instance.commandHistory_[i] + '\n', -1);
[1505]167    }
[6105]168    */
[1505]169
170    void Shell::registerListener(ShellListener* listener)
171    {
[6105]172        this->listeners_.push_back(listener);
[1505]173    }
174
175    void Shell::unregisterListener(ShellListener* listener)
176    {
177        for (std::list<ShellListener*>::iterator it = this->listeners_.begin(); it != this->listeners_.end(); )
178        {
179            if ((*it) == listener)
[6105]180                it = this->listeners_.erase(it);
[1505]181            else
182                ++it;
183        }
184    }
185
186    void Shell::setCursorPosition(unsigned int cursor)
187    {
188        this->inputBuffer_->setCursorPosition(cursor);
[6105]189        this->updateListeners<&ShellListener::cursorChanged>();
[1505]190    }
191
[6417]192    void Shell::addOutput(const std::string& text, LineType type)
[1505]193    {
[6417]194        this->outputBuffer_ << text;
195        this->outputChanged(type);
[1505]196    }
197
[6105]198    void Shell::clearOutput()
[1505]199    {
[6105]200        this->outputLines_.clear();
201        this->scrollIterator_ = this->outputLines_.begin();
[1505]202
203        this->scrollPosition_ = 0;
[6105]204        this->bFinishedLastLine_ = true;
[1505]205
[6105]206        this->updateListeners<&ShellListener::linesChanged>();
[1505]207    }
208
[6417]209    Shell::LineList::const_iterator Shell::getNewestLineIterator() const
[1505]210    {
211        if (this->scrollPosition_)
212            return this->scrollIterator_;
213        else
[6105]214            return this->outputLines_.begin();
[1505]215    }
216
[6417]217    Shell::LineList::const_iterator Shell::getEndIterator() const
[1505]218    {
[6105]219        return this->outputLines_.end();
[1505]220    }
221
222    void Shell::addToHistory(const std::string& command)
223    {
224        ModifyConfigValue(commandHistory_, set, this->historyOffset_, command);
225        this->historyPosition_ = 0;
226        ModifyConfigValue(historyOffset_, set, (this->historyOffset_ + 1) % this->maxHistoryLength_);
227    }
228
[6417]229    const std::string& Shell::getFromHistory() const
[1505]230    {
[3301]231        unsigned int index = mod(static_cast<int>(this->historyOffset_) - static_cast<int>(this->historyPosition_), this->maxHistoryLength_);
[1505]232        if (index < this->commandHistory_.size() && this->historyPosition_ != 0)
233            return this->commandHistory_[index];
234        else
[6417]235            return BLANKSTRING;
[1505]236    }
237
[6417]238    void Shell::outputChanged(int lineType)
[1505]239    {
[6105]240        bool newline = false;
[1505]241        do
242        {
[6105]243            std::string output;
244            std::getline(this->outputBuffer_, output);
[1505]245
[6105]246            bool eof = this->outputBuffer_.eof();
247            bool fail = this->outputBuffer_.fail();
248            if (eof)
249                this->outputBuffer_.flush();
250            if (eof || fail)
251                this->outputBuffer_.clear();
252            newline = (!eof && !fail);
253
[6417]254            if (!newline && output.empty())
[1505]255                break;
256
[6105]257            if (this->bFinishedLastLine_)
[1505]258            {
[6417]259                this->outputLines_.push_front(std::make_pair(output, static_cast<LineType>(lineType)));
[1505]260
261                if (this->scrollPosition_)
262                    this->scrollPosition_++;
263                else
[6105]264                    this->scrollIterator_ = this->outputLines_.begin();
[1505]265
[6105]266                this->bFinishedLastLine_ = newline;
[1505]267
268                if (!this->scrollPosition_)
[6105]269                    this->updateListeners<&ShellListener::lineAdded>();
[1505]270            }
271            else
272            {
[6417]273                this->outputLines_.front().first += output;
[6105]274                this->bFinishedLastLine_ = newline;
275                this->updateListeners<&ShellListener::onlyLastLineChanged>();
[1505]276            }
[6417]277            this->bFinishedLastLine_ = newline;
[1505]278
279        } while (newline);
280    }
281
[6105]282    void Shell::clearInput()
283    {
284        this->inputBuffer_->clear();
285        this->historyPosition_ = 0;
286        this->updateListeners<&ShellListener::inputChanged>();
287        this->updateListeners<&ShellListener::cursorChanged>();
288    }
289
290    void Shell::setPromptPrefix(const std::string& str)
291    {
292    }
293
294
295    // ##########################################
296    // ###   InputBuffer callback functions   ###
297    // ##########################################
298
[1505]299    void Shell::inputChanged()
300    {
[6105]301        this->updateListeners<&ShellListener::inputChanged>();
302        this->updateListeners<&ShellListener::cursorChanged>();
[1505]303    }
304
305    void Shell::execute()
306    {
307        this->addToHistory(this->inputBuffer_->get());
[6105]308        this->updateListeners<&ShellListener::executed>();
[1505]309
310        if (!CommandExecutor::execute(this->inputBuffer_->get()))
[6417]311        {
312            this->outputBuffer_ << "Error: Can't execute \"" << this->inputBuffer_->get() << "\"." << std::endl;
313            this->outputChanged(Error);
314        }
[1505]315
[6105]316        this->clearInput();
[1505]317    }
318
[6105]319    void Shell::hintAndComplete()
[1505]320    {
321        this->inputBuffer_->set(CommandExecutor::complete(this->inputBuffer_->get()));
[6417]322        this->outputBuffer_ << CommandExecutor::hint(this->inputBuffer_->get()) << std::endl;
323        this->outputChanged(Hint);
[1505]324
325        this->inputChanged();
326    }
327
328    void Shell::backspace()
329    {
330        this->inputBuffer_->removeBehindCursor();
[6105]331        this->updateListeners<&ShellListener::inputChanged>();
332        this->updateListeners<&ShellListener::cursorChanged>();
[1505]333    }
334
[6105]335    void Shell::exit()
[1505]336    {
[6105]337        if (this->inputBuffer_->getSize() > 0)
338        {
339            this->clearInput();
340            return;
341        }
342
343        this->clearInput();
344        this->scrollPosition_ = 0;
345        this->scrollIterator_ = this->outputLines_.begin();
346
347        this->updateListeners<&ShellListener::exit>();
[1505]348    }
349
[6105]350    void Shell::deleteChar()
[1505]351    {
[6105]352        this->inputBuffer_->removeAtCursor();
353        this->updateListeners<&ShellListener::inputChanged>();
[1505]354    }
355
[6105]356    void Shell::cursorRight()
[1505]357    {
358        this->inputBuffer_->increaseCursor();
[6105]359        this->updateListeners<&ShellListener::cursorChanged>();
[1505]360    }
361
[6105]362    void Shell::cursorLeft()
[1505]363    {
364        this->inputBuffer_->decreaseCursor();
[6105]365        this->updateListeners<&ShellListener::cursorChanged>();
[1505]366    }
367
[6105]368    void Shell::cursorEnd()
[1505]369    {
370        this->inputBuffer_->setCursorToEnd();
[6105]371        this->updateListeners<&ShellListener::cursorChanged>();
[1505]372    }
373
[6105]374    void Shell::cursorHome()
[1505]375    {
376        this->inputBuffer_->setCursorToBegin();
[6105]377        this->updateListeners<&ShellListener::cursorChanged>();
[1505]378    }
379
[6105]380    void Shell::historyUp()
[1505]381    {
382        if (this->historyPosition_ < this->commandHistory_.size())
383        {
384            this->historyPosition_++;
385            this->inputBuffer_->set(this->getFromHistory());
386        }
387    }
388
[6105]389    void Shell::historyDown()
[1505]390    {
391        if (this->historyPosition_ > 0)
392        {
393            this->historyPosition_--;
394            this->inputBuffer_->set(this->getFromHistory());
395        }
396    }
397
[6105]398    void Shell::historySearchUp()
[1505]399    {
[6105]400        if (this->historyPosition_ == this->historyOffset_)
401            return;
402        unsigned int cursorPosition = this->getCursorPosition();
[6417]403        const std::string& input_str(this->getInput().substr(0, cursorPosition)); // only search for the expression from the beginning of the inputline until the cursor position
[6105]404        for (unsigned int newPos = this->historyPosition_ + 1; newPos <= this->historyOffset_; newPos++)
[1505]405        {
[6105]406            if (getLowercase(this->commandHistory_[this->historyOffset_ - newPos]).find(getLowercase(input_str)) == 0) // search case insensitive
407            {
408                this->historyPosition_ = newPos;
409                this->inputBuffer_->set(this->getFromHistory());
410                this->setCursorPosition(cursorPosition);
411                return;
412            }
413        }
414    }
[1505]415
[6105]416    void Shell::historySearchDown()
417    {
418        if (this->historyPosition_ == 0)
419            return;
420        unsigned int cursorPosition = this->getCursorPosition();
[6417]421        const std::string& input_str(this->getInput().substr(0, cursorPosition)); // only search for the expression from the beginning
[6105]422        for (unsigned int newPos = this->historyPosition_ - 1; newPos > 0; newPos--)
423        {
424            if (getLowercase(this->commandHistory_[this->historyOffset_ - newPos]).find(getLowercase(input_str)) == 0) // sear$
425            {
426                this->historyPosition_ = newPos;
427                this->inputBuffer_->set(this->getFromHistory());
428                this->setCursorPosition(cursorPosition);
429                return;
430            }
[1505]431        }
432    }
433
[6105]434    void Shell::scrollUp()
[1505]435    {
[6105]436        if (this->scrollIterator_ != this->outputLines_.end())
[1505]437        {
[6105]438            ++this->scrollIterator_;
439            ++this->scrollPosition_;
[1505]440
[6105]441            this->updateListeners<&ShellListener::linesChanged>();
[1505]442        }
443    }
444
[6105]445    void Shell::scrollDown()
[1505]446    {
[6105]447        if (this->scrollIterator_ != this->outputLines_.begin())
[1505]448        {
[6105]449            --this->scrollIterator_;
450            --this->scrollPosition_;
451
452            this->updateListeners<&ShellListener::linesChanged>();
[1505]453        }
454    }
455}
Note: See TracBrowser for help on using the repository browser.