Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/core/Shell.cc @ 1792

Last change on this file since 1792 was 1792, checked in by rgrieder, 16 years ago

Changed initialisation of TclThreadManager, TclBind and Shell to match the simple singleton concept (runtime assert in the c'tor).
That simplifies things a lot. The instances now 'belong' to GSRoot.

  • Property svn:eol-style set to native
File size: 11.6 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 *      ...
26 *
27 */
28
29#include "Shell.h"
30#include "CommandExecutor.h"
31#include "CoreIncludes.h"
32#include "ConfigValueIncludes.h"
33#include "Core.h"
34#include "ConsoleCommand.h"
35#include "util/OutputHandler.h"
36
37#define SHELL_UPDATE_LISTENERS(function) \
38    for (std::list<ShellListener*>::iterator it = this->listeners_.begin(); it != this->listeners_.end(); ) \
39        (*(it++))->function()
40
41namespace orxonox
42{
43    SetConsoleCommand(Shell, clearShell, true);
44    SetConsoleCommand(Shell, history, true);
45
46    SetConsoleCommandShortcut(OutputHandler, log);
47    SetConsoleCommandShortcut(OutputHandler, error);
48    SetConsoleCommandShortcut(OutputHandler, warning);
49    SetConsoleCommandShortcut(OutputHandler, info);
50    SetConsoleCommandShortcut(OutputHandler, debug);
51
52    Shell* Shell::singletonRef_s = 0;
53
54    Shell::Shell()
55    {
56        assert(singletonRef_s == 0);
57        singletonRef_s = this;
58
59        int level = Core::getSoftDebugLevel(OutputHandler::LD_Shell);
60        Core::setSoftDebugLevel(OutputHandler::LD_Shell, -1);
61
62        RegisterRootObject(Shell);
63
64        this->scrollPosition_ = 0;
65        this->maxHistoryLength_ = 100;
66        this->historyPosition_ = 0;
67        this->historyOffset_ = 0;
68        this->finishedLastLine_ = true;
69        this->bAddOutputLevel_ = false;
70
71        this->clearLines();
72
73        this->inputBuffer_ = new InputBuffer();
74        this->configureInputBuffer();
75
76        this->outputBuffer_.registerListener(this);
77        OutputHandler::getOutStream().setOutputBuffer(this->outputBuffer_);
78
79        this->setConfigValues();
80
81        Core::setSoftDebugLevel(OutputHandler::LD_Shell, level);
82    }
83
84    Shell::~Shell()
85    {
86        if (this->inputBuffer_)
87            delete this->inputBuffer_;
88        singletonRef_s = 0;
89    }
90
91    void Shell::setConfigValues()
92    {
93        SetConfigValue(maxHistoryLength_, 100).callback(this, &Shell::commandHistoryLengthChanged);
94        SetConfigValue(historyOffset_, 0).callback(this, &Shell::commandHistoryOffsetChanged);
95        SetConfigValueVector(commandHistory_, std::vector<std::string>());
96    }
97
98    void Shell::commandHistoryOffsetChanged()
99    {
100        if (this->historyOffset_ >= this->maxHistoryLength_)
101            this->historyOffset_ = 0;
102    }
103
104    void Shell::commandHistoryLengthChanged()
105    {
106        this->commandHistoryOffsetChanged();
107
108        while (this->commandHistory_.size() > this->maxHistoryLength_)
109        {
110            unsigned int index = this->commandHistory_.size() - 1;
111            this->commandHistory_.erase(this->commandHistory_.begin() + index);
112            ModifyConfigValue(commandHistory_, remove, index);
113        }
114    }
115
116    void Shell::configureInputBuffer()
117    {
118        this->inputBuffer_->registerListener(this, &Shell::inputChanged, true);
119        this->inputBuffer_->registerListener(this, &Shell::execute, '\r', false);
120        this->inputBuffer_->registerListener(this, &Shell::hintandcomplete, '\t', true);
121        this->inputBuffer_->registerListener(this, &Shell::backspace, '\b', true);
122        this->inputBuffer_->registerListener(this, &Shell::deletechar, KeyCode::Delete);
123        this->inputBuffer_->registerListener(this, &Shell::exit, (char)27, true);
124        this->inputBuffer_->registerListener(this, &Shell::cursor_right, KeyCode::Right);
125        this->inputBuffer_->registerListener(this, &Shell::cursor_left, KeyCode::Left);
126        this->inputBuffer_->registerListener(this, &Shell::cursor_end, KeyCode::End);
127        this->inputBuffer_->registerListener(this, &Shell::cursor_home, KeyCode::Home);
128        this->inputBuffer_->registerListener(this, &Shell::history_up, KeyCode::Up);
129        this->inputBuffer_->registerListener(this, &Shell::history_down, KeyCode::Down);
130        this->inputBuffer_->registerListener(this, &Shell::scroll_up, KeyCode::PageUp);
131        this->inputBuffer_->registerListener(this, &Shell::scroll_down, KeyCode::PageDown);
132    }
133
134    void Shell::clearShell()
135    {
136        Shell::getInstance().clearLines();
137    }
138
139    void Shell::history()
140    {
141        Shell& instance = Shell::getInstance();
142
143        for (int i = instance.historyOffset_; i < (int)instance.commandHistory_.size(); ++i)
144            instance.addLine(instance.commandHistory_[i], -1);
145        for (int i =  0; i < (int)instance.historyOffset_; ++i)
146            instance.addLine(instance.commandHistory_[i], -1);
147    }
148
149    void Shell::registerListener(ShellListener* listener)
150    {
151        this->listeners_.insert(this->listeners_.end(), listener);
152    }
153
154    void Shell::unregisterListener(ShellListener* listener)
155    {
156        for (std::list<ShellListener*>::iterator it = this->listeners_.begin(); it != this->listeners_.end(); )
157        {
158            if ((*it) == listener)
159                this->listeners_.erase(it++);
160            else
161                ++it;
162        }
163    }
164
165    void Shell::setCursorPosition(unsigned int cursor)
166    {
167        this->inputBuffer_->setCursorPosition(cursor);
168        SHELL_UPDATE_LISTENERS(cursorChanged);
169    }
170
171    void Shell::setInput(const std::string& input)
172    {
173        this->inputBuffer_->set(input);
174        this->inputChanged();
175    }
176
177    void Shell::addLine(const std::string& line, int level)
178    {
179        int original_level = OutputHandler::getOutStream().getOutputLevel();
180        OutputHandler::getOutStream().setOutputLevel(level);
181
182        if (!this->finishedLastLine_)
183            this->outputBuffer_ << std::endl;
184
185        this->outputBuffer_ << line << std::endl;
186        OutputHandler::getOutStream().setOutputLevel(original_level);
187    }
188
189    void Shell::clearLines()
190    {
191        this->lines_.clear();
192        this->scrollIterator_ = this->lines_.begin();
193
194        this->scrollPosition_ = 0;
195        this->finishedLastLine_ = true;
196
197        SHELL_UPDATE_LISTENERS(linesChanged);
198    }
199
200    std::list<std::string>::const_iterator Shell::getNewestLineIterator() const
201    {
202        if (this->scrollPosition_)
203            return this->scrollIterator_;
204        else
205            return this->lines_.begin();
206    }
207
208    std::list<std::string>::const_iterator Shell::getEndIterator() const
209    {
210        return this->lines_.end();
211    }
212
213    void Shell::addToHistory(const std::string& command)
214    {
215        ModifyConfigValue(commandHistory_, set, this->historyOffset_, command);
216        this->historyPosition_ = 0;
217        ModifyConfigValue(historyOffset_, set, (this->historyOffset_ + 1) % this->maxHistoryLength_);
218    }
219
220    std::string Shell::getFromHistory() const
221    {
222        unsigned int index = mod(((int)this->historyOffset_) - ((int)this->historyPosition_), this->maxHistoryLength_);
223        if (index < this->commandHistory_.size() && this->historyPosition_ != 0)
224            return this->commandHistory_[index];
225        else
226            return "";
227    }
228
229    void Shell::outputChanged()
230    {
231        std::string output;
232        bool newline;
233        do
234        {
235            newline = this->outputBuffer_.getLine(&output);
236
237            if (!newline && output == "")
238                break;
239
240            if (this->finishedLastLine_)
241            {
242                if (this->bAddOutputLevel_)
243                    output.insert(0, 1, (char)OutputHandler::getOutStream().getOutputLevel());
244
245                this->lines_.insert(this->lines_.begin(), output);
246
247                if (this->scrollPosition_)
248                    this->scrollPosition_++;
249                else
250                    this->scrollIterator_ = this->lines_.begin();
251
252                this->finishedLastLine_ = newline;
253
254                if (!this->scrollPosition_)
255                {
256                    SHELL_UPDATE_LISTENERS(lineAdded);
257                }
258            }
259            else
260            {
261                (*this->lines_.begin()) += output;
262                this->finishedLastLine_ = newline;
263                SHELL_UPDATE_LISTENERS(onlyLastLineChanged);
264            }
265
266        } while (newline);
267    }
268
269    void Shell::inputChanged()
270    {
271        SHELL_UPDATE_LISTENERS(inputChanged);
272        SHELL_UPDATE_LISTENERS(cursorChanged);
273    }
274
275    void Shell::execute()
276    {
277        this->addToHistory(this->inputBuffer_->get());
278        this->addLine(this->inputBuffer_->get(), 0);
279
280        if (!CommandExecutor::execute(this->inputBuffer_->get()))
281            this->addLine("Error: Can't execute \"" + this->inputBuffer_->get() + "\".", 1);
282
283        this->clear();
284    }
285
286    void Shell::hintandcomplete()
287    {
288        this->inputBuffer_->set(CommandExecutor::complete(this->inputBuffer_->get()));
289        this->addLine(CommandExecutor::hint(this->inputBuffer_->get()), -1);
290
291        this->inputChanged();
292    }
293
294    void Shell::backspace()
295    {
296        this->inputBuffer_->removeBehindCursor();
297        SHELL_UPDATE_LISTENERS(inputChanged);
298        SHELL_UPDATE_LISTENERS(cursorChanged);
299    }
300
301    void Shell::deletechar()
302    {
303        this->inputBuffer_->removeAtCursor();
304        SHELL_UPDATE_LISTENERS(inputChanged);
305    }
306
307    void Shell::clear()
308    {
309        this->inputBuffer_->clear();
310        this->historyPosition_ = 0;
311        SHELL_UPDATE_LISTENERS(inputChanged);
312        SHELL_UPDATE_LISTENERS(cursorChanged);
313    }
314
315    void Shell::cursor_right()
316    {
317        this->inputBuffer_->increaseCursor();
318        SHELL_UPDATE_LISTENERS(cursorChanged);
319    }
320
321    void Shell::cursor_left()
322    {
323        this->inputBuffer_->decreaseCursor();
324        SHELL_UPDATE_LISTENERS(cursorChanged);
325    }
326
327    void Shell::cursor_end()
328    {
329        this->inputBuffer_->setCursorToEnd();
330        SHELL_UPDATE_LISTENERS(cursorChanged);
331    }
332
333    void Shell::cursor_home()
334    {
335        this->inputBuffer_->setCursorToBegin();
336        SHELL_UPDATE_LISTENERS(cursorChanged);
337    }
338
339    void Shell::history_up()
340    {
341        if (this->historyPosition_ < this->commandHistory_.size())
342        {
343            this->historyPosition_++;
344            this->inputBuffer_->set(this->getFromHistory());
345        }
346    }
347
348    void Shell::history_down()
349    {
350        if (this->historyPosition_ > 0)
351        {
352            this->historyPosition_--;
353            this->inputBuffer_->set(this->getFromHistory());
354        }
355    }
356
357    void Shell::scroll_up()
358    {
359        if (this->scrollIterator_ != this->lines_.end())
360        {
361            ++this->scrollIterator_;
362            ++this->scrollPosition_;
363
364            SHELL_UPDATE_LISTENERS(linesChanged);
365        }
366    }
367
368    void Shell::scroll_down()
369    {
370        if (this->scrollIterator_ != this->lines_.begin())
371        {
372            --this->scrollIterator_;
373            --this->scrollPosition_;
374
375            SHELL_UPDATE_LISTENERS(linesChanged);
376        }
377    }
378
379    void Shell::exit()
380    {
381        if (this->inputBuffer_->getSize() > 0)
382        {
383            this->clear();
384            return;
385        }
386
387        this->clear();
388        this->scrollPosition_ = 0;
389        this->scrollIterator_ = this->lines_.begin();
390
391        SHELL_UPDATE_LISTENERS(exit);
392    }
393}
Note: See TracBrowser for help on using the repository browser.