Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Fixed build error arisen by the merging.

  • Property svn:eol-style set to native
File size: 21.7 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        OgreRenderer::destroyOgreResourceProvider(*resourceProvider_);
248        OgreRenderer::destroyOgreImageCodec(*imageCodec_);
249        OgreRenderer::destroy(*guiRenderer_);
250        LuaScriptModule::destroy(*scriptModule_);
251#endif
252        delete luaState_;
253    }
254
255    void GUIManager::setConfigValues(void)
256    {
257        SetConfigValue(guiScheme_, GUIManager::defaultScheme_) .description("Changes the current GUI scheme.") .callback(this, &GUIManager::changedGUIScheme);
258    }
259
260    void GUIManager::changedGUIScheme(void)
261    {
262
263    }
264
265    /**
266    @brief
267        used to tick the GUI
268    @param time
269        clock which provides time value for the GUI System
270
271        Ticking the GUI means updating it with a certain regularity.
272        The elapsed time since the last call is given in the time value provided by the clock.
273        This time value is then used to provide a fluent animation of the GUI.
274    */
275    void GUIManager::preUpdate(const Clock& time)
276    {
277        assert(guiSystem_);
278        this->protectedCall(boost::bind(&CEGUI::System::injectTimePulse, _1, time.getDeltaTime()));
279    }
280
281    /**
282    @brief
283        Tells the GUIManager which SceneManager to use
284    @param camera
285        The current camera on which the GUI should be displayed on.
286
287        In fact the GUIManager needs the SceneManager and not the Camera to display the GUI.
288        This means the GUI is not bound to a camera but rather to the SceneManager.
289        Hiding the GUI when needed can therefore not be resolved by just NOT setting the current camera.
290    */
291    void GUIManager::setCamera(Ogre::Camera* camera)
292    {
293        this->camera_ = camera;
294#ifdef ORXONOX_OLD_CEGUI
295        if (camera == NULL)
296            this->guiRenderer_->setTargetSceneManager(0);
297        else
298            this->guiRenderer_->setTargetSceneManager(camera->getSceneManager());
299#endif
300    }
301
302    /**
303    @brief
304        Executes Lua code
305    @param str
306        reference to string object holding the Lua code which is to be executed
307    */
308    void GUIManager::executeCode(const std::string& str)
309    {
310        this->luaState_->doString(str, rootFileInfo_);
311    }
312
313    /** Loads a GUI sheet by Lua script
314    @param name
315        The name of the GUI (like the script name, but without the extension)
316    */
317    void GUIManager::loadGUI(const std::string& name)
318    {
319        this->executeCode("loadSheet(\"" + name + "\")");
320    }
321
322    /**
323    @brief
324        Displays specified GUI on screen
325    @param name
326        The name of the GUI
327    @param bHidePrevious
328        If true all displayed GUIs on the stack, that are below this GUI are hidden.
329    @param bNoInput
330        If true the GUI is transparent to input.
331
332        The function executes the Lua function with the same name in case the GUIManager is ready.
333    */
334    /*static*/ void GUIManager::showGUI(const std::string& name, bool bHidePrevious, bool bNoInput)
335    {
336        GUIManager::getInstance().executeCode("showMenuSheet(\"" + name + "\", " + multi_cast<std::string>(bHidePrevious) + ", " + multi_cast<std::string>(bNoInput) + ")");
337    }
338
339    /**
340    @brief
341        Hack-ish. Needed for GUIOverlay.
342    */
343    void GUIManager::showGUIExtra(const std::string& name, const std::string& ptr, bool bHidePrevious, bool bNoInput)
344    {
345        this->executeCode("showMenuSheet(\"" + name + "\", " + multi_cast<std::string>(bHidePrevious) + ", " + multi_cast<std::string>(bNoInput) + ", " + ptr + ")");
346    }
347
348    /**
349    @brief
350        Hides specified GUI.
351    @param name
352        The name of the GUI.
353    */
354    /*static*/ void GUIManager::hideGUI(const std::string& name)
355    {
356        GUIManager::getInstance().executeCode("hideMenuSheet(\"" + name + "\")");
357    }
358
359    /**
360    @brief
361        Toggles specified GUI.
362        If the GUI with the input name is already shown and on the top, it is hidden, else it is shown.
363    */
364    /*static*/ void GUIManager::toggleGUI(const std::string& name, bool bHidePrevious, bool bNoInput)
365    {
366        GUIManager::getInstance().executeCode("getGUIFirstActive(\"" + name + "\", " + multi_cast<std::string>(bHidePrevious) + ", " + multi_cast<std::string>(bNoInput) + ")");
367    }
368
369    /**
370    @brief
371        Helper method to toggle a specified GUI.
372        Is called by lua.
373    */
374    void GUIManager::toggleGUIHelper(const std::string& name, bool bHidePrevious, bool bNoInput, bool show)
375    {
376        if(show)
377            GUIManager::showGUI(name, bHidePrevious, bNoInput);
378        else
379            GUIManager::hideGUI(name);
380    }
381
382    const std::string& GUIManager::createInputState(const std::string& name, TriBool::Value showCursor, TriBool::Value useKeyboard, bool bBlockJoyStick)
383    {
384        InputState* state = InputManager::getInstance().createInputState(name);
385        if (!state)
386            return BLANKSTRING;
387
388        /* Table that maps isFullScreen() and showCursor to mouseExclusive
389        isFullscreen / showCursor | True  | False | Dontcare
390        ----------------------------------------------------
391        true                      | True  | True  | Dontcare
392        ----------------------------------------------------
393        false                     | False | True  | Dontcare
394        */
395
396#ifdef ORXONOX_PLATFORM_APPLE
397        // There is no non exclusive mode on OS X yet
398        state->setMouseExclusive(TriBool::True);
399#else
400        if (showCursor == TriBool::Dontcare)
401            state->setMouseExclusive(TriBool::Dontcare);
402        else if (GraphicsManager::getInstance().isFullScreen() || showCursor == TriBool::False)
403            state->setMouseExclusive(TriBool::True);
404        else
405            state->setMouseExclusive(TriBool::False);
406#endif
407
408        if (showCursor == TriBool::True)
409            state->setMouseHandler(this);
410        else if (showCursor == TriBool::False)
411            state->setMouseHandler(&InputHandler::EMPTY);
412
413        if (useKeyboard == TriBool::True)
414            state->setKeyHandler(this);
415        else if (useKeyboard == TriBool::False)
416            state->setKeyHandler(&InputHandler::EMPTY);
417
418        if (bBlockJoyStick)
419            state->setJoyStickHandler(&InputHandler::EMPTY);
420
421        return state->getName();
422    }
423
424    void GUIManager::keyESC()
425    {
426        this->executeCode("keyESC()");
427    }
428
429    void GUIManager::setBackgroundImage(const std::string& imageSet, const std::string imageName)
430    {
431        if (imageSet.empty() || imageName.empty())
432            this->setBackgroundImage("");
433        else
434            this->setBackgroundImage("set: " + imageSet + " image: " + imageName);
435    }
436
437    void GUIManager::setBackgroundImage(const std::string& image)
438    {
439        if (image.empty())
440            this->rootWindow_->setProperty("Alpha", "0.0");
441        else
442            this->rootWindow_->setProperty("Alpha", "1.0");
443        this->rootWindow_->setProperty("Image", image);
444    }
445
446    void GUIManager::buttonPressed(const KeyEvent& evt)
447    {
448        this->protectedCall(boost::bind(&CEGUI::System::injectKeyDown, _1, evt.getKeyCode()));
449        this->protectedCall(boost::bind(&CEGUI::System::injectChar, _1, evt.getText()));
450    }
451
452    void GUIManager::buttonReleased(const KeyEvent& evt)
453    {
454        this->protectedCall(boost::bind(&CEGUI::System::injectKeyUp, _1, evt.getKeyCode()));
455    }
456
457    /**
458    @brief
459        Function receiving a mouse button pressed event.
460    @param id
461        ID of the mouse button which got pressed
462
463        This function is inherited by MouseHandler and injects the event into CEGUI.
464        It is for CEGUI to process the event.
465    */
466    void GUIManager::buttonPressed(MouseButtonCode::ByEnum id)
467    {
468        this->protectedCall(boost::bind(&CEGUI::System::injectMouseButtonDown, _1, convertButton(id)));
469    }
470
471    /**
472    @brief
473        Function receiving a mouse button released event.
474    @param id
475        ID of the mouse button which got released
476
477        This function is inherited by MouseHandler and injects the event into CEGUI.
478        It is for CEGUI to process the event.
479    */
480    void GUIManager::buttonReleased(MouseButtonCode::ByEnum id)
481    {
482        this->protectedCall(boost::bind(&CEGUI::System::injectMouseButtonUp, _1, convertButton(id)));
483    }
484
485    void GUIManager::mouseMoved(IntVector2 abs, IntVector2 rel, IntVector2 clippingSize)
486    {
487        this->protectedCall(boost::bind(&CEGUI::System::injectMousePosition, _1, (float)abs.x, (float)abs.y));
488    }
489
490    void GUIManager::mouseScrolled(int abs, int rel)
491    {
492        this->protectedCall(boost::bind(&CEGUI::System::injectMouseWheelChange, _1, (float)rel));
493    }
494
495    /**
496        @brief Indicates that the mouse left the application's window.
497    */
498    void GUIManager::mouseLeft()
499    {
500        this->protectedCall(boost::bind(&CEGUI::System::injectMouseLeaves, _1));
501    }
502
503    /**
504    @brief
505        converts mouse event code to CEGUI event code
506    @param button
507        code of the mouse button as we use it in Orxonox
508    @return
509        code of the mouse button as it is used by CEGUI
510
511        Simple conversion from mouse event code in Orxonox to the one used in CEGUI.
512     */
513    static inline CEGUI::MouseButton convertButton(MouseButtonCode::ByEnum button)
514    {
515        switch (button)
516        {
517        case MouseButtonCode::Left:
518            return CEGUI::LeftButton;
519
520        case MouseButtonCode::Right:
521            return CEGUI::RightButton;
522
523        case MouseButtonCode::Middle:
524            return CEGUI::MiddleButton;
525
526        case MouseButtonCode::Button3:
527            return CEGUI::X1Button;
528
529        case MouseButtonCode::Button4:
530            return CEGUI::X2Button;
531
532        default:
533            return CEGUI::NoButton;
534        }
535    }
536
537    /** Executes a CEGUI function normally, but catches CEGUI::ScriptException.
538        When a ScriptException occurs, the error message will be displayed and
539        the program carries on.
540    @remarks
541        The exception behaviour may pose problems if the code is not written
542        exception-safe (and you can forget about that in Lua). The program might
543        be left in an undefined state. But otherwise one script error would
544        terminate the whole program...
545    @note
546        Your life gets easier if you use boost::bind to create the object/function.
547    @param function
548        Any callable object/function that takes this->guiSystem_ as its only parameter.
549    @return
550        True if input was handled, false otherwise. A caught exception yields true.
551    */
552    template <typename FunctionType>
553    bool GUIManager::protectedCall(FunctionType function)
554    {
555        try
556        {
557            return function(this->guiSystem_);
558        }
559        catch (CEGUI::ScriptException& ex)
560        {
561            // Display the error and proceed. See @remarks why this can be dangerous.
562            COUT(1) << ex.getMessage() << std::endl;
563            return true;
564        }
565    }
566
567    /**
568    @brief
569        Subscribe the input function to the input event for the input window.
570        This is a helper to be used in lua, because subscribeScriptedEvent() doesn't work in lua.
571    @param window
572        The window for which the event is subscribed.
573    @param event
574        The type of event to which we subscribe.
575    @param function
576        The function that is called when the event occurs.
577    */
578    void GUIManager::subscribeEventHelper(CEGUI::Window* window, const std::string& event, const std::string& function)
579    {
580        window->subscribeScriptedEvent(event, function);
581    }
582
583    /**
584    @brief
585        Set the input tooltip text for the input ListboxItem.
586    @param item
587        The ListboxItem for which the tooltip should be set.
588    @param tooltip
589        The tooltip text that should be set.
590    */
591    void GUIManager::setTooltipTextHelper(CEGUI::ListboxItem* item, const std::string& tooltip)
592    {
593        item->setTooltipText(tooltip);
594    }
595
596    /**
597    @brief
598        Set whether the tooltips for the input Listbox are enabled.
599    @param listbox
600        The Listbox for which to enable (or disable) tooltips.
601    @param enabled
602        Whether to enable or disable the tooltips.
603    */
604    void GUIManager::setItemTooltipsEnabledHelper(CEGUI::Listbox* listbox, bool enabled)
605    {
606        listbox->setItemTooltipsEnabled(enabled);
607    }
608
609    /**
610        @brief Callback of window event listener, called if the window is resized. Sets the display size of CEGUI.
611    */
612    void GUIManager::windowResized(unsigned int newWidth, unsigned int newHeight)
613    {
614        this->guiRenderer_->setDisplaySize(CEGUI::Size((float)newWidth, (float)newHeight));
615    }
616
617    /**
618        @brief Notify CEGUI if the windows loses the focus (stops highlighting of menu items, etc).
619    */
620    void GUIManager::windowFocusChanged(bool bFocus)
621    {
622        if (!bFocus)
623            this->mouseLeft();
624    }
625
626}
Note: See TracBrowser for help on using the repository browser.