Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/consolecommands3/src/libraries/core/command/Shell.cc @ 7236

Last change on this file since 7236 was 7236, checked in by landauf, 14 years ago

replaced the temporary names of all ConsoleCommand related classes and functions by their real names

  • Property svn:eol-style set to native
File size: 16.3 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"
[7203]34#include "core/CoreIncludes.h"
35#include "core/ConfigFileManager.h"
36#include "core/ConfigValueIncludes.h"
[1505]37#include "CommandExecutor.h"
38#include "ConsoleCommand.h"
39
40namespace orxonox
41{
[7236]42    SetConsoleCommand("log",     OutputHandler::log    );
43    SetConsoleCommand("error",   OutputHandler::error  );
44    SetConsoleCommand("warning", OutputHandler::warning);
45    SetConsoleCommand("info",    OutputHandler::info   );
46    SetConsoleCommand("debug",   OutputHandler::debug  );
[1747]47
[7229]48    unsigned int Shell::cacheSize_s;
49
[6417]50    Shell::Shell(const std::string& consoleName, bool bScrollable)
[6105]51        : OutputListener(consoleName)
52        , inputBuffer_(new InputBuffer())
53        , consoleName_(consoleName)
54        , bScrollable_(bScrollable)
[1505]55    {
56        RegisterRootObject(Shell);
57
58        this->scrollPosition_ = 0;
59        this->maxHistoryLength_ = 100;
60        this->historyPosition_ = 0;
61        this->historyOffset_ = 0;
[6105]62        this->bFinishedLastLine_ = true;
[1505]63
[6105]64        this->clearOutput();
[1755]65        this->configureInputBuffer();
[1505]66
[6417]67        // Specify file for the command history
68        ConfigFileManager::getInstance().setFilename(ConfigFileType::CommandHistory, "commandHistory.ini");
[3280]69
[6417]70        // Use a stringstream object to buffer the output
[6105]71        this->outputStream_ = &this->outputBuffer_;
72
[1505]73        this->setConfigValues();
[1792]74
[6105]75        // Get the previous output and add it to the Shell
76        for (OutputHandler::OutputVectorIterator it = OutputHandler::getInstance().getOutputVectorBegin();
77            it != OutputHandler::getInstance().getOutputVectorEnd(); ++it)
78        {
79            if (it->first <= this->getSoftDebugLevel())
80            {
81                this->outputBuffer_ << it->second;
82                this->outputChanged(it->first);
83            }
84        }
85
86        // Register the shell as output listener
87        OutputHandler::getInstance().registerOutputListener(this);
[1505]88    }
89
[1755]90    Shell::~Shell()
91    {
[6105]92        OutputHandler::getInstance().unregisterOutputListener(this);
93        this->inputBuffer_->destroy();
[1755]94    }
95
[1505]96    void Shell::setConfigValues()
97    {
[6105]98        SetConfigValue(maxHistoryLength_, 100)
[3280]99            .callback(this, &Shell::commandHistoryLengthChanged);
[6105]100        SetConfigValue(historyOffset_, 0)
[3280]101            .callback(this, &Shell::commandHistoryOffsetChanged);
[6417]102        setConfigValueGeneric(this, &commandHistory_, ConfigFileType::CommandHistory, "Shell", "commandHistory_", std::vector<std::string>());
[7229]103        SetConfigValue(cacheSize_s, 32);
[6105]104
105#ifdef ORXONOX_RELEASE
106        const unsigned int defaultLevel = 1;
107#else
108        const unsigned int defaultLevel = 3;
109#endif
[7167]110        SetConfigValueExternal(softDebugLevel_, "OutputHandler", "softDebugLevel" + this->consoleName_, defaultLevel)
[6105]111            .description("The maximal level of debug output shown in the Shell");
112        this->setSoftDebugLevel(this->softDebugLevel_);
[1747]113    }
[1505]114
[1747]115    void Shell::commandHistoryOffsetChanged()
116    {
[1505]117        if (this->historyOffset_ >= this->maxHistoryLength_)
118            this->historyOffset_ = 0;
[1747]119    }
[1505]120
[1747]121    void Shell::commandHistoryLengthChanged()
122    {
123        this->commandHistoryOffsetChanged();
124
[1505]125        while (this->commandHistory_.size() > this->maxHistoryLength_)
126        {
127            unsigned int index = this->commandHistory_.size() - 1;
128            this->commandHistory_.erase(this->commandHistory_.begin() + index);
129            ModifyConfigValue(commandHistory_, remove, index);
130        }
131    }
132
[1755]133    void Shell::configureInputBuffer()
[1505]134    {
135        this->inputBuffer_->registerListener(this, &Shell::inputChanged, true);
[6105]136        this->inputBuffer_->registerListener(this, &Shell::execute,         '\r',   false);
137        this->inputBuffer_->registerListener(this, &Shell::execute,         '\n',   false);
138        this->inputBuffer_->registerListener(this, &Shell::hintAndComplete, '\t',   true);
139        this->inputBuffer_->registerListener(this, &Shell::backspace,       '\b',   true);
140        this->inputBuffer_->registerListener(this, &Shell::backspace,       '\177', true);
141        this->inputBuffer_->registerListener(this, &Shell::exit,            '\033', true); // escape
142        this->inputBuffer_->registerListener(this, &Shell::deleteChar,      KeyCode::Delete);
143        this->inputBuffer_->registerListener(this, &Shell::cursorRight,     KeyCode::Right);
144        this->inputBuffer_->registerListener(this, &Shell::cursorLeft,      KeyCode::Left);
145        this->inputBuffer_->registerListener(this, &Shell::cursorEnd,       KeyCode::End);
146        this->inputBuffer_->registerListener(this, &Shell::cursorHome,      KeyCode::Home);
147        this->inputBuffer_->registerListener(this, &Shell::historyUp,       KeyCode::Up);
148        this->inputBuffer_->registerListener(this, &Shell::historyDown,     KeyCode::Down);
149        if (this->bScrollable_)
150        {
151            this->inputBuffer_->registerListener(this, &Shell::scrollUp,    KeyCode::PageUp);
152            this->inputBuffer_->registerListener(this, &Shell::scrollDown,  KeyCode::PageDown);
153        }
154        else
155        {
156            this->inputBuffer_->registerListener(this, &Shell::historySearchUp,   KeyCode::PageUp);
157            this->inputBuffer_->registerListener(this, &Shell::historySearchDown, KeyCode::PageDown);
158        }
[1505]159    }
160
[6105]161    /*
[1505]162    void Shell::history()
163    {
164        Shell& instance = Shell::getInstance();
165
[3300]166        for (unsigned int i = instance.historyOffset_; i < instance.commandHistory_.size(); ++i)
[6417]167            instance.addOutput(instance.commandHistory_[i] + '\n', -1);
[3300]168        for (unsigned int i =  0; i < instance.historyOffset_; ++i)
[6417]169            instance.addOutput(instance.commandHistory_[i] + '\n', -1);
[1505]170    }
[6105]171    */
[1505]172
173    void Shell::registerListener(ShellListener* listener)
174    {
[6105]175        this->listeners_.push_back(listener);
[1505]176    }
177
178    void Shell::unregisterListener(ShellListener* listener)
179    {
180        for (std::list<ShellListener*>::iterator it = this->listeners_.begin(); it != this->listeners_.end(); )
181        {
182            if ((*it) == listener)
[6105]183                it = this->listeners_.erase(it);
[1505]184            else
185                ++it;
186        }
187    }
188
189    void Shell::setCursorPosition(unsigned int cursor)
190    {
191        this->inputBuffer_->setCursorPosition(cursor);
[6105]192        this->updateListeners<&ShellListener::cursorChanged>();
[1505]193    }
194
[6417]195    void Shell::addOutput(const std::string& text, LineType type)
[1505]196    {
[6417]197        this->outputBuffer_ << text;
198        this->outputChanged(type);
[1505]199    }
200
[6105]201    void Shell::clearOutput()
[1505]202    {
[6105]203        this->outputLines_.clear();
204        this->scrollIterator_ = this->outputLines_.begin();
[1505]205
206        this->scrollPosition_ = 0;
[6105]207        this->bFinishedLastLine_ = true;
[1505]208
[6105]209        this->updateListeners<&ShellListener::linesChanged>();
[1505]210    }
211
[6417]212    Shell::LineList::const_iterator Shell::getNewestLineIterator() const
[1505]213    {
214        if (this->scrollPosition_)
215            return this->scrollIterator_;
216        else
[6105]217            return this->outputLines_.begin();
[1505]218    }
219
[6417]220    Shell::LineList::const_iterator Shell::getEndIterator() const
[1505]221    {
[6105]222        return this->outputLines_.end();
[1505]223    }
224
225    void Shell::addToHistory(const std::string& command)
226    {
[7191]227        if (command == "")
228            return;
229
230        size_t previous_offset = mod(this->historyOffset_ - 1, this->maxHistoryLength_);
231        if (previous_offset < this->commandHistory_.size() && command == this->commandHistory_[previous_offset])
232            return;
233
[1505]234        ModifyConfigValue(commandHistory_, set, this->historyOffset_, command);
235        this->historyPosition_ = 0;
236        ModifyConfigValue(historyOffset_, set, (this->historyOffset_ + 1) % this->maxHistoryLength_);
237    }
238
[6417]239    const std::string& Shell::getFromHistory() const
[1505]240    {
[3301]241        unsigned int index = mod(static_cast<int>(this->historyOffset_) - static_cast<int>(this->historyPosition_), this->maxHistoryLength_);
[1505]242        if (index < this->commandHistory_.size() && this->historyPosition_ != 0)
243            return this->commandHistory_[index];
244        else
[6417]245            return BLANKSTRING;
[1505]246    }
247
[6417]248    void Shell::outputChanged(int lineType)
[1505]249    {
[6105]250        bool newline = false;
[1505]251        do
252        {
[6105]253            std::string output;
254            std::getline(this->outputBuffer_, output);
[1505]255
[6105]256            bool eof = this->outputBuffer_.eof();
257            bool fail = this->outputBuffer_.fail();
258            if (eof)
259                this->outputBuffer_.flush();
260            if (eof || fail)
261                this->outputBuffer_.clear();
262            newline = (!eof && !fail);
263
[6417]264            if (!newline && output.empty())
[1505]265                break;
266
[6105]267            if (this->bFinishedLastLine_)
[1505]268            {
[6417]269                this->outputLines_.push_front(std::make_pair(output, static_cast<LineType>(lineType)));
[1505]270
271                if (this->scrollPosition_)
272                    this->scrollPosition_++;
273                else
[6105]274                    this->scrollIterator_ = this->outputLines_.begin();
[1505]275
[6105]276                this->bFinishedLastLine_ = newline;
[1505]277
278                if (!this->scrollPosition_)
[6105]279                    this->updateListeners<&ShellListener::lineAdded>();
[1505]280            }
281            else
282            {
[6417]283                this->outputLines_.front().first += output;
[6105]284                this->bFinishedLastLine_ = newline;
285                this->updateListeners<&ShellListener::onlyLastLineChanged>();
[1505]286            }
[6417]287            this->bFinishedLastLine_ = newline;
[1505]288
289        } while (newline);
290    }
291
[6105]292    void Shell::clearInput()
293    {
294        this->inputBuffer_->clear();
295        this->historyPosition_ = 0;
296        this->updateListeners<&ShellListener::inputChanged>();
297        this->updateListeners<&ShellListener::cursorChanged>();
298    }
299
300    void Shell::setPromptPrefix(const std::string& str)
301    {
302    }
303
304
305    // ##########################################
306    // ###   InputBuffer callback functions   ###
307    // ##########################################
308
[1505]309    void Shell::inputChanged()
310    {
[6105]311        this->updateListeners<&ShellListener::inputChanged>();
312        this->updateListeners<&ShellListener::cursorChanged>();
[1505]313    }
314
315    void Shell::execute()
316    {
317        this->addToHistory(this->inputBuffer_->get());
[6105]318        this->updateListeners<&ShellListener::executed>();
[1505]319
[7228]320        int error;
321        const std::string& result = CommandExecutor::query(this->inputBuffer_->get(), &error);
322        if (error)
[6417]323        {
[7228]324            switch (error)
325            {
326                case CommandExecutor::Error:       this->outputBuffer_ << "Error: Can't execute \"" << this->inputBuffer_->get() << "\", command doesn't exist. (S)" << std::endl; break;
327                case CommandExecutor::Incomplete:  this->outputBuffer_ << "Error: Can't execute \"" << this->inputBuffer_->get() << "\", not enough arguments given. (S)" << std::endl; break;
328                case CommandExecutor::Deactivated: this->outputBuffer_ << "Error: Can't execute \"" << this->inputBuffer_->get() << "\", command is not active. (S)" << std::endl; break;
329                case CommandExecutor::Denied:      this->outputBuffer_ << "Error: Can't execute \"" << this->inputBuffer_->get() << "\", access denied. (S)" << std::endl; break;
330            }
[6417]331            this->outputChanged(Error);
332        }
[7189]333        else if (result != "")
334        {
335            this->outputBuffer_ << result << std::endl;
336            this->outputChanged(Command);
337        }
[1505]338
[6105]339        this->clearInput();
[1505]340    }
341
[6105]342    void Shell::hintAndComplete()
[1505]343    {
[7228]344        this->inputBuffer_->set(CommandExecutor::evaluate(this->inputBuffer_->get()).complete());
345        this->outputBuffer_ << CommandExecutor::evaluate(this->inputBuffer_->get()).hint() << std::endl;
[6417]346        this->outputChanged(Hint);
[1505]347
348        this->inputChanged();
349    }
350
351    void Shell::backspace()
352    {
353        this->inputBuffer_->removeBehindCursor();
[6105]354        this->updateListeners<&ShellListener::inputChanged>();
355        this->updateListeners<&ShellListener::cursorChanged>();
[1505]356    }
357
[6105]358    void Shell::exit()
[1505]359    {
[6105]360        if (this->inputBuffer_->getSize() > 0)
361        {
362            this->clearInput();
363            return;
364        }
365
366        this->clearInput();
367        this->scrollPosition_ = 0;
368        this->scrollIterator_ = this->outputLines_.begin();
369
370        this->updateListeners<&ShellListener::exit>();
[1505]371    }
372
[6105]373    void Shell::deleteChar()
[1505]374    {
[6105]375        this->inputBuffer_->removeAtCursor();
376        this->updateListeners<&ShellListener::inputChanged>();
[1505]377    }
378
[6105]379    void Shell::cursorRight()
[1505]380    {
381        this->inputBuffer_->increaseCursor();
[6105]382        this->updateListeners<&ShellListener::cursorChanged>();
[1505]383    }
384
[6105]385    void Shell::cursorLeft()
[1505]386    {
387        this->inputBuffer_->decreaseCursor();
[6105]388        this->updateListeners<&ShellListener::cursorChanged>();
[1505]389    }
390
[6105]391    void Shell::cursorEnd()
[1505]392    {
393        this->inputBuffer_->setCursorToEnd();
[6105]394        this->updateListeners<&ShellListener::cursorChanged>();
[1505]395    }
396
[6105]397    void Shell::cursorHome()
[1505]398    {
399        this->inputBuffer_->setCursorToBegin();
[6105]400        this->updateListeners<&ShellListener::cursorChanged>();
[1505]401    }
402
[6105]403    void Shell::historyUp()
[1505]404    {
405        if (this->historyPosition_ < this->commandHistory_.size())
406        {
407            this->historyPosition_++;
408            this->inputBuffer_->set(this->getFromHistory());
409        }
410    }
411
[6105]412    void Shell::historyDown()
[1505]413    {
414        if (this->historyPosition_ > 0)
415        {
416            this->historyPosition_--;
417            this->inputBuffer_->set(this->getFromHistory());
418        }
419    }
420
[6105]421    void Shell::historySearchUp()
[1505]422    {
[6105]423        if (this->historyPosition_ == this->historyOffset_)
424            return;
425        unsigned int cursorPosition = this->getCursorPosition();
[6417]426        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]427        for (unsigned int newPos = this->historyPosition_ + 1; newPos <= this->historyOffset_; newPos++)
[1505]428        {
[6105]429            if (getLowercase(this->commandHistory_[this->historyOffset_ - newPos]).find(getLowercase(input_str)) == 0) // search case insensitive
430            {
431                this->historyPosition_ = newPos;
432                this->inputBuffer_->set(this->getFromHistory());
433                this->setCursorPosition(cursorPosition);
434                return;
435            }
436        }
437    }
[1505]438
[6105]439    void Shell::historySearchDown()
440    {
441        if (this->historyPosition_ == 0)
442            return;
443        unsigned int cursorPosition = this->getCursorPosition();
[6417]444        const std::string& input_str(this->getInput().substr(0, cursorPosition)); // only search for the expression from the beginning
[6105]445        for (unsigned int newPos = this->historyPosition_ - 1; newPos > 0; newPos--)
446        {
447            if (getLowercase(this->commandHistory_[this->historyOffset_ - newPos]).find(getLowercase(input_str)) == 0) // sear$
448            {
449                this->historyPosition_ = newPos;
450                this->inputBuffer_->set(this->getFromHistory());
451                this->setCursorPosition(cursorPosition);
452                return;
453            }
[1505]454        }
455    }
456
[6105]457    void Shell::scrollUp()
[1505]458    {
[6105]459        if (this->scrollIterator_ != this->outputLines_.end())
[1505]460        {
[6105]461            ++this->scrollIterator_;
462            ++this->scrollPosition_;
[1505]463
[6105]464            this->updateListeners<&ShellListener::linesChanged>();
[1505]465        }
466    }
467
[6105]468    void Shell::scrollDown()
[1505]469    {
[6105]470        if (this->scrollIterator_ != this->outputLines_.begin())
[1505]471        {
[6105]472            --this->scrollIterator_;
473            --this->scrollPosition_;
474
475            this->updateListeners<&ShellListener::linesChanged>();
[1505]476        }
477    }
478}
Note: See TracBrowser for help on using the repository browser.