2
Copyright (C) 2004 Miguel Guzman (Aganor)
4
This program is free software; you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation; either version 2 of the License, or
7
(at your option) any later version.
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU General Public License for more details.
14
You should have received a copy of the GNU General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
#include "GUIManager.h"
23
#include "services/EmberServices.h"
24
#include "services/config/ConfigService.h"
25
#include "services/scripting/ScriptingService.h"
27
#include <CEGUIWindowManager.h>
28
#include <CEGUISchemeManager.h>
29
#include <CEGUIExceptions.h>
30
#include <CEGUIFactoryModule.h>
31
#include <elements/CEGUIPushButton.h>
32
#include <elements/CEGUIGUISheet.h>
33
#include <elements/CEGUIMultiLineEditbox.h>
34
#include <elements/CEGUIEditbox.h>
37
#include "widgets/Widget.h"
38
#include "MousePicker.h"
40
#include "AvatarCamera.h"
41
#include "EmberOgre.h"
42
#include "services/input/Input.h"
43
#include "gui/ActiveWidgetHandler.h"
44
#include "gui/CEGUILogger.h"
46
#include "widgets/WidgetDefinitions.h"
48
#include "EmberEntity.h"
49
#include "EmberPhysicalEntity.h"
50
#include "AvatarEmberEntity.h"
52
#include "framework/IScriptingProvider.h"
54
#include "components/ogre/scripting/LuaScriptingProvider.h"
56
#include "GUICEGUIAdapter.h"
58
#include "widgets/icons/IconManager.h"
59
#include "widgets/EntityIconManager.h"
61
#include "services/input/Input.h"
69
#include "EntityWorldPickListener.h"
71
template<> EmberOgre::GUIManager* Ember::Singleton<EmberOgre::GUIManager>::ms_Singleton = 0;
73
using namespace CEGUI;
74
using namespace EmberOgre::Gui;
75
using namespace Ember;
79
unsigned long GUIManager::msAutoGenId(0);
82
GUIManager::GUIManager(Ogre::RenderWindow* window, Ogre::SceneManager* sceneMgr)
83
: ToggleInputMode("toggle_inputmode", this, "Toggle the input mode.")
84
, ReloadGui("reloadgui", this, "Reloads the gui.")
85
, ToggleGui("toggle_gui", this, "Toggle the gui display")
86
, mGuiCommandMapper("gui", "key_bindings_gui")
88
, mEntityWorldPickListener(0)
97
, mActiveWidgetHandler(0)
98
, mCEGUILogger(new Gui::CEGUILogger()) ///by creating an instance here we'll indirectly tell CEGUI to use this one instead of trying to create one itself
100
mGuiCommandMapper.restrictToInputMode(Input::IM_GUI );
102
///we need this here just to force the linker to also link in the WidgetDefinitions
107
S_LOG_INFO("Starting CEGUI");
108
mDefaultScheme = "EmberLook";
109
S_LOG_VERBOSE("Setting default scheme to "<< mDefaultScheme);
111
Ember::ConfigService* configSrv = Ember::EmberServices::getSingletonPtr()->getConfigService();
112
if (chdir(configSrv->getEmberDataDirectory().c_str())) {
113
S_LOG_WARNING("Failed to change to the data directory. Gui loading might fail.");
116
///The OgreCEGUIRenderer is the main interface between Ogre and CEGUI. Note that the third argument tells the renderer to render the gui after all of the regular render queues have been processed, thus making sure that the gui always is on top.
117
mGuiRenderer = new CEGUI::OgreCEGUIRenderer(window, Ogre::RENDER_QUEUE_OVERLAY, true, 0, sceneMgr);
118
CEGUI::ResourceProvider* resourceProvider = mGuiRenderer->createResourceProvider();
119
resourceProvider->setDefaultResourceGroup("Gui");
121
Ember::IScriptingProvider* provider = Ember::EmberServices::getSingleton().getScriptingService()->getProviderFor("LuaScriptingProvider");
123
LuaScriptingProvider* luaScriptProvider = static_cast<LuaScriptingProvider*>(provider);
124
mLuaScriptModule = new LuaScriptModule(luaScriptProvider->getLuaState());
125
if (luaScriptProvider->getErrorHandlingFunctionName().size() != 0) {
126
mLuaScriptModule->setDefaultPCallErrorHandler(luaScriptProvider->getErrorHandlingFunctionName());
127
mLuaScriptModule->executeString(""); ///We must call this to make CEGUI set up the error function internally. If we don't, CEGUI will never correctly set it up. The reason for this is that we never use the execute* methods in the CEGUI lua module later on, instead loading our scripts ourselves. And CEGUI is currently set up to require the execute* methods to be called in order for the error function to be registered.
129
mGuiSystem = new CEGUI::System(mGuiRenderer, resourceProvider, 0, mLuaScriptModule, "cegui/datafiles/configs/cegui.config");
131
Ember::EmberServices::getSingleton().getScriptingService()->EventStopping.connect(sigc::mem_fun(*this, &GUIManager::scriptingServiceStopping));
133
mGuiSystem = new CEGUI::System(mGuiRenderer, resourceProvider, 0, 0, "cegui/datafiles/configs/cegui.config");
135
CEGUI::SchemeManager::SchemeIterator schemeI(SchemeManager::getSingleton().getIterator());
136
if (schemeI.isAtEnd()) {
137
// S_LOG_FAILURE("Could not load any CEGUI schemes. This means that there's something wromg with how CEGUI is setup. Check the CEGUI log for more detail. We'll now exit Ember.");
138
throw Ember::Exception("Could not load any CEGUI schemes. This means that there's something wrong with how CEGUI is setup. Check the CEGUI log for more detail. We'll now exit Ember.");
141
mWindowManager = &CEGUI::WindowManager::getSingleton();
145
mGuiSystem->setDefaultMouseCursor(getDefaultScheme(), "MouseArrow");
146
} catch (const CEGUI::Exception& ex) {
147
S_LOG_FAILURE("CEGUI - could not set mouse pointer. Make sure that the correct scheme " << getDefaultScheme() << " is available. Message: " << ex.getMessage().c_str());
148
throw Ember::Exception(ex.getMessage().c_str());
152
mSheet = mWindowManager->createWindow((CEGUI::utf8*)"DefaultGUISheet", (CEGUI::utf8*)"root_wnd");
153
mGuiSystem->setGUISheet(mSheet);
155
mSheet->moveToBack();
156
mSheet->setDistributesCapturedInputs(false);
158
BIND_CEGUI_EVENT(mSheet, CEGUI::ButtonBase::EventMouseButtonDown, GUIManager::mSheet_MouseButtonDown);
159
BIND_CEGUI_EVENT(mSheet, CEGUI::Window::EventInputCaptureLost, GUIManager::mSheet_CaptureLost);
160
BIND_CEGUI_EVENT(mSheet, CEGUI::ButtonBase::EventMouseDoubleClick, GUIManager::mSheet_MouseDoubleClick);
162
///set a default tool tip
163
CEGUI::System::getSingleton().setDefaultTooltip(getDefaultScheme() + "/Tooltip");
165
S_LOG_INFO("CEGUI system set up");
167
mPicker = new MousePicker();
169
///create a new entity world pick listener which listens for event
170
///TODO: should this really be here?
171
mEntityWorldPickListener = new EntityWorldPickListener();
173
///don't connect it yet since there's no AvatarCamera yet, wait until that's created
174
EmberOgre::getSingleton().EventAvatarControllerCreated.connect(sigc::mem_fun(*this, &GUIManager::EmberOgre_AvatarControllerCreated));
176
getInput().EventKeyPressed.connect(sigc::mem_fun(*this, &GUIManager::pressedKey));
177
getInput().setInputMode(Input::IM_GUI);
179
///add adapter for CEGUI, this will route input event to the gui
180
mCEGUIAdapter = new GUICEGUIAdapter(mGuiSystem, mGuiRenderer);
181
getInput().addAdapter(mCEGUIAdapter);
183
mGuiCommandMapper.bindToInput(getInput());
185
///connect to the creation of the avatar, since we want to switch to movement mode when that happens
186
EmberOgre::getSingleton().EventCreatedAvatarEntity.connect(sigc::mem_fun(*this, &GUIManager::EmberOgre_CreatedAvatarEntity));
188
mActiveWidgetHandler = new Gui::ActiveWidgetHandler(*this);
190
Ogre::Root::getSingleton().addFrameListener(this);
193
} catch (const CEGUI::Exception& ex) {
194
S_LOG_FAILURE("GUIManager - error when creating gui. Message: " << ex.getMessage().c_str());
195
throw Ember::Exception(ex.getMessage().c_str());
202
GUIManager::~GUIManager()
204
S_LOG_INFO("Shutting down GUI manager.");
206
WidgetStore widgetStoreCopy(mWidgets);
207
for (WidgetStore::iterator I = widgetStoreCopy.begin(); I != widgetStoreCopy.end(); ++I) {
208
S_LOG_INFO("Deleting widget " << (*I)->getPrefix() << ".");
212
delete mActiveWidgetHandler;
213
delete mEntityIconManager;
217
///note that we normally would delete the mCEGUILogger here, but we don't have to since mGuiSystem will do that in it's desctructor, even though it doesn't own the logger
218
Ogre::Root::getSingleton().removeFrameListener(this);
219
delete mCEGUIAdapter;
221
delete mEntityWorldPickListener;
224
delete mLuaScriptModule;
225
//delete mMousePicker;
228
WidgetLoader::removeAllWidgetFactories();
232
void GUIManager::initialize()
234
Ember::ConfigService* configSrv = Ember::EmberServices::getSingletonPtr()->getConfigService();
235
chdir(configSrv->getEmberDataDirectory().c_str());
237
mDebugText = (CEGUI::GUISheet*)mWindowManager->createWindow("DefaultGUISheet", (CEGUI::utf8*)"DebugText");
238
mSheet->addChildWindow(mDebugText);
239
mDebugText->setMaxSize(CEGUI::UVector2(UDim(1.0f, 0), UDim(0, 25)));
240
mDebugText->setPosition(CEGUI::UVector2(UDim(0.0f, 0), UDim(1.0f, -25)));
241
mDebugText->setSize(CEGUI::UVector2(UDim(1.0f, 0), UDim(0, 25)));
243
/* mDebugText->setFrameEnabled(false);
244
mDebugText->setBackgroundEnabled(false);*/
245
//stxt->setHorizontalFormatting(StaticText::WordWrapCentred);
248
//the console and quit widgets are not lua scripts, and should be loaded explicit
249
// mConsoleWidget = static_cast<ConsoleWidget*>(createWidget("ConsoleWidget"));
250
// if (!mConsoleWidget) {
251
// throw Ember::Exception("Could not create console widget.");
253
createWidget("Quit");
254
} catch (const std::exception& e) {
255
S_LOG_FAILURE("GUIManager - error when initializing widgets: " << e.what());
257
} catch (const CEGUI::Exception& e) {
258
S_LOG_FAILURE("GUIManager - error when initializing widgets: " << e.getMessage().c_str());
262
mIconManager = new Gui::Icons::IconManager();
263
} catch (const std::exception& e) {
264
S_LOG_FAILURE("GUIManager - error when creating icon manager: " << e.what());
265
} catch (const CEGUI::Exception& e) {
266
S_LOG_FAILURE("GUIManager - error when creating icon manager: " << e.getMessage().c_str());
270
mEntityIconManager = new Gui::EntityIconManager(*this);
271
} catch (const std::exception& e) {
272
S_LOG_FAILURE("GUIManager - error when creating entity icon manager: " << e.what());
273
} catch (const CEGUI::Exception& e) {
274
S_LOG_FAILURE("GUIManager - error when creating entity icon manager: " << e.getMessage().c_str());
278
std::vector<std::string> widgetsToLoad;
279
widgetsToLoad.push_back("IngameChatWidget");
280
// widgetsToLoad.push_back("InventoryWidget");
281
widgetsToLoad.push_back("InspectWidget");
282
widgetsToLoad.push_back("MakeEntityWidget");
283
widgetsToLoad.push_back("JesusEdit");
284
widgetsToLoad.push_back("ServerWidget");
285
widgetsToLoad.push_back("Help");
286
widgetsToLoad.push_back("MeshPreview");
288
///this should be defined in some kind of text file, which should be different depending on what game you're playing (like mason)
290
///load the bootstrap script which will load all other scripts
291
Ember::EmberServices::getSingleton().getScriptingService()->loadScript("lua/Bootstrap.lua");
292
} catch (const std::exception& e) {
293
S_LOG_FAILURE("Error when loading bootstrap script. Error message: " << e.what());
294
} catch (const CEGUI::Exception& e) {
295
S_LOG_FAILURE("Error when loading bootstrap script. Error message: " << e.getMessage().c_str());
298
for (std::vector<std::string>::iterator I = widgetsToLoad.begin(); I != widgetsToLoad.end(); ++I) {
300
S_LOG_VERBOSE("Loading widget " << *I);
302
} catch (const std::exception& e) {
303
S_LOG_FAILURE("Error when initializing widget " << *I << " : " << e.what());
304
} catch (const CEGUI::Exception& e) {
305
S_LOG_FAILURE("Error when initializing widget " << *I << " : " << e.getMessage().c_str());
311
void GUIManager::scriptingServiceStopping()
313
mGuiSystem->setScriptingModule(0);
314
delete mLuaScriptModule;
315
mLuaScriptModule = 0;
318
void GUIManager::EmitEntityAction(const std::string& action, EmberEntity* entity)
320
EventEntityAction.emit(action, entity);
324
CEGUI::Window* GUIManager::createWindow(const std::string& windowType)
326
std::stringstream ss;
327
ss << "_autoWindow_" << (msAutoGenId++);
328
return createWindow(windowType, ss.str());
331
CEGUI::Window* GUIManager::createWindow(const std::string& windowType, const std::string& windowName)
334
CEGUI::Window* window = mWindowManager->createWindow(windowType, windowName);
336
} catch (const CEGUI::Exception& ex) {
337
S_LOG_FAILURE("Error when creating new window of type " << windowType << " with name " << windowName << ".\n" << ex.getMessage().c_str());
339
} catch (const std::exception& ex) {
340
S_LOG_FAILURE("Error when creating new window of type " << windowType << " with name " << windowName << ".\n" << ex.what());
345
Widget* GUIManager::createWidget()
347
return createWidget("Widget");
350
Widget* GUIManager::createWidget(const std::string& name)
355
widget = WidgetLoader::createWidget(name);
357
S_LOG_FAILURE( "Could not find widget with name " << name );
361
widget->buildWidget();
363
S_LOG_INFO( "Successfully loaded widget " << name );
364
} catch (const std::exception& e) {
365
S_LOG_FAILURE( "Error when loading widget " << name << ": " << e.what());
367
} catch (const CEGUI::Exception& e) {
368
S_LOG_FAILURE( "Error when loading widget " << name << ": " << e.getMessage().c_str());
374
void GUIManager::destroyWidget(Widget* widget)
378
S_LOG_WARNING("Trying to destroy null widget.");
381
removeWidget(widget);
386
void GUIManager::setDebugText(const std::string& text)
390
mDebugText->setText(text);
394
Input& GUIManager::getInput() const
396
return Input::getSingleton();
400
CEGUI::Window* GUIManager::getMainSheet() const
405
void GUIManager::removeWidget(Widget* widget)
407
WidgetStore::iterator I = std::find(mWidgets.begin(), mWidgets.end(), widget);
408
if (I != mWidgets.end()) {
413
void GUIManager::addWidget(Widget* widget)
415
mWidgets.push_back(widget);
423
bool GUIManager::frameStarted(const Ogre::FrameEvent& evt)
426
CEGUI::System::getSingleton().injectTimePulse(evt.timeSinceLastFrame);
427
} catch (const CEGUI::Exception& ex) {
428
S_LOG_WARNING("Error in CEGUI: " << ex.getMessage().c_str());
430
// if (mPreviousInputMode == IM_GUI) {
431
// if (!mInput->getInputMode()) {
432
// EventInputModeChanged.emit(IM_MOVEMENT);
433
// mPreviousInputMode = IM_MOVEMENT;
436
// if (mInput->isInGUIMode()) {
437
// EventInputModeChanged.emit(IM_GUI);
438
// mPreviousInputMode = IM_GUI;
444
//iterate over all widgets and send them a frameStarted event
445
WidgetStore::iterator I = mWidgets.begin();
446
WidgetStore::iterator I_end = mWidgets.end();
448
for (; I != I_end; ++I) {
449
Widget* aWidget = *I;
451
aWidget->frameStarted(evt);
452
} catch (const CEGUI::Exception& ex) {
453
S_LOG_WARNING("Error in CEGUI: " << ex.getMessage().c_str());
457
EventFrameStarted.emit(evt.timeSinceLastFrame);
464
bool GUIManager::mSheet_MouseButtonDown(const CEGUI::EventArgs& args)
467
const CEGUI::MouseEventArgs& mouseArgs = static_cast<const CEGUI::MouseEventArgs&>(args);
468
S_LOG_VERBOSE("Main sheet is capturing input");
469
CEGUI::Window* aWindow = CEGUI::Window::getCaptureWindow();
471
aWindow->releaseInput();
472
aWindow->deactivate();
474
//mSheet->activate();
475
//mSheet->captureInput();
478
const CEGUI::Point& position = CEGUI::MouseCursor::getSingleton().getDisplayIndependantPosition();
479
MousePickerArgs pickerArgs;
480
pickerArgs.windowX = mouseArgs.position.d_x;
481
pickerArgs.windowY = mouseArgs.position.d_y;
482
pickerArgs.pickType = MPT_CLICK;
483
mPicker->doMousePicking(position.d_x, position.d_y, pickerArgs);
491
bool GUIManager::mSheet_MouseDoubleClick(const CEGUI::EventArgs& args)
494
const CEGUI::MouseEventArgs& mouseArgs = static_cast<const CEGUI::MouseEventArgs&>(args);
495
S_LOG_VERBOSE("Main sheet double click.");
496
CEGUI::Window* aWindow = CEGUI::Window::getCaptureWindow();
498
aWindow->releaseInput();
499
aWindow->deactivate();
501
//mSheet->activate();
502
//mSheet->captureInput();
505
const CEGUI::Point& position = CEGUI::MouseCursor::getSingleton().getDisplayIndependantPosition();
506
MousePickerArgs pickerArgs;
507
pickerArgs.windowX = mouseArgs.position.d_x;
508
pickerArgs.windowY = mouseArgs.position.d_y;
509
pickerArgs.pickType = MPT_DOUBLECLICK;
510
mPicker->doMousePicking(position.d_x, position.d_y, pickerArgs);
517
bool GUIManager::mSheet_CaptureLost(const CEGUI::EventArgs& args)
519
S_LOG_VERBOSE("Main sheet lost input");
523
const bool GUIManager::isInMovementKeysMode() const {
524
return mSheet->isCapturedByThis() || !isInGUIMode();
527
const bool GUIManager::isInGUIMode() const {
528
return getInput().getInputMode() == Input::IM_GUI;
531
void GUIManager::pressedKey(const SDL_keysym& key, Input::InputMode inputMode)
533
if ((key.mod & KMOD_CTRL || key.mod & KMOD_LCTRL || key.mod & KMOD_RCTRL) && (key.sym == SDLK_c || key.sym == SDLK_x)) {
535
bool cut = (key.sym == SDLK_x);
536
CEGUI::Window* active = mSheet->getActiveChild();
539
CEGUI::String seltext;
540
const CEGUI::String& type = active->getType();
542
if (type.find("/MultiLineEditbox") != CEGUI::String::npos) {
543
CEGUI::MultiLineEditbox* edit = static_cast<CEGUI::MultiLineEditbox*>(active);
544
CEGUI::String::size_type beg = edit->getSelectionStartIndex();
545
CEGUI::String::size_type len = edit->getSelectionLength();
546
seltext = edit->getText().substr( beg, len ).c_str();
548
// are we cutting or just copying?
550
if (edit->isReadOnly()) return;
551
CEGUI::String newtext = edit->getText();
552
edit->setText( newtext.erase( beg, len ) );
555
} else if (type.find("/Editbox") != CEGUI::String::npos) {
556
CEGUI::Editbox* edit = static_cast<CEGUI::Editbox*>(active);
557
CEGUI::String::size_type beg = edit->getSelectionStartIndex();
558
CEGUI::String::size_type len = edit->getSelectionLength();
559
seltext = edit->getText().substr( beg, len ).c_str();
561
// are we cutting or just copying?
563
if (edit->isReadOnly()) return;
564
CEGUI::String newtext = edit->getText();
565
edit->setText( newtext.erase( beg, len ) );
568
getInput().writeToClipboard( seltext.c_str() );
574
void GUIManager::runCommand(const std::string &command, const std::string &args)
576
if (command == ToggleInputMode.getCommand()) {
577
getInput().toggleInputMode();
578
} else if (command == ToggleGui.getCommand()) {
580
S_LOG_VERBOSE("Toggle Gui Initiated -- " << getInput().getInputMode() );
582
if (mWindow->getViewport(0)->getOverlaysEnabled()) {
583
// disable overlays so gui disappears
584
S_LOG_INFO("Disabling GUI");
585
mWindow->getViewport(0)->setOverlaysEnabled(false);
587
getInput().removeAdapter(mCEGUIAdapter);
592
S_LOG_INFO("Enabling GUI");
593
mWindow->getViewport(0)->setOverlaysEnabled(true);
595
getInput().addAdapter(mCEGUIAdapter);
597
} else if (command == ReloadGui.getCommand()) {
598
Ogre::TextureManager* texMgr = Ogre::TextureManager::getSingletonPtr();
599
Ogre::ResourcePtr resource = texMgr->getByName("cegui/" + getDefaultScheme() + ".png");
600
if (!resource.isNull()) {
606
// void GUIManager::pushMousePicker( MousePicker * mousePicker )
608
// mMousePickers.push(mousePicker);
611
// MousePicker * GUIManager::popMousePicker()
613
// ///only pop if there's more than one registered picker
614
// if (mMousePickers.size() > 1)
615
// mMousePickers.pop();
616
// return mMousePickers.top();
619
void GUIManager::EmberOgre_CreatedAvatarEntity(AvatarEmberEntity* entity)
621
///switch to movement mode, since it appears most people don't know how to change from gui mode
622
getInput().setInputMode(Input::IM_MOVEMENT);
625
void GUIManager::EmberOgre_AvatarControllerCreated(AvatarController& controller)
627
EmberOgre::getSingleton().getMainCamera()->pushWorldPickListener(mEntityWorldPickListener);
630
const std::string& GUIManager::getLayoutDir() const
632
static std::string dir("cegui/datafiles/layouts/");
636
const std::string& GUIManager::getDefaultScheme() const
638
return mDefaultScheme;
641
EntityWorldPickListener* GUIManager::getEntityPickListener() const
643
return mEntityWorldPickListener;
646
Gui::Icons::IconManager* GUIManager::getIconManager()
651
Gui::EntityIconManager* GUIManager::getEntityIconManager()
653
return mEntityIconManager;