Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/environment/src/libraries/core/GUIManager.cc @ 8347

Last change on this file since 8347 was 8043, checked in by rgrieder, 14 years ago

Removed unnecessary changes in mac_osx branch.

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