#include "console.hpp" #include #include #include #include "../mwworld/esmstore.hpp" #include "../mwscript/extensions.hpp" #include "../mwbase/environment.hpp" namespace MWGui { class ConsoleInterpreterContext : public MWScript::InterpreterContext { Console& mConsole; public: ConsoleInterpreterContext (Console& console, MWWorld::Ptr reference); virtual void report (const std::string& message); }; ConsoleInterpreterContext::ConsoleInterpreterContext (Console& console, MWWorld::Ptr reference) : MWScript::InterpreterContext ( reference.isEmpty() ? 0 : &reference.getRefData().getLocals(), reference), mConsole (console) {} void ConsoleInterpreterContext::report (const std::string& message) { mConsole.printOK (message); } bool Console::compile (const std::string& cmd, Compiler::Output& output) { try { ErrorHandler::reset(); std::istringstream input (cmd + '\n'); Compiler::Scanner scanner (*this, input, mCompilerContext.getExtensions()); Compiler::LineParser parser (*this, mCompilerContext, output.getLocals(), output.getLiterals(), output.getCode(), true); scanner.scan (parser); return isGood(); } catch (const Compiler::SourceException&) { // error has already been reported via error handler } catch (const std::exception& error) { printError (std::string ("An exception has been thrown: ") + error.what()); } return false; } void Console::report (const std::string& message, const Compiler::TokenLoc& loc, Type type) { std::ostringstream error; error << "column " << loc.mColumn << " (" << loc.mLiteral << "):"; printError (error.str()); printError ((type==ErrorMessage ? "error: " : "warning: ") + message); } void Console::report (const std::string& message, Type type) { printError ((type==ErrorMessage ? "error: " : "warning: ") + message); } void Console::listNames() { if (mNames.empty()) { // keywords std::istringstream input (""); Compiler::Scanner scanner (*this, input, mCompilerContext.getExtensions()); scanner.listKeywords (mNames); // identifier const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); for (MWWorld::ESMStore::iterator it = store.begin(); it != store.end(); ++it) { it->second->listIdentifier (mNames); } // sort std::sort (mNames.begin(), mNames.end()); } } Console::Console(int w, int h, bool consoleOnlyScripts) : Layout("openmw_console.layout"), mCompilerContext (MWScript::CompilerContext::Type_Console), mConsoleOnlyScripts (consoleOnlyScripts) { setCoord(10,10, w-10, h/2); getWidget(command, "edit_Command"); getWidget(history, "list_History"); // Set up the command line box command->eventEditSelectAccept += newDelegate(this, &Console::acceptCommand); command->eventKeyButtonPressed += newDelegate(this, &Console::keyPress); // Set up the log window history->setOverflowToTheLeft(true); history->setEditStatic(true); history->setVisibleVScroll(true); // compiler MWScript::registerExtensions (mExtensions, mConsoleOnlyScripts); mCompilerContext.setExtensions (&mExtensions); } void Console::enable() { setVisible(true); // Give keyboard focus to the combo box whenever the console is // turned on MyGUI::InputManager::getInstance().setKeyFocusWidget(command); } void Console::disable() { setVisible(false); setSelectedObject(MWWorld::Ptr()); // Remove keyboard focus from the console input whenever the // console is turned off MyGUI::InputManager::getInstance().setKeyFocusWidget(NULL); } void Console::setFont(const std::string &fntName) { history->setFontName(fntName); command->setFontName(fntName); } void Console::clearHistory() { history->setCaption(""); } void Console::print(const std::string &msg) { history->addText(msg); } void Console::printOK(const std::string &msg) { print("#FF00FF" + msg + "\n"); } void Console::printError(const std::string &msg) { print("#FF2222" + msg + "\n"); } void Console::execute (const std::string& command) { // Log the command print("#FFFFFF> " + command + "\n"); Compiler::Locals locals; Compiler::Output output (locals); if (compile (command + "\n", output)) { try { ConsoleInterpreterContext interpreterContext (*this, mPtr); Interpreter::Interpreter interpreter; MWScript::installOpcodes (interpreter, mConsoleOnlyScripts); std::vector code; output.getCode (code); interpreter.run (&code[0], code.size(), interpreterContext); } catch (const std::exception& error) { printError (std::string ("An exception has been thrown: ") + error.what()); } } } void Console::executeFile (const std::string& path) { std::ifstream stream (path.c_str()); if (!stream.is_open()) printError ("failed to open file: " + path); else { std::string line; while (std::getline (stream, line)) execute (line); } } void Console::keyPress(MyGUI::WidgetPtr _sender, MyGUI::KeyCode key, MyGUI::Char _char) { if( key == MyGUI::KeyCode::Tab) { std::vector matches; listNames(); command->setCaption(complete( command->getCaption(), matches )); #if 0 int i = 0; for(std::vector::iterator it=matches.begin(); it < matches.end(); it++,i++ ) { printOK( *it ); if( i == 50 ) break; } #endif } if(command_history.empty()) return; // Traverse history with up and down arrows if(key == MyGUI::KeyCode::ArrowUp) { // If the user was editing a string, store it for later if(current == command_history.end()) editString = command->getCaption(); if(current != command_history.begin()) { current--; command->setCaption(*current); } } else if(key == MyGUI::KeyCode::ArrowDown) { if(current != command_history.end()) { current++; if(current != command_history.end()) command->setCaption(*current); else // Restore the edit string command->setCaption(editString); } } } void Console::acceptCommand(MyGUI::EditPtr _sender) { const std::string &cm = command->getCaption(); if(cm.empty()) return; // Add the command to the history, and set the current pointer to // the end of the list command_history.push_back(cm); current = command_history.end(); editString.clear(); execute (cm); command->setCaption(""); } std::string Console::complete( std::string input, std::vector &matches ) { using namespace std; string output=input; string tmp=input; bool has_front_quote = false; /* Does the input string contain things that don't have to be completed? If yes erase them. */ /* Are there quotation marks? */ if( tmp.find('"') != string::npos ) { int numquotes=0; for(string::iterator it=tmp.begin(); it < tmp.end(); ++it) { if( *it == '"' ) numquotes++; } /* Is it terminated?*/ if( numquotes % 2 ) { tmp.erase( 0, tmp.rfind('"')+1 ); has_front_quote = true; } else { size_t pos; if( ( ((pos = tmp.rfind(' ')) != string::npos ) ) && ( pos > tmp.rfind('"') ) ) { tmp.erase( 0, tmp.rfind(' ')+1); } else { tmp.clear(); } has_front_quote = false; } } /* No quotation marks. Are there spaces?*/ else { size_t rpos; if( (rpos=tmp.rfind(' ')) != string::npos ) { if( rpos == 0 ) { tmp.clear(); } else { tmp.erase(0, rpos+1); } } } /* Erase the input from the output string so we can easily append the completed form later. */ output.erase(output.end()-tmp.length(), output.end()); /* Is there still something in the input string? If not just display all commands and return the unchanged input. */ if( tmp.length() == 0 ) { matches=mNames; return input; } /* Iterate through the vector. */ for(vector::iterator it=mNames.begin(); it < mNames.end();++it) { bool string_different=false; /* Is the string shorter than the input string? If yes skip it. */ if( (*it).length() < tmp.length() ) continue; /* Is the beginning of the string different from the input string? If yes skip it. */ for( string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();iter++, iter2++) { if( tolower(*iter) != tolower(*iter2) ) { string_different=true; break; } } if( string_different ) continue; /* The beginning of the string matches the input string, save it for the next test. */ matches.push_back(*it); } /* There are no matches. Return the unchanged input. */ if( matches.empty() ) { return input; } /* Only one match. We're done. */ if( matches.size() == 1 ) { /* Adding quotation marks when the input string started with a quotation mark or has spaces in it*/ if( ( matches.front().find(' ') != string::npos ) ) { if( !has_front_quote ) output.append(string("\"")); return output.append(matches.front() + string("\" ")); } else if( has_front_quote ) { return output.append(matches.front() + string("\" ")); } else { return output.append(matches.front() + string(" ")); } } /* Check if all matching strings match further than input. If yes complete to this match. */ int i = tmp.length(); for(string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); iter++, i++) { for(vector::iterator it=matches.begin(); it < matches.end();++it) { if( tolower((*it)[i]) != tolower(*iter) ) { /* Append the longest match to the end of the output string*/ output.append(matches.front().substr( 0, i)); return output; } } } /* All keywords match with the shortest. Append it to the output string and return it. */ return output.append(matches.front()); } void Console::onResChange(int width, int height) { setCoord(10,10, width-10, height/2); } void Console::setSelectedObject(const MWWorld::Ptr& object) { mPtr = object; if (!mPtr.isEmpty()) setTitle("#{sConsoleTitle} (" + mPtr.getCellRef().mRefID + ")"); else setTitle("#{sConsoleTitle}"); MyGUI::InputManager::getInstance().setKeyFocusWidget(command); } void Console::onReferenceUnavailable() { setSelectedObject(MWWorld::Ptr()); } }