Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/GUIManager.cc @ 6880

Last change on this file since 6880 was 6763, checked in by rgrieder, 15 years ago

Using our own error handler (including the debugger) for Lua code executed through CEGUILuaFunctor.

  • Property svn:eol-style set to native
File size: 15.5 KB
RevLine 
[1638]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 *      Reto Grieder
[2896]24 *      Benjamin Knecht
[1638]25 *   Co-authors:
[3196]26 *      ...
[1638]27 *
28 */
29
30#include "GUIManager.h"
31
[6746]32#include <boost/bind.hpp>
[3338]33#include <memory>
[3196]34extern "C" {
35#include <lua.h>
36}
[2710]37#include <CEGUIDefaultLogger.h>
[3196]38#include <CEGUIExceptions.h>
39#include <CEGUIInputEvent.h>
[5695]40#include <CEGUIMouseCursor.h>
[3196]41#include <CEGUIResourceProvider.h>
42#include <CEGUISystem.h>
[6417]43#include <CEGUIWindow.h>
[6746]44#include <CEGUIWindowManager.h>
[2710]45#include <ogreceguirenderer/OgreCEGUIRenderer.h>
[3196]46
[2710]47#include "SpecialConfig.h" // Configures the macro below
48#ifdef CEGUILUA_USE_INTERNAL_LIBRARY
49#   include <ceguilua/CEGUILua.h>
50#else
51#   include <CEGUILua.h>
52#endif
53
[5929]54#include "util/Clock.h"
[6417]55#include "util/Convert.h"
[3280]56#include "util/Debug.h"
[1764]57#include "util/Exception.h"
[3280]58#include "util/OrxAssert.h"
[6417]59#include "ConsoleCommand.h"
60#include "Core.h"
[6746]61#include "GraphicsManager.h"
[5695]62#include "LuaState.h"
[5929]63#include "PathConfig.h"
[5695]64#include "Resource.h"
[6746]65#include "input/InputManager.h"
66#include "input/InputState.h"
67#include "input/KeyBinderManager.h"
[1638]68
69namespace orxonox
70{
[6417]71    static void key_esc()
72        { GUIManager::getInstance().keyESC(); }
73    SetConsoleCommandShortcutExternAlias(key_esc, "keyESC");
74
[3280]75    class CEGUILogger : public CEGUI::DefaultLogger
76    {
77    public:
[5929]78        void logEvent(const CEGUI::String& message, CEGUI::LoggingLevel level = CEGUI::Standard)
[3280]79        {
[3327]80            int orxonoxLevel = CEGUI::Standard;
[3280]81            switch (level)
82            {
83                case CEGUI::Errors:      orxonoxLevel = 1; break;
84                case CEGUI::Warnings:    orxonoxLevel = 2; break;
85                case CEGUI::Standard:    orxonoxLevel = 4; break;
86                case CEGUI::Informative: orxonoxLevel = 5; break;
87                case CEGUI::Insane:      orxonoxLevel = 6; break;
88                default: OrxAssert(false, "CEGUI log level out of range, inpect immediately!");
89            }
[6105]90            OutputHandler::getOutStream(orxonoxLevel)
[3280]91                << "CEGUI: " << message << std::endl;
92
93            CEGUI::DefaultLogger::logEvent(message, level);
94        }
95    };
96
[3196]97    static CEGUI::MouseButton convertButton(MouseButtonCode::ByEnum button);
[3339]98
[3366]99    GUIManager* GUIManager::singletonPtr_s = 0;
[1646]100
[6417]101    SetConsoleCommandShortcut(GUIManager, showGUI).accessLevel(AccessLevel::User).defaultValue(1, false).defaultValue(2, true);
102    SetConsoleCommandShortcut(GUIManager, hideGUI).accessLevel(AccessLevel::User);
103
[2896]104    /**
105    @brief
[3338]106        Constructs the GUIManager by starting up CEGUI
[2896]107
108        Creates the interface to Ogre, sets up the CEGUI renderer and the Lua script module together with the Lua engine.
109        The log is set up and connected to the CEGUILogger.
110        After Lua setup tolua++-elements are linked to Lua-state to give Lua access to C++-code.
111        Finally initial Lua code is executed (maybe we can do this with the CEGUI startup script automatically).
[3338]112    @param renderWindow
113        Ogre's render window. Without this, the GUI cannot be displayed.
114    @return true if success, otherwise false
[2896]115    */
[6746]116    GUIManager::GUIManager(const std::pair<int, int>& mousePosition)
117        : resourceProvider_(NULL)
[5929]118        , camera_(NULL)
[1638]119    {
120        using namespace CEGUI;
121
[3338]122        COUT(3) << "Initialising CEGUI." << std::endl;
[1638]123
[5695]124        // Note: No SceneManager specified yet
[6746]125        guiRenderer_.reset(new OgreCEGUIRenderer(GraphicsManager::getInstance().getRenderWindow(), Ogre::RENDER_QUEUE_OVERLAY, false, 3000));
[5695]126        resourceProvider_ = guiRenderer_->createResourceProvider();
127        resourceProvider_->setDefaultResourceGroup("GUI");
[1776]128
[6749]129        // Setup scripting
[5695]130        luaState_.reset(new LuaState());
[6417]131        rootFileInfo_ = Resource::getInfo("InitialiseGUI.lua");
132        // This is necessary to ensure that input events also use the right resource info when triggering lua functions
133        luaState_->setDefaultResourceInfo(this->rootFileInfo_);
[5695]134        scriptModule_.reset(new LuaScriptModule(luaState_->getInternalLuaState()));
[6763]135        scriptModule_->setDefaultPCallErrorHandler(LuaState::ERROR_HANDLER_NAME);
[1638]136
[5695]137        // Create our own logger to specify the filepath
138        std::auto_ptr<CEGUILogger> ceguiLogger(new CEGUILogger());
[5929]139        ceguiLogger->setLogFilename(PathConfig::getLogPathString() + "cegui.log");
[5695]140        // set the log level according to ours (translate by subtracting 1)
141        ceguiLogger->setLoggingLevel(
[6105]142            static_cast<LoggingLevel>(OutputHandler::getInstance().getSoftDebugLevel("logFile") - 1));
[5695]143        this->ceguiLogger_ = ceguiLogger.release();
[2710]144
[6749]145        // Create the CEGUI system singleton
[5695]146        guiSystem_.reset(new System(guiRenderer_.get(), resourceProvider_, 0, scriptModule_.get()));
[1776]147
[5695]148        // Align CEGUI mouse with OIS mouse
[6502]149        guiSystem_->injectMousePosition((float)mousePosition.first, (float)mousePosition.second);
[5695]150
[6746]151        // Initialise the Lua framework and load the schemes
152        this->luaState_->doFile("InitialiseGUI.lua");
153
154        // Create the root nodes
155        this->rootWindow_ = CEGUI::WindowManager::getSingleton().createWindow("MenuWidgets/StaticImage", "AbsoluteRootWindow");
156        this->rootWindow_->setProperty("FrameEnabled", "False");
157        this->hudRootWindow_ = CEGUI::WindowManager::getSingleton().createWindow("DefaultWindow", "HUDRootWindow");
158        this->menuRootWindow_ = CEGUI::WindowManager::getSingleton().createWindow("DefaultWindow", "MenuRootWindow");
159        // And connect them
160        CEGUI::System::getSingleton().setGUISheet(this->rootWindow_);
161        this->rootWindow_->addChildWindow(this->hudRootWindow_);
162        this->rootWindow_->addChildWindow(this->menuRootWindow_);
163
[6749]164        // No background to start with (sets the alpha value to 0)
165        this->setBackgroundImage("");
166
[6746]167        // Set up the sheet manager in the Lua framework
168        this->luaState_->doFile("SheetManager.lua");
[3338]169    }
[1776]170
[3338]171    /**
172    @brief
[3339]173        Basically shuts down CEGUI (member smart pointers) but first unloads our Tolua modules.
[3338]174    */
175    GUIManager::~GUIManager()
176    {
[1638]177    }
178
[2896]179    /**
180    @brief
181        used to tick the GUI
182    @param time
183        clock which provides time value for the GUI System
184
185        Ticking the GUI means updating it with a certain regularity.
186        The elapsed time since the last call is given in the time value provided by the clock.
187        This time value is then used to provide a fluent animation of the GUI.
188    */
[6417]189    void GUIManager::preUpdate(const Clock& time)
[1638]190    {
[2896]191        assert(guiSystem_);
[6746]192        this->protectedCall(boost::bind(&CEGUI::System::injectTimePulse, _1, time.getDeltaTime()));
[2896]193    }
[1638]194
[2896]195    /**
196    @brief
197        Tells the GUIManager which SceneManager to use
198    @param camera
199        The current camera on which the GUI should be displayed on.
200
201        In fact the GUIManager needs the SceneManager and not the Camera to display the GUI.
202        This means the GUI is not bound to a camera but rather to the SceneManager.
[3337]203        Hiding the GUI when needed can therefore not be resolved by just NOT setting the current camera.
[2896]204    */
205    void GUIManager::setCamera(Ogre::Camera* camera)
[1638]206    {
[5929]207        this->camera_ = camera;
[2927]208        if (camera == NULL)
209            this->guiRenderer_->setTargetSceneManager(0);
210        else
211            this->guiRenderer_->setTargetSceneManager(camera->getSceneManager());
[2896]212    }
213
214    /**
215    @brief
[3338]216        Executes Lua code
217    @param str
218        reference to string object holding the Lua code which is to be executed
219    */
220    void GUIManager::executeCode(const std::string& str)
221    {
[5695]222        this->luaState_->doString(str, rootFileInfo_);
[3338]223    }
224
[6746]225    /** Loads a GUI sheet by Lua script
226    @param name
227        The name of the GUI (like the script name, but without the extension)
228    */
229    void GUIManager::loadGUI(const std::string& name)
230    {
231        this->executeCode("loadSheet(\"" + name + "\")");
232    }
233
[3338]234    /**
235    @brief
[2896]236        Displays specified GUI on screen
237    @param name
238        The name of the GUI
239
240        The function executes the Lua function with the same name in case the GUIManager is ready.
241    */
[6746]242    /*static*/ void GUIManager::showGUI(const std::string& name, bool bHidePrevious)
[2896]243    {
[6746]244        GUIManager::getInstance().executeCode("showMenuSheet(\"" + name + "\", " + multi_cast<std::string>(bHidePrevious) + ")");
[1638]245    }
246
[6417]247    /**
248    @brief
249        Hack-ish. Needed for GUIOverlay.
250    */
[6746]251    void GUIManager::showGUIExtra(const std::string& name, const std::string& ptr, bool bHidePrevious)
[6417]252    {
[6746]253        this->executeCode("showMenuSheet(\"" + name + "\", " + multi_cast<std::string>(bHidePrevious) + ", " + ptr + ")");
[6417]254    }
255
256    /**
257    @brief
258        Hides specified GUI.
259    @param name
260        The name of the GUI.
261    */
262    /*static*/ void GUIManager::hideGUI(const std::string& name)
263    {
[6746]264        GUIManager::getInstance().executeCode("hideMenuSheet(\"" + name + "\")");
[6417]265    }
266
[6746]267    const std::string& GUIManager::createInputState(const std::string& name, TriBool::Value showCursor, TriBool::Value useKeyboard, bool bBlockJoyStick)
268    {
269        InputState* state = InputManager::getInstance().createInputState(name);
270
271        /* Table that maps isFullScreen() and showCursor to mouseExclusive
272        isFullscreen / showCursor | True  | False | Dontcare
273        ----------------------------------------------------
274        true                      | True  | True  | Dontcare
275        ----------------------------------------------------
276        false                     | False | True  | Dontcare
277        */
278        if (showCursor == TriBool::Dontcare)
279            state->setMouseExclusive(TriBool::Dontcare);
280        else if (GraphicsManager::getInstance().isFullScreen() || showCursor == TriBool::False)
281            state->setMouseExclusive(TriBool::True);
282        else
283            state->setMouseExclusive(TriBool::False);
284
285        if (showCursor == TriBool::True)
286            state->setMouseHandler(this);
287        else if (showCursor == TriBool::False)
288            state->setMouseHandler(&InputHandler::EMPTY);
289
290        if (useKeyboard == TriBool::True)
291            state->setKeyHandler(this);
292        else if (useKeyboard == TriBool::False)
293            state->setKeyHandler(&InputHandler::EMPTY);
294
295        if (bBlockJoyStick)
296            state->setJoyStickHandler(&InputHandler::EMPTY);
297
298        return state->getName();
299    }
300
[6417]301    void GUIManager::keyESC()
302    {
303        this->executeCode("keyESC()");
304    }
305
[6746]306    void GUIManager::setBackgroundImage(const std::string& imageSet, const std::string imageName)
[6417]307    {
[6746]308        if (imageSet.empty() || imageName.empty())
309            this->setBackgroundImage("");
310        else
311            this->setBackgroundImage("set: " + imageSet + " image: " + imageName);
[6417]312    }
313
[6746]314    void GUIManager::setBackgroundImage(const std::string& image)
315    {
316        if (image.empty())
317            this->rootWindow_->setProperty("Alpha", "0.0");
318        else
319            this->rootWindow_->setProperty("Alpha", "1.0");
320        this->rootWindow_->setProperty("Image", image);
321    }
322
[3196]323    void GUIManager::keyPressed(const KeyEvent& evt)
324    {
[6746]325        this->protectedCall(boost::bind(&CEGUI::System::injectKeyDown, _1, evt.getKeyCode()));
326        this->protectedCall(boost::bind(&CEGUI::System::injectChar, _1, evt.getText()));
[3196]327    }
[6746]328
[3196]329    void GUIManager::keyReleased(const KeyEvent& evt)
330    {
[6746]331        this->protectedCall(boost::bind(&CEGUI::System::injectKeyUp, _1, evt.getKeyCode()));
[3196]332    }
333
[2896]334    /**
335    @brief
336        Function receiving a mouse button pressed event.
337    @param id
338        ID of the mouse button which got pressed
[1638]339
[2896]340        This function is inherited by MouseHandler and injects the event into CEGUI.
341        It is for CEGUI to process the event.
342    */
[3327]343    void GUIManager::buttonPressed(MouseButtonCode::ByEnum id)
[1638]344    {
[6746]345        this->protectedCall(boost::bind(&CEGUI::System::injectMouseButtonDown, _1, convertButton(id)));
[1638]346    }
347
[2896]348    /**
349    @brief
350        Function receiving a mouse button released event.
351    @param id
352        ID of the mouse button which got released
353
354        This function is inherited by MouseHandler and injects the event into CEGUI.
355        It is for CEGUI to process the event.
356    */
[3327]357    void GUIManager::buttonReleased(MouseButtonCode::ByEnum id)
[1638]358    {
[6746]359        this->protectedCall(boost::bind(&CEGUI::System::injectMouseButtonUp, _1, convertButton(id)));
[1638]360    }
361
[3196]362    void GUIManager::mouseMoved(IntVector2 abs, IntVector2 rel, IntVector2 clippingSize)
363    {
[6746]364        this->protectedCall(boost::bind(&CEGUI::System::injectMousePosition, _1, (float)abs.x, (float)abs.y));
[3196]365    }
[6746]366
[3196]367    void GUIManager::mouseScrolled(int abs, int rel)
368    {
[6746]369        this->protectedCall(boost::bind(&CEGUI::System::injectMouseWheelChange, _1, (float)rel));
[3196]370    }
371
[2896]372    /**
373    @brief
374        converts mouse event code to CEGUI event code
375    @param button
376        code of the mouse button as we use it in Orxonox
377    @return
378        code of the mouse button as it is used by CEGUI
[1638]379
[6105]380        Simple conversion from mouse event code in Orxonox to the one used in CEGUI.
[2896]381     */
[3196]382    static inline CEGUI::MouseButton convertButton(MouseButtonCode::ByEnum button)
[1638]383    {
384        switch (button)
385        {
[1887]386        case MouseButtonCode::Left:
[1638]387            return CEGUI::LeftButton;
388
[1887]389        case MouseButtonCode::Right:
[1638]390            return CEGUI::RightButton;
391
[1887]392        case MouseButtonCode::Middle:
[1638]393            return CEGUI::MiddleButton;
394
[1887]395        case MouseButtonCode::Button3:
[1638]396            return CEGUI::X1Button;
397
[1887]398        case MouseButtonCode::Button4:
[1638]399            return CEGUI::X2Button;
400
401        default:
402            return CEGUI::NoButton;
403        }
404    }
[6417]405
[6746]406    /** Executes a CEGUI function normally, but catches CEGUI::ScriptException.
407        When a ScriptException occurs, the error message will be displayed and
408        the program carries on.
409    @remarks
410        The exception behaviour may pose problems if the code is not written
411        exception-safe (and you can forget about that in Lua). The program might
412        be left in an undefined state. But otherwise one script error would
413        terminate the whole program...
414    @note
415        Your life gets easier if you use boost::bind to create the object/function.
416    @param function
417        Any callable object/function that takes this->guiSystem_ as its only parameter.
418    @return
419        True if input was handled, false otherwise. A caught exception yields true.
420    */
421    template <typename FunctionType>
422    bool GUIManager::protectedCall(FunctionType function)
423    {
424        try
425        {
426            return function(this->guiSystem_);
427        }
428        catch (CEGUI::ScriptException& ex)
429        {
430            // Display the error and proceed. See @remarks why this can be dangerous.
431            COUT(1) << ex.getMessage() << std::endl;
432            return true;
433        }
434    }
435
[6417]436    void GUIManager::subscribeEventHelper(CEGUI::Window* window, const std::string& event, const std::string& function)
437    {
438        window->subscribeScriptedEvent(event, function);
439    }
[1638]440}
Note: See TracBrowser for help on using the repository browser.