Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/kicklib2/src/libraries/core/GUIManager.cc @ 8304

Last change on this file since 8304 was 8304, checked in by rgrieder, 13 years ago

New CEGUI (≥ v0.7) doesn't delete the Logger anymore.

  • Property svn:eol-style set to native
File size: 21.8 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 *      Reto Grieder
24 *      Benjamin Knecht
25 *   Co-authors:
26 *      ...
27 *
28 */
29
30#include "GUIManager.h"
31
32#include <memory>
33#include <boost/bind.hpp>
34#include <OgreRenderQueue.h>
35#include <OgreRenderWindow.h>
36
37#include <CEGUIDefaultLogger.h>
38#include <CEGUIExceptions.h>
39#include <CEGUIInputEvent.h>
40#include <CEGUIMouseCursor.h>
41#include <CEGUIResourceProvider.h>
42#include <CEGUISystem.h>
43#include <CEGUIWindow.h>
44#include <CEGUIWindowManager.h>
45#include <elements/CEGUIListbox.h>
46#include <elements/CEGUIListboxItem.h>
47
48#ifdef ORXONOX_OLD_CEGUI
49#  include <CEGUILua.h>
50#  include <ogreceguirenderer/OgreCEGUIRenderer.h>
51extern "C" {
52#  include <lauxlib.h>
53}
54#else
55#  include <ScriptingModules/LuaScriptModule/CEGUILua.h>
56#  include <RendererModules/Ogre/CEGUIOgreImageCodec.h>
57#  include <RendererModules/Ogre/CEGUIOgreRenderer.h>
58#  include <RendererModules/Ogre/CEGUIOgreResourceProvider.h>
59#endif
60
61#include "util/Clock.h"
62#include "util/Convert.h"
63#include "util/Debug.h"
64#include "util/Exception.h"
65#include "util/OrxAssert.h"
66#include "ConfigValueIncludes.h"
67#include "Core.h"
68#include "CoreIncludes.h"
69#include "Game.h"
70#include "GraphicsManager.h"
71#include "LuaState.h"
72#include "PathConfig.h"
73#include "Resource.h"
74#include "command/ConsoleCommand.h"
75#include "input/InputManager.h"
76#include "input/InputState.h"
77#include "input/KeyBinderManager.h"
78
79namespace orxonox
80{
81    static void key_esc()
82        { GUIManager::getInstance().keyESC(); }
83    SetConsoleCommand("keyESC", &key_esc);
84
85    class CEGUILogger : public CEGUI::DefaultLogger
86    {
87    public:
88        void logEvent(const CEGUI::String& message, CEGUI::LoggingLevel level = CEGUI::Standard)
89        {
90            int orxonoxLevel = CEGUI::Standard;
91            switch (level)
92            {
93                case CEGUI::Errors:      orxonoxLevel = 1; break;
94                case CEGUI::Warnings:    orxonoxLevel = 2; break;
95                case CEGUI::Standard:    orxonoxLevel = 4; break;
96                case CEGUI::Informative: orxonoxLevel = 5; break;
97                case CEGUI::Insane:      orxonoxLevel = 6; break;
98                default: OrxAssert(false, "CEGUI log level out of range, inspect immediately!");
99            }
100            OutputHandler::getOutStream(orxonoxLevel)
101                << "CEGUI: " << message << std::endl;
102
103            CEGUI::DefaultLogger::logEvent(message, level);
104        }
105    };
106
107#ifdef ORXONOX_OLD_CEGUI
108    /** Class with the same memory layout as CEGUI::LuaScriptModule. <br>
109        We need this to fix a problem with an uninitialised member variable
110        in CEGUI < 0.7 <br>
111        Notice that "public" modifier for the otherwise private variables.
112    */
113    class CEGUILUA_API LuaScriptModuleWorkaround : public CEGUI::ScriptModule
114    {
115    public:
116        LuaScriptModuleWorkaround();
117        ~LuaScriptModuleWorkaround();
118
119    public:
120        bool d_ownsState;
121        lua_State* d_state;
122        CEGUI::String d_errFuncName;
123        int d_errFuncIndex;
124        CEGUI::String d_activeErrFuncName;
125        int d_activeErrFuncIndex;
126    };
127#endif
128
129    static CEGUI::MouseButton convertButton(MouseButtonCode::ByEnum button);
130
131    GUIManager* GUIManager::singletonPtr_s = 0;
132    /*static*/ const std::string GUIManager::defaultScheme_ = "TaharezGreen";
133
134    SetConsoleCommand("showGUI", &GUIManager::showGUI).defaultValue(1, false).defaultValue(2, false);
135    SetConsoleCommand("hideGUI", &GUIManager::hideGUI);
136    SetConsoleCommand("toggleGUI", &GUIManager::toggleGUI).defaultValue(1, false).defaultValue(2, false);
137
138    /**
139    @brief
140        Constructs the GUIManager by starting up CEGUI
141
142        Creates the interface to Ogre, sets up the CEGUI renderer and the Lua script module together with the Lua engine.
143        The log is set up and connected to the CEGUILogger.
144        After Lua setup tolua++-elements are linked to Lua-state to give Lua access to C++-code.
145        Finally initial Lua code is executed (maybe we can do this with the CEGUI startup script automatically).
146    @return true if success, otherwise false
147    */
148    GUIManager::GUIManager(const std::pair<int, int>& mousePosition)
149        : destroyer_(*this, &GUIManager::cleanup)
150        , guiRenderer_(NULL)
151        , resourceProvider_(NULL)
152#ifndef ORXONOX_OLD_CEGUI
153        , imageCodec_(NULL)
154#endif
155        , luaState_(NULL)
156        , scriptModule_(NULL)
157        , guiSystem_(NULL)
158        , camera_(NULL)
159    {
160        RegisterRootObject(GUIManager);
161        this->setConfigValues();
162
163        using namespace CEGUI;
164
165        COUT(3) << "Initialising CEGUI." << std::endl;
166
167        // Note: No SceneManager specified yet
168#ifdef ORXONOX_OLD_CEGUI
169        guiRenderer_ = new OgreCEGUIRenderer(GraphicsManager::getInstance().getRenderWindow(), Ogre::RENDER_QUEUE_OVERLAY, false, 3000);
170        resourceProvider_ = guiRenderer_->createResourceProvider();
171#else
172        guiRenderer_ = &OgreRenderer::create(*GraphicsManager::getInstance().getRenderWindow());
173        resourceProvider_ = &OgreRenderer::createOgreResourceProvider();
174        imageCodec_ = &OgreRenderer::createOgreImageCodec();
175#endif
176        resourceProvider_->setDefaultResourceGroup("General");
177
178        // Setup scripting
179        luaState_ = new LuaState();
180        rootFileInfo_ = Resource::getInfo("InitialiseGUI.lua");
181        // This is necessary to ensure that input events also use the right resource info when triggering lua functions
182        luaState_->setDefaultResourceInfo(this->rootFileInfo_);
183#ifdef ORXONOX_OLD_CEGUI
184        scriptModule_ = new LuaScriptModule(luaState_->getInternalLuaState());
185        // Ugly workaround: older CEGUILua versions don't initialise the member
186        // d_activeErrFuncIndex at all. That leads to "error in error handling"
187        // problems when a Lua error occurs.
188        // We fix this by setting the member manually.
189        reinterpret_cast<LuaScriptModuleWorkaround*>(scriptModule_)->d_activeErrFuncIndex = LUA_NOREF;
190        luaState_->doString("ORXONOX_OLD_CEGUI = true");
191#else
192        scriptModule_ = &LuaScriptModule::create(luaState_->getInternalLuaState());
193#endif
194        scriptModule_->setDefaultPCallErrorHandler(LuaState::ERROR_HANDLER_NAME);
195
196        // Create our own logger to specify the filepath
197        std::auto_ptr<CEGUILogger> ceguiLogger(new CEGUILogger());
198        ceguiLogger->setLogFilename(PathConfig::getLogPathString() + "cegui.log");
199        // Set the log level according to ours (translate by subtracting 1)
200        ceguiLogger->setLoggingLevel(
201            static_cast<LoggingLevel>(OutputHandler::getInstance().getSoftDebugLevel("logFile") - 1));
202        this->ceguiLogger_ = ceguiLogger.release();
203
204        // Create the CEGUI system singleton
205#ifdef ORXONOX_OLD_CEGUI
206        guiSystem_ = new System(guiRenderer_, resourceProvider_, 0, scriptModule_);
207        // Add functions that have been renamed in newer versions
208        luaState_->doString("CEGUI.SchemeManager.create = CEGUI.SchemeManager.loadScheme");
209        luaState_->doString("CEGUI.Window.getUnclippedOuterRect = CEGUI.Window.getUnclippedPixelRect");
210#else
211        guiSystem_ = &System::create(*guiRenderer_, resourceProvider_, 0, imageCodec_, scriptModule_);
212#endif
213
214        // Align CEGUI mouse with OIS mouse
215        guiSystem_->injectMousePosition((float)mousePosition.first, (float)mousePosition.second);
216
217        // Initialise the Lua framework and load the schemes
218        this->luaState_->doFile("InitialiseGUI.lua");
219
220        // Create the root nodes
221        this->rootWindow_ = CEGUI::WindowManager::getSingleton().createWindow("MenuWidgets/StaticImage", "AbsoluteRootWindow");
222        this->rootWindow_->setProperty("FrameEnabled", "False");
223        this->hudRootWindow_ = CEGUI::WindowManager::getSingleton().createWindow("DefaultWindow", "HUDRootWindow");
224        this->menuRootWindow_ = CEGUI::WindowManager::getSingleton().createWindow("DefaultWindow", "MenuRootWindow");
225        // And connect them
226        CEGUI::System::getSingleton().setGUISheet(this->rootWindow_);
227        this->rootWindow_->addChildWindow(this->hudRootWindow_);
228        this->rootWindow_->addChildWindow(this->menuRootWindow_);
229
230        // No background to start with (sets the alpha value to 0)
231        this->setBackgroundImage("");
232
233        // Set up the sheet manager in the Lua framework
234        this->luaState_->doFile("SheetManager.lua");
235    }
236
237    void GUIManager::cleanup()
238    {
239        using namespace CEGUI;
240
241#ifdef ORXONOX_OLD_CEGUI
242        delete guiSystem_;
243        delete guiRenderer_;
244        delete scriptModule_;
245#else
246        System::destroy();
247        delete ceguiLogger_;
248        OgreRenderer::destroyOgreResourceProvider(*resourceProvider_);
249        OgreRenderer::destroyOgreImageCodec(*imageCodec_);
250        OgreRenderer::destroy(*guiRenderer_);
251        LuaScriptModule::destroy(*scriptModule_);
252#endif
253        delete luaState_;
254    }
255
256    void GUIManager::setConfigValues(void)
257    {
258        SetConfigValue(guiScheme_, GUIManager::defaultScheme_) .description("Changes the current GUI scheme.") .callback(this, &GUIManager::changedGUIScheme);
259    }
260
261    void GUIManager::changedGUIScheme(void)
262    {
263
264    }
265
266    /**
267    @brief
268        used to tick the GUI
269    @param time
270        clock which provides time value for the GUI System
271
272        Ticking the GUI means updating it with a certain regularity.
273        The elapsed time since the last call is given in the time value provided by the clock.
274        This time value is then used to provide a fluent animation of the GUI.
275    */
276    void GUIManager::preUpdate(const Clock& time)
277    {
278        assert(guiSystem_);
279        this->protectedCall(boost::bind(&CEGUI::System::injectTimePulse, _1, time.getDeltaTime()));
280    }
281
282    /**
283    @brief
284        Tells the GUIManager which SceneManager to use
285    @param camera
286        The current camera on which the GUI should be displayed on.
287
288        In fact the GUIManager needs the SceneManager and not the Camera to display the GUI.
289        This means the GUI is not bound to a camera but rather to the SceneManager.
290        Hiding the GUI when needed can therefore not be resolved by just NOT setting the current camera.
291    */
292    void GUIManager::setCamera(Ogre::Camera* camera)
293    {
294        this->camera_ = camera;
295#ifdef ORXONOX_OLD_CEGUI
296        if (camera == NULL)
297            this->guiRenderer_->setTargetSceneManager(0);
298        else
299            this->guiRenderer_->setTargetSceneManager(camera->getSceneManager());
300#endif
301    }
302
303    /**
304    @brief
305        Executes Lua code
306    @param str
307        reference to string object holding the Lua code which is to be executed
308    */
309    void GUIManager::executeCode(const std::string& str)
310    {
311        this->luaState_->doString(str, rootFileInfo_);
312    }
313
314    /** Loads a GUI sheet by Lua script
315    @param name
316        The name of the GUI (like the script name, but without the extension)
317    */
318    void GUIManager::loadGUI(const std::string& name)
319    {
320        this->executeCode("loadSheet(\"" + name + "\")");
321    }
322
323    /**
324    @brief
325        Displays specified GUI on screen
326    @param name
327        The name of the GUI
328    @param bHidePrevious
329        If true all displayed GUIs on the stack, that are below this GUI are hidden.
330    @param bNoInput
331        If true the GUI is transparent to input.
332
333        The function executes the Lua function with the same name in case the GUIManager is ready.
334    */
335    /*static*/ void GUIManager::showGUI(const std::string& name, bool bHidePrevious, bool bNoInput)
336    {
337        GUIManager::getInstance().executeCode("showMenuSheet(\"" + name + "\", " + multi_cast<std::string>(bHidePrevious) + ", " + multi_cast<std::string>(bNoInput) + ")");
338    }
339
340    /**
341    @brief
342        Hack-ish. Needed for GUIOverlay.
343    */
344    void GUIManager::showGUIExtra(const std::string& name, const std::string& ptr, bool bHidePrevious, bool bNoInput)
345    {
346        this->executeCode("showMenuSheet(\"" + name + "\", " + multi_cast<std::string>(bHidePrevious) + ", " + multi_cast<std::string>(bNoInput) + ", " + ptr + ")");
347    }
348
349    /**
350    @brief
351        Hides specified GUI.
352    @param name
353        The name of the GUI.
354    */
355    /*static*/ void GUIManager::hideGUI(const std::string& name)
356    {
357        GUIManager::getInstance().executeCode("hideMenuSheet(\"" + name + "\")");
358    }
359
360    /**
361    @brief
362        Toggles specified GUI.
363        If the GUI with the input name is already shown and on the top, it is hidden, else it is shown.
364    */
365    /*static*/ void GUIManager::toggleGUI(const std::string& name, bool bHidePrevious, bool bNoInput)
366    {
367        GUIManager::getInstance().executeCode("getGUIFirstActive(\"" + name + "\", " + multi_cast<std::string>(bHidePrevious) + ", " + multi_cast<std::string>(bNoInput) + ")");
368    }
369
370    /**
371    @brief
372        Helper method to toggle a specified GUI.
373        Is called by lua.
374    */
375    void GUIManager::toggleGUIHelper(const std::string& name, bool bHidePrevious, bool bNoInput, bool show)
376    {
377        if(show)
378            GUIManager::showGUI(name, bHidePrevious, bNoInput);
379        else
380            GUIManager::hideGUI(name);
381    }
382
383    const std::string& GUIManager::createInputState(const std::string& name, TriBool::Value showCursor, TriBool::Value useKeyboard, bool bBlockJoyStick)
384    {
385        InputState* state = InputManager::getInstance().createInputState(name);
386        if (!state)
387            return BLANKSTRING;
388
389        /* Table that maps isFullScreen() and showCursor to mouseExclusive
390        isFullscreen / showCursor | True  | False | Dontcare
391        ----------------------------------------------------
392        true                      | True  | True  | Dontcare
393        ----------------------------------------------------
394        false                     | False | True  | Dontcare
395        */
396
397#ifdef ORXONOX_PLATFORM_APPLE
398        // There is no non exclusive mode on OS X yet
399        state->setMouseExclusive(TriBool::True);
400#else
401        if (showCursor == TriBool::Dontcare)
402            state->setMouseExclusive(TriBool::Dontcare);
403        else if (GraphicsManager::getInstance().isFullScreen() || showCursor == TriBool::False)
404            state->setMouseExclusive(TriBool::True);
405        else
406            state->setMouseExclusive(TriBool::False);
407#endif
408
409        if (showCursor == TriBool::True)
410            state->setMouseHandler(this);
411        else if (showCursor == TriBool::False)
412            state->setMouseHandler(&InputHandler::EMPTY);
413
414        if (useKeyboard == TriBool::True)
415            state->setKeyHandler(this);
416        else if (useKeyboard == TriBool::False)
417            state->setKeyHandler(&InputHandler::EMPTY);
418
419        if (bBlockJoyStick)
420            state->setJoyStickHandler(&InputHandler::EMPTY);
421
422        return state->getName();
423    }
424
425    void GUIManager::keyESC()
426    {
427        this->executeCode("keyESC()");
428    }
429
430    void GUIManager::setBackgroundImage(const std::string& imageSet, const std::string imageName)
431    {
432        if (imageSet.empty() || imageName.empty())
433            this->setBackgroundImage("");
434        else
435            this->setBackgroundImage("set: " + imageSet + " image: " + imageName);
436    }
437
438    void GUIManager::setBackgroundImage(const std::string& image)
439    {
440        if (image.empty())
441            this->rootWindow_->setProperty("Alpha", "0.0");
442        else
443            this->rootWindow_->setProperty("Alpha", "1.0");
444        this->rootWindow_->setProperty("Image", image);
445    }
446
447    void GUIManager::buttonPressed(const KeyEvent& evt)
448    {
449        this->protectedCall(boost::bind(&CEGUI::System::injectKeyDown, _1, evt.getKeyCode()));
450        this->protectedCall(boost::bind(&CEGUI::System::injectChar, _1, evt.getText()));
451    }
452
453    void GUIManager::buttonReleased(const KeyEvent& evt)
454    {
455        this->protectedCall(boost::bind(&CEGUI::System::injectKeyUp, _1, evt.getKeyCode()));
456    }
457
458    /**
459    @brief
460        Function receiving a mouse button pressed event.
461    @param id
462        ID of the mouse button which got pressed
463
464        This function is inherited by MouseHandler and injects the event into CEGUI.
465        It is for CEGUI to process the event.
466    */
467    void GUIManager::buttonPressed(MouseButtonCode::ByEnum id)
468    {
469        this->protectedCall(boost::bind(&CEGUI::System::injectMouseButtonDown, _1, convertButton(id)));
470    }
471
472    /**
473    @brief
474        Function receiving a mouse button released event.
475    @param id
476        ID of the mouse button which got released
477
478        This function is inherited by MouseHandler and injects the event into CEGUI.
479        It is for CEGUI to process the event.
480    */
481    void GUIManager::buttonReleased(MouseButtonCode::ByEnum id)
482    {
483        this->protectedCall(boost::bind(&CEGUI::System::injectMouseButtonUp, _1, convertButton(id)));
484    }
485
486    void GUIManager::mouseMoved(IntVector2 abs, IntVector2 rel, IntVector2 clippingSize)
487    {
488        this->protectedCall(boost::bind(&CEGUI::System::injectMousePosition, _1, (float)abs.x, (float)abs.y));
489    }
490
491    void GUIManager::mouseScrolled(int abs, int rel)
492    {
493        this->protectedCall(boost::bind(&CEGUI::System::injectMouseWheelChange, _1, (float)rel));
494    }
495
496    /**
497        @brief Indicates that the mouse left the application's window.
498    */
499    void GUIManager::mouseLeft()
500    {
501        this->protectedCall(boost::bind(&CEGUI::System::injectMouseLeaves, _1));
502    }
503
504    /**
505    @brief
506        converts mouse event code to CEGUI event code
507    @param button
508        code of the mouse button as we use it in Orxonox
509    @return
510        code of the mouse button as it is used by CEGUI
511
512        Simple conversion from mouse event code in Orxonox to the one used in CEGUI.
513     */
514    static inline CEGUI::MouseButton convertButton(MouseButtonCode::ByEnum button)
515    {
516        switch (button)
517        {
518        case MouseButtonCode::Left:
519            return CEGUI::LeftButton;
520
521        case MouseButtonCode::Right:
522            return CEGUI::RightButton;
523
524        case MouseButtonCode::Middle:
525            return CEGUI::MiddleButton;
526
527        case MouseButtonCode::Button3:
528            return CEGUI::X1Button;
529
530        case MouseButtonCode::Button4:
531            return CEGUI::X2Button;
532
533        default:
534            return CEGUI::NoButton;
535        }
536    }
537
538    /** Executes a CEGUI function normally, but catches CEGUI::ScriptException.
539        When a ScriptException occurs, the error message will be displayed and
540        the program carries on.
541    @remarks
542        The exception behaviour may pose problems if the code is not written
543        exception-safe (and you can forget about that in Lua). The program might
544        be left in an undefined state. But otherwise one script error would
545        terminate the whole program...
546    @note
547        Your life gets easier if you use boost::bind to create the object/function.
548    @param function
549        Any callable object/function that takes this->guiSystem_ as its only parameter.
550    @return
551        True if input was handled, false otherwise. A caught exception yields true.
552    */
553    template <typename FunctionType>
554    bool GUIManager::protectedCall(FunctionType function)
555    {
556        try
557        {
558            return function(this->guiSystem_);
559        }
560        catch (CEGUI::ScriptException& ex)
561        {
562            // Display the error and proceed. See @remarks why this can be dangerous.
563            COUT(1) << ex.getMessage() << std::endl;
564            return true;
565        }
566    }
567
568    /**
569    @brief
570        Subscribe the input function to the input event for the input window.
571        This is a helper to be used in lua, because subscribeScriptedEvent() doesn't work in lua.
572    @param window
573        The window for which the event is subscribed.
574    @param event
575        The type of event to which we subscribe.
576    @param function
577        The function that is called when the event occurs.
578    */
579    void GUIManager::subscribeEventHelper(CEGUI::Window* window, const std::string& event, const std::string& function)
580    {
581        window->subscribeScriptedEvent(event, function);
582    }
583
584    /**
585    @brief
586        Set the input tooltip text for the input ListboxItem.
587    @param item
588        The ListboxItem for which the tooltip should be set.
589    @param tooltip
590        The tooltip text that should be set.
591    */
592    void GUIManager::setTooltipTextHelper(CEGUI::ListboxItem* item, const std::string& tooltip)
593    {
594        item->setTooltipText(tooltip);
595    }
596
597    /**
598    @brief
599        Set whether the tooltips for the input Listbox are enabled.
600    @param listbox
601        The Listbox for which to enable (or disable) tooltips.
602    @param enabled
603        Whether to enable or disable the tooltips.
604    */
605    void GUIManager::setItemTooltipsEnabledHelper(CEGUI::Listbox* listbox, bool enabled)
606    {
607        listbox->setItemTooltipsEnabled(enabled);
608    }
609
610    /**
611        @brief Callback of window event listener, called if the window is resized. Sets the display size of CEGUI.
612    */
613    void GUIManager::windowResized(unsigned int newWidth, unsigned int newHeight)
614    {
615        this->guiRenderer_->setDisplaySize(CEGUI::Size((float)newWidth, (float)newHeight));
616    }
617
618    /**
619        @brief Notify CEGUI if the windows loses the focus (stops highlighting of menu items, etc).
620    */
621    void GUIManager::windowFocusChanged(bool bFocus)
622    {
623        if (!bFocus)
624            this->mouseLeft();
625    }
626
627}
Note: See TracBrowser for help on using the repository browser.