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
Line 
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:
25 *      Reto Grieder
26 *
27 */
28
29#include "Shell.h"
30
31#include "util/OutputHandler.h"
32#include "util/StringUtils.h"
33#include "util/SubString.h"
34#include "core/CoreIncludes.h"
35#include "core/ConfigFileManager.h"
36#include "core/ConfigValueIncludes.h"
37#include "CommandExecutor.h"
38#include "ConsoleCommand.h"
39
40namespace orxonox
41{
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  );
47
48    unsigned int Shell::cacheSize_s;
49
50    Shell::Shell(const std::string& consoleName, bool bScrollable)
51        : OutputListener(consoleName)
52        , inputBuffer_(new InputBuffer())
53        , consoleName_(consoleName)
54        , bScrollable_(bScrollable)
55    {
56        RegisterRootObject(Shell);
57
58        this->scrollPosition_ = 0;
59        this->maxHistoryLength_ = 100;
60        this->historyPosition_ = 0;
61        this->historyOffset_ = 0;
62        this->bFinishedLastLine_ = true;
63
64        this->clearOutput();
65        this->configureInputBuffer();
66
67        // Specify file for the command history
68        ConfigFileManager::getInstance().setFilename(ConfigFileType::CommandHistory, "commandHistory.ini");
69
70        // Use a stringstream object to buffer the output
71        this->outputStream_ = &this->outputBuffer_;
72
73        this->setConfigValues();
74
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);
88    }
89
90    Shell::~Shell()
91    {
92        OutputHandler::getInstance().unregisterOutputListener(this);
93        this->inputBuffer_->destroy();
94    }
95
96    void Shell::setConfigValues()
97    {
98        SetConfigValue(maxHistoryLength_, 100)
99            .callback(this, &Shell::commandHistoryLengthChanged);
100        SetConfigValue(historyOffset_, 0)
101            .callback(this, &Shell::commandHistoryOffsetChanged);
102        setConfigValueGeneric(this, &commandHistory_, ConfigFileType::CommandHistory, "Shell", "commandHistory_", std::vector<std::string>());
103        SetConfigValue(cacheSize_s, 32);
104
105#ifdef ORXONOX_RELEASE
106        const unsigned int defaultLevel = 1;
107#else
108        const unsigned int defaultLevel = 3;
109#endif
110        SetConfigValueExternal(softDebugLevel_, "OutputHandler", "softDebugLevel" + this->consoleName_, defaultLevel)
111            .description("The maximal level of debug output shown in the Shell");
112        this->setSoftDebugLevel(this->softDebugLevel_);
113    }
114
115    void Shell::commandHistoryOffsetChanged()
116    {
117        if (this->historyOffset_ >= this->maxHistoryLength_)
118            this->historyOffset_ = 0;
119    }
120
121    void Shell::commandHistoryLengthChanged()
122    {
123        this->commandHistoryOffsetChanged();
124
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
133    void Shell::configureInputBuffer()
134    {
135        this->inputBuffer_->registerListener(this, &Shell::inputChanged, true);
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        }
159    }
160
161    /*
162    void Shell::history()
163    {
164        Shell& instance = Shell::getInstance();
165
166        for (unsigned int i = instance.historyOffset_; i < instance.commandHistory_.size(); ++i)
167            instance.addOutput(instance.commandHistory_[i] + '\n', -1);
168        for (unsigned int i =  0; i < instance.historyOffset_; ++i)
169            instance.addOutput(instance.commandHistory_[i] + '\n', -1);
170    }
171    */
172
173    void Shell::registerListener(ShellListener* listener)
174    {
175        this->listeners_.push_back(listener);
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)
183                it = this->listeners_.erase(it);
184            else
185                ++it;
186        }
187    }
188
189    void Shell::setCursorPosition(unsigned int cursor)
190    {
191        this->inputBuffer_->setCursorPosition(cursor);
192        this->updateListeners<&ShellListener::cursorChanged>();
193    }
194
195    void Shell::addOutput(const std::string& text, LineType type)
196    {
197        this->outputBuffer_ << text;
198        this->outputChanged(type);
199    }
200
201    void Shell::clearOutput()
202    {
203        this->outputLines_.clear();
204        this->scrollIterator_ = this->outputLines_.begin();
205
206        this->scrollPosition_ = 0;
207        this->bFinishedLastLine_ = true;
208
209        this->updateListeners<&ShellListener::linesChanged>();
210    }
211
212    Shell::LineList::const_iterator Shell::getNewestLineIterator() const
213    {
214        if (this->scrollPosition_)
215            return this->scrollIterator_;
216        else
217            return this->outputLines_.begin();
218    }
219
220    Shell::LineList::const_iterator Shell::getEndIterator() const
221    {
222        return this->outputLines_.end();
223    }
224
225    void Shell::addToHistory(const std::string& command)
226    {
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
234        ModifyConfigValue(commandHistory_, set, this->historyOffset_, command);
235        this->historyPosition_ = 0;
236        ModifyConfigValue(historyOffset_, set, (this->historyOffset_ + 1) % this->maxHistoryLength_);
237    }
238
239    const std::string& Shell::getFromHistory() const
240    {
241        unsigned int index = mod(static_cast<int>(this->historyOffset_) - static_cast<int>(this->historyPosition_), this->maxHistoryLength_);
242        if (index < this->commandHistory_.size() && this->historyPosition_ != 0)
243            return this->commandHistory_[index];
244        else
245            return BLANKSTRING;
246    }
247
248    void Shell::outputChanged(int lineType)
249    {
250        bool newline = false;
251        do
252        {
253            std::string output;
254            std::getline(this->outputBuffer_, output);
255
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
264            if (!newline && output.empty())
265                break;
266
267            if (this->bFinishedLastLine_)
268            {
269                this->outputLines_.push_front(std::make_pair(output, static_cast<LineType>(lineType)));
270
271                if (this->scrollPosition_)
272                    this->scrollPosition_++;
273                else
274                    this->scrollIterator_ = this->outputLines_.begin();
275
276                this->bFinishedLastLine_ = newline;
277
278                if (!this->scrollPosition_)
279                    this->updateListeners<&ShellListener::lineAdded>();
280            }
281            else
282            {
283                this->outputLines_.front().first += output;
284                this->bFinishedLastLine_ = newline;
285                this->updateListeners<&ShellListener::onlyLastLineChanged>();
286            }
287            this->bFinishedLastLine_ = newline;
288
289        } while (newline);
290    }
291
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
309    void Shell::inputChanged()
310    {
311        this->updateListeners<&ShellListener::inputChanged>();
312        this->updateListeners<&ShellListener::cursorChanged>();
313    }
314
315    void Shell::execute()
316    {
317        this->addToHistory(this->inputBuffer_->get());
318        this->updateListeners<&ShellListener::executed>();
319
320        int error;
321        const std::string& result = CommandExecutor::query(this->inputBuffer_->get(), &error);
322        if (error)
323        {
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            }
331            this->outputChanged(Error);
332        }
333        else if (result != "")
334        {
335            this->outputBuffer_ << result << std::endl;
336            this->outputChanged(Command);
337        }
338
339        this->clearInput();
340    }
341
342    void Shell::hintAndComplete()
343    {
344        this->inputBuffer_->set(CommandExecutor::evaluate(this->inputBuffer_->get()).complete());
345        this->outputBuffer_ << CommandExecutor::evaluate(this->inputBuffer_->get()).hint() << std::endl;
346        this->outputChanged(Hint);
347
348        this->inputChanged();
349    }
350
351    void Shell::backspace()
352    {
353        this->inputBuffer_->removeBehindCursor();
354        this->updateListeners<&ShellListener::inputChanged>();
355        this->updateListeners<&ShellListener::cursorChanged>();
356    }
357
358    void Shell::exit()
359    {
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>();
371    }
372
373    void Shell::deleteChar()
374    {
375        this->inputBuffer_->removeAtCursor();
376        this->updateListeners<&ShellListener::inputChanged>();
377    }
378
379    void Shell::cursorRight()
380    {
381        this->inputBuffer_->increaseCursor();
382        this->updateListeners<&ShellListener::cursorChanged>();
383    }
384
385    void Shell::cursorLeft()
386    {
387        this->inputBuffer_->decreaseCursor();
388        this->updateListeners<&ShellListener::cursorChanged>();
389    }
390
391    void Shell::cursorEnd()
392    {
393        this->inputBuffer_->setCursorToEnd();
394        this->updateListeners<&ShellListener::cursorChanged>();
395    }
396
397    void Shell::cursorHome()
398    {
399        this->inputBuffer_->setCursorToBegin();
400        this->updateListeners<&ShellListener::cursorChanged>();
401    }
402
403    void Shell::historyUp()
404    {
405        if (this->historyPosition_ < this->commandHistory_.size())
406        {
407            this->historyPosition_++;
408            this->inputBuffer_->set(this->getFromHistory());
409        }
410    }
411
412    void Shell::historyDown()
413    {
414        if (this->historyPosition_ > 0)
415        {
416            this->historyPosition_--;
417            this->inputBuffer_->set(this->getFromHistory());
418        }
419    }
420
421    void Shell::historySearchUp()
422    {
423        if (this->historyPosition_ == this->historyOffset_)
424            return;
425        unsigned int cursorPosition = this->getCursorPosition();
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
427        for (unsigned int newPos = this->historyPosition_ + 1; newPos <= this->historyOffset_; newPos++)
428        {
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    }
438
439    void Shell::historySearchDown()
440    {
441        if (this->historyPosition_ == 0)
442            return;
443        unsigned int cursorPosition = this->getCursorPosition();
444        const std::string& input_str(this->getInput().substr(0, cursorPosition)); // only search for the expression from the beginning
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            }
454        }
455    }
456
457    void Shell::scrollUp()
458    {
459        if (this->scrollIterator_ != this->outputLines_.end())
460        {
461            ++this->scrollIterator_;
462            ++this->scrollPosition_;
463
464            this->updateListeners<&ShellListener::linesChanged>();
465        }
466    }
467
468    void Shell::scrollDown()
469    {
470        if (this->scrollIterator_ != this->outputLines_.begin())
471        {
472            --this->scrollIterator_;
473            --this->scrollPosition_;
474
475            this->updateListeners<&ShellListener::linesChanged>();
476        }
477    }
478}
Note: See TracBrowser for help on using the repository browser.