/* 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_ #include "shell_input.h" #include "shell_command.h" #include "shell_completion.h" #include "event_handler.h" #include "debug.h" #include "list.h" #include "compiler.h" #include "stdlibincl.h" #include "key_names.h" using namespace std; SHELL_COMMAND(help, ShellInput, help) ->describe("retrieve some help about the input mode") ->setAlias("help"); /** * constructor * this also generates a ShellCompletion automatically. */ ShellInput::ShellInput () : Text ((const char*)NULL) { this->pressedKey = SDLK_FIRST; this->setClassID(CL_SHELL_INPUT, "ShellInput"); this->inputLine = new char[1]; this->inputLine[0] = '\0'; this->history = new tList; this->historyIT = this->history->getIterator(); this->setHistoryLength(50); this->historyScrolling = false; this->delayed = 0; this->setRepeatDelay(.3, .05); // subscribe all keyboard commands to ES_SEHLL EventHandler* evh = EventHandler::getInstance(); for (int i = 1; i < SDLK_LAST; i++) evh->subscribe(this, ES_SHELL, i); this->completion = new ShellCompletion(this); } /** * standard deconstructor */ ShellInput::~ShellInput () { // delete what has to be deleted here delete[] this->inputLine; delete this->completion; char* histEl = this->historyIT->firstElement(); while (histEl != NULL) { delete[] histEl; histEl = this->historyIT->nextElement(); } delete this->historyIT; delete this->history; } /** * 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; } /** * deletes the InputLine */ void ShellInput::flush() { if (likely(this->inputLine != NULL)) { delete[] this->inputLine; } this->inputLine = new char[1]; *this->inputLine = '\0'; this->setText(this->inputLine, true); } /** * sets the entire text of the InputLine to text * @param text the new Text to set as InputLine */ void ShellInput::setInputText(const char* text) { delete[] this->inputLine; if (text == NULL) { this->inputLine = new char[1]; this->inputLine[0] = '\0'; } else { this->inputLine = new char[strlen(text)+1]; strcpy(this->inputLine, text); } this->setText(this->inputLine, true); } /** * adds one character to the inputLine * @param character the character to add to the inputLine */ void ShellInput::addCharacter(char character) { if (this->historyScrolling) { delete[] this->history->lastElement(); this->history->remove(this->history->lastElement()); this->historyScrolling = false; } char* addCharLine = new char[strlen(this->inputLine)+2]; sprintf(addCharLine, "%s%c", this->inputLine, character); delete[] this->inputLine; this->inputLine = addCharLine; this->setText(this->inputLine, true); } /** * adds multiple Characters to thr inputLine * @param characters a \\0 terminated char-array to add to the InputLine */ void ShellInput::addCharacters(const char* characters) { if (this->historyScrolling) { delete[] this->history->lastElement(); this->history->remove(this->history->lastElement()); this->historyScrolling = false; } char* addCharLine = new char[strlen(this->inputLine)+strlen(characters)+1]; sprintf(addCharLine, "%s%s", this->inputLine, characters); delete[] this->inputLine; this->inputLine = addCharLine; this->setText(this->inputLine, true); } /** * 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) { delete[] this->history->lastElement(); this->history->remove(this->history->lastElement()); this->historyScrolling = false; } if (strlen(this->inputLine) == 0) return; if (characterCount > strlen(this->inputLine)) characterCount = strlen(this->inputLine); char* removeCharLine = new char[strlen(this->inputLine)-characterCount+1]; strncpy(removeCharLine, this->inputLine, strlen(this->inputLine)-characterCount); removeCharLine[strlen(this->inputLine)-characterCount] = '\0'; delete[] this->inputLine; this->inputLine = removeCharLine; this->setText(this->inputLine, true); } /** * executes the command stored in the inputLine * @return true if the command was commited successfully, false otherwise */ bool ShellInput::executeCommand() { ShellBuffer::addBufferLineStatic("Execute Command: %s\n", this->inputLine); char* newCommand = new char[strlen(this->inputLine)+1]; strcpy(newCommand, this->inputLine); // removing the eventually added Entry (from scrolling) to the List if (this->historyScrolling) { delete[] this->history->lastElement(); this->history->remove(this->history->lastElement()); this->historyScrolling = false; } // adding the new Command to the History this->history->add(newCommand); if (this->history->getSize() > this->historyLength) { delete[] this->history->firstElement(); this->history->remove(this->history->firstElement()); } ShellCommandBase::execute(this->inputLine); this->flush(); return false; } /** * moves one entry up in the history. */ void ShellInput::historyMoveUp() { if (!this->historyScrolling) { char* currentText = new char[strlen(this->inputLine)+1]; strcpy(currentText, this->inputLine); this->history->add(currentText); this->historyScrolling = true; this->historyIT->lastElement(); } char* prevElem = this->historyIT->prevStep(); if (prevElem == NULL) return; else { this->flush(); this->setInputText(prevElem); } } /** * moves one entry down in the history */ void ShellInput::historyMoveDown() { if (!this->historyScrolling) return; char* nextElem = this->historyIT->nextStep(); if (nextElem == NULL) return; else { this->flush(); this->setInputText(nextElem); } } /** * prints out some nice help about the Shell */ void ShellInput::help(const char* className, const char* functionName) { printf("%s::%s\n", className, functionName); if (strlen(className) == 0) { 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'"); } else if (strlen (className) > 0 && strlen (functionName) == 0) { ShellCommandClass::help(className); //PRINTF(1)("%s::%s\n", className, functionName); } } /** * ticks the ShellInput * @param dt the time passed since the last update */ void ShellInput::tick(float dt) { if (this->delayed > 0.0) this->delayed -= dt; else if (this->pressedKey != SDLK_FIRST ) { this->delayed = this->repeatRate; if (this->pressedKey == SDLK_BACKSPACE) this->removeCharacters(1); else if (pressedKey < 127) this->addCharacter(this->pressedKey); } } /** * 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)); if (event.type == SDLK_F1) this->help(); else if (event.type == SDLK_F2) ;//this->debug(); else if (event.type == SDLK_UP) this->historyMoveUp(); else if (event.type == SDLK_DOWN) this->historyMoveDown(); else if (event.type == SDLK_TAB) this->completion->autoComplete(); else if (event.type == SDLK_BACKSPACE) { this->delayed = this->repeatDelay; this->pressedKey = SDLK_BACKSPACE; this->removeCharacters(1); } else if (event.type == SDLK_RETURN) this->executeCommand(); // any other keyboard key else if (likely(event.type < 127)) { Uint8 *keystate = SDL_GetKeyState(NULL); this->delayed = this->repeatDelay; if (unlikely( keystate[SDLK_LSHIFT] || keystate[SDLK_RSHIFT] )) { this->pressedKey = event.type-32; this->addCharacter(event.type-32); } else { this->pressedKey = event.type; this->addCharacter(event.type); } } } else // if(!event.bPressed) { if (this->pressedKey == event.type || (this->pressedKey == event.type - 32)) { this->pressedKey = SDLK_FIRST; this->delayed = 0.0; } } }