/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Grauer co-programmer: ... */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_SHELL #include "shell_input.h" #include "shell_command.h" #include "shell_command_class.h" #include "debug.h" #include "compiler.h" #include "key_names.h" #include "game_world.h" #include "state.h" namespace OrxShell { ObjectListDefinition(ShellInput); /** * @brief constructor * this also generates a ShellCompletion automatically. */ ShellInput::ShellInput () : Text ("") { this->registerObject(this, ShellInput::_objectList); this->pressedKey = SDLK_FIRST; this->historyIT = ShellInput::history.begin(); this->setHistoryLength(50); this->historyScrolling = false; this->delayed = 0; this->setRepeatDelay(.3, .05); // subscribe all keyboard commands to ES_SHELL for (int i = 1; i < SDLK_LAST; i++) { //if (!this->isEventSubscribed(ES_SHELL, i)) this->subscribeEvent(ES_SHELL, i); } // unsubscribe unused TODO improve. this->unsubscribeEvent(ES_SHELL, SDLK_BACKQUOTE); this->unsubscribeEvent(ES_SHELL, SDLK_F12); this->unsubscribeEvent(ES_SHELL, SDLK_PAGEUP); this->unsubscribeEvent(ES_SHELL, SDLK_PAGEDOWN); } std::list ShellInput::history; /** * @brief standard deconstructor */ ShellInput::~ShellInput () {} /** * @brief sets the Repeate-delay and rate * @param repeatDelay the Delay it takes, to repeate a key * @param repeatRate the rate to repeate a pressed key */ void ShellInput::setRepeatDelay(float repeatDelay, float repeatRate) { this->repeatDelay = repeatDelay; this->repeatRate = repeatRate; } /** * @brief deletes the InputLine */ void ShellInput::flush() { this->inputLineBegin.clear(); this->inputLineEnd.clear(); this->clear(); } /** * @brief sets the entire text of the InputLine to text * @param text the new Text to set as InputLine */ void ShellInput::setInputText(const std::string& text) { this->inputLineBegin = text; this->inputLineEnd.clear(); this->setText(text); } /** * @brief adds one character to the inputLine * @param character the character to add to the inputLine */ void ShellInput::addCharacter(char character) { if (this->historyScrolling) { this->history.pop_back(); this->historyScrolling = false; } this->inputLineBegin += character; this->setText(this->inputLineBegin + this->inputLineEnd); } /** * @brief adds multiple Characters to thr inputLine * @param characters a \\0 terminated char-array to add to the InputLine */ void ShellInput::addCharacters(const std::string& characters) { if (this->historyScrolling) { this->history.pop_back(); this->historyScrolling = false; } this->inputLineBegin += characters; this->setText(this->inputLineBegin + this->inputLineEnd); } /** * @brief removes characterCount characters from the InputLine * @param characterCount the count of Characters to remove from the input Line */ void ShellInput::removeCharacters(unsigned int characterCount) { if (this->historyScrolling) { this->history.pop_back(); this->historyScrolling = false; } if (this->inputLineBegin.size() < characterCount) characterCount = this->inputLineBegin.size(); this->inputLineBegin.erase(this->inputLineBegin.size() - characterCount, this->inputLineBegin.size()); this->setText(this->inputLineBegin + this->inputLineEnd); } /** * @brief executes the command stored in the inputLine * @return true if the command was commited successfully, false otherwise */ bool ShellInput::executeCommand() { if (this->getInput().empty()) return false; DebugBuffer::addBufferLineStatic("Execute Command: %s\n", this->getInput().c_str()); ShellCommand::execute(this->getInput()); // removing the eventually added Entry (from scrolling) to the List if (this->historyScrolling) { this->history.pop_back(); this->historyScrolling = false; } // adding the new Command to the History if (history.empty() || history.back() != this->getInput()) this->history.push_back(this->getInput()); if (this->history.size() > this->historyLength) { this->history.pop_front(); } this->flush(); return true; } /** * @brief moves one entry up in the history. */ void ShellInput::historyMoveUp() { if (!this->historyScrolling) { this->history.push_back(this->getInput()); this->historyScrolling = true; this->historyIT = --this->history.end(); } if(this->historyIT != this->history.begin()) { std::string prevElem = *(--this->historyIT); /*if (prevElem == NULL) /// TODO STD return; else */ { this->flush(); this->setInputText(prevElem); } } } /** * @brief moves one entry down in the history */ void ShellInput::historyMoveDown() { if (!this->historyScrolling) return; if (!this->history.empty() && this->historyIT != --this->history.end()) { std::string nextElem = *(++this->historyIT); /* if (nextElem == NULL) /// TODO FIX STD return; else */ { this->flush(); this->setInputText(nextElem); } } } /** * @brief moves the cursor chars Characters to the right. * @param chars how much to move the cursor. */ void ShellInput::moveCursor(int chars) { if (chars > 0) { PRINTF(5)("move cursor %d to the right\n", chars); if (chars >= (int) this->inputLineEnd.size()) chars = inputLineEnd.size(); this->inputLineBegin += this->inputLineEnd.substr(0, chars); this->inputLineEnd.erase(0, chars); } else if (chars < 0) { chars = -chars; PRINTF(5)("move cursor %d to the left\n", chars); if (chars >= (int) this->inputLineBegin.size()) chars = inputLineBegin.size(); this->inputLineEnd = this->inputLineBegin.substr(this->inputLineBegin.size() - chars) + this->inputLineEnd; this->inputLineBegin.erase(this->inputLineBegin.size() - chars); } } /** * @brief prints out some nice help about the Shell */ void ShellInput::help(const std::string& className, const std::string& functionName) { if (className.empty()) { PRINT(0)("== Help for the most important Shell-commands\n"); PRINT(0)(" F1 - HELP; F2 - DEBUG; '`' - open/close shell\n"); PRINT(0)(" input order:\n"); PRINT(0)(" ClassName [objectName] function [parameter1, [parameter2 ...]] or\n"); PRINT(0)(" Alias [parameter]\n"); PRINT(0)("- Also try 'help className' or pushing 'TAB'\n"); } else if (!className.empty() && functionName.empty()) { ShellCommandClass::help(className); //PRINTF(1)("%s::%s\n", className, functionName); } } /** * @brief ticks the ShellInput * @param dt the time passed since the last update */ void ShellInput::tick(float dt) { if (this->delayed > 0.0) { StoryEntity* storyEntity = State::getCurrentStoryEntity(); float speed = 1; if ( storyEntity && storyEntity->isA( GameWorld::staticClassID() ) ) { speed = ((GameWorld*)storyEntity)->getSpeed(); } this->delayed -= dt / speed; } else if (this->pressedKey != SDLK_FIRST ) { this->delayed = this->repeatRate; switch (this->pressedKey ) { case SDLK_BACKSPACE: this->removeCharacters(1); break; case SDLK_UP: this->historyMoveUp(); break; case SDLK_DOWN: this->historyMoveDown(); break; default: { if (likely(pressedKey < 127)) this->addCharacter(this->pressedKey); } } } } /** * @brief listens for some event * @param event the Event happened */ void ShellInput::process(const Event &event) { if (event.bPressed) { PRINTF(5)("Shell received command %s\n", SDLKToKeyname(event.type).c_str()); if (event.type == SDLK_F1) this->help(); else if (event.type == SDLK_F2) { ;//this->debug(); } else if (event.type == SDLK_UP) { this->historyMoveUp(); this->pressedKey = event.type; this->pressedEvent = event.type; } else if (event.type == SDLK_DOWN) { this->historyMoveDown(); this->pressedKey = event.type; this->pressedEvent = event.type; } else if (event.type == SDLK_LEFT) { this->moveCursor(-1); this->pressedKey = event.type; this->pressedEvent = event.type; } else if (event.type == SDLK_RIGHT) { this->moveCursor(+1); this->pressedKey = event.type; this->pressedEvent = event.type; } else if (event.type == SDLK_TAB) { this->completion.autoComplete(this->inputLineBegin); this->setText(this->getInput()); } else if (event.type == SDLK_BACKSPACE) { this->delayed = this->repeatDelay; this->pressedKey = SDLK_BACKSPACE; this->pressedEvent = SDLK_BACKSPACE; this->removeCharacters(1); } else if (event.type == SDLK_DELETE) { if (!this->inputLineEnd.empty()) { this->inputLineEnd.erase(0, 1); this->setText(this->getInput()); } } else if (event.type == SDLK_RETURN) { this->executeCommand(); this->pressedKey = event.type; this->pressedEvent = event.type; } // any other keyboard key else if (likely(event.type < 127)) { this->addCharacter(event.x); this->pressedKey = event.x; this->pressedEvent = event.type; } this->delayed = this->repeatDelay; } else // if(!event.bPressed) { if (this->pressedEvent == event.type) { this->pressedEvent = 0; this->pressedKey = 0; this->delayed = 0.0; } } } }