From dcf1e6645623b7dda09b658c65dfb8984f01d502 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Aug 2012 17:26:01 +0200 Subject: [PATCH 01/27] fix main menu placement after resolution changes --- apps/openmw/mwgui/mainmenu.cpp | 10 +++++++++- apps/openmw/mwgui/mainmenu.hpp | 2 ++ apps/openmw/mwgui/window_manager.cpp | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index e2fefd6497..7618ecb157 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -13,11 +13,20 @@ namespace MWGui MainMenu::MainMenu(int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") + , mButtonBox(0) + { + onResChange(w,h); + } + + void MainMenu::onResChange(int w, int h) { setCoord(0,0,w,h); int height = 64 * 3; + if (mButtonBox) + MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); + mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(w/2 - 64, h/2 - height/2, 128, height), MyGUI::Align::Default); int curH = 0; @@ -57,7 +66,6 @@ namespace MWGui mExitGame->setImageResource ("Menu_ExitGame"); mExitGame->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::exitGame); curH += 64; - } void MainMenu::returnToGame(MyGUI::Widget* sender) diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 5fa2f69430..fd583d1876 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -8,6 +8,8 @@ namespace MWGui public: MainMenu(int w, int h); + void onResChange(int w, int h); + private: MyGUI::Button* mReturn; MyGUI::Button* mNewGame; diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 659af04470..db7600da9f 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -639,6 +639,7 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector int y = Settings::Manager::getInt("resolution y", "Video"); mHud->onResChange(x, y); mConsole->onResChange(x, y); + mMenu->onResChange(x, y); mSettingsWindow->center(); mAlchemyWindow->center(); mScrollWindow->center(); From b68f9d6a2861ab9fe8e9a3185e64e83086d16070 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 11 Aug 2012 17:30:55 +0200 Subject: [PATCH 02/27] Issue #107: MechanicsManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 7 +- apps/openmw/mwbase/environment.hpp | 14 ++-- apps/openmw/mwbase/mechanicsmanager.hpp | 77 +++++++++++++++++++ apps/openmw/mwclass/creature.cpp | 4 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 4 +- apps/openmw/mwgui/charactercreation.cpp | 1 + apps/openmw/mwgui/charactercreation.hpp | 1 - apps/openmw/mwgui/stats_window.cpp | 3 +- apps/openmw/mwgui/window_manager.cpp | 2 +- ...icsmanager.cpp => mechanicsmanagerimp.cpp} | 2 +- ...icsmanager.hpp => mechanicsmanagerimp.hpp} | 35 ++++----- apps/openmw/mwworld/scene.cpp | 5 +- apps/openmw/mwworld/worldimp.cpp | 9 +-- 16 files changed, 120 insertions(+), 52 deletions(-) create mode 100644 apps/openmw/mwbase/mechanicsmanager.hpp rename apps/openmw/mwmechanics/{mechanicsmanager.cpp => mechanicsmanagerimp.cpp} (99%) rename apps/openmw/mwmechanics/{mechanicsmanager.hpp => mechanicsmanagerimp.hpp} (60%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 172c6a4948..f4454a216c 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -60,12 +60,12 @@ add_openmw_dir (mwclass ) add_openmw_dir (mwmechanics - mechanicsmanager stat creaturestats magiceffects movement actors drawstate spells + mechanicsmanagerimp stat creaturestats magiceffects movement actors drawstate spells activespells npcstats ) add_openmw_dir (mwbase - environment world scriptmanager dialoguemanager journal soundmanager + environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2467f91d13..98fee43841 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -32,7 +32,7 @@ #include "mwdialogue/dialoguemanagerimp.hpp" #include "mwdialogue/journalimp.hpp" -#include "mwmechanics/mechanicsmanager.hpp" +#include "mwmechanics/mechanicsmanagerimp.hpp" void OMW::Engine::executeLocalScripts() diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 7d109b000c..f75c3588fc 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -5,13 +5,12 @@ #include "../mwinput/inputmanager.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" - #include "world.hpp" #include "scriptmanager.hpp" #include "dialoguemanager.hpp" #include "journal.hpp" #include "soundmanager.hpp" +#include "mechanicsmanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -49,7 +48,7 @@ void MWBase::Environment::setWindowManager (MWGui::WindowManager *windowManager) mWindowManager = windowManager; } -void MWBase::Environment::setMechanicsManager (MWMechanics::MechanicsManager *mechanicsManager) +void MWBase::Environment::setMechanicsManager (MechanicsManager *mechanicsManager) { mMechanicsManager = mechanicsManager; } @@ -98,7 +97,7 @@ MWGui::WindowManager *MWBase::Environment::getWindowManager() const return mWindowManager; } -MWMechanics::MechanicsManager *MWBase::Environment::getMechanicsManager() const +MWBase::MechanicsManager *MWBase::Environment::getMechanicsManager() const { assert (mMechanicsManager); return mMechanicsManager; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index d03267c25f..f2e7249d3e 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -6,11 +6,6 @@ namespace MWGui class WindowManager; } -namespace MWMechanics -{ - class MechanicsManager; -} - namespace MWInput { struct MWInputManager; @@ -23,6 +18,7 @@ namespace MWBase class DialogueManager; class Journal; class SoundManager; + class MechanicsManager; /// \brief Central hub for mw-subsystems /// @@ -38,7 +34,7 @@ namespace MWBase SoundManager *mSoundManager; ScriptManager *mScriptManager; MWGui::WindowManager *mWindowManager; - MWMechanics::MechanicsManager *mMechanicsManager; + MechanicsManager *mMechanicsManager; DialogueManager *mDialogueManager; Journal *mJournal; MWInput::MWInputManager *mInputManager; @@ -64,7 +60,7 @@ namespace MWBase void setWindowManager (MWGui::WindowManager *windowManager); - void setMechanicsManager (MWMechanics::MechanicsManager *mechanicsManager); + void setMechanicsManager (MechanicsManager *mechanicsManager); void setDialogueManager (DialogueManager *dialogueManager); @@ -79,11 +75,11 @@ namespace MWBase SoundManager *getSoundManager() const; - MWBase::ScriptManager *getScriptManager() const; + ScriptManager *getScriptManager() const; MWGui::WindowManager *getWindowManager() const; - MWMechanics::MechanicsManager *getMechanicsManager() const; + MechanicsManager *getMechanicsManager() const; DialogueManager *getDialogueManager() const; diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp new file mode 100644 index 0000000000..c5f1847af1 --- /dev/null +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -0,0 +1,77 @@ +#ifndef GAME_MWBASE_MECHANICSMANAGER_H +#define GAME_MWBASE_MECHANICSMANAGER_H + +#include +#include + +namespace Ogre +{ + class Vector3; +} + +namespace ESM +{ + struct Class; +} + +namespace MWWorld +{ + class Ptr; + class CellStore; +} + +namespace MWBase +{ + /// \brief Interface for game mechanics manager (implemented in MWMechanics) + class MechanicsManager + { + MechanicsManager (const MechanicsManager&); + ///< not implemented + + MechanicsManager& operator= (const MechanicsManager&); + ///< not implemented + + public: + + MechanicsManager() {} + + virtual ~MechanicsManager() {} + + virtual void addActor (const MWWorld::Ptr& ptr) = 0; + ///< Register an actor for stats management + + virtual void removeActor (const MWWorld::Ptr& ptr) = 0; + ///< Deregister an actor for stats management + + virtual void dropActors (const MWWorld::CellStore *cellStore) = 0; + ///< Deregister all actors in the given cell. + + virtual void watchActor (const MWWorld::Ptr& ptr) = 0; + ///< On each update look for changes in a previously registered actor and update the + /// GUI accordingly. + + virtual void update (std::vector >& movement, + float duration, bool paused) = 0; + ///< Update actor stats and store desired velocity vectors in \a movement + /// + /// \param paused In game type does not currently advance (this usually means some GUI + /// component is up). + + virtual void setPlayerName (const std::string& name) = 0; + ///< Set player name. + + virtual void setPlayerRace (const std::string& id, bool male) = 0; + ///< Set player race. + + virtual void setPlayerBirthsign (const std::string& id) = 0; + ///< Set player birthsign. + + virtual void setPlayerClass (const std::string& id) = 0; + ///< Set player class to stock class. + + virtual void setPlayerClass (const ESM::Class& class_) = 0; + ///< Set player class to custom class. + }; +} + +#endif diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 0f3141f5c6..57301e1a25 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -4,10 +4,10 @@ #include #include "../mwmechanics/creaturestats.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwmechanics/magiceffects.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -104,7 +104,7 @@ namespace MWClass if (!model.empty()) { return "meshes\\" + model; } - return ""; + return ""; } std::string Creature::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 81c0c85f55..30173701ec 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -11,11 +11,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/movement.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 985d255735..e3e9fd7529 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWDIALOG_DIALOGUEMANAGERIMP_H #define GAME_MWDIALOG_DIALOGUEMANAGERIMP_H +#include "../mwbase/dialoguemanager.hpp" + #include #include @@ -8,8 +10,6 @@ #include "../mwscript/interpretercontext.hpp" #include -#include "../mwbase/dialoguemanager.hpp" - #include "../mwworld/ptr.hpp" #include diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index c0081544b0..fd3a7872e3 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -10,6 +10,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" namespace { diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index e9c90877e6..1fd67bff7b 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -7,7 +7,6 @@ #include "../mwbase/world.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwmechanics/stat.hpp" namespace MWGui diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 190d195945..3f5162bfba 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -8,11 +8,12 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" +#include "../mwmechanics/npcstats.hpp" #include "window_manager.hpp" #include "tooltips.hpp" diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 659af04470..45aee089cd 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -21,10 +21,10 @@ #include "alchemywindow.hpp" #include "spellwindow.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwinput/inputmanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/cellstore.hpp" diff --git a/apps/openmw/mwmechanics/mechanicsmanager.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp similarity index 99% rename from apps/openmw/mwmechanics/mechanicsmanager.cpp rename to apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index fe5485d615..5ad67dde46 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1,5 +1,5 @@ -#include "mechanicsmanager.hpp" +#include "mechanicsmanagerimp.hpp" #include diff --git a/apps/openmw/mwmechanics/mechanicsmanager.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp similarity index 60% rename from apps/openmw/mwmechanics/mechanicsmanager.hpp rename to apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 97bb369fda..d5fd3b6f2f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -1,8 +1,7 @@ -#ifndef GAME_MWMECHANICS_MECHANICSMANAGER_H -#define GAME_MWMECHANICS_MECHANICSMANAGER_H +#ifndef GAME_MWMECHANICS_MECHANICSMANAGERIMP_H +#define GAME_MWMECHANICS_MECHANICSMANAGERIMP_H -#include -#include +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" @@ -22,7 +21,7 @@ namespace MWWorld namespace MWMechanics { - class MechanicsManager + class MechanicsManager : public MWBase::MechanicsManager { MWWorld::Ptr mWatched; CreatureStats mWatchedCreature; @@ -38,43 +37,41 @@ namespace MWMechanics public: - MechanicsManager (); + MechanicsManager(); - void configureGUI(); - - void addActor (const MWWorld::Ptr& ptr); + virtual void addActor (const MWWorld::Ptr& ptr); ///< Register an actor for stats management - void removeActor (const MWWorld::Ptr& ptr); + virtual void removeActor (const MWWorld::Ptr& ptr); ///< Deregister an actor for stats management - void dropActors (const MWWorld::CellStore *cellStore); + virtual void dropActors (const MWWorld::CellStore *cellStore); ///< Deregister all actors in the given cell. - void watchActor (const MWWorld::Ptr& ptr); + virtual void watchActor (const MWWorld::Ptr& ptr); ///< On each update look for changes in a previously registered actor and update the /// GUI accordingly. - void update (std::vector >& movement, float duration, - bool paused); + virtual void update (std::vector >& movement, + float duration, bool paused); ///< Update actor stats and store desired velocity vectors in \a movement /// /// \param paused In game type does not currently advance (this usually means some GUI /// component is up). - void setPlayerName (const std::string& name); + virtual void setPlayerName (const std::string& name); ///< Set player name. - void setPlayerRace (const std::string& id, bool male); + virtual void setPlayerRace (const std::string& id, bool male); ///< Set player race. - void setPlayerBirthsign (const std::string& id); + virtual void setPlayerBirthsign (const std::string& id); ///< Set player birthsign. - void setPlayerClass (const std::string& id); + virtual void setPlayerClass (const std::string& id); ///< Set player class to stock class. - void setPlayerClass (const ESM::Class& class_); + virtual void setPlayerClass (const ESM::Class& class_); ///< Set player class to custom class. }; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index ec557e35c7..9318fd6f13 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -3,8 +3,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" /// FIXME #include "../mwbase/soundmanager.hpp" - -#include "../mwmechanics/mechanicsmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwgui/window_manager.hpp" @@ -154,7 +153,7 @@ namespace MWWorld } world->getPlayer().setCell(cell); - MWMechanics::MechanicsManager *mechMgr = + MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); mechMgr->addActor(player); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d9fbc5b77d..3a9547a445 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -5,12 +5,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwrender/sky.hpp" #include "../mwrender/player.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" - #include "../mwgui/window_manager.hpp" #include "player.hpp" @@ -575,7 +574,7 @@ namespace MWWorld mRendering->moveObjectToCell(copy, vec, currCell); if (MWWorld::Class::get(ptr).isActor()) { - MWMechanics::MechanicsManager *mechMgr = + MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); mechMgr->removeActor(ptr); @@ -604,11 +603,11 @@ namespace MWWorld if (cell->isExterior()) { int cellX, cellY; positionToIndex(x, y, cellX, cellY); - + cell = getExterior(cellX, cellY); } moveObject(ptr, *cell, x, y, z); - + return cell != ptr.getCell(); } From 0231533d05eaf715031cdfc155c1e2045aa6c630 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 11 Aug 2012 17:53:39 +0200 Subject: [PATCH 03/27] Issue #107: InputManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 3 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 7 ++-- apps/openmw/mwbase/environment.hpp | 12 ++---- apps/openmw/mwbase/inputmanager.hpp | 37 +++++++++++++++++++ apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 1 - apps/openmw/mwgui/bookwindow.cpp | 2 - apps/openmw/mwgui/container.cpp | 2 - apps/openmw/mwgui/scrollwindow.cpp | 2 - apps/openmw/mwgui/settingswindow.cpp | 3 +- apps/openmw/mwgui/window_manager.cpp | 3 +- .../{inputmanager.cpp => inputmanagerimp.cpp} | 6 +-- .../{inputmanager.hpp => inputmanagerimp.hpp} | 20 +++++----- apps/openmw/mwrender/renderingmanager.cpp | 2 +- apps/openmw/mwscript/controlextensions.cpp | 10 +---- apps/openmw/mwscript/guiextensions.cpp | 1 - apps/openmw/mwscript/interpretercontext.cpp | 2 - 17 files changed, 65 insertions(+), 50 deletions(-) create mode 100644 apps/openmw/mwbase/inputmanager.hpp rename apps/openmw/mwinput/{inputmanager.cpp => inputmanagerimp.cpp} (98%) rename apps/openmw/mwinput/{inputmanager.hpp => inputmanagerimp.hpp} (62%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index f4454a216c..7bd7f1882e 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -20,7 +20,7 @@ add_openmw_dir (mwrender ) add_openmw_dir (mwinput - inputmanager + inputmanagerimp mouselookevent ) @@ -66,6 +66,7 @@ add_openmw_dir (mwmechanics add_openmw_dir (mwbase environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager + inputmanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 98fee43841..c484c22ea2 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -13,7 +13,7 @@ #include #include -#include "mwinput/inputmanager.hpp" +#include "mwinput/inputmanagerimp.hpp" #include "mwgui/window_manager.hpp" #include "mwgui/cursorreplace.hpp" diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index f75c3588fc..28e71fcf0c 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -3,14 +3,13 @@ #include -#include "../mwinput/inputmanager.hpp" - #include "world.hpp" #include "scriptmanager.hpp" #include "dialoguemanager.hpp" #include "journal.hpp" #include "soundmanager.hpp" #include "mechanicsmanager.hpp" +#include "inputmanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -63,7 +62,7 @@ void MWBase::Environment::setJournal (Journal *journal) mJournal = journal; } -void MWBase::Environment::setInputManager (MWInput::MWInputManager *inputManager) +void MWBase::Environment::setInputManager (InputManager *inputManager) { mInputManager = inputManager; } @@ -115,7 +114,7 @@ MWBase::Journal *MWBase::Environment::getJournal() const return mJournal; } -MWInput::MWInputManager *MWBase::Environment::getInputManager() const +MWBase::InputManager *MWBase::Environment::getInputManager() const { assert (mInputManager); return mInputManager; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index f2e7249d3e..4a21b57a4e 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -6,11 +6,6 @@ namespace MWGui class WindowManager; } -namespace MWInput -{ - struct MWInputManager; -} - namespace MWBase { class World; @@ -19,6 +14,7 @@ namespace MWBase class Journal; class SoundManager; class MechanicsManager; + class InputManager; /// \brief Central hub for mw-subsystems /// @@ -37,7 +33,7 @@ namespace MWBase MechanicsManager *mMechanicsManager; DialogueManager *mDialogueManager; Journal *mJournal; - MWInput::MWInputManager *mInputManager; + InputManager *mInputManager; float mFrameDuration; Environment (const Environment&); @@ -66,7 +62,7 @@ namespace MWBase void setJournal (Journal *journal); - void setInputManager (MWInput::MWInputManager *inputManager); + void setInputManager (InputManager *inputManager); void setFrameDuration (float duration); ///< Set length of current frame in seconds. @@ -85,7 +81,7 @@ namespace MWBase Journal *getJournal() const; - MWInput::MWInputManager *getInputManager() const; + InputManager *getInputManager() const; float getFrameDuration() const; diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp new file mode 100644 index 0000000000..d865bfb0ec --- /dev/null +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -0,0 +1,37 @@ +#ifndef GAME_MWBASE_INPUTMANAGER_H +#define GAME_MWBASE_INPUTMANAGER_H + +#include + +#include + +namespace MWBase +{ + /// \brief Interface for input manager (implemented in MWInput) + class InputManager + { + InputManager (const InputManager&); + ///< not implemented + + InputManager& operator= (const InputManager&); + ///< not implemented + + public: + + InputManager() {} + + virtual ~InputManager() {} + + virtual void update() = 0; + + virtual void changeInputMode(bool guiMode) = 0; + + virtual void processChangedSettings(const Settings::CategorySettingVector& changed) = 0; + + virtual void setDragDrop(bool dragDrop) = 0; + + virtual void toggleControlSwitch (const std::string& sw, bool value) = 0; + }; +} + +#endif diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 644da49e27..251577d3bf 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -19,7 +19,6 @@ #include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" -#include "../mwinput/inputmanager.hpp" #include "../mwgui/dialogue.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 19a5e93985..92f0226ed1 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -6,8 +6,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwinput/inputmanager.hpp" - #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 66dea08497..89cd102339 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -19,8 +19,6 @@ #include "../mwclass/container.hpp" -#include "../mwinput/inputmanager.hpp" - #include "window_manager.hpp" #include "widgets.hpp" #include "countdialog.hpp" diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 67a02e53b4..fb2239c6da 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -4,8 +4,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwinput/inputmanager.hpp" - #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index d1e1f7095e..06177aaa8d 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -13,11 +13,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwrender/renderingmanager.hpp" -#include "../mwinput/inputmanager.hpp" - #include "window_manager.hpp" #include "confirmationdialog.hpp" diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 45aee089cd..b4eaaacf49 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -21,10 +21,9 @@ #include "alchemywindow.hpp" #include "spellwindow.hpp" -#include "../mwinput/inputmanager.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/cellstore.hpp" diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp similarity index 98% rename from apps/openmw/mwinput/inputmanager.cpp rename to apps/openmw/mwinput/inputmanagerimp.cpp index e29e3c2683..d53e67d485 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1,4 +1,4 @@ -#include "inputmanager.hpp" +#include "inputmanagerimp.hpp" #include @@ -501,8 +501,8 @@ private: impl->adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video")); } - void MWInputManager::toggleControlSwitch(std::string sw, bool value) + void MWInputManager::toggleControlSwitch (const std::string& sw, bool value) { - impl->toggleControlSwitch(sw, value); + impl->toggleControlSwitch(sw, value); } } diff --git a/apps/openmw/mwinput/inputmanager.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp similarity index 62% rename from apps/openmw/mwinput/inputmanager.hpp rename to apps/openmw/mwinput/inputmanagerimp.hpp index 2486f82d67..862c4fd207 100644 --- a/apps/openmw/mwinput/inputmanager.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -1,10 +1,12 @@ -#ifndef _MWINPUT_MWINPUTMANAGER_H -#define _MWINPUT_MWINPUTMANAGER_H +#ifndef _MWINPUT_MWINPUTMANAGERIMP_H +#define _MWINPUT_MWINPUTMANAGERIMP_H #include "../mwgui/mode.hpp" #include +#include "../mwbase/inputmanager.hpp" + namespace OEngine { namespace Render @@ -38,7 +40,7 @@ namespace MWInput This class is just an interface. All the messy details are in inputmanager.cpp. */ - struct MWInputManager + struct MWInputManager : public MWBase::InputManager { InputImpl *impl; @@ -48,17 +50,17 @@ namespace MWInput MWGui::WindowManager &_windows, bool debug, OMW::Engine& engine); - ~MWInputManager(); + virtual ~MWInputManager(); - void update(); + virtual void update(); - void changeInputMode(bool guiMode); + virtual void changeInputMode(bool guiMode); - void processChangedSettings(const Settings::CategorySettingVector& changed); + virtual void processChangedSettings(const Settings::CategorySettingVector& changed); - void setDragDrop(bool dragDrop); + virtual void setDragDrop(bool dragDrop); - void toggleControlSwitch(std::string sw, bool value); + virtual void toggleControlSwitch (const std::string& sw, bool value); }; } #endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8719557ca3..590c177097 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -22,12 +22,12 @@ #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" // FIXME #include "../mwworld/ptr.hpp" #include "../mwworld/player.hpp" #include "../mwgui/window_manager.hpp" // FIXME -#include "../mwinput/inputmanager.hpp" // FIXME #include "shadows.hpp" #include "localmap.hpp" diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 084698c5b5..bd14e7b8dd 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -8,19 +8,16 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" -#include "../mwinput/inputmanager.hpp" - #include "interpretercontext.hpp" #include "ref.hpp" -#include - namespace MWScript { namespace Control @@ -41,11 +38,6 @@ namespace MWScript MWBase::Environment::get() .getInputManager() ->toggleControlSwitch(mControl, mEnable); - - if (mEnable) - std::cout << "enable: " << mControl << std::endl; - else - std::cout << "disable: " << mControl << std::endl; } }; diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 8e5897298b..3d14692d9a 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -10,7 +10,6 @@ #include "../mwbase/environment.hpp" #include "../mwgui/window_manager.hpp" -#include "../mwinput/inputmanager.hpp" #include "interpretercontext.hpp" diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 327eed9136..131d7865b5 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -15,8 +15,6 @@ #include "../mwgui/window_manager.hpp" -#include "../mwinput/inputmanager.hpp" - #include "locals.hpp" #include "globalscripts.hpp" From 2beaa9d9ae1be8bf9dea6d19bfe1536485550e44 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 11 Aug 2012 23:26:20 +0400 Subject: [PATCH 04/27] Workaround to allow main loop to know Ogre::Root's mQueuedEnd value This change is needed because of changes in exit handling --- libs/openengine/ogre/renderer.cpp | 26 ++++++++++++++++++++++---- libs/openengine/ogre/renderer.hpp | 21 +++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 4074a1a997..e342f4c5f7 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -24,6 +24,21 @@ using namespace Ogre; using namespace OEngine::Render; +#if defined(__APPLE__) && !defined(__LP64__) + +CustomRoot::CustomRoot(const Ogre::String& pluginFileName, + const Ogre::String& configFileName, + const Ogre::String& logFileName) +: Ogre::Root(pluginFileName, configFileName, logFileName) +{} + +bool CustomRoot::isQueuedEnd() const +{ + return mQueuedEnd; +} + +#endif + void OgreRenderer::cleanup() { delete mFader; @@ -36,7 +51,6 @@ void OgreRenderer::cleanup() void OgreRenderer::start() { #if defined(__APPLE__) && !defined(__LP64__) - bool quit = false; // OSX Carbon Message Pump do { EventRef event = NULL; @@ -54,11 +68,11 @@ void OgreRenderer::start() ReleaseEvent(event); } - if (!Ogre::Root::getSingleton().renderOneFrame()) { - quit = true; + if (!mRoot->renderOneFrame()) { + break; } - } while (!quit); + } while (!mRoot->isQueuedEnd()); #else mRoot->startRendering(); #endif @@ -120,7 +134,11 @@ void OgreRenderer::configure(const std::string &logPath, // Disable logging log->setDebugOutputEnabled(false); +#if defined(__APPLE__) && !defined(__LP64__) + mRoot = new CustomRoot("", "", ""); +#else mRoot = new Root("", "", ""); +#endif #if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) || defined(ENABLE_PLUGIN_CgProgramManager) || defined(ENABLE_PLUGIN_OctreeSceneManager) || defined(ENABLE_PLUGIN_ParticleFX) loadPlugins(); diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 247c8f95a2..9b7003368e 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -27,9 +27,15 @@ #include "OgreTexture.h" #include +#if defined(__APPLE__) && !defined(__LP64__) +#include +#endif + namespace Ogre { +#if !defined(__APPLE__) || defined(__LP64__) class Root; +#endif class RenderWindow; class SceneManager; class Camera; @@ -48,10 +54,25 @@ namespace OEngine std::string fsaa; }; +#if defined(__APPLE__) && !defined(__LP64__) + class CustomRoot : public Ogre::Root { + public: + bool isQueuedEnd() const; + + CustomRoot(const Ogre::String& pluginFileName = "plugins.cfg", + const Ogre::String& configFileName = "ogre.cfg", + const Ogre::String& logFileName = "Ogre.log"); + }; +#endif + class Fader; class OgreRenderer { +#if defined(__APPLE__) && !defined(__LP64__) + CustomRoot *mRoot; +#else Ogre::Root *mRoot; +#endif Ogre::RenderWindow *mWindow; Ogre::SceneManager *mScene; Ogre::Camera *mCamera; From a3b27ae756212eaed6fd2942cf40542e91de3492 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 10:50:03 +0200 Subject: [PATCH 05/27] Issue #107: Moved ClassPoint from window manager to character creation --- apps/openmw/mwgui/charactercreation.cpp | 8 ++++++++ apps/openmw/mwgui/window_manager.hpp | 8 -------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index fd3a7872e3..2c0b3de85f 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -104,6 +104,14 @@ namespace {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} } } }; + + struct ClassPoint + { + const char *id; + // Specialization points to match, in order: Stealth, Combat, Magic + // Note: Order is taken from http://www.uesp.net/wiki/Morrowind:Class_Quiz + unsigned int points[3]; + }; } using namespace MWGui; diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 3653615a69..69a9717dd2 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -79,14 +79,6 @@ namespace MWGui class AlchemyWindow; class SpellWindow; - struct ClassPoint - { - const char *id; - // Specialization points to match, in order: Stealth, Combat, Magic - // Note: Order is taken from http://www.uesp.net/wiki/Morrowind:Class_Quiz - unsigned int points[3]; - }; - class WindowManager { public: From dbac3d2e5f7da94bef0baf014e1ec6f0e7dcb658 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 10:52:29 +0200 Subject: [PATCH 06/27] Issue #107: fixing WindowManager::removeDialog --- apps/openmw/mwgui/window_manager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index b4eaaacf49..ca5223a186 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -424,7 +424,6 @@ void WindowManager::updateSkillArea() void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) { - assert(dialog); if (!dialog) return; dialog->setVisible(false); From 020fde70b80e58cfd9feb7419d5d5d782a1d372e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 11:10:17 +0200 Subject: [PATCH 07/27] Issue #107: moving function implementations from hpp to cpp file --- apps/openmw/mwgui/window_manager.cpp | 76 ++++++++++++++++++++++++++++ apps/openmw/mwgui/window_manager.hpp | 71 ++++++++------------------ 2 files changed, 98 insertions(+), 49 deletions(-) diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index ca5223a186..38ea74a57a 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -743,3 +743,79 @@ void WindowManager::executeInConsole (const std::string& path) { mConsole->executeFile (path); } + +void WindowManager::wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) +{ + mFPS = fps; + mTriangleCount = triangleCount; + mBatchCount = batchCount; +} + +MyGUI::Gui* WindowManager::getGui() const { return mGui; } + +MWGui::DialogueWindow* WindowManager::getDialogueWindow() { return mDialogueWindow; } +MWGui::ContainerWindow* WindowManager::getContainerWindow() { return mContainerWindow; } +MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } +MWGui::BookWindow* WindowManager::getBookWindow() { return mBookWindow; } +MWGui::ScrollWindow* WindowManager::getScrollWindow() { return mScrollWindow; } +MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } +MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } +MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } +MWGui::SpellWindow* WindowManager::getSpellWindow() { return mSpellWindow; } +MWGui::Console* WindowManager::getConsole() { return mConsole; } + +bool WindowManager::isAllowed (GuiWindow wnd) const +{ + return mAllowed & wnd; +} + +void WindowManager::allow (GuiWindow wnd) +{ + mAllowed = (GuiWindow)(mAllowed | wnd); + updateVisible(); +} + +void WindowManager::disallowAll() +{ + mAllowed = GW_None; + updateVisible(); +} + +void WindowManager::toggleVisible (GuiWindow wnd) +{ + mShown = (mShown & wnd) ? (GuiWindow) (mShown & ~wnd) : (GuiWindow) (mShown | wnd); + updateVisible(); +} + +bool WindowManager::isGuiMode() const +{ + return !mGuiModes.empty(); +} + +MWGui::GuiMode WindowManager::getMode() const +{ + if (mGuiModes.empty()) + throw std::runtime_error ("getMode() called, but there is no active mode"); + + return mGuiModes.back(); +} + +std::map > WindowManager::getPlayerSkillValues() +{ + return mPlayerSkillValues; +} + +std::map > WindowManager::getPlayerAttributeValues() +{ + return mPlayerAttributes; +} + +WindowManager::SkillList WindowManager::getPlayerMinorSkills() +{ + return mPlayerMinorSkills; +} + +WindowManager::SkillList WindowManager::getPlayerMajorSkills() +{ + return mPlayerMajorSkills; +} diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 69a9717dd2..60fc63c8cf 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -100,61 +100,34 @@ namespace MWGui void popGuiMode(); void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack - GuiMode getMode() const - { - if (mGuiModes.empty()) - throw std::runtime_error ("getMode() called, but there is no active mode"); - return mGuiModes.back(); - } + GuiMode getMode() const; - bool isGuiMode() const { return !mGuiModes.empty(); } + bool isGuiMode() const; - void toggleVisible(GuiWindow wnd) - { - mShown = (mShown & wnd) ? (GuiWindow) (mShown & ~wnd) : (GuiWindow) (mShown | wnd); - updateVisible(); - } + void toggleVisible(GuiWindow wnd); // Disallow all inventory mode windows - void disallowAll() - { - mAllowed = GW_None; - updateVisible(); - } + void disallowAll(); // Allow one or more windows - void allow(GuiWindow wnd) - { - mAllowed = (GuiWindow)(mAllowed | wnd); - updateVisible(); - } + void allow(GuiWindow wnd); - bool isAllowed(GuiWindow wnd) const - { - return mAllowed & wnd; - } + bool isAllowed(GuiWindow wnd) const; - MWGui::DialogueWindow* getDialogueWindow() {return mDialogueWindow;} - MWGui::ContainerWindow* getContainerWindow() {return mContainerWindow;} - MWGui::InventoryWindow* getInventoryWindow() {return mInventoryWindow;} - MWGui::BookWindow* getBookWindow() {return mBookWindow;} - MWGui::ScrollWindow* getScrollWindow() {return mScrollWindow;} - MWGui::CountDialog* getCountDialog() {return mCountDialog;} - MWGui::ConfirmationDialog* getConfirmationDialog() {return mConfirmationDialog;} - MWGui::TradeWindow* getTradeWindow() {return mTradeWindow;} - MWGui::SpellWindow* getSpellWindow() {return mSpellWindow;} - MWGui::Console* getConsole() {return mConsole;} + MWGui::DialogueWindow* getDialogueWindow(); + MWGui::ContainerWindow* getContainerWindow(); + MWGui::InventoryWindow* getInventoryWindow(); + MWGui::BookWindow* getBookWindow(); + MWGui::ScrollWindow* getScrollWindow(); + MWGui::CountDialog* getCountDialog(); + MWGui::ConfirmationDialog* getConfirmationDialog(); + MWGui::TradeWindow* getTradeWindow(); + MWGui::SpellWindow* getSpellWindow(); + MWGui::Console* getConsole(); - MyGUI::Gui* getGui() const { return mGui; } + MyGUI::Gui* getGui() const; - void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) - { - mFPS = fps; - mTriangleCount = triangleCount; - mBatchCount = batchCount; - } - -// MWMechanics::DynamicStat getValue(const std::string& id); + void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount); ///< Set value for the given ID. void setValue (const std::string& id, const MWMechanics::Stat& value); @@ -211,10 +184,10 @@ namespace MWGui void onFrame (float frameDuration); - std::map > getPlayerSkillValues() { return mPlayerSkillValues; } - std::map > getPlayerAttributeValues() { return mPlayerAttributes; } - SkillList getPlayerMinorSkills() { return mPlayerMinorSkills; } - SkillList getPlayerMajorSkills() { return mPlayerMajorSkills; } + std::map > getPlayerSkillValues(); + std::map > getPlayerAttributeValues(); + SkillList getPlayerMinorSkills(); + SkillList getPlayerMajorSkills(); /** * Fetches a GMST string from the store, if there is no setting with the given From e0ba7cf952104664e08cfcca9f2e025eb2ebe4d3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 13:46:14 +0200 Subject: [PATCH 08/27] Issue #107: Fixing up the window manager interface --- apps/openmw/mwgui/charactercreation.cpp | 12 ++++++------ apps/openmw/mwgui/window_manager.cpp | 12 +++++++----- apps/openmw/mwgui/window_manager.hpp | 10 +++++----- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 2c0b3de85f..5e0df5cbd9 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -268,20 +268,20 @@ void CharacterCreation::spawnDialog(const char id) mReviewDialog->setFatigue(mPlayerFatigue); { - std::map > attributes = mWM->getPlayerAttributeValues(); - for (std::map >::iterator it = attributes.begin(); + std::map > attributes = mWM->getPlayerAttributeValues(); + for (std::map >::iterator it = attributes.begin(); it != attributes.end(); ++it) { - mReviewDialog->setAttribute(it->first, it->second); + mReviewDialog->setAttribute(static_cast (it->first), it->second); } } { - std::map > skills = mWM->getPlayerSkillValues(); - for (std::map >::iterator it = skills.begin(); + std::map > skills = mWM->getPlayerSkillValues(); + for (std::map >::iterator it = skills.begin(); it != skills.end(); ++it) { - mReviewDialog->setSkillValue(it->first, it->second); + mReviewDialog->setSkillValue(static_cast (it->first), it->second); } mReviewDialog->configureSkills(mWM->getPlayerMajorSkills(), mWM->getPlayerMinorSkills()); } diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 38ea74a57a..94cad28186 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -338,10 +338,12 @@ void WindowManager::setValue (const std::string& id, const MWMechanics::Stat& value) +void WindowManager::setValue (int parSkill, const MWMechanics::Stat& value) { - mStatsWindow->setValue(parSkill, value); - mCharGen->setValue(parSkill, value); + /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we + /// allow custom skills. + mStatsWindow->setValue(static_cast (parSkill), value); + mCharGen->setValue(static_cast (parSkill), value); mPlayerSkillValues[parSkill] = value; } @@ -800,12 +802,12 @@ MWGui::GuiMode WindowManager::getMode() const return mGuiModes.back(); } -std::map > WindowManager::getPlayerSkillValues() +std::map > WindowManager::getPlayerSkillValues() { return mPlayerSkillValues; } -std::map > WindowManager::getPlayerAttributeValues() +std::map > WindowManager::getPlayerAttributeValues() { return mPlayerAttributes; } diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 60fc63c8cf..de90037b9f 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -131,7 +131,7 @@ namespace MWGui ///< Set value for the given ID. void setValue (const std::string& id, const MWMechanics::Stat& value); - void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); + void setValue (int parSkill, const MWMechanics::Stat& value); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setValue (const std::string& id, const std::string& value); void setValue (const std::string& id, int value); @@ -184,8 +184,8 @@ namespace MWGui void onFrame (float frameDuration); - std::map > getPlayerSkillValues(); - std::map > getPlayerAttributeValues(); + std::map > getPlayerSkillValues(); + std::map > getPlayerAttributeValues(); SkillList getPlayerMinorSkills(); SkillList getPlayerMajorSkills(); @@ -233,9 +233,9 @@ namespace MWGui ESM::Class mPlayerClass; std::string mPlayerName; std::string mPlayerRaceId; - std::map > mPlayerAttributes; + std::map > mPlayerAttributes; SkillList mPlayerMajorSkills, mPlayerMinorSkills; - std::map > mPlayerSkillValues; + std::map > mPlayerSkillValues; MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; From 4ca3cb81d49e2ce68d56c6d9fe6731780c6f3107 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 14:07:48 +0200 Subject: [PATCH 09/27] Issue #107: some include clean up --- apps/openmw/mwgui/window_manager.cpp | 37 ++++++++++++++++------------ apps/openmw/mwgui/window_manager.hpp | 21 ++++++++-------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 94cad28186..369eae089a 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -1,4 +1,25 @@ #include "window_manager.hpp" + +#include +#include + +#include "MyGUI_UString.h" + +#include +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/inputmanager.hpp" + +#include "../mwworld/ptr.hpp" +#include "../mwworld/cellstore.hpp" + +#include "console.hpp" +#include "journalwindow.hpp" +#include "charactercreation.hpp" #include "text_input.hpp" #include "review.hpp" #include "dialogue.hpp" @@ -21,22 +42,6 @@ #include "alchemywindow.hpp" #include "spellwindow.hpp" -#include "../mwbase/environment.hpp" -#include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/inputmanager.hpp" - -#include "../mwworld/ptr.hpp" -#include "../mwworld/cellstore.hpp" - -#include "console.hpp" -#include "journalwindow.hpp" -#include "charactercreation.hpp" - -#include - -#include -#include - using namespace MWGui; WindowManager::WindowManager( diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index de90037b9f..59a6ab3950 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -10,12 +10,11 @@ this class. **/ -#include "MyGUI_UString.h" +#include +#include #include #include -#include -#include #include "../mwmechanics/stat.hpp" @@ -23,8 +22,9 @@ namespace MyGUI { - class Gui; - class Widget; + class Gui; + class Widget; + class UString; } namespace Compiler @@ -38,16 +38,17 @@ namespace MWWorld class CellStore; } -namespace MWMechanics -{ - class MechanicsManager; -} - namespace OEngine { namespace GUI { class Layout; + class MyGUIManager; + } + + namespace Render + { + class OgreRenderer; } } From f37d3cd3c9c607ee0932c740d5ebbd571aa1966b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 14:09:55 +0200 Subject: [PATCH 10/27] Issue #107: added a few todo comments --- apps/openmw/mwgui/window_manager.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 59a6ab3950..fccaa83473 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -115,6 +115,7 @@ namespace MWGui bool isAllowed(GuiWindow wnd) const; + /// \todo investigate, if we really need to expose every single lousy UI element to the outside world MWGui::DialogueWindow* getDialogueWindow(); MWGui::ContainerWindow* getContainerWindow(); MWGui::InventoryWindow* getInventoryWindow(); @@ -185,6 +186,7 @@ namespace MWGui void onFrame (float frameDuration); + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. std::map > getPlayerSkillValues(); std::map > getPlayerAttributeValues(); SkillList getPlayerMinorSkills(); @@ -230,6 +232,7 @@ namespace MWGui CharacterCreation* mCharGen; + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. // Various stats about player as needed by window manager ESM::Class mPlayerClass; std::string mPlayerName; From 484cce12a85e320b29e818341afe05ace8410830 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 14:36:46 +0200 Subject: [PATCH 11/27] Issue #107: removed redundant getStore function from window manager --- apps/openmw/mwdialogue/journalimp.cpp | 2 ++ apps/openmw/mwgui/birth.cpp | 15 ++++++++++----- apps/openmw/mwgui/class.cpp | 10 ++++++---- apps/openmw/mwgui/race.cpp | 9 ++++++--- apps/openmw/mwgui/review.cpp | 7 +++++-- apps/openmw/mwgui/stats_window.cpp | 14 +++++++------- apps/openmw/mwgui/widgets.cpp | 17 +++++++++++------ apps/openmw/mwgui/window_manager.cpp | 5 ----- apps/openmw/mwgui/window_manager.hpp | 4 +--- apps/openmw/mwrender/localmap.cpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 1 + apps/openmw/mwscript/interpretercontext.cpp | 1 + apps/openmw/mwworld/scene.cpp | 2 ++ 13 files changed, 54 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 052b921943..dc322cade8 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -1,6 +1,8 @@ #include "journalimp.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 0f1a04c27b..023702c8b8 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -1,11 +1,16 @@ #include "birth.hpp" -#include "window_manager.hpp" -#include "widgets.hpp" -#include "components/esm_store/store.hpp" #include #include +#include "components/esm_store/store.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "window_manager.hpp" +#include "widgets.hpp" + using namespace MWGui; using namespace Widgets; @@ -114,7 +119,7 @@ void BirthDialog::updateBirths() { mBirthList->removeAllItems(); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); ESMS::RecListT::MapType::const_iterator it = store.birthSigns.list.begin(); ESMS::RecListT::MapType::const_iterator end = store.birthSigns.list.end(); @@ -144,7 +149,7 @@ void BirthDialog::updateSpells() const int lineHeight = 18; MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::BirthSign *birth = store.birthSigns.find(mCurrentBirthId); std::string texturePath = std::string("textures\\") + birth->texture; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 48e01eb1e9..e5b3e261d7 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -7,6 +7,9 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "window_manager.hpp" #include "tooltips.hpp" @@ -57,8 +60,7 @@ void GenerateClassResultDialog::setClassId(const std::string &classId) { mCurrentClassId = classId; mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); - const ESMS::ESMStore &store = mWindowManager.getStore(); - mClassName->setCaption(store.classes.find(mCurrentClassId)->name); + mClassName->setCaption(MWBase::Environment::get().getWorld()->getStore().classes.find(mCurrentClassId)->name); } // widget controls @@ -193,7 +195,7 @@ void PickClassDialog::updateClasses() { mClassList->removeAllItems(); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); ESMS::RecListT::MapType::const_iterator it = store.classes.list.begin(); ESMS::RecListT::MapType::const_iterator end = store.classes.list.end(); @@ -217,7 +219,7 @@ void PickClassDialog::updateStats() { if (mCurrentClassId.empty()) return; - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Class *klass = store.classes.search(mCurrentClassId); if (!klass) return; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index f90a9eddec..62434cb91a 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -8,6 +8,9 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "window_manager.hpp" #include "widgets.hpp" #include "tooltips.hpp" @@ -209,7 +212,7 @@ void RaceDialog::updateRaces() { mRaceList->removeAllItems(); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); ESMS::RecListT::MapType::const_iterator it = store.races.list.begin(); ESMS::RecListT::MapType::const_iterator end = store.races.list.end(); @@ -243,7 +246,7 @@ void RaceDialog::updateSkills() const int lineHeight = 18; MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.races.find(mCurrentRaceId); int count = sizeof(race->data.bonus)/sizeof(race->data.bonus[0]); // TODO: Find a portable macro for this ARRAYSIZE? for (int i = 0; i < count; ++i) @@ -281,7 +284,7 @@ void RaceDialog::updateSpellPowers() const int lineHeight = 18; MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.races.find(mCurrentRaceId); std::vector::const_iterator it = race->powers.list.begin(); diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 7061f65c17..997899c526 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -7,6 +7,9 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "window_manager.hpp" #include "widgets.hpp" #include "tooltips.hpp" @@ -138,7 +141,7 @@ void ReviewDialog::setPlayerName(const std::string &name) void ReviewDialog::setRace(const std::string &raceId) { mRaceId = raceId; - const ESM::Race *race = mWindowManager.getStore().races.search(mRaceId); + const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().races.search(mRaceId); if (race) { ToolTips::createRaceToolTip(mRaceWidget, race); @@ -156,7 +159,7 @@ void ReviewDialog::setClass(const ESM::Class& class_) void ReviewDialog::setBirthSign(const std::string& signId) { mBirthSignId = signId; - const ESM::BirthSign *sign = mWindowManager.getStore().birthSigns.search(mBirthSignId); + const ESM::BirthSign *sign = MWBase::Environment::get().getWorld()->getStore().birthSigns.search(mBirthSignId); if (sign) { mBirthSignWidget->setCaption(sign->name); diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 3f5162bfba..4ee56abc7f 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -57,7 +57,7 @@ StatsWindow::StatsWindow (WindowManager& parWindowManager) { 0, 0 } }; - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); for (int i=0; names[i][0]; ++i) { setText (names[i][0], store.gameSettings.find (names[i][1])->str); @@ -383,12 +383,12 @@ void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, float modified = stat.getModified(); int progressPercent = (modified - float(static_cast(modified))) * 100; - const ESM::Skill* skill = mWindowManager.getStore().skills.search(skillId); + const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().skills.search(skillId); assert(skill); std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; - const ESM::Attribute* attr = mWindowManager.getStore().attributes.search(skill->data.attribute); + const ESM::Attribute* attr = MWBase::Environment::get().getWorld()->getStore().attributes.search(skill->data.attribute); assert(attr); std::string state = "normal"; @@ -443,7 +443,7 @@ void StatsWindow::updateSkillArea() if (!mMiscSkills.empty()) addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); // race tooltip const ESM::Race* playerRace = store.races.find (MWBase::Environment::get().getWorld()->getPlayer().getRace()); @@ -485,8 +485,8 @@ void StatsWindow::updateSkillArea() text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->ranks[it->second+1]; ESM::RankData rankData = faction->data.rankData[it->second+1]; - const ESM::Attribute* attr1 = mWindowManager.getStore().attributes.search(faction->data.attribute1); - const ESM::Attribute* attr2 = mWindowManager.getStore().attributes.search(faction->data.attribute2); + const ESM::Attribute* attr1 = MWBase::Environment::get().getWorld()->getStore().attributes.search(faction->data.attribute1); + const ESM::Attribute* attr2 = MWBase::Environment::get().getWorld()->getStore().attributes.search(faction->data.attribute2); assert(attr1 && attr2); text += "\n#BF9959#{" + attr1->name + "}: " + boost::lexical_cast(rankData.attribute1) @@ -496,7 +496,7 @@ void StatsWindow::updateSkillArea() text += "\n#BF9959"; for (int i=0; i<6; ++i) { - const ESM::Skill* skill = mWindowManager.getStore().skills.search(faction->data.skillID[i]); + const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().skills.search(faction->data.skillID[i]); assert(skill); text += "#{"+ESM::Skill::sSkillNameIds[faction->data.skillID[i]]+"}"; if (i<5) diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index c54ac6e389..1529387071 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -1,9 +1,14 @@ #include "widgets.hpp" -#include "window_manager.hpp" -#include "components/esm_store/store.hpp" #include +#include "components/esm_store/store.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "window_manager.hpp" + #undef min #undef max @@ -190,7 +195,7 @@ void MWAttribute::initialiseOverride() assignWidget(mAttributeNameWidget, "StatName"); assignWidget(mAttributeValueWidget, "StatValue"); - + MyGUI::ButtonPtr button; assignWidget(button, "StatNameButton"); if (button) @@ -224,7 +229,7 @@ void MWSpell::setSpellId(const std::string &spellId) void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::WidgetPtr creator, MyGUI::IntCoord &coord, int flags) { - const ESMS::ESMStore &store = mWindowManager->getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Spell *spell = store.spells.search(mId); MYGUI_ASSERT(spell, "spell with id '" << mId << "' not found"); @@ -255,7 +260,7 @@ void MWSpell::updateWidgets() { if (mSpellNameWidget && mWindowManager) { - const ESMS::ESMStore &store = mWindowManager->getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Spell *spell = store.spells.search(mId); if (spell) static_cast(mSpellNameWidget)->setCaption(spell->name); @@ -384,7 +389,7 @@ void MWSpellEffect::updateWidgets() if (!mWindowManager) return; - const ESMS::ESMStore &store = mWindowManager->getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::MagicEffect *magicEffect = store.magicEffects.search(mEffectParams.mEffectID); if (!magicEffect) return; diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 369eae089a..eada0c88a2 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -497,11 +497,6 @@ void WindowManager::onFrame (float frameDuration) mConsole->checkReferenceAvailable(); } -const ESMS::ESMStore& WindowManager::getStore() const -{ - return MWBase::Environment::get().getWorld()->getStore(); -} - void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) { if (!(cell->cell->data.flags & ESM::Cell::Interior)) diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index fccaa83473..224c2a9203 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -13,8 +13,8 @@ #include #include -#include #include +#include #include "../mwmechanics/stat.hpp" @@ -201,8 +201,6 @@ namespace MWGui */ const std::string &getGameSettingString(const std::string &id, const std::string &default_); - const ESMS::ESMStore& getStore() const; - void processChangedSettings(const Settings::CategorySettingVector& changed); void executeInConsole (const std::string& path); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 962f19a57f..b5fa135e0d 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 590c177097..d039418ba3 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 131d7865b5..0ba04fb381 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -5,6 +5,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 9318fd6f13..1c7093e60e 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1,5 +1,7 @@ #include "scene.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" /// FIXME #include "../mwbase/soundmanager.hpp" From 35a64edf2acd3d588aa114ee1049c04bc4a5ddc8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 16:07:04 +0200 Subject: [PATCH 12/27] Issue #107: removed a template from the window manager --- apps/openmw/mwgui/charactercreation.cpp | 83 ++++++++++++++----------- apps/openmw/mwgui/class.cpp | 31 ++++----- apps/openmw/mwgui/window_manager.hpp | 10 --- 3 files changed, 58 insertions(+), 66 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 5e0df5cbd9..72394de80d 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -187,8 +187,8 @@ void CharacterCreation::spawnDialog(const char id) switch (id) { case GM_Name: - if(mNameDialog) - mWM->removeDialog(mNameDialog); + mWM->removeDialog(mNameDialog); + mNameDialog = 0; mNameDialog = new TextInputDialog(*mWM); mNameDialog->setTextLabel(mWM->getGameSettingString("sName", "Name")); mNameDialog->setTextInput(mPlayerName); @@ -198,8 +198,8 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_Race: - if (mRaceDialog) - mWM->removeDialog(mRaceDialog); + mWM->removeDialog(mRaceDialog); + mRaceDialog = 0; mRaceDialog = new RaceDialog(*mWM); mRaceDialog->setNextButtonShow(mCreationStage >= CSE_RaceChosen); mRaceDialog->setRaceId(mPlayerRaceId); @@ -209,16 +209,16 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_Class: - if (mClassChoiceDialog) - mWM->removeDialog(mClassChoiceDialog); + mWM->removeDialog(mClassChoiceDialog); + mClassChoiceDialog = 0; mClassChoiceDialog = new ClassChoiceDialog(*mWM); mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); mClassChoiceDialog->open(); break; case GM_ClassPick: - if (mPickClassDialog) - mWM->removeDialog(mPickClassDialog); + mWM->removeDialog(mPickClassDialog); + mPickClassDialog = 0; mPickClassDialog = new PickClassDialog(*mWM); mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); mPickClassDialog->setClassId(mPlayerClass.name); @@ -228,8 +228,8 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_Birth: - if (mBirthSignDialog) - mWM->removeDialog(mBirthSignDialog); + mWM->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; mBirthSignDialog = new BirthDialog(*mWM); mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen); mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone); @@ -238,8 +238,8 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_ClassCreate: - if (mCreateClassDialog) - mWM->removeDialog(mCreateClassDialog); + mWM->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; mCreateClassDialog = new CreateClassDialog(*mWM); mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); @@ -255,8 +255,8 @@ void CharacterCreation::spawnDialog(const char id) showClassQuestionDialog(); break; case GM_Review: - if (mReviewDialog) - mWM->removeDialog(mReviewDialog); + mWM->removeDialog(mReviewDialog); + mReviewDialog = 0; mReviewDialog = new ReviewDialog(*mWM); mReviewDialog->setPlayerName(mPlayerName); mReviewDialog->setRace(mPlayerRaceId); @@ -311,24 +311,24 @@ void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& v void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) { - if (mReviewDialog) - mWM->removeDialog(mReviewDialog); + mWM->removeDialog(mReviewDialog); + mReviewDialog = 0; mWM->popGuiMode(); } void CharacterCreation::onReviewDialogBack() { - if (mReviewDialog) - mWM->removeDialog(mReviewDialog); + mWM->removeDialog(mReviewDialog); + mReviewDialog = 0; mWM->pushGuiMode(GM_Birth); } void CharacterCreation::onReviewActivateDialog(int parDialog) { - if (mReviewDialog) - mWM->removeDialog(mReviewDialog); + mWM->removeDialog(mReviewDialog); + mReviewDialog = 0; mCreationStage = CSE_ReviewNext; mWM->popGuiMode(); @@ -363,6 +363,7 @@ void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) mWM->setPlayerClass(mPlayerClass); } mWM->removeDialog(mPickClassDialog); + mPickClassDialog = 0; } //TODO This bit gets repeated a few times; wrap it in a function @@ -391,6 +392,7 @@ void CharacterCreation::onPickClassDialogBack() if (!classId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); mWM->removeDialog(mPickClassDialog); + mPickClassDialog = 0; } mWM->popGuiMode(); @@ -399,10 +401,8 @@ void CharacterCreation::onPickClassDialogBack() void CharacterCreation::onClassChoice(int _index) { - if (mClassChoiceDialog) - { - mWM->removeDialog(mClassChoiceDialog); - } + mWM->removeDialog(mClassChoiceDialog); + mClassChoiceDialog = 0; mWM->popGuiMode(); @@ -432,6 +432,7 @@ void CharacterCreation::onNameDialogDone(WindowBase* parWindow) mWM->setValue("name", mPlayerName); MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName); mWM->removeDialog(mNameDialog); + mNameDialog = 0; } if (mCreationStage == CSE_ReviewNext) @@ -459,6 +460,7 @@ void CharacterCreation::onRaceDialogBack() if (!mPlayerRaceId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerRace(mPlayerRaceId, mRaceDialog->getGender() == RaceDialog::GM_Male); mWM->removeDialog(mRaceDialog); + mRaceDialog = 0; } mWM->popGuiMode(); @@ -474,6 +476,7 @@ void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) if (!mPlayerRaceId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerRace(mPlayerRaceId, mRaceDialog->getGender() == RaceDialog::GM_Male); mWM->removeDialog(mRaceDialog); + mRaceDialog = 0; } if (mCreationStage == CSE_ReviewNext) @@ -501,6 +504,7 @@ void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) if (!mPlayerBirthSignId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mPlayerBirthSignId); mWM->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; } if (mCreationStage >= CSE_BirthSignChosen) @@ -521,6 +525,7 @@ void CharacterCreation::onBirthSignDialogBack() { MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mBirthSignDialog->getBirthId()); mWM->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; } mWM->popGuiMode(); @@ -556,6 +561,7 @@ void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) mWM->setPlayerClass(klass); mWM->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; } if (mCreationStage == CSE_ReviewNext) @@ -577,8 +583,8 @@ void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) void CharacterCreation::onCreateClassDialogBack() { - if (mCreateClassDialog) - mWM->removeDialog(mCreateClassDialog); + mWM->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; mWM->popGuiMode(); mWM->pushGuiMode(GM_Class); @@ -588,8 +594,9 @@ void CharacterCreation::onClassQuestionChosen(int _index) { MWBase::Environment::get().getSoundManager()->stopSay(); - if (mGenerateClassQuestionDialog) - mWM->removeDialog(mGenerateClassQuestionDialog); + mWM->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = 0; + if (_index < 0 || _index >= 3) { mWM->popGuiMode(); @@ -666,8 +673,9 @@ void CharacterCreation::showClassQuestionDialog() } } - if (mGenerateClassResultDialog) - mWM->removeDialog(mGenerateClassResultDialog); + mWM->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; + mGenerateClassResultDialog = new GenerateClassResultDialog(*mWM); mGenerateClassResultDialog->setClassId(mGenerateClass); mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); @@ -683,8 +691,9 @@ void CharacterCreation::showClassQuestionDialog() return; } - if (mGenerateClassQuestionDialog) - mWM->removeDialog(mGenerateClassQuestionDialog); + mWM->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = 0; + mGenerateClassQuestionDialog = new InfoBoxDialog(*mWM); InfoBoxDialog::ButtonList buttons; @@ -704,8 +713,9 @@ void CharacterCreation::onGenerateClassBack() if(mCreationStage < CSE_ClassChosen) mCreationStage = CSE_ClassChosen; - if (mGenerateClassResultDialog) - mWM->removeDialog(mGenerateClassResultDialog); + mWM->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); mWM->popGuiMode(); @@ -714,8 +724,9 @@ void CharacterCreation::onGenerateClassBack() void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) { - if (mGenerateClassResultDialog) - mWM->removeDialog(mGenerateClassResultDialog); + mWM->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); const ESM::Class *klass = MWBase::Environment::get().getWorld()->getStore().classes.find(mGenerateClass); mPlayerClass = *klass; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index e5b3e261d7..7e3194d6be 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -558,26 +558,17 @@ void CreateClassDialog::open() void CreateClassDialog::onDialogCancel() { - if (mSpecDialog) - { - mWindowManager.removeDialog(mSpecDialog); - mSpecDialog = 0; - } - if (mAttribDialog) - { - mWindowManager.removeDialog(mAttribDialog); - mAttribDialog = 0; - } - if (mSkillDialog) - { - mWindowManager.removeDialog(mSkillDialog); - mSkillDialog = 0; - } - if (mDescDialog) - { - mWindowManager.removeDialog(mDescDialog); - mDescDialog = 0; - } + mWindowManager.removeDialog(mSpecDialog); + mSpecDialog = 0; + + mWindowManager.removeDialog(mAttribDialog); + mAttribDialog = 0; + + mWindowManager.removeDialog(mSkillDialog); + mSkillDialog = 0; + + mWindowManager.removeDialog(mDescDialog); + mDescDialog = 0; } void CreateClassDialog::onSpecializationClicked(MyGUI::WidgetPtr _sender) diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 224c2a9203..db3945e55d 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -177,8 +177,6 @@ namespace MWGui void unsetSelectedSpell(); void unsetSelectedWeapon(); - template - void removeDialog(T*& dialog); ///< Casts to OEngine::GUI::Layout and calls removeDialog, then resets pointer to nullptr. void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. void messageBox (const std::string& message, const std::vector& buttons); @@ -271,14 +269,6 @@ namespace MWGui */ void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result); }; - - template - void WindowManager::removeDialog(T*& dialog) - { - OEngine::GUI::Layout *d = static_cast(dialog); - removeDialog(d); - dialog = 0; - } } #endif From b6f427bf5ef9848bfc4a3e190890e3a33ff545d7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 17:20:46 +0200 Subject: [PATCH 13/27] fix OpenMW not exiting when the window is closed (alt f4 etc) --- apps/openmw/mwrender/renderingmanager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7180fea667..2ff18fbeb5 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -775,6 +775,7 @@ void RenderingManager::windowResized(Ogre::RenderWindow* rw) void RenderingManager::windowClosed(Ogre::RenderWindow* rw) { + Ogre::Root::getSingleton ().queueEndRendering (); } bool RenderingManager::waterShaderSupported() From 6534c2a55a76a269a2c3f9c498268ba91b2ce047 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 18:11:09 +0200 Subject: [PATCH 14/27] Issue #107: WindowManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 9 +- apps/openmw/mwbase/environment.hpp | 12 +- apps/openmw/mwbase/windowmanager.hpp | 210 ++++++++++++++ apps/openmw/mwclass/activator.cpp | 5 +- apps/openmw/mwclass/apparatus.cpp | 2 +- apps/openmw/mwclass/armor.cpp | 2 +- apps/openmw/mwclass/book.cpp | 2 +- apps/openmw/mwclass/clothing.cpp | 2 +- apps/openmw/mwclass/container.cpp | 2 +- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/door.cpp | 2 +- apps/openmw/mwclass/ingredient.cpp | 2 +- apps/openmw/mwclass/light.cpp | 2 +- apps/openmw/mwclass/lockpick.cpp | 2 +- apps/openmw/mwclass/misc.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwclass/potion.cpp | 2 +- apps/openmw/mwclass/probe.cpp | 2 +- apps/openmw/mwclass/repair.cpp | 2 +- apps/openmw/mwclass/weapon.cpp | 2 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 7 +- apps/openmw/mwdialogue/journalimp.cpp | 2 +- apps/openmw/mwgui/alchemywindow.cpp | 5 +- apps/openmw/mwgui/alchemywindow.hpp | 2 +- apps/openmw/mwgui/birth.cpp | 4 +- apps/openmw/mwgui/birth.hpp | 5 +- apps/openmw/mwgui/bookwindow.cpp | 4 +- apps/openmw/mwgui/bookwindow.hpp | 3 +- apps/openmw/mwgui/charactercreation.cpp | 2 +- apps/openmw/mwgui/charactercreation.hpp | 8 +- apps/openmw/mwgui/class.cpp | 20 +- apps/openmw/mwgui/class.hpp | 21 +- apps/openmw/mwgui/confirmationdialog.cpp | 2 +- apps/openmw/mwgui/confirmationdialog.hpp | 2 +- apps/openmw/mwgui/container.cpp | 4 +- apps/openmw/mwgui/container.hpp | 2 +- apps/openmw/mwgui/countdialog.cpp | 2 +- apps/openmw/mwgui/countdialog.hpp | 2 +- apps/openmw/mwgui/dialogue.cpp | 4 +- apps/openmw/mwgui/dialogue.hpp | 2 +- apps/openmw/mwgui/dialogue_history.cpp | 5 +- apps/openmw/mwgui/hud.cpp | 2 +- apps/openmw/mwgui/inventorywindow.cpp | 4 +- apps/openmw/mwgui/inventorywindow.hpp | 2 +- apps/openmw/mwgui/journalwindow.cpp | 5 +- apps/openmw/mwgui/journalwindow.hpp | 4 +- apps/openmw/mwgui/mainmenu.cpp | 4 +- apps/openmw/mwgui/map_window.cpp | 5 +- apps/openmw/mwgui/map_window.hpp | 4 +- apps/openmw/mwgui/messagebox.cpp | 2 +- apps/openmw/mwgui/messagebox.hpp | 28 +- apps/openmw/mwgui/race.cpp | 4 +- apps/openmw/mwgui/race.hpp | 2 +- apps/openmw/mwgui/review.cpp | 4 +- apps/openmw/mwgui/review.hpp | 2 +- apps/openmw/mwgui/scrollwindow.cpp | 4 +- apps/openmw/mwgui/scrollwindow.hpp | 2 +- apps/openmw/mwgui/settingswindow.cpp | 4 +- apps/openmw/mwgui/settingswindow.hpp | 3 +- apps/openmw/mwgui/spellwindow.cpp | 4 +- apps/openmw/mwgui/spellwindow.hpp | 2 +- apps/openmw/mwgui/stats_window.cpp | 4 +- apps/openmw/mwgui/stats_window.hpp | 3 +- apps/openmw/mwgui/text_input.cpp | 5 +- apps/openmw/mwgui/text_input.hpp | 2 +- apps/openmw/mwgui/tooltips.cpp | 4 +- apps/openmw/mwgui/tooltips.hpp | 8 +- apps/openmw/mwgui/tradewindow.cpp | 4 +- apps/openmw/mwgui/tradewindow.hpp | 2 +- apps/openmw/mwgui/widgets.cpp | 3 +- apps/openmw/mwgui/widgets.hpp | 33 ++- apps/openmw/mwgui/window_base.cpp | 5 +- apps/openmw/mwgui/window_base.hpp | 11 +- apps/openmw/mwgui/window_manager.hpp | 274 ------------------ apps/openmw/mwgui/window_pinnable_base.cpp | 6 +- apps/openmw/mwgui/window_pinnable_base.hpp | 5 +- ...indow_manager.cpp => windowmanagerimp.cpp} | 2 +- apps/openmw/mwgui/windowmanagerimp.hpp | 254 ++++++++++++++++ apps/openmw/mwinput/inputmanagerimp.cpp | 8 +- apps/openmw/mwinput/inputmanagerimp.hpp | 4 +- .../mwmechanics/mechanicsmanagerimp.cpp | 7 +- apps/openmw/mwrender/localmap.cpp | 3 +- apps/openmw/mwrender/renderingmanager.cpp | 3 +- apps/openmw/mwscript/guiextensions.cpp | 3 +- apps/openmw/mwscript/interpretercontext.cpp | 3 +- apps/openmw/mwworld/actionalchemy.cpp | 2 +- apps/openmw/mwworld/actionopen.cpp | 3 +- apps/openmw/mwworld/actionread.cpp | 3 +- apps/openmw/mwworld/actiontake.cpp | 3 +- apps/openmw/mwworld/scene.cpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 3 +- 93 files changed, 662 insertions(+), 484 deletions(-) create mode 100644 apps/openmw/mwbase/windowmanager.hpp delete mode 100644 apps/openmw/mwgui/window_manager.hpp rename apps/openmw/mwgui/{window_manager.cpp => windowmanagerimp.cpp} (99%) create mode 100644 apps/openmw/mwgui/windowmanagerimp.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 7bd7f1882e..02fe0b72c7 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -25,7 +25,7 @@ add_openmw_dir (mwinput ) add_openmw_dir (mwgui - text_input widgets race class birth review window_manager console dialogue + text_input widgets race class birth review windowmanagerimp console dialogue dialogue_history window_base stats_window messagebox journalwindow charactercreation map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow @@ -66,7 +66,7 @@ add_openmw_dir (mwmechanics add_openmw_dir (mwbase environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager - inputmanager + inputmanager windowmanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index c484c22ea2..75835120fd 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -15,7 +15,7 @@ #include "mwinput/inputmanagerimp.hpp" -#include "mwgui/window_manager.hpp" +#include "mwgui/windowmanagerimp.hpp" #include "mwgui/cursorreplace.hpp" #include "mwscript/scriptmanagerimp.hpp" diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 28e71fcf0c..8d786db910 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -10,6 +10,7 @@ #include "soundmanager.hpp" #include "mechanicsmanager.hpp" #include "inputmanager.hpp" +#include "windowmanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -42,7 +43,7 @@ void MWBase::Environment::setScriptManager (ScriptManager *scriptManager) mScriptManager = scriptManager; } -void MWBase::Environment::setWindowManager (MWGui::WindowManager *windowManager) +void MWBase::Environment::setWindowManager (WindowManager *windowManager) { mWindowManager = windowManager; } @@ -90,7 +91,7 @@ MWBase::ScriptManager *MWBase::Environment::getScriptManager() const return mScriptManager; } -MWGui::WindowManager *MWBase::Environment::getWindowManager() const +MWBase::WindowManager *MWBase::Environment::getWindowManager() const { assert (mWindowManager); return mWindowManager; @@ -145,6 +146,10 @@ void MWBase::Environment::cleanup() delete mScriptManager; mScriptManager = 0; +/// \todo Re-enable (currently throwing an exception) +// delete mWindowManager; + mWindowManager = 0; + delete mWorld; mWorld = 0; } diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 4a21b57a4e..a80e7ef870 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -1,11 +1,6 @@ #ifndef GAME_BASE_INVIRONMENT_H #define GAME_BASE_INVIRONMENT_H -namespace MWGui -{ - class WindowManager; -} - namespace MWBase { class World; @@ -15,6 +10,7 @@ namespace MWBase class SoundManager; class MechanicsManager; class InputManager; + class WindowManager; /// \brief Central hub for mw-subsystems /// @@ -29,7 +25,7 @@ namespace MWBase World *mWorld; SoundManager *mSoundManager; ScriptManager *mScriptManager; - MWGui::WindowManager *mWindowManager; + WindowManager *mWindowManager; MechanicsManager *mMechanicsManager; DialogueManager *mDialogueManager; Journal *mJournal; @@ -54,7 +50,7 @@ namespace MWBase void setScriptManager (MWBase::ScriptManager *scriptManager); - void setWindowManager (MWGui::WindowManager *windowManager); + void setWindowManager (WindowManager *windowManager); void setMechanicsManager (MechanicsManager *mechanicsManager); @@ -73,7 +69,7 @@ namespace MWBase ScriptManager *getScriptManager() const; - MWGui::WindowManager *getWindowManager() const; + WindowManager *getWindowManager() const; MechanicsManager *getMechanicsManager() const; diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp new file mode 100644 index 0000000000..931353a900 --- /dev/null +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -0,0 +1,210 @@ +#ifndef GAME_MWBASE_WINDOWMANAGER_H +#define GAME_MWBASE_WINDOWMANAGER_H + +#include +#include +#include + +#include + +#include "../mwmechanics/stat.hpp" + +#include "../mwgui/mode.hpp" + +namespace MyGUI +{ + class Gui; + class Widget; + class UString; +} + +namespace OEngine +{ + namespace GUI + { + class Layout; + } +} + +namespace ESM +{ + struct Class; +} + +namespace MWWorld +{ + class CellStore; + class Ptr; +} + +namespace MWGui +{ + class Console; + class SpellWindow; + class TradeWindow; + class ConfirmationDialog; + class CountDialog; + class ScrollWindow; + class BookWindow; + class InventoryWindow; + class ContainerWindow; + class DialogueWindow; +} + +namespace MWBase +{ + /// \brief Interface for widnow manager (implemented in MWGui) + class WindowManager + { + WindowManager (const WindowManager&); + ///< not implemented + + WindowManager& operator= (const WindowManager&); + ///< not implemented + + public: + + typedef std::vector SkillList; + + WindowManager() {} + + virtual ~WindowManager() {} + + /** + * Should be called each frame to update windows/gui elements. + * This could mean updating sizes of gui elements or opening + * new dialogs. + */ + virtual void update() = 0; + + virtual void pushGuiMode (MWGui::GuiMode mode) = 0; + virtual void popGuiMode() = 0; + + virtual void removeGuiMode (MWGui::GuiMode mode) = 0; + ///< can be anywhere in the stack + + virtual MWGui::GuiMode getMode() const = 0; + + virtual bool isGuiMode() const = 0; + + virtual void toggleVisible (MWGui::GuiWindow wnd) = 0; + + /// Disallow all inventory mode windows + virtual void disallowAll() = 0; + + /// Allow one or more windows + virtual void allow (MWGui::GuiWindow wnd) = 0; + + virtual bool isAllowed (MWGui::GuiWindow wnd) const = 0; + + /// \todo investigate, if we really need to expose every single lousy UI element to the outside world + virtual MWGui::DialogueWindow* getDialogueWindow() = 0; + virtual MWGui::ContainerWindow* getContainerWindow() = 0; + virtual MWGui::InventoryWindow* getInventoryWindow() = 0; + virtual MWGui::BookWindow* getBookWindow() = 0; + virtual MWGui::ScrollWindow* getScrollWindow() = 0; + virtual MWGui::CountDialog* getCountDialog() = 0; + virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0; + virtual MWGui::TradeWindow* getTradeWindow() = 0; + virtual MWGui::SpellWindow* getSpellWindow() = 0; + virtual MWGui::Console* getConsole() = 0; + + virtual MyGUI::Gui* getGui() const = 0; + + virtual void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) = 0; + + /// Set value for the given ID. + virtual void setValue (const std::string& id, const MWMechanics::Stat& value) = 0; + virtual void setValue (int parSkill, const MWMechanics::Stat& value) = 0; + virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value) = 0; + virtual void setValue (const std::string& id, const std::string& value) = 0; + virtual void setValue (const std::string& id, int value) = 0; + + virtual void setPlayerClass (const ESM::Class &class_) = 0; + ///< set current class of player + + virtual void configureSkills (const SkillList& major, const SkillList& minor) = 0; + ///< configure skill groups, each set contains the skill ID for that group. + + virtual void setReputation (int reputation) = 0; + ///< set the current reputation value + + virtual void setBounty (int bounty) = 0; + ///< set the current bounty value + + virtual void updateSkillArea() = 0; + ///< update display of skills, factions, birth sign, reputation and bounty + + virtual void changeCell(MWWorld::CellStore* cell) = 0; + ///< change the active cell + + virtual void setPlayerPos(const float x, const float y) = 0; + ///< set player position in map space + + virtual void setPlayerDir(const float x, const float y) = 0; + ///< set player view direction in map space + + virtual void setFocusObject(const MWWorld::Ptr& focus) = 0; + virtual void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) = 0; + + virtual void setMouseVisible(bool visible) = 0; + virtual void getMousePosition(int &x, int &y) = 0; + virtual void getMousePosition(float &x, float &y) = 0; + virtual void setDragDrop(bool dragDrop) = 0; + virtual bool getWorldMouseOver() = 0; + + virtual void toggleFogOfWar() = 0; + + virtual void toggleFullHelp() = 0; + ///< show extra info in item tooltips (owner, script) + + virtual bool getFullHelp() const = 0; + + virtual void setInteriorMapTexture(const int x, const int y) = 0; + ///< set the index of the map texture that should be used (for interiors) + + /// sets the visibility of the hud health/magicka/stamina bars + virtual void setHMSVisibility(bool visible) = 0; + + /// sets the visibility of the hud minimap + virtual void setMinimapVisibility(bool visible) = 0; + virtual void setWeaponVisibility(bool visible) = 0; + virtual void setSpellVisibility(bool visible) = 0; + + virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; + virtual void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) = 0; + virtual void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) = 0; + virtual void unsetSelectedSpell() = 0; + virtual void unsetSelectedWeapon() = 0; + + virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0; + ///< Hides dialog and schedules dialog to be deleted. + + virtual void messageBox (const std::string& message, const std::vector& buttons) = 0; + virtual int readPressedButton() = 0; + ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) + + virtual void onFrame (float frameDuration) = 0; + + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. + virtual std::map > getPlayerSkillValues() = 0; + virtual std::map > getPlayerAttributeValues() = 0; + virtual SkillList getPlayerMinorSkills() = 0; + virtual SkillList getPlayerMajorSkills() = 0; + + /** + * Fetches a GMST string from the store, if there is no setting with the given + * ID or it is not a string the default string is returned. + * + * @param id Identifier for the GMST setting, e.g. "aName" + * @param default Default value if the GMST setting cannot be used. + */ + virtual const std::string &getGameSettingString(const std::string &id, const std::string &default_) = 0; + + virtual void processChangedSettings(const Settings::CategorySettingVector& changed) = 0; + + virtual void executeInConsole (const std::string& path) = 0; + }; +} + +#endif diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 9b0082efc1..7b8cbd3dad 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -4,6 +4,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld//cellstore.hpp" #include "../mwworld/ptr.hpp" @@ -12,7 +13,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace MWClass @@ -34,7 +34,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Activator::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -104,4 +104,3 @@ namespace MWClass return MWWorld::Ptr(&cell.activators.insert(*ref), &cell); } } - diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 3c22cc5fb2..8a117af5dc 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -16,7 +17,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace MWClass diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 43c1e0a436..c48d7ff4db 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -19,7 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace MWClass diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 29a53140a8..1e187342d9 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actionread.hpp" @@ -15,7 +16,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace MWClass diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index ef91b0fac4..597eb22fec 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -15,7 +16,6 @@ #include "../mwworld/physicssystem.hpp" #include "../mwgui/tooltips.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 7d31b945d5..ad6da90dd3 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/nullaction.hpp" @@ -15,7 +16,6 @@ #include "../mwworld/actionopen.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 57301e1a25..a22c2d661d 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -17,7 +18,6 @@ #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 19eda16ef1..3b283a9d1b 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/ptr.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index eb92accb61..8f9f8a315c 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -6,13 +6,13 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index ed8fd1de60..40ecf0a0f2 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -15,7 +16,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 1472b7e8bd..8fdd95760d 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 8b5b414955..2cd0f63a14 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -15,7 +16,6 @@ #include "../mwworld/physicssystem.hpp" #include "../mwworld/manualref.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 30173701ec..94c98ba425 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -12,6 +12,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" @@ -26,7 +27,6 @@ #include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 27903fdf73..6bff855536 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/physicssystem.hpp" #include "../mwworld/player.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 8a563865d8..25aae445bd 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 096d3d0eee..ebba8c44e0 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -6,13 +6,13 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index bc8e3e648f..2a9863cdf3 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 251577d3bf..acd7868921 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -13,6 +13,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwbase/journal.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/refdata.hpp" @@ -20,22 +21,20 @@ #include "../mwworld/containerstore.hpp" #include "../mwgui/dialogue.hpp" -#include "../mwgui/window_manager.hpp" #include -#include "../mwscript/extensions.hpp" - #include #include #include #include #include +#include #include #include "../mwscript/compilercontext.hpp" #include "../mwscript/interpretercontext.hpp" -#include +#include "../mwscript/extensions.hpp" #include "../mwclass/npc.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index dc322cade8..d626cd3159 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -5,8 +5,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/messagebox.hpp" namespace MWDialogue diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 81e5640d44..5cf09b18a3 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -5,13 +5,12 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/containerstore.hpp" -#include "window_manager.hpp" - namespace { std::string getIconPath(MWWorld::Ptr ptr) @@ -27,7 +26,7 @@ namespace namespace MWGui { - AlchemyWindow::AlchemyWindow(WindowManager& parWindowManager) + AlchemyWindow::AlchemyWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_alchemy_window.layout", parWindowManager) , ContainerBase(0) { diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index 81c33a96df..53e7178d50 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -10,7 +10,7 @@ namespace MWGui class AlchemyWindow : public WindowBase, public ContainerBase { public: - AlchemyWindow(WindowManager& parWindowManager); + AlchemyWindow(MWBase::WindowManager& parWindowManager); virtual void open(); diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 023702c8b8..05a337cbce 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -7,14 +7,14 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "window_manager.hpp" #include "widgets.hpp" using namespace MWGui; using namespace Widgets; -BirthDialog::BirthDialog(WindowManager& parWindowManager) +BirthDialog::BirthDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_birth.layout", parWindowManager) { // Centre dialog diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index 770e4ba36c..92665081db 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -10,14 +10,13 @@ namespace MWGui { + /// \todo remove using namespace MyGUI; - class WindowManager; - class BirthDialog : public WindowBase { public: - BirthDialog(WindowManager& parWindowManager); + BirthDialog(MWBase::WindowManager& parWindowManager); enum Gender { diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 92f0226ed1..57e59657a7 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -5,16 +5,16 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" #include "formatting.hpp" -#include "window_manager.hpp" using namespace MWGui; -BookWindow::BookWindow (WindowManager& parWindowManager) : +BookWindow::BookWindow (MWBase::WindowManager& parWindowManager) : WindowBase("openmw_book.layout", parWindowManager) { getWidget(mCloseButton, "CloseButton"); diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index 9ea0114338..fedb783b25 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -10,7 +10,7 @@ namespace MWGui class BookWindow : public WindowBase { public: - BookWindow(WindowManager& parWindowManager); + BookWindow(MWBase::WindowManager& parWindowManager); void open(MWWorld::Ptr book); void setTakeButtonShow(bool show); @@ -43,4 +43,3 @@ namespace MWGui } #endif - diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 72394de80d..8c82b3e43d 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -116,7 +116,7 @@ namespace using namespace MWGui; -CharacterCreation::CharacterCreation(WindowManager* _wm) +CharacterCreation::CharacterCreation(MWBase::WindowManager* _wm) : mNameDialog(0) , mRaceDialog(0) , mClassChoiceDialog(0) diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index 1fd67bff7b..d65763d0cd 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -1,17 +1,15 @@ #ifndef CHARACTER_CREATION_HPP #define CHARACTER_CREATION_HPP -#include "window_manager.hpp" - #include #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwmechanics/stat.hpp" namespace MWGui { - class WindowManager; class WindowBase; class TextInputDialog; @@ -31,7 +29,7 @@ namespace MWGui public: typedef std::vector SkillList; - CharacterCreation(WindowManager* _wm); + CharacterCreation(MWBase::WindowManager* _wm); ~CharacterCreation(); //Show a dialog @@ -60,7 +58,7 @@ namespace MWGui BirthDialog* mBirthSignDialog; ReviewDialog* mReviewDialog; - WindowManager* mWM; + MWBase::WindowManager* mWM; //Player data std::string mPlayerName; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 7e3194d6be..eaf1918198 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -9,8 +9,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "window_manager.hpp" #include "tooltips.hpp" #undef min @@ -20,7 +20,7 @@ using namespace MWGui; /* GenerateClassResultDialog */ -GenerateClassResultDialog::GenerateClassResultDialog(WindowManager& parWindowManager) +GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_generate_class_result.layout", parWindowManager) { // Centre dialog @@ -77,7 +77,7 @@ void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) /* PickClassDialog */ -PickClassDialog::PickClassDialog(WindowManager& parWindowManager) +PickClassDialog::PickClassDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_class.layout", parWindowManager) { // Centre dialog @@ -283,7 +283,7 @@ void InfoBoxDialog::layoutVertically(MyGUI::WidgetPtr widget, int margin) widget->setSize(width, pos); } -InfoBoxDialog::InfoBoxDialog(WindowManager& parWindowManager) +InfoBoxDialog::InfoBoxDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_infobox.layout", parWindowManager) , mCurrentButton(-1) { @@ -367,7 +367,7 @@ void InfoBoxDialog::onButtonClicked(MyGUI::WidgetPtr _sender) /* ClassChoiceDialog */ -ClassChoiceDialog::ClassChoiceDialog(WindowManager& parWindowManager) +ClassChoiceDialog::ClassChoiceDialog(MWBase::WindowManager& parWindowManager) : InfoBoxDialog(parWindowManager) { setText(""); @@ -381,7 +381,7 @@ ClassChoiceDialog::ClassChoiceDialog(WindowManager& parWindowManager) /* CreateClassDialog */ -CreateClassDialog::CreateClassDialog(WindowManager& parWindowManager) +CreateClassDialog::CreateClassDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_create_class.layout", parWindowManager) , mSpecDialog(nullptr) , mAttribDialog(nullptr) @@ -694,7 +694,7 @@ void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) /* SelectSpecializationDialog */ -SelectSpecializationDialog::SelectSpecializationDialog(WindowManager& parWindowManager) +SelectSpecializationDialog::SelectSpecializationDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_select_specialization.layout", parWindowManager) { // Centre dialog @@ -759,7 +759,7 @@ void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) /* SelectAttributeDialog */ -SelectAttributeDialog::SelectAttributeDialog(WindowManager& parWindowManager) +SelectAttributeDialog::SelectAttributeDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_select_attribute.layout", parWindowManager) { // Centre dialog @@ -811,7 +811,7 @@ void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) /* SelectSkillDialog */ -SelectSkillDialog::SelectSkillDialog(WindowManager& parWindowManager) +SelectSkillDialog::SelectSkillDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_select_skill.layout", parWindowManager) { // Centre dialog @@ -907,7 +907,7 @@ void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) /* DescriptionDialog */ -DescriptionDialog::DescriptionDialog(WindowManager& parWindowManager) +DescriptionDialog::DescriptionDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_class_description.layout", parWindowManager) { // Centre dialog diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 4d8d9fa231..4baceed1ef 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -12,14 +12,13 @@ namespace MWGui { + /// \todo remove! using namespace MyGUI; - class WindowManager; - class InfoBoxDialog : public WindowBase { public: - InfoBoxDialog(WindowManager& parWindowManager); + InfoBoxDialog(MWBase::WindowManager& parWindowManager); typedef std::vector ButtonList; @@ -64,13 +63,13 @@ namespace MWGui Class_Create = 2, Class_Back = 3 }; - ClassChoiceDialog(WindowManager& parWindowManager); + ClassChoiceDialog(MWBase::WindowManager& parWindowManager); }; class GenerateClassResultDialog : public WindowBase { public: - GenerateClassResultDialog(WindowManager& parWindowManager); + GenerateClassResultDialog(MWBase::WindowManager& parWindowManager); std::string getClassId() const; void setClassId(const std::string &classId); @@ -99,7 +98,7 @@ namespace MWGui class PickClassDialog : public WindowBase { public: - PickClassDialog(WindowManager& parWindowManager); + PickClassDialog(MWBase::WindowManager& parWindowManager); const std::string &getClassId() const { return mCurrentClassId; } void setClassId(const std::string &classId); @@ -138,7 +137,7 @@ namespace MWGui class SelectSpecializationDialog : public WindowBase { public: - SelectSpecializationDialog(WindowManager& parWindowManager); + SelectSpecializationDialog(MWBase::WindowManager& parWindowManager); ~SelectSpecializationDialog(); ESM::Class::Specialization getSpecializationId() const { return mSpecializationId; } @@ -169,7 +168,7 @@ namespace MWGui class SelectAttributeDialog : public WindowBase { public: - SelectAttributeDialog(WindowManager& parWindowManager); + SelectAttributeDialog(MWBase::WindowManager& parWindowManager); ~SelectAttributeDialog(); ESM::Attribute::AttributeID getAttributeId() const { return mAttributeId; } @@ -202,7 +201,7 @@ namespace MWGui class SelectSkillDialog : public WindowBase { public: - SelectSkillDialog(WindowManager& parWindowManager); + SelectSkillDialog(MWBase::WindowManager& parWindowManager); ~SelectSkillDialog(); ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } @@ -238,7 +237,7 @@ namespace MWGui class DescriptionDialog : public WindowBase { public: - DescriptionDialog(WindowManager& parWindowManager); + DescriptionDialog(MWBase::WindowManager& parWindowManager); ~DescriptionDialog(); std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } @@ -254,7 +253,7 @@ namespace MWGui class CreateClassDialog : public WindowBase { public: - CreateClassDialog(WindowManager& parWindowManager); + CreateClassDialog(MWBase::WindowManager& parWindowManager); virtual ~CreateClassDialog(); std::string getName() const; diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 00bdca6382..1c68da9e57 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -7,7 +7,7 @@ namespace MWGui { - ConfirmationDialog::ConfirmationDialog(WindowManager& parWindowManager) : + ConfirmationDialog::ConfirmationDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_confirmation_dialog.layout", parWindowManager) { getWidget(mMessage, "Message"); diff --git a/apps/openmw/mwgui/confirmationdialog.hpp b/apps/openmw/mwgui/confirmationdialog.hpp index d278274a03..a78028b1c4 100644 --- a/apps/openmw/mwgui/confirmationdialog.hpp +++ b/apps/openmw/mwgui/confirmationdialog.hpp @@ -8,7 +8,7 @@ namespace MWGui class ConfirmationDialog : public WindowBase { public: - ConfirmationDialog(WindowManager& parWindowManager); + ConfirmationDialog(MWBase::WindowManager& parWindowManager); void open(const std::string& message); typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 89cd102339..75e2bb3b85 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -11,6 +11,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/containerstore.hpp" @@ -19,7 +20,6 @@ #include "../mwclass/container.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "countdialog.hpp" #include "tradewindow.hpp" @@ -591,7 +591,7 @@ MWWorld::ContainerStore& ContainerBase::getContainerStore() // ------------------------------------------------------------------------------------------------ -ContainerWindow::ContainerWindow(WindowManager& parWindowManager,DragAndDrop* dragAndDrop) +ContainerWindow::ContainerWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop) : ContainerBase(dragAndDrop) , WindowBase("openmw_container_window.layout", parWindowManager) { diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 88e445a7bb..27c3288ae7 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -127,7 +127,7 @@ namespace MWGui class ContainerWindow : public ContainerBase, public WindowBase { public: - ContainerWindow(WindowManager& parWindowManager,DragAndDrop* dragAndDrop); + ContainerWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop); virtual ~ContainerWindow(); diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 07f1acb73b..6d94153540 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -7,7 +7,7 @@ namespace MWGui { - CountDialog::CountDialog(WindowManager& parWindowManager) : + CountDialog::CountDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_count_window.layout", parWindowManager) { getWidget(mSlider, "CountSlider"); diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index aac17b846d..6002dadfed 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -8,7 +8,7 @@ namespace MWGui class CountDialog : public WindowBase { public: - CountDialog(WindowManager& parWindowManager); + CountDialog(MWBase::WindowManager& parWindowManager); void open(const std::string& item, const std::string& message, const int maxCount); typedef MyGUI::delegates::CMultiDelegate2 EventHandle_WidgetInt; diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 30089dd465..4342b1130a 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -11,9 +11,9 @@ #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "dialogue_history.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "list.hpp" #include "tradewindow.hpp" @@ -42,7 +42,7 @@ std::string::size_type find_str_ci(const std::string& str, const std::string& su } -DialogueWindow::DialogueWindow(WindowManager& parWindowManager) +DialogueWindow::DialogueWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_dialogue_window.layout", parWindowManager) , mEnabled(true) , mShowTrade(false) diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index e2824bead0..e7f2b076cd 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -29,7 +29,7 @@ namespace MWGui class DialogueWindow: public WindowBase, public ReferenceInterface { public: - DialogueWindow(WindowManager& parWindowManager); + DialogueWindow(MWBase::WindowManager& parWindowManager); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/dialogue_history.cpp b/apps/openmw/mwgui/dialogue_history.cpp index 009f42044d..f72f199ea3 100644 --- a/apps/openmw/mwgui/dialogue_history.cpp +++ b/apps/openmw/mwgui/dialogue_history.cpp @@ -1,5 +1,7 @@ #include "dialogue_history.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" + #include "widgets.hpp" #include "components/esm_store/store.hpp" @@ -71,4 +73,3 @@ void DialogueHistory::addDialogText(const UString& parText) addText(parText); addText("\n"); } - diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 1c79433869..bdbb316b05 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -9,6 +9,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -16,7 +17,6 @@ #include "../mwgui/widgets.hpp" #include "inventorywindow.hpp" -#include "window_manager.hpp" #include "container.hpp" #include "console.hpp" diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 090b7dd6e6..7b68f7b6d6 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -10,6 +10,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" @@ -18,7 +19,6 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/inventorystore.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "bookwindow.hpp" #include "scrollwindow.hpp" @@ -40,7 +40,7 @@ namespace namespace MWGui { - InventoryWindow::InventoryWindow(WindowManager& parWindowManager,DragAndDrop* dragAndDrop) + InventoryWindow::InventoryWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop) : ContainerBase(dragAndDrop) , WindowPinnableBase("openmw_inventory_window.layout", parWindowManager) , mTrading(false) diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 82da3efea7..fbdb79977a 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -9,7 +9,7 @@ namespace MWGui class InventoryWindow : public ContainerBase, public WindowPinnableBase { public: - InventoryWindow(WindowManager& parWindowManager,DragAndDrop* dragAndDrop); + InventoryWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop); virtual void open(); diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 475a70631e..597c27c6df 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -4,11 +4,10 @@ #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwdialogue/journalentry.hpp" -#include "window_manager.hpp" - namespace { struct book @@ -82,7 +81,7 @@ book formatText(std::string text,book mBook,int maxLine, int lineSize) } -MWGui::JournalWindow::JournalWindow (WindowManager& parWindowManager) +MWGui::JournalWindow::JournalWindow (MWBase::WindowManager& parWindowManager) : WindowBase("openmw_journal.layout", parWindowManager) , mLastPos(0) , mVisible(false) diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index 0c85ebf081..fc05bbdbcd 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -10,12 +10,10 @@ namespace MWGui { - class WindowManager; - class JournalWindow : public WindowBase { public: - JournalWindow(WindowManager& parWindowManager); + JournalWindow(MWBase::WindowManager& parWindowManager); void open(); virtual void setVisible(bool visible); // only used to play close sound diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index e2fefd6497..bb6b8163e9 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -2,11 +2,9 @@ #include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "window_manager.hpp" +#include "../mwbase/windowmanager.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index 4ebeb38742..1ffedaac48 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -1,5 +1,6 @@ #include "map_window.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" #include @@ -154,7 +155,7 @@ void LocalMapBase::setPlayerDir(const float x, const float y) // ------------------------------------------------------------------------------------------ -MapWindow::MapWindow(WindowManager& parWindowManager) : +MapWindow::MapWindow(MWBase::WindowManager& parWindowManager) : MWGui::WindowPinnableBase("openmw_map_window.layout", parWindowManager), mGlobal(false) { diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/map_window.hpp index 8d3392b827..447c16901a 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/map_window.hpp @@ -45,11 +45,11 @@ namespace MWGui class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase { public: - MapWindow(WindowManager& parWindowManager); + MapWindow(MWBase::WindowManager& parWindowManager); virtual ~MapWindow(){} void setCellName(const std::string& cellName); - + private: void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 2b00ca05ce..b660af7dd4 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -2,7 +2,7 @@ using namespace MWGui; -MessageBoxManager::MessageBoxManager (WindowManager *windowManager) +MessageBoxManager::MessageBoxManager (MWBase::WindowManager *windowManager) { mWindowManager = windowManager; // defines diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 75393ec947..5e4c468d56 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -5,13 +5,13 @@ #include #include "window_base.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" #undef MessageBox namespace MWGui { - class InteractiveMessageBox; class MessageBoxManager; class MessageBox; @@ -25,27 +25,27 @@ namespace MWGui class MessageBoxManager { public: - MessageBoxManager (WindowManager* windowManager); + MessageBoxManager (MWBase::WindowManager* windowManager); void onFrame (float frameDuration); void createMessageBox (const std::string& message); bool createInteractiveMessageBox (const std::string& message, const std::vector& buttons); bool isInteractiveMessageBox (); - + void removeMessageBox (float time, MessageBox *msgbox); bool removeMessageBox (MessageBox *msgbox); void setMessageBoxSpeed (int speed); - + int readPressedButton (); - - WindowManager *mWindowManager; - + + MWBase::WindowManager *mWindowManager; + private: std::vector mMessageBoxes; InteractiveMessageBox* mInterMessageBoxe; std::vector mTimers; float mMessageBoxSpeed; }; - + class MessageBox : public OEngine::GUI::Layout { public: @@ -53,9 +53,9 @@ namespace MWGui void setMessage (const std::string& message); int getHeight (); void update (int height); - + bool mMarkedToDelete; - + protected: MessageBoxManager& mMessageBoxManager; int mHeight; @@ -65,16 +65,16 @@ namespace MWGui int mBottomPadding; int mNextBoxPadding; }; - + class InteractiveMessageBox : public OEngine::GUI::Layout { public: InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons); void mousePressed (MyGUI::Widget* _widget); int readPressedButton (); - + bool mMarkedToDelete; - + private: MessageBoxManager& mMessageBoxManager; MyGUI::EditPtr mMessageWidget; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 62434cb91a..ceb0452fba 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -10,15 +10,15 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "tooltips.hpp" using namespace MWGui; using namespace Widgets; -RaceDialog::RaceDialog(WindowManager& parWindowManager) +RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_race.layout", parWindowManager) , mGenderIndex(0) , mFaceIndex(0) diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index b523b86902..3da6b0ace8 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -24,7 +24,7 @@ namespace MWGui class RaceDialog : public WindowBase { public: - RaceDialog(WindowManager& parWindowManager); + RaceDialog(MWBase::WindowManager& parWindowManager); enum Gender { diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 997899c526..8dd894c258 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -9,8 +9,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "tooltips.hpp" @@ -22,7 +22,7 @@ using namespace Widgets; const int ReviewDialog::sLineHeight = 18; -ReviewDialog::ReviewDialog(WindowManager& parWindowManager) +ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_review.layout", parWindowManager) , mLastPos(0) { diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 27b1670333..f0bde6ecde 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -30,7 +30,7 @@ namespace MWGui }; typedef std::vector SkillList; - ReviewDialog(WindowManager& parWindowManager); + ReviewDialog(MWBase::WindowManager& parWindowManager); void setPlayerName(const std::string &name); void setRace(const std::string &raceId); diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index fb2239c6da..ff83b0e3e4 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -3,16 +3,16 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" #include "formatting.hpp" -#include "window_manager.hpp" using namespace MWGui; -ScrollWindow::ScrollWindow (WindowManager& parWindowManager) : +ScrollWindow::ScrollWindow (MWBase::WindowManager& parWindowManager) : WindowBase("openmw_scroll.layout", parWindowManager) { getWidget(mTextView, "TextView"); diff --git a/apps/openmw/mwgui/scrollwindow.hpp b/apps/openmw/mwgui/scrollwindow.hpp index d58596b4be..b8f52fb658 100644 --- a/apps/openmw/mwgui/scrollwindow.hpp +++ b/apps/openmw/mwgui/scrollwindow.hpp @@ -10,7 +10,7 @@ namespace MWGui class ScrollWindow : public WindowBase { public: - ScrollWindow (WindowManager& parWindowManager); + ScrollWindow (MWBase::WindowManager& parWindowManager); void open (MWWorld::Ptr scroll); void setTakeButtonShow(bool show); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 06177aaa8d..f6597a64ef 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -14,10 +14,10 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/inputmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwrender/renderingmanager.hpp" -#include "window_manager.hpp" #include "confirmationdialog.hpp" namespace @@ -81,7 +81,7 @@ namespace namespace MWGui { - SettingsWindow::SettingsWindow(WindowManager& parWindowManager) : + SettingsWindow::SettingsWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_settings_window.layout", parWindowManager) { getWidget(mOkButton, "OkButton"); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 63fbed46b6..ca11b6f9c2 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -13,7 +13,7 @@ namespace MWGui class SettingsWindow : public WindowBase { public: - SettingsWindow(WindowManager& parWindowManager); + SettingsWindow(MWBase::WindowManager& parWindowManager); private: static int const sFovMin = 30; @@ -77,4 +77,3 @@ namespace MWGui } #endif - diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index b528eecc22..8754f5d105 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -9,6 +9,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" @@ -17,7 +18,6 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" -#include "window_manager.hpp" #include "inventorywindow.hpp" #include "confirmationdialog.hpp" @@ -42,7 +42,7 @@ namespace namespace MWGui { - SpellWindow::SpellWindow(WindowManager& parWindowManager) + SpellWindow::SpellWindow(MWBase::WindowManager& parWindowManager) : WindowPinnableBase("openmw_spell_window.layout", parWindowManager) , mHeight(0) , mWidth(0) diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index 87e4783ba2..caa67fd740 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -8,7 +8,7 @@ namespace MWGui class SpellWindow : public WindowPinnableBase { public: - SpellWindow(WindowManager& parWindowManager); + SpellWindow(MWBase::WindowManager& parWindowManager); void updateSpells(); diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 4ee56abc7f..32dac71deb 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -9,20 +9,20 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" -#include "window_manager.hpp" #include "tooltips.hpp" using namespace MWGui; const int StatsWindow::sLineHeight = 18; -StatsWindow::StatsWindow (WindowManager& parWindowManager) +StatsWindow::StatsWindow (MWBase::WindowManager& parWindowManager) : WindowPinnableBase("openmw_stats_window.layout", parWindowManager) , mSkillAreaWidget(NULL) , mSkillClientWidget(NULL) diff --git a/apps/openmw/mwgui/stats_window.hpp b/apps/openmw/mwgui/stats_window.hpp index 2469c12e9c..a46ab7531d 100644 --- a/apps/openmw/mwgui/stats_window.hpp +++ b/apps/openmw/mwgui/stats_window.hpp @@ -22,7 +22,7 @@ namespace MWGui typedef std::vector SkillList; - StatsWindow(WindowManager& parWindowManager); + StatsWindow(MWBase::WindowManager& parWindowManager); /// automatically updates all the data in the stats window, but only if it has changed. void onFrame(); @@ -82,4 +82,3 @@ namespace MWGui }; } #endif - diff --git a/apps/openmw/mwgui/text_input.cpp b/apps/openmw/mwgui/text_input.cpp index 7d5b0cc6db..802cd30637 100644 --- a/apps/openmw/mwgui/text_input.cpp +++ b/apps/openmw/mwgui/text_input.cpp @@ -1,9 +1,10 @@ #include "text_input.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" using namespace MWGui; -TextInputDialog::TextInputDialog(WindowManager& parWindowManager) +TextInputDialog::TextInputDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_text_input.layout", parWindowManager) { // Centre dialog diff --git a/apps/openmw/mwgui/text_input.hpp b/apps/openmw/mwgui/text_input.hpp index 835d5deaa5..7a33257224 100644 --- a/apps/openmw/mwgui/text_input.hpp +++ b/apps/openmw/mwgui/text_input.hpp @@ -18,7 +18,7 @@ namespace MWGui class TextInputDialog : public WindowBase { public: - TextInputDialog(WindowManager& parWindowManager); + TextInputDialog(MWBase::WindowManager& parWindowManager); std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); } diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 679c7a59eb..edc787cef7 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -8,16 +8,16 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" -#include "window_manager.hpp" #include "widgets.hpp" using namespace MWGui; using namespace MyGUI; -ToolTips::ToolTips(WindowManager* windowManager) : +ToolTips::ToolTips(MWBase::WindowManager* windowManager) : Layout("openmw_tooltips.layout") , mGameMode(true) , mWindowManager(windowManager) diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 036bdfaa34..700f5f7238 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -9,8 +9,6 @@ namespace MWGui { - class WindowManager; - // Info about tooltip that is supplied by the MWWorld::Class object struct ToolTipInfo { @@ -29,7 +27,7 @@ namespace MWGui class ToolTips : public OEngine::GUI::Layout { public: - ToolTips(WindowManager* windowManager); + ToolTips(MWBase::WindowManager* windowManager); void onFrame(float frameDuration); @@ -45,7 +43,7 @@ namespace MWGui void setFocusObject(const MWWorld::Ptr& focus); void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); - ///< set the screen-space position of the tooltip for focused object + ///< set the screen-space position of the tooltip for focused object static std::string getValueString(const int value, const std::string& prefix); ///< @return "prefix: value" or "" if value is 0 @@ -71,7 +69,7 @@ namespace MWGui private: MyGUI::Widget* mDynamicToolTipBox; - WindowManager* mWindowManager; + MWBase::WindowManager* mWindowManager; MWWorld::Ptr mFocusObject; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 75e39fd879..8471367c68 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -5,16 +5,16 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" -#include "window_manager.hpp" #include "inventorywindow.hpp" namespace MWGui { - TradeWindow::TradeWindow(WindowManager& parWindowManager) : + TradeWindow::TradeWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_trade_window.layout", parWindowManager) , ContainerBase(NULL) // no drag&drop , mCurrentBalance(0) diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index b391fd8fa2..1daeefa960 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -23,7 +23,7 @@ namespace MWGui class TradeWindow : public ContainerBase, public WindowBase { public: - TradeWindow(WindowManager& parWindowManager); + TradeWindow(MWBase::WindowManager& parWindowManager); void startTrade(MWWorld::Ptr actor); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 1529387071..40ee1c7adf 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -6,8 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #undef min #undef max diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index d4947895b1..9a2762369c 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -10,14 +10,19 @@ #undef MYGUI_EXPORT #define MYGUI_EXPORT +namespace MWBase +{ + class WindowManager; +} + /* This file contains various custom widgets used in OpenMW. */ namespace MWGui { + /// \todo remove! using namespace MyGUI; - class WindowManager; namespace Widgets { @@ -81,12 +86,12 @@ namespace MWGui typedef MWMechanics::Stat SkillValue; - void setWindowManager(WindowManager *m) { mManager = m; } + void setWindowManager(MWBase::WindowManager *m) { mManager = m; } /// \todo remove void setSkillId(ESM::Skill::SkillEnum skillId); void setSkillNumber(int skillId); void setSkillValue(const SkillValue& value); - WindowManager *getWindowManager() const { return mManager; } + MWBase::WindowManager *getWindowManager() const { return mManager; } ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } const SkillValue& getSkillValue() const { return mValue; } @@ -109,7 +114,7 @@ namespace MWGui void updateWidgets(); - WindowManager *mManager; + MWBase::WindowManager *mManager; ESM::Skill::SkillEnum mSkillId; SkillValue mValue; MyGUI::WidgetPtr mSkillNameWidget, mSkillValueWidget; @@ -124,11 +129,11 @@ namespace MWGui typedef MWMechanics::Stat AttributeValue; - void setWindowManager(WindowManager *m) { mManager = m; } + void setWindowManager(MWBase::WindowManager *m) { mManager = m; } void setAttributeId(int attributeId); void setAttributeValue(const AttributeValue& value); - WindowManager *getWindowManager() const { return mManager; } + MWBase::WindowManager *getWindowManager() const { return mManager; } int getAttributeId() const { return mId; } const AttributeValue& getAttributeValue() const { return mValue; } @@ -151,7 +156,7 @@ namespace MWGui void updateWidgets(); - WindowManager *mManager; + MWBase::WindowManager *mManager; int mId; AttributeValue mValue; MyGUI::WidgetPtr mAttributeNameWidget, mAttributeValueWidget; @@ -170,7 +175,7 @@ namespace MWGui typedef MWMechanics::Stat SpellValue; - void setWindowManager(WindowManager* parWindowManager) { mWindowManager = parWindowManager; } + void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setSpellId(const std::string &id); /** @@ -192,7 +197,7 @@ namespace MWGui private: void updateWidgets(); - WindowManager* mWindowManager; + MWBase::WindowManager* mWindowManager; std::string mId; MyGUI::TextBox* mSpellNameWidget; }; @@ -212,7 +217,7 @@ namespace MWGui EF_Constant = 0x02 // constant effect means that duration will not be displayed }; - void setWindowManager(WindowManager* parWindowManager) { mWindowManager = parWindowManager; } + void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setEffectList(const SpellEffectList& list); static SpellEffectList effectListFromESM(const ESM::EffectList* effects); @@ -234,7 +239,7 @@ namespace MWGui private: void updateWidgets(); - WindowManager* mWindowManager; + MWBase::WindowManager* mWindowManager; SpellEffectList mEffectList; }; typedef MWEffectList* MWEffectListPtr; @@ -247,7 +252,7 @@ namespace MWGui typedef ESM::ENAMstruct SpellEffectValue; - void setWindowManager(WindowManager* parWindowManager) { mWindowManager = parWindowManager; } + void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setSpellEffect(const SpellEffectParams& params); std::string effectIDToString(const short effectID); @@ -262,12 +267,12 @@ namespace MWGui virtual ~MWSpellEffect(); virtual void initialiseOverride(); - + private: void updateWidgets(); - WindowManager* mWindowManager; + MWBase::WindowManager* mWindowManager; SpellEffectParams mEffectParams; MyGUI::ImageBox* mImageWidget; MyGUI::TextBox* mTextWidget; diff --git a/apps/openmw/mwgui/window_base.cpp b/apps/openmw/mwgui/window_base.cpp index 4330579311..dbb37efbb0 100644 --- a/apps/openmw/mwgui/window_base.cpp +++ b/apps/openmw/mwgui/window_base.cpp @@ -1,11 +1,12 @@ #include "window_base.hpp" -#include "window_manager.hpp" #include +#include "../mwbase/windowmanager.hpp" + using namespace MWGui; -WindowBase::WindowBase(const std::string& parLayout, WindowManager& parWindowManager) +WindowBase::WindowBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager) : Layout(parLayout) , mWindowManager(parWindowManager) { diff --git a/apps/openmw/mwgui/window_base.hpp b/apps/openmw/mwgui/window_base.hpp index 9cfdbe2612..74d874bb82 100644 --- a/apps/openmw/mwgui/window_base.hpp +++ b/apps/openmw/mwgui/window_base.hpp @@ -3,6 +3,11 @@ #include +namespace MWBase +{ + class WindowManager; +} + namespace MWGui { class WindowManager; @@ -10,7 +15,7 @@ namespace MWGui class WindowBase: public OEngine::GUI::Layout { public: - WindowBase(const std::string& parLayout, WindowManager& parWindowManager); + WindowBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager); // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_WindowBase; @@ -25,9 +30,9 @@ namespace MWGui EventHandle_WindowBase eventDone; protected: - WindowManager& mWindowManager; + /// \todo remove + MWBase::WindowManager& mWindowManager; }; } #endif - diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp deleted file mode 100644 index db3945e55d..0000000000 --- a/apps/openmw/mwgui/window_manager.hpp +++ /dev/null @@ -1,274 +0,0 @@ -#ifndef MWGUI_WINDOWMANAGER_H -#define MWGUI_WINDOWMANAGER_H - -/** - This class owns and controls all the MW specific windows in the - GUI. It can enable/disable Gui mode, and is responsible for sending - and retrieving information from the Gui. - - MyGUI should be initialized separately before creating instances of - this class. -**/ - -#include -#include - -#include -#include - -#include "../mwmechanics/stat.hpp" - -#include "mode.hpp" - -namespace MyGUI -{ - class Gui; - class Widget; - class UString; -} - -namespace Compiler -{ - class Extensions; -} - -namespace MWWorld -{ - class Ptr; - class CellStore; -} - -namespace OEngine -{ - namespace GUI - { - class Layout; - class MyGUIManager; - } - - namespace Render - { - class OgreRenderer; - } -} - -namespace MWGui -{ - class WindowBase; - class HUD; - class MapWindow; - class MainMenu; - class StatsWindow; - class InventoryWindow; - class Console; - class JournalWindow; - class CharacterCreation; - class ContainerWindow; - class DragAndDrop; - class InventoryWindow; - class ToolTips; - class ScrollWindow; - class BookWindow; - class TextInputDialog; - class InfoBoxDialog; - class DialogueWindow; - class MessageBoxManager; - class CountDialog; - class TradeWindow; - class SettingsWindow; - class ConfirmationDialog; - class AlchemyWindow; - class SpellWindow; - - class WindowManager - { - public: - typedef std::pair Faction; - typedef std::vector FactionList; - typedef std::vector SkillList; - - WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, bool consoleOnlyScripts); - virtual ~WindowManager(); - - /** - * Should be called each frame to update windows/gui elements. - * This could mean updating sizes of gui elements or opening - * new dialogs. - */ - void update(); - - void pushGuiMode(GuiMode mode); - void popGuiMode(); - void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack - - GuiMode getMode() const; - - bool isGuiMode() const; - - void toggleVisible(GuiWindow wnd); - - // Disallow all inventory mode windows - void disallowAll(); - - // Allow one or more windows - void allow(GuiWindow wnd); - - bool isAllowed(GuiWindow wnd) const; - - /// \todo investigate, if we really need to expose every single lousy UI element to the outside world - MWGui::DialogueWindow* getDialogueWindow(); - MWGui::ContainerWindow* getContainerWindow(); - MWGui::InventoryWindow* getInventoryWindow(); - MWGui::BookWindow* getBookWindow(); - MWGui::ScrollWindow* getScrollWindow(); - MWGui::CountDialog* getCountDialog(); - MWGui::ConfirmationDialog* getConfirmationDialog(); - MWGui::TradeWindow* getTradeWindow(); - MWGui::SpellWindow* getSpellWindow(); - MWGui::Console* getConsole(); - - MyGUI::Gui* getGui() const; - - void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount); - - ///< Set value for the given ID. - void setValue (const std::string& id, const MWMechanics::Stat& value); - void setValue (int parSkill, const MWMechanics::Stat& value); - void setValue (const std::string& id, const MWMechanics::DynamicStat& value); - void setValue (const std::string& id, const std::string& value); - void setValue (const std::string& id, int value); - - void setPlayerClass (const ESM::Class &class_); ///< set current class of player - void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group. - void setReputation (int reputation); ///< set the current reputation value - void setBounty (int bounty); ///< set the current bounty value - void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty - - void changeCell(MWWorld::CellStore* cell); ///< change the active cell - void setPlayerPos(const float x, const float y); ///< set player position in map space - void setPlayerDir(const float x, const float y); ///< set player view direction in map space - - void setFocusObject(const MWWorld::Ptr& focus); - void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); - - void setMouseVisible(bool visible); - void getMousePosition(int &x, int &y); - void getMousePosition(float &x, float &y); - void setDragDrop(bool dragDrop); - bool getWorldMouseOver(); - - void toggleFogOfWar(); - void toggleFullHelp(); ///< show extra info in item tooltips (owner, script) - bool getFullHelp() const; - - void setInteriorMapTexture(const int x, const int y); - ///< set the index of the map texture that should be used (for interiors) - - // sets the visibility of the hud health/magicka/stamina bars - void setHMSVisibility(bool visible); - // sets the visibility of the hud minimap - void setMinimapVisibility(bool visible); - void setWeaponVisibility(bool visible); - void setSpellVisibility(bool visible); - - void setSelectedSpell(const std::string& spellId, int successChancePercent); - void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent); - void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent); - void unsetSelectedSpell(); - void unsetSelectedWeapon(); - - void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. - - void messageBox (const std::string& message, const std::vector& buttons); - int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) - - void onFrame (float frameDuration); - - /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - std::map > getPlayerSkillValues(); - std::map > getPlayerAttributeValues(); - SkillList getPlayerMinorSkills(); - SkillList getPlayerMajorSkills(); - - /** - * Fetches a GMST string from the store, if there is no setting with the given - * ID or it is not a string the default string is returned. - * - * @param id Identifier for the GMST setting, e.g. "aName" - * @param default Default value if the GMST setting cannot be used. - */ - const std::string &getGameSettingString(const std::string &id, const std::string &default_); - - void processChangedSettings(const Settings::CategorySettingVector& changed); - - void executeInConsole (const std::string& path); - - private: - OEngine::GUI::MyGUIManager *mGuiManager; - HUD *mHud; - MapWindow *mMap; - MainMenu *mMenu; - ToolTips *mToolTips; - StatsWindow *mStatsWindow; - MessageBoxManager *mMessageBoxManager; - Console *mConsole; - JournalWindow* mJournal; - DialogueWindow *mDialogueWindow; - ContainerWindow *mContainerWindow; - DragAndDrop* mDragAndDrop; - InventoryWindow *mInventoryWindow; - ScrollWindow* mScrollWindow; - BookWindow* mBookWindow; - CountDialog* mCountDialog; - TradeWindow* mTradeWindow; - SettingsWindow* mSettingsWindow; - ConfirmationDialog* mConfirmationDialog; - AlchemyWindow* mAlchemyWindow; - SpellWindow* mSpellWindow; - - CharacterCreation* mCharGen; - - /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - // Various stats about player as needed by window manager - ESM::Class mPlayerClass; - std::string mPlayerName; - std::string mPlayerRaceId; - std::map > mPlayerAttributes; - SkillList mPlayerMajorSkills, mPlayerMinorSkills; - std::map > mPlayerSkillValues; - MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; - - - MyGUI::Gui *mGui; // Gui - std::vector mGuiModes; - - std::vector mGarbageDialogs; - void cleanupGarbage(); - - GuiWindow mShown; // Currently shown windows in inventory mode - - /* Currently ALLOWED windows in inventory mode. This is used at - the start of the game, when windows are enabled one by one - through script commands. You can manipulate this through using - allow() and disableAll(). - */ - GuiWindow mAllowed; - - void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings - - int mShowFPSLevel; - float mFPS; - unsigned int mTriangleCount; - unsigned int mBatchCount; - - void onDialogueWindowBye(); - - /** - * Called when MyGUI tries to retrieve a tag. This usually corresponds to a GMST string, - * so this method will retrieve the GMST with the name \a _tag and place the result in \a _result - */ - void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result); - }; -} - -#endif diff --git a/apps/openmw/mwgui/window_pinnable_base.cpp b/apps/openmw/mwgui/window_pinnable_base.cpp index ecdf311c6e..4ddf49d27b 100644 --- a/apps/openmw/mwgui/window_pinnable_base.cpp +++ b/apps/openmw/mwgui/window_pinnable_base.cpp @@ -1,9 +1,10 @@ #include "window_pinnable_base.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" using namespace MWGui; -WindowPinnableBase::WindowPinnableBase(const std::string& parLayout, WindowManager& parWindowManager) +WindowPinnableBase::WindowPinnableBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager) : WindowBase(parLayout, parWindowManager), mPinned(false), mVisible(false) { MyGUI::WindowPtr t = static_cast(mMainWidget); @@ -30,4 +31,3 @@ void WindowPinnableBase::onWindowButtonPressed(MyGUI::Window* sender, const std: eventDone(this); } - diff --git a/apps/openmw/mwgui/window_pinnable_base.hpp b/apps/openmw/mwgui/window_pinnable_base.hpp index 86bc3b85c8..250dde1f85 100644 --- a/apps/openmw/mwgui/window_pinnable_base.hpp +++ b/apps/openmw/mwgui/window_pinnable_base.hpp @@ -10,13 +10,13 @@ namespace MWGui class WindowPinnableBase: public WindowBase { public: - WindowPinnableBase(const std::string& parLayout, WindowManager& parWindowManager); + WindowPinnableBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager); void setVisible(bool b); bool pinned() { return mPinned; } private: void onWindowButtonPressed(MyGUI::Window* sender, const std::string& eventName); - + protected: virtual void onPinToggled() = 0; @@ -26,4 +26,3 @@ namespace MWGui } #endif - diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp similarity index 99% rename from apps/openmw/mwgui/window_manager.cpp rename to apps/openmw/mwgui/windowmanagerimp.cpp index eada0c88a2..db8401fce9 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1,4 +1,4 @@ -#include "window_manager.hpp" +#include "windowmanagerimp.hpp" #include #include diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp new file mode 100644 index 0000000000..eaa6a16832 --- /dev/null +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -0,0 +1,254 @@ +#ifndef MWGUI_WINDOWMANAGERIMP_H +#define MWGUI_WINDOWMANAGERIMP_H + +/** + This class owns and controls all the MW specific windows in the + GUI. It can enable/disable Gui mode, and is responsible for sending + and retrieving information from the Gui. + + MyGUI should be initialized separately before creating instances of + this class. +**/ + +#include +#include + +#include + +#include "../mwbase/windowmanager.hpp" + +namespace MyGUI +{ + class Gui; + class Widget; + class UString; +} + +namespace Compiler +{ + class Extensions; +} + +namespace OEngine +{ + namespace GUI + { + class Layout; + class MyGUIManager; + } + + namespace Render + { + class OgreRenderer; + } +} + +namespace MWGui +{ + class WindowBase; + class HUD; + class MapWindow; + class MainMenu; + class StatsWindow; + class InventoryWindow; + class JournalWindow; + class CharacterCreation; + class DragAndDrop; + class ToolTips; + class TextInputDialog; + class InfoBoxDialog; + class MessageBoxManager; + class SettingsWindow; + class AlchemyWindow; + + class WindowManager : public MWBase::WindowManager + { + public: + typedef std::pair Faction; + typedef std::vector FactionList; + + WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, bool consoleOnlyScripts); + virtual ~WindowManager(); + + /** + * Should be called each frame to update windows/gui elements. + * This could mean updating sizes of gui elements or opening + * new dialogs. + */ + virtual void update(); + + virtual void pushGuiMode(GuiMode mode); + virtual void popGuiMode(); + virtual void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack + + virtual GuiMode getMode() const; + + virtual bool isGuiMode() const; + + virtual void toggleVisible(GuiWindow wnd); + + // Disallow all inventory mode windows + virtual void disallowAll(); + + // Allow one or more windows + virtual void allow(GuiWindow wnd); + + virtual bool isAllowed(GuiWindow wnd) const; + + /// \todo investigate, if we really need to expose every single lousy UI element to the outside world + virtual MWGui::DialogueWindow* getDialogueWindow(); + virtual MWGui::ContainerWindow* getContainerWindow(); + virtual MWGui::InventoryWindow* getInventoryWindow(); + virtual MWGui::BookWindow* getBookWindow(); + virtual MWGui::ScrollWindow* getScrollWindow(); + virtual MWGui::CountDialog* getCountDialog(); + virtual MWGui::ConfirmationDialog* getConfirmationDialog(); + virtual MWGui::TradeWindow* getTradeWindow(); + virtual MWGui::SpellWindow* getSpellWindow(); + virtual MWGui::Console* getConsole(); + + virtual MyGUI::Gui* getGui() const; + + virtual void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount); + + ///< Set value for the given ID. + virtual void setValue (const std::string& id, const MWMechanics::Stat& value); + virtual void setValue (int parSkill, const MWMechanics::Stat& value); + virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value); + virtual void setValue (const std::string& id, const std::string& value); + virtual void setValue (const std::string& id, int value); + + virtual void setPlayerClass (const ESM::Class &class_); ///< set current class of player + virtual void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group. + virtual void setReputation (int reputation); ///< set the current reputation value + virtual void setBounty (int bounty); ///< set the current bounty value + virtual void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty + + virtual void changeCell(MWWorld::CellStore* cell); ///< change the active cell + virtual void setPlayerPos(const float x, const float y); ///< set player position in map space + virtual void setPlayerDir(const float x, const float y); ///< set player view direction in map space + + virtual void setFocusObject(const MWWorld::Ptr& focus); + virtual void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); + + virtual void setMouseVisible(bool visible); + virtual void getMousePosition(int &x, int &y); + virtual void getMousePosition(float &x, float &y); + virtual void setDragDrop(bool dragDrop); + virtual bool getWorldMouseOver(); + + virtual void toggleFogOfWar(); + virtual void toggleFullHelp(); ///< show extra info in item tooltips (owner, script) + virtual bool getFullHelp() const; + + virtual void setInteriorMapTexture(const int x, const int y); + ///< set the index of the map texture that should be used (for interiors) + + // sets the visibility of the hud health/magicka/stamina bars + virtual void setHMSVisibility(bool visible); + // sets the visibility of the hud minimap + virtual void setMinimapVisibility(bool visible); + virtual void setWeaponVisibility(bool visible); + virtual void setSpellVisibility(bool visible); + + virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); + virtual void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent); + virtual void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent); + virtual void unsetSelectedSpell(); + virtual void unsetSelectedWeapon(); + + virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. + + virtual void messageBox (const std::string& message, const std::vector& buttons); + virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) + + virtual void onFrame (float frameDuration); + + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. + virtual std::map > getPlayerSkillValues(); + virtual std::map > getPlayerAttributeValues(); + virtual SkillList getPlayerMinorSkills(); + virtual SkillList getPlayerMajorSkills(); + + /** + * Fetches a GMST string from the store, if there is no setting with the given + * ID or it is not a string the default string is returned. + * + * @param id Identifier for the GMST setting, e.g. "aName" + * @param default Default value if the GMST setting cannot be used. + */ + virtual const std::string &getGameSettingString(const std::string &id, const std::string &default_); + + virtual void processChangedSettings(const Settings::CategorySettingVector& changed); + + virtual void executeInConsole (const std::string& path); + + private: + OEngine::GUI::MyGUIManager *mGuiManager; + HUD *mHud; + MapWindow *mMap; + MainMenu *mMenu; + ToolTips *mToolTips; + StatsWindow *mStatsWindow; + MessageBoxManager *mMessageBoxManager; + Console *mConsole; + JournalWindow* mJournal; + DialogueWindow *mDialogueWindow; + ContainerWindow *mContainerWindow; + DragAndDrop* mDragAndDrop; + InventoryWindow *mInventoryWindow; + ScrollWindow* mScrollWindow; + BookWindow* mBookWindow; + CountDialog* mCountDialog; + TradeWindow* mTradeWindow; + SettingsWindow* mSettingsWindow; + ConfirmationDialog* mConfirmationDialog; + AlchemyWindow* mAlchemyWindow; + SpellWindow* mSpellWindow; + + CharacterCreation* mCharGen; + + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. + // Various stats about player as needed by window manager + ESM::Class mPlayerClass; + std::string mPlayerName; + std::string mPlayerRaceId; + std::map > mPlayerAttributes; + SkillList mPlayerMajorSkills, mPlayerMinorSkills; + std::map > mPlayerSkillValues; + MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; + + + MyGUI::Gui *mGui; // Gui + std::vector mGuiModes; + + std::vector mGarbageDialogs; + void cleanupGarbage(); + + GuiWindow mShown; // Currently shown windows in inventory mode + + /* Currently ALLOWED windows in inventory mode. This is used at + the start of the game, when windows are enabled one by one + through script commands. You can manipulate this through using + allow() and disableAll(). + */ + GuiWindow mAllowed; + + void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings + + int mShowFPSLevel; + float mFPS; + unsigned int mTriangleCount; + unsigned int mBatchCount; + + void onDialogueWindowBye(); + + /** + * Called when MyGUI tries to retrieve a tag. This usually corresponds to a GMST string, + * so this method will retrieve the GMST with the name \a _tag and place the result in \a _result + */ + void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result); + }; +} + +#endif diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index d53e67d485..0b409fd506 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -9,7 +9,7 @@ #include -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include #include @@ -83,7 +83,7 @@ namespace MWInput MouseLookEventPtr mouse; OEngine::GUI::EventInjectorPtr guiEvents; MWWorld::Player &player; - MWGui::WindowManager &windows; + MWBase::WindowManager &windows; OMW::Engine& mEngine; bool mDragDrop; @@ -230,7 +230,7 @@ private: public: InputImpl(OEngine::Render::OgreRenderer &_ogre, MWWorld::Player &_player, - MWGui::WindowManager &_windows, + MWBase::WindowManager &_windows, bool debug, OMW::Engine& engine) : ogre(_ogre), @@ -457,7 +457,7 @@ private: /***CONSTRUCTOR***/ MWInputManager::MWInputManager(OEngine::Render::OgreRenderer &ogre, MWWorld::Player &player, - MWGui::WindowManager &windows, + MWBase::WindowManager &windows, bool debug, OMW::Engine& engine) { diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 862c4fd207..5092198da8 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -20,7 +20,7 @@ namespace MWWorld class Player; } -namespace MWGui +namespace MWBase { class WindowManager; } @@ -47,7 +47,7 @@ namespace MWInput public: MWInputManager(OEngine::Render::OgreRenderer &_ogre, MWWorld::Player&_player, - MWGui::WindowManager &_windows, + MWBase::WindowManager &_windows, bool debug, OMW::Engine& engine); virtual ~MWInputManager(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5ad67dde46..7fd0b29c38 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -5,12 +5,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwgui/window_manager.hpp" - namespace MWMechanics { void MechanicsManager::buildPlayer() @@ -265,8 +264,8 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getPlayer().getClass().name); mUpdatePlayer = false; - MWGui::WindowManager::SkillList majorSkills (5); - MWGui::WindowManager::SkillList minorSkills (5); + MWBase::WindowManager::SkillList majorSkills (5); + MWBase::WindowManager::SkillList minorSkills (5); for (int i=0; i<5; ++i) { diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index b5fa135e0d..704a10cfef 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -8,8 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include "renderconst.hpp" #include "renderingmanager.hpp" diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d039418ba3..8fb151b5c4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -24,12 +24,11 @@ #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" // FIXME +#include "../mwbase/windowmanager.hpp" // FIXME #include "../mwworld/ptr.hpp" #include "../mwworld/player.hpp" -#include "../mwgui/window_manager.hpp" // FIXME - #include "shadows.hpp" #include "localmap.hpp" #include "water.hpp" diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 3d14692d9a..d740e5feba 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -8,8 +8,7 @@ #include #include "../mwbase/environment.hpp" - -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include "interpretercontext.hpp" diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 0ba04fb381..075ac56462 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -10,12 +10,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwgui/window_manager.hpp" - #include "locals.hpp" #include "globalscripts.hpp" diff --git a/apps/openmw/mwworld/actionalchemy.cpp b/apps/openmw/mwworld/actionalchemy.cpp index a7ee4fd0e3..bba75bc499 100644 --- a/apps/openmw/mwworld/actionalchemy.cpp +++ b/apps/openmw/mwworld/actionalchemy.cpp @@ -1,7 +1,7 @@ #include "actionalchemy.hpp" #include "../mwbase/environment.hpp" -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" namespace MWWorld { diff --git a/apps/openmw/mwworld/actionopen.cpp b/apps/openmw/mwworld/actionopen.cpp index c73ef91494..15a9f510dd 100644 --- a/apps/openmw/mwworld/actionopen.cpp +++ b/apps/openmw/mwworld/actionopen.cpp @@ -1,9 +1,8 @@ #include "actionopen.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" -#include "../mwclass/container.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/container.hpp" #include "class.hpp" diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index c81d79e03b..5c6ab93c12 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -1,7 +1,8 @@ #include "actionread.hpp" #include "../mwbase/environment.hpp" -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" + #include "../mwgui/bookwindow.hpp" #include "../mwgui/scrollwindow.hpp" diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index 5207f1a100..90f3c000e2 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -3,8 +3,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include "class.hpp" #include "containerstore.hpp" diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 1c7093e60e..e67280ee83 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -6,8 +6,7 @@ #include "../mwbase/world.hpp" /// FIXME #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" - -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include "player.hpp" #include "localscripts.hpp" diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3a9547a445..4eda332ef9 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -6,12 +6,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwrender/sky.hpp" #include "../mwrender/player.hpp" -#include "../mwgui/window_manager.hpp" - #include "player.hpp" #include "manualref.hpp" #include "cellfunctors.hpp" From 86d6f190bf3fd958b996afd245282e14b939c061 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 20:45:02 +0200 Subject: [PATCH 15/27] Input system rewrite --- CMakeLists.txt | 12 +- apps/openmw/CMakeLists.txt | 1 + apps/openmw/engine.cpp | 24 +- apps/openmw/mwbase/inputmanager.hpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 825 +++---- apps/openmw/mwinput/inputmanagerimp.hpp | 173 +- apps/openmw/mwinput/mouselookevent.cpp | 28 - apps/openmw/mwinput/mouselookevent.hpp | 57 - extern/oics/CMakeLists.txt | 20 + extern/oics/ICSChannel.cpp | 258 +++ extern/oics/ICSChannel.h | 122 ++ extern/oics/ICSChannelListener.h | 46 + extern/oics/ICSControl.cpp | 161 ++ extern/oics/ICSControl.h | 107 + extern/oics/ICSControlListener.h | 46 + extern/oics/ICSInputControlSystem.cpp | 929 ++++++++ extern/oics/ICSInputControlSystem.h | 256 +++ .../oics/ICSInputControlSystem_joystick.cpp | 665 ++++++ .../oics/ICSInputControlSystem_keyboard.cpp | 156 ++ extern/oics/ICSInputControlSystem_mouse.cpp | 397 ++++ extern/oics/ICSPrerequisites.cpp | 27 + extern/oics/ICSPrerequisites.h | 111 + extern/oics/tinystr.cpp | 116 + extern/oics/tinystr.h | 319 +++ extern/oics/tinyxml.cpp | 1888 +++++++++++++++++ extern/oics/tinyxml.h | 1802 ++++++++++++++++ extern/oics/tinyxmlerror.cpp | 53 + extern/oics/tinyxmlparser.cpp | 1638 ++++++++++++++ files/input-default.xml | 100 + libs/mangle/.gitignore | 3 - libs/mangle/Doxyfile | 1510 ------------- libs/mangle/LICENSE.txt | 26 - libs/mangle/README.txt | 129 -- .../input/clients/ogre_input_capture.hpp | 29 - libs/mangle/input/driver.hpp | 69 - libs/mangle/input/event.hpp | 46 - libs/mangle/input/filters/eventlist.hpp | 47 - libs/mangle/input/servers/ois_driver.cpp | 154 -- libs/mangle/input/servers/ois_driver.hpp | 50 - libs/mangle/input/servers/sdl_driver.cpp | 54 - libs/mangle/input/servers/sdl_driver.hpp | 27 - libs/mangle/input/tests/.gitignore | 2 - libs/mangle/input/tests/Makefile | 15 - libs/mangle/input/tests/common.cpp | 35 - libs/mangle/input/tests/evtlist_test.cpp | 45 - libs/mangle/input/tests/ois_driver_test.cpp | 51 - .../input/tests/output/evtlist_test.out | 12 - .../input/tests/output/ois_driver_test.out | 5 - .../input/tests/output/sdl_driver_test.out | 5 - libs/mangle/input/tests/plugins.cfg | 12 - libs/mangle/input/tests/sdl_driver_test.cpp | 16 - libs/mangle/input/tests/test.sh | 18 - libs/mangle/rend2d/driver.hpp | 63 - libs/mangle/rend2d/servers/sdl_driver.cpp | 259 --- libs/mangle/rend2d/servers/sdl_driver.hpp | 125 -- libs/mangle/rend2d/servers/sdl_gl_driver.cpp | 311 --- libs/mangle/rend2d/servers/sdl_gl_driver.hpp | 132 -- libs/mangle/rend2d/sprite.hpp | 57 - libs/mangle/rend2d/tests/.gitignore | 1 - libs/mangle/rend2d/tests/Makefile | 15 - .../rend2d/tests/output/sdl_move_test.out | 0 libs/mangle/rend2d/tests/output/sdl_test.out | 11 - .../rend2d/tests/output/sdlgl_move_test.out | 0 libs/mangle/rend2d/tests/sdl_move_test.cpp | 30 - libs/mangle/rend2d/tests/sdl_test.cpp | 65 - libs/mangle/rend2d/tests/sdlgl_move_test.cpp | 31 - libs/mangle/rend2d/tests/test.sh | 18 - libs/mangle/rend2d/tests/tile1-blue.png | Bin 273 -> 0 bytes libs/mangle/rend2d/tests/tile1-yellow.png | Bin 257 -> 0 bytes libs/mangle/testall.sh | 16 - libs/mangle/tests/.gitignore | 1 - libs/mangle/tests/Makefile | 14 - .../tests/ogrevfs_audiere_openal_test.cpp | 49 - .../output/ogrevfs_audiere_openal_test.out | 1 - libs/mangle/tests/sound.zip | Bin 18159 -> 0 bytes libs/mangle/tests/test.sh | 18 - libs/mangle/tools/shared_ptr.hpp | 3 - libs/openengine/gui/events.cpp | 77 - libs/openengine/gui/events.hpp | 32 - libs/openengine/ogre/mouselook.cpp | 57 - libs/openengine/ogre/mouselook.hpp | 56 - 81 files changed, 9806 insertions(+), 4335 deletions(-) delete mode 100644 apps/openmw/mwinput/mouselookevent.cpp delete mode 100644 apps/openmw/mwinput/mouselookevent.hpp create mode 100644 extern/oics/CMakeLists.txt create mode 100644 extern/oics/ICSChannel.cpp create mode 100644 extern/oics/ICSChannel.h create mode 100644 extern/oics/ICSChannelListener.h create mode 100644 extern/oics/ICSControl.cpp create mode 100644 extern/oics/ICSControl.h create mode 100644 extern/oics/ICSControlListener.h create mode 100644 extern/oics/ICSInputControlSystem.cpp create mode 100644 extern/oics/ICSInputControlSystem.h create mode 100644 extern/oics/ICSInputControlSystem_joystick.cpp create mode 100644 extern/oics/ICSInputControlSystem_keyboard.cpp create mode 100644 extern/oics/ICSInputControlSystem_mouse.cpp create mode 100644 extern/oics/ICSPrerequisites.cpp create mode 100644 extern/oics/ICSPrerequisites.h create mode 100644 extern/oics/tinystr.cpp create mode 100644 extern/oics/tinystr.h create mode 100644 extern/oics/tinyxml.cpp create mode 100644 extern/oics/tinyxml.h create mode 100644 extern/oics/tinyxmlerror.cpp create mode 100644 extern/oics/tinyxmlparser.cpp create mode 100644 files/input-default.xml delete mode 100644 libs/mangle/.gitignore delete mode 100644 libs/mangle/Doxyfile delete mode 100644 libs/mangle/LICENSE.txt delete mode 100644 libs/mangle/README.txt delete mode 100644 libs/mangle/input/clients/ogre_input_capture.hpp delete mode 100644 libs/mangle/input/driver.hpp delete mode 100644 libs/mangle/input/event.hpp delete mode 100644 libs/mangle/input/filters/eventlist.hpp delete mode 100644 libs/mangle/input/servers/ois_driver.cpp delete mode 100644 libs/mangle/input/servers/ois_driver.hpp delete mode 100644 libs/mangle/input/servers/sdl_driver.cpp delete mode 100644 libs/mangle/input/servers/sdl_driver.hpp delete mode 100644 libs/mangle/input/tests/.gitignore delete mode 100644 libs/mangle/input/tests/Makefile delete mode 100644 libs/mangle/input/tests/common.cpp delete mode 100644 libs/mangle/input/tests/evtlist_test.cpp delete mode 100644 libs/mangle/input/tests/ois_driver_test.cpp delete mode 100644 libs/mangle/input/tests/output/evtlist_test.out delete mode 100644 libs/mangle/input/tests/output/ois_driver_test.out delete mode 100644 libs/mangle/input/tests/output/sdl_driver_test.out delete mode 100644 libs/mangle/input/tests/plugins.cfg delete mode 100644 libs/mangle/input/tests/sdl_driver_test.cpp delete mode 100755 libs/mangle/input/tests/test.sh delete mode 100644 libs/mangle/rend2d/driver.hpp delete mode 100644 libs/mangle/rend2d/servers/sdl_driver.cpp delete mode 100644 libs/mangle/rend2d/servers/sdl_driver.hpp delete mode 100644 libs/mangle/rend2d/servers/sdl_gl_driver.cpp delete mode 100644 libs/mangle/rend2d/servers/sdl_gl_driver.hpp delete mode 100644 libs/mangle/rend2d/sprite.hpp delete mode 100644 libs/mangle/rend2d/tests/.gitignore delete mode 100644 libs/mangle/rend2d/tests/Makefile delete mode 100644 libs/mangle/rend2d/tests/output/sdl_move_test.out delete mode 100644 libs/mangle/rend2d/tests/output/sdl_test.out delete mode 100644 libs/mangle/rend2d/tests/output/sdlgl_move_test.out delete mode 100644 libs/mangle/rend2d/tests/sdl_move_test.cpp delete mode 100644 libs/mangle/rend2d/tests/sdl_test.cpp delete mode 100644 libs/mangle/rend2d/tests/sdlgl_move_test.cpp delete mode 100755 libs/mangle/rend2d/tests/test.sh delete mode 100644 libs/mangle/rend2d/tests/tile1-blue.png delete mode 100644 libs/mangle/rend2d/tests/tile1-yellow.png delete mode 100755 libs/mangle/testall.sh delete mode 100644 libs/mangle/tests/.gitignore delete mode 100644 libs/mangle/tests/Makefile delete mode 100644 libs/mangle/tests/ogrevfs_audiere_openal_test.cpp delete mode 100644 libs/mangle/tests/output/ogrevfs_audiere_openal_test.out delete mode 100644 libs/mangle/tests/sound.zip delete mode 100755 libs/mangle/tests/test.sh delete mode 100644 libs/mangle/tools/shared_ptr.hpp delete mode 100644 libs/openengine/gui/events.cpp delete mode 100644 libs/openengine/gui/events.hpp delete mode 100644 libs/openengine/ogre/mouselook.cpp delete mode 100644 libs/openengine/ogre/mouselook.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 543d9cb983..79e33f1815 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,19 +96,13 @@ ENDIF() set(LIBDIR ${CMAKE_SOURCE_DIR}/libs) -set(MANGLE_INPUT ${LIBDIR}/mangle/input/servers/ois_driver.cpp) -set(MANGLE_ALL ${MANGLE_INPUT}) -source_group(libs\\mangle FILES ${MANGLE_ALL}) - set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/renderer.cpp - ${LIBDIR}/openengine/ogre/mouselook.cpp ${LIBDIR}/openengine/ogre/fader.cpp ${LIBDIR}/openengine/ogre/imagerotate.cpp ${LIBDIR}/openengine/ogre/atlas.cpp ) set(OENGINE_GUI - ${LIBDIR}/openengine/gui/events.cpp ${LIBDIR}/openengine/gui/manager.cpp ) @@ -135,7 +129,7 @@ set(OENGINE_BULLET set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_BULLET}) source_group(libs\\openengine FILES ${OENGINE_ALL}) -set(OPENMW_LIBS ${MANGLE_ALL} ${OENGINE_ALL}) +set(OPENMW_LIBS ${OENGINE_ALL}) set(OPENMW_LIBS_HEADER) # Sound setup @@ -291,6 +285,9 @@ endif (APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg "${OpenMW_BINARY_DIR}/settings-default.cfg") +configure_file(${OpenMW_SOURCE_DIR}/files/input-default.xml + "${OpenMW_BINARY_DIR}/input-default.xml") + configure_file(${OpenMW_SOURCE_DIR}/files/transparency-overrides.cfg "${OpenMW_BINARY_DIR}/transparency-overrides.cfg") @@ -443,6 +440,7 @@ endif(WIN32) # Extern add_subdirectory (extern/shiny) +add_subdirectory (extern/oics) # Components add_subdirectory (components) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 02fe0b72c7..72c5117d24 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -104,6 +104,7 @@ target_link_libraries(openmw ${MYGUI_PLATFORM_LIBRARIES} "shiny" "shiny.OgrePlatform" + "oics" components ) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 75835120fd..128d975534 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -67,7 +67,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) mEnvironment.setFrameDuration (evt.timeSinceLastFrame); // update input - MWBase::Environment::get().getInputManager()->update(); + MWBase::Environment::get().getInputManager()->update(evt.timeSinceLastFrame); // sound if (mUseSound) @@ -270,6 +270,24 @@ void OMW::Engine::go() else if (boost::filesystem::exists(globaldefault)) settings.loadUser(globaldefault); + // Get the path for the keybinder xml file + std::string keybinderDefault; + + // load user settings if they exist, otherwise just load the default settings as user settings + const std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); + const std::string keybinderDefaultLocal = (mCfgMgr.getLocalPath() / "input-default.xml").string(); + const std::string keybinderDefaultGlobal = (mCfgMgr.getGlobalPath() / "input-default.xml").string(); + + bool keybinderUserExists = boost::filesystem::exists(keybinderUser); + + if (boost::filesystem::exists(keybinderDefaultLocal)) + keybinderDefault = keybinderDefaultLocal; + else if (boost::filesystem::exists(keybinderDefaultGlobal)) + keybinderDefault = keybinderDefaultGlobal; + else + throw std::runtime_error ("No default input settings found! Make sure the file \"input-default.xml\" was properly installed."); + + mFpsLevel = settings.getInt("fps", "HUD"); // load nif overrides @@ -366,9 +384,9 @@ void OMW::Engine::go() // Sets up the input system - mEnvironment.setInputManager (new MWInput::MWInputManager (*mOgre, + mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, MWBase::Environment::get().getWorld()->getPlayer(), - *MWBase::Environment::get().getWindowManager(), mDebug, *this)); + *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderDefault, keybinderUser, keybinderUserExists)); std::cout << "\nPress Q/ESC or close window to exit.\n"; diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index d865bfb0ec..5d73025a7d 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -22,7 +22,7 @@ namespace MWBase virtual ~InputManager() {} - virtual void update() = 0; + virtual void update(float dt) = 0; virtual void changeInputMode(bool guiMode) = 0; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 8def5c74d4..d1c400ceab 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1,357 +1,200 @@ #include "inputmanagerimp.hpp" #include +#include -#include -#include +#include +#include -#include +#include + +#include +#include + +#include #include -#include "../mwbase/windowmanager.hpp" - -#include -#include - -#include - -#include "mouselookevent.hpp" - #include "../engine.hpp" #include "../mwworld/player.hpp" #include "../mwbase/world.hpp" - -#include -#include -#include -#include +#include "../mwbase/windowmanager.hpp" namespace MWInput { - enum Actions + InputManager::InputManager(OEngine::Render::OgreRenderer &ogre, + MWWorld::Player &player, + MWBase::WindowManager &windows, + bool debug, + OMW::Engine& engine, + const std::string& defaultFile, + const std::string& userFile, bool userFileExists) + : mOgre(ogre) + , mPlayer(player) + , mWindows(windows) + , mEngine(engine) + , mMouseLookEnabled(true) + , mMouseX(ogre.getWindow()->getWidth ()/2.f) + , mMouseY(ogre.getWindow()->getHeight ()/2.f) + , mUserFile(userFile) + , mDragDrop(false) { - A_Quit, // Exit the program + Ogre::RenderWindow* window = ogre.getWindow (); + size_t windowHnd; - A_Screenshot, // Take a screenshot + window->getCustomAttribute("WINDOW", &windowHnd); - A_Inventory, // Toggle inventory screen + std::ostringstream windowHndStr; + OIS::ParamList pl; - A_Console, // Toggle console screen + windowHndStr << windowHnd; + pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str())); - A_MoveLeft, // Move player left / right - A_MoveRight, - A_MoveForward, // Forward / Backward - A_MoveBackward, - - A_Activate, - - A_Use, //Use weapon, spell, etc. - A_Jump, - A_AutoMove, //Toggle Auto-move forward - A_Rest, //Rest - A_Journal, //Journal - A_Weapon, //Draw/Sheath weapon - A_Spell, //Ready/Unready Casting - A_AlwaysRun, //Toggle Always Run - A_CycleSpellLeft, //cycling through spells - A_CycleSpellRight, - A_CycleWeaponLeft,//Cycling through weapons - A_CycleWeaponRight, - A_ToggleSneak, //Toggles Sneak, add Push-Sneak later - A_ToggleWalk, //Toggle Walking/Running - A_Crouch, - - A_QuickSave, - A_QuickLoad, - A_QuickMenu, - A_GameMenu, - A_ToggleWeapon, - A_ToggleSpell, - - A_LAST // Marker for the last item - }; - - // Class that handles all input and key bindings for OpenMW - class InputImpl - { - OEngine::Input::DispatcherPtr disp; - OEngine::Render::OgreRenderer &ogre; - Mangle::Input::OISDriver input; - OEngine::Input::Poller poller; - MouseLookEventPtr mouse; - OEngine::GUI::EventInjectorPtr guiEvents; - MWWorld::Player &player; - MWBase::WindowManager &windows; - OMW::Engine& mEngine; - - bool mDragDrop; - - std::map mControlSwitch; - - /* InputImpl Methods */ -public: - void adjustMouseRegion(int width, int height) - { - input.adjustMouseClippingSize(width, height); - } -private: - void toggleSpell() - { - if (windows.isGuiMode()) return; - - MWMechanics::DrawState_ state = player.getDrawState(); - if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) + // Set non-exclusive mouse and keyboard input if the user requested + // it. + if (debug) { - player.setDrawState(MWMechanics::DrawState_Spell); - std::cout << "Player has now readied his hands for spellcasting!\n"; + #if defined OIS_WIN32_PLATFORM + pl.insert(std::make_pair(std::string("w32_mouse"), + std::string("DISCL_FOREGROUND" ))); + pl.insert(std::make_pair(std::string("w32_mouse"), + std::string("DISCL_NONEXCLUSIVE"))); + pl.insert(std::make_pair(std::string("w32_keyboard"), + std::string("DISCL_FOREGROUND"))); + pl.insert(std::make_pair(std::string("w32_keyboard"), + std::string("DISCL_NONEXCLUSIVE"))); + #elif defined OIS_LINUX_PLATFORM + pl.insert(std::make_pair(std::string("x11_mouse_grab"), + std::string("false"))); + pl.insert(std::make_pair(std::string("x11_mouse_hide"), + std::string("false"))); + pl.insert(std::make_pair(std::string("x11_keyboard_grab"), + std::string("false"))); + pl.insert(std::make_pair(std::string("XAutoRepeatOn"), + std::string("true"))); + #endif } + + #ifdef __APPLE_CC__ + // Give the application window focus to receive input events + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); + #endif + + mInputManager = OIS::InputManager::createInputSystem( pl ); + + // Create all devices + mKeyboard = static_cast(mInputManager->createInputObject + ( OIS::OISKeyboard, true )); + mMouse = static_cast(mInputManager->createInputObject + ( OIS::OISMouse, true )); + + mKeyboard->setEventCallback (this); + mMouse->setEventCallback (this); + + adjustMouseRegion (window->getWidth(), window->getHeight()); + + MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, mMouse->getMouseState ().Z.abs); + + std::string configFile; + if (userFileExists) + configFile = userFile; else + configFile = defaultFile; + + std::cout << "Loading input configuration: " << configFile << std::endl; + + mInputCtrl = new ICS::InputControlSystem(configFile, true, NULL, NULL, A_LAST); + + for (int i = 0; i < A_LAST; ++i) { - player.setDrawState(MWMechanics::DrawState_Nothing); - std::cout << "Player does not have any kind of attack ready now.\n"; + mInputCtrl->getChannel (i)->addListener (this); } + + mControlSwitch["playercontrols"] = true; + mControlSwitch["playerfighting"] = true; + mControlSwitch["playerjumping"] = true; + mControlSwitch["playerlooking"] = true; + mControlSwitch["playermagic"] = true; + mControlSwitch["playerviewswitch"] = true; + mControlSwitch["vanitymode"] = true; + + changeInputMode(false); } - void toggleWeapon() + InputManager::~InputManager() { - if (windows.isGuiMode()) return; + mInputCtrl->save (mUserFile); - MWMechanics::DrawState_ state = player.getDrawState(); - if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - { - player.setDrawState(MWMechanics::DrawState_Weapon); - std::cout << "Player is now drawing his weapon.\n"; - } - else - { - player.setDrawState(MWMechanics::DrawState_Nothing); - std::cout << "Player does not have any kind of attack ready now.\n"; - } + delete mInputCtrl; + + mInputManager->destroyInputObject(mKeyboard); + mInputManager->destroyInputObject(mMouse); + OIS::InputManager::destroyInputSystem(mInputManager); } - void screenshot() + void InputManager::channelChanged(ICS::Channel* channel, float currentValue, float previousValue) { - mEngine.screenshot(); - - std::vector empty; - windows.messageBox ("Screenshot saved", empty); - } - - /* toggleInventory() is called when the user presses the button to toggle the inventory screen. */ - void toggleInventory() - { - using namespace MWGui; - if (mDragDrop) return; - bool gameMode = !windows.isGuiMode(); + int action = channel->getNumber(); + if (currentValue == 1) + { + // trigger action activated - // Toggle between game mode and inventory mode - if(gameMode) - windows.pushGuiMode(GM_Inventory); - else if(windows.getMode() == GM_Inventory) - windows.popGuiMode(); - - // .. but don't touch any other mode. + switch (action) + { + case A_GameMenu: + toggleMainMenu (); + break; + case A_Quit: + exitNow(); + break; + case A_Screenshot: + screenshot(); + break; + case A_Inventory: + toggleInventory (); + break; + case A_Console: + toggleConsole (); + break; + case A_Activate: + activate(); + break; + case A_Journal: + toggleJournal (); + break; + case A_AutoMove: + toggleAutoMove (); + break; + case A_ToggleSneak: + /// \todo implement + break; + case A_ToggleWalk: + toggleWalking (); + break; + case A_ToggleWeapon: + toggleWeapon (); + break; + case A_ToggleSpell: + toggleSpell (); + break; + } + } } - // Toggle console - void toggleConsole() - { - using namespace MWGui; - - if (mDragDrop) - return; - - bool gameMode = !windows.isGuiMode(); - - // Switch to console mode no matter what mode we are currently - // in, except of course if we are already in console mode - if (!gameMode) - { - if (windows.getMode() == GM_Console) - windows.popGuiMode(); - else - windows.pushGuiMode(GM_Console); - } - else - windows.pushGuiMode(GM_Console); - } - - void toggleJournal() - { - using namespace MWGui; - - // Toggle between game mode and journal mode - bool gameMode = !windows.isGuiMode(); - - if(gameMode) - windows.pushGuiMode(GM_Journal); - else if(windows.getMode() == GM_Journal) - windows.popGuiMode(); - // .. but don't touch any other mode. - } - - void activate() - { - mEngine.activate(); - } - - void toggleAutoMove() - { - if (windows.isGuiMode()) return; - player.setAutoMove (!player.getAutoMove()); - } - - void toggleWalking() - { - if (windows.isGuiMode()) return; - player.toggleRunning(); - } - - void toggleMainMenu() - { - if (windows.isGuiMode () && (windows.getMode () == MWGui::GM_MainMenu || windows.getMode () == MWGui::GM_Settings)) - windows.popGuiMode(); - else - windows.pushGuiMode (MWGui::GM_MainMenu); - } - - // Exit program now button (which is disabled in GUI mode) - void exitNow() - { - if(!windows.isGuiMode()) - Ogre::Root::getSingleton().queueEndRendering (); - } - - public: - InputImpl(OEngine::Render::OgreRenderer &_ogre, - MWWorld::Player &_player, - MWBase::WindowManager &_windows, - bool debug, - OMW::Engine& engine) - : ogre(_ogre), - input(ogre.getWindow(), !debug), - poller(input), - player(_player), - windows(_windows), - mEngine (engine), - mDragDrop(false) - { - using namespace OEngine::Input; - using namespace OEngine::Render; - using namespace OEngine::GUI; - using namespace Mangle::Input; - using namespace OIS; - - disp = DispatcherPtr(new Dispatcher(A_LAST)); - - // Bind MW-specific functions - disp->funcs.bind(A_Quit, boost::bind(&InputImpl::exitNow, this), - "Quit program"); - disp->funcs.bind(A_Screenshot, boost::bind(&InputImpl::screenshot, this), - "Screenshot"); - disp->funcs.bind(A_Inventory, boost::bind(&InputImpl::toggleInventory, this), - "Toggle inventory screen"); - disp->funcs.bind(A_Console, boost::bind(&InputImpl::toggleConsole, this), - "Toggle console"); - disp->funcs.bind(A_Journal, boost::bind(&InputImpl::toggleJournal, this), - "Toggle journal"); - disp->funcs.bind(A_Activate, boost::bind(&InputImpl::activate, this), - "Activate"); - disp->funcs.bind(A_AutoMove, boost::bind(&InputImpl::toggleAutoMove, this), - "Auto Move"); - disp->funcs.bind(A_ToggleWalk, boost::bind(&InputImpl::toggleWalking, this), - "Toggle Walk/Run"); - disp->funcs.bind(A_ToggleWeapon,boost::bind(&InputImpl::toggleWeapon,this), - "Draw Weapon"); - disp->funcs.bind(A_ToggleSpell,boost::bind(&InputImpl::toggleSpell,this), - "Ready hands"); - disp->funcs.bind(A_GameMenu, boost::bind(&InputImpl::toggleMainMenu, this), - "Toggle main menu"); - - mouse = MouseLookEventPtr(new MouseLookEvent()); - - // This event handler pumps events into MyGUI - guiEvents = EventInjectorPtr(new EventInjector(windows.getGui())); - - // Hook 'mouse' and 'disp' up as event handlers into 'input' - // (the OIS driver and event source.) We do this through an - // EventList which dispatches the event to multiple handlers for - // us. - { - EventList *lst = new EventList; - input.setEvent(EventPtr(lst)); - lst->add(mouse,Event::EV_MouseMove); - lst->add(disp,Event::EV_KeyDown); - lst->add(guiEvents,Event::EV_ALL); - } - - mControlSwitch["playercontrols"] = true; - mControlSwitch["playerfighting"] = true; - mControlSwitch["playerjumping"] = true; - mControlSwitch["playerlooking"] = true; - mControlSwitch["playermagic"] = true; - mControlSwitch["playerviewswitch"] = true; - mControlSwitch["vanitymode"] = true; - - changeInputMode(false); - - /********************************** - Key binding section - - The rest of this function has hard coded key bindings, and is - intended to be replaced by user defined bindings later. - **********************************/ - - // Key bindings for keypress events - // NOTE: These keys do not require constant polling - use in conjuction with variables in loops. - - disp->bind(A_Quit, KC_Q); - disp->bind(A_GameMenu, KC_ESCAPE); - disp->bind(A_Screenshot, KC_SYSRQ); - disp->bind(A_Inventory, KC_I); - disp->bind(A_Console, KC_F1); - disp->bind(A_Journal, KC_J); - disp->bind(A_Activate, KC_SPACE); - disp->bind(A_AutoMove, KC_Z); - disp->bind(A_ToggleSneak, KC_X); - disp->bind(A_ToggleWalk, KC_C); - disp->bind(A_ToggleWeapon,KC_F); - disp->bind(A_ToggleSpell,KC_R); - - // Key bindings for polled keys - // NOTE: These keys are constantly being polled. Only add keys that must be checked each frame. - - // Arrow keys - poller.bind(A_MoveLeft, KC_LEFT); - poller.bind(A_MoveRight, KC_RIGHT); - poller.bind(A_MoveForward, KC_UP); - poller.bind(A_MoveBackward, KC_DOWN); - - // WASD keys - poller.bind(A_MoveLeft, KC_A); - poller.bind(A_MoveRight, KC_D); - poller.bind(A_MoveForward, KC_W); - poller.bind(A_MoveBackward, KC_S); - - poller.bind(A_Jump, KC_E); - poller.bind(A_Crouch, KC_LCONTROL); - } - - void setDragDrop(bool dragDrop) - { - mDragDrop = dragDrop; - } - - //NOTE: Used to check for movement keys - void update () + void InputManager::update(float dt) { // Tell OIS to handle all input events - input.capture(); + mKeyboard->capture(); + mMouse->capture(); + + // update values of channels (as a result of pressed keys) + mInputCtrl->update(dt); // Update windows/gui as a result of input events // For instance this could mean opening a new window/dialog, @@ -359,150 +202,314 @@ private: // ensure that window/gui changes appear quickly while // avoiding that window/gui changes does not happen in // event callbacks (which may crash) - windows.update(); + mWindows.update(); // Disable movement in Gui mode + if (mWindows.isGuiMode()) return; - if (windows.isGuiMode()) return; // Configure player movement according to keyboard input. Actual movement will // be done in the physics system. - if (mControlSwitch["playercontrols"]) { - if (poller.isDown(A_MoveLeft)) + if (mControlSwitch["playercontrols"]) + { + if (actionIsActive(A_MoveLeft)) { - player.setAutoMove (false); - player.setLeftRight (1); + mPlayer.setAutoMove (false); + mPlayer.setLeftRight (1); } - else if (poller.isDown(A_MoveRight)) + else if (actionIsActive(A_MoveRight)) { - player.setAutoMove (false); - player.setLeftRight (-1); + mPlayer.setAutoMove (false); + mPlayer.setLeftRight (-1); } else - player.setLeftRight (0); + mPlayer.setLeftRight (0); - if (poller.isDown(A_MoveForward)) + if (actionIsActive(A_MoveForward)) { - player.setAutoMove (false); - player.setForwardBackward (1); + mPlayer.setAutoMove (false); + mPlayer.setForwardBackward (1); } - else if (poller.isDown(A_MoveBackward)) + else if (actionIsActive(A_MoveBackward)) { - player.setAutoMove (false); - player.setForwardBackward (-1); + mPlayer.setAutoMove (false); + mPlayer.setForwardBackward (-1); } else - player.setForwardBackward (0); + mPlayer.setForwardBackward (0); - if (poller.isDown(A_Jump) && mControlSwitch["playerjumping"]) - player.setUpDown (1); - else if (poller.isDown(A_Crouch)) - player.setUpDown (-1); + if (actionIsActive(A_Jump) && mControlSwitch["playerjumping"]) + mPlayer.setUpDown (1); + else if (actionIsActive(A_Crouch)) + mPlayer.setUpDown (-1); else - player.setUpDown (0); + mPlayer.setUpDown (0); } + } - // Switch between gui modes. Besides controlling the Gui windows - // this also makes sure input is directed to the right place - void changeInputMode(bool guiMode) + void InputManager::setDragDrop(bool dragDrop) { - // Are we in GUI mode now? - if(guiMode) - { - // Disable mouse look - mouse->disable(); + mDragDrop = dragDrop; + } - // Enable GUI events - guiEvents->enabled = true; + void InputManager::changeInputMode(bool guiMode) + { + // Are we in GUI mode now? + if(guiMode) + { + // Disable mouse look + mMouseLookEnabled = false; + + // Enable GUI events + mGuiCursorEnabled = true; } - else + else { // Start mouse-looking again if allowed. if (mControlSwitch["playerlooking"]) { - mouse->enable(); + mMouseLookEnabled = true; } - // Disable GUI events - guiEvents->enabled = false; + // Disable GUI events + mGuiCursorEnabled = false; } } - void toggleControlSwitch(std::string sw, bool value) + void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed) + { + bool changeRes = false; + for (Settings::CategorySettingVector::const_iterator it = changed.begin(); + it != changed.end(); ++it) + { + if (it->first == "Video" && (it->second == "resolution x" || it->second == "resolution y")) + changeRes = true; + } + + if (changeRes) + adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video")); + } + + void InputManager::toggleControlSwitch (const std::string& sw, bool value) { if (mControlSwitch[sw] == value) { return; } /// \note 7 switches at all, if-else is relevant if (sw == "playercontrols" && !value) { - player.setLeftRight(0); - player.setForwardBackward(0); - player.setAutoMove(false); - player.setUpDown(0); + mPlayer.setLeftRight(0); + mPlayer.setForwardBackward(0); + mPlayer.setAutoMove(false); + mPlayer.setUpDown(0); } else if (sw == "playerjumping" && !value) { /// \fixme maybe crouching at this time - player.setUpDown(0); + mPlayer.setUpDown(0); } else if (sw == "playerlooking") { if (value) { - mouse->enable(); + mMouseLookEnabled = true; } else { - mouse->disable(); + mMouseLookEnabled = false; } } mControlSwitch[sw] = value; } - }; - - /***CONSTRUCTOR***/ - MWInputManager::MWInputManager(OEngine::Render::OgreRenderer &ogre, - MWWorld::Player &player, - MWBase::WindowManager &windows, - bool debug, - OMW::Engine& engine) - { - impl = new InputImpl(ogre,player,windows,debug, engine); - } - - /***DESTRUCTOR***/ - MWInputManager::~MWInputManager() - { - delete impl; - } - - void MWInputManager::update() - { - impl->update(); - } - - void MWInputManager::setDragDrop(bool dragDrop) - { - impl->setDragDrop(dragDrop); - } - - void MWInputManager::changeInputMode(bool guiMode) - { - impl->changeInputMode(guiMode); - } - - void MWInputManager::processChangedSettings(const Settings::CategorySettingVector& changed) - { - bool changeRes = false; - for (Settings::CategorySettingVector::const_iterator it = changed.begin(); - it != changed.end(); ++it) - { - if (it->first == "Video" && ( - it->second == "resolution x" - || it->second == "resolution y")) - changeRes = true; - } - - if (changeRes) - impl->adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video")); - } - - void MWInputManager::toggleControlSwitch (const std::string& sw, bool value) + void InputManager::adjustMouseRegion(int width, int height) { - impl->toggleControlSwitch(sw, value); + const OIS::MouseState &ms = mMouse->getMouseState(); + ms.width = width; + ms.height = height; } + + bool InputManager::keyPressed( const OIS::KeyEvent &arg ) + { + mInputCtrl->keyPressed (arg); + + if (mGuiCursorEnabled) + MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), arg.text); + + return true; + } + + bool InputManager::keyReleased( const OIS::KeyEvent &arg ) + { + mInputCtrl->keyReleased (arg); + + if (mGuiCursorEnabled) + MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(arg.key)); + + return true; + } + + bool InputManager::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) + { + mInputCtrl->mousePressed (arg, id); + + if (mGuiCursorEnabled) + MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + + return true; + } + + bool InputManager::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) + { + mInputCtrl->mouseReleased (arg, id); + + if (mGuiCursorEnabled) + MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + + return true; + } + + bool InputManager::mouseMoved( const OIS::MouseEvent &arg ) + { + mInputCtrl->mouseMoved (arg); + + if (mGuiCursorEnabled) + { + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + + // We keep track of our own mouse position, so that moving the mouse while in + // game mode does not move the position of the GUI cursor + mMouseX += arg.state.X.rel; + mMouseY += arg.state.Y.rel; + mMouseX = std::max(0, std::min(mMouseX, viewSize.width)); + mMouseY = std::max(0, std::min(mMouseY, viewSize.height)); + + MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, arg.state.Z.abs); + } + + if (mMouseLookEnabled) + { + float x = arg.state.X.rel * 0.2; + float y = arg.state.Y.rel * 0.2; + + MWBase::World *world = MWBase::Environment::get().getWorld(); + world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); + } + + return true; + } + + void InputManager::toggleMainMenu() + { + if (mWindows.isGuiMode () && (mWindows.getMode () == MWGui::GM_MainMenu || mWindows.getMode () == MWGui::GM_Settings)) + mWindows.popGuiMode(); + else + mWindows.pushGuiMode (MWGui::GM_MainMenu); + } + + void InputManager::toggleSpell() + { + if (mWindows.isGuiMode()) return; + + MWMechanics::DrawState_ state = mPlayer.getDrawState(); + if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) + { + mPlayer.setDrawState(MWMechanics::DrawState_Spell); + std::cout << "Player has now readied his hands for spellcasting!\n" << std::endl; + } + else + { + mPlayer.setDrawState(MWMechanics::DrawState_Nothing); + std::cout << "Player does not have any kind of attack ready now.\n" << std::endl; + } + } + + void InputManager::toggleWeapon() + { + if (mWindows.isGuiMode()) return; + + MWMechanics::DrawState_ state = mPlayer.getDrawState(); + if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) + { + mPlayer.setDrawState(MWMechanics::DrawState_Weapon); + std::cout << "Player is now drawing his weapon.\n" << std::endl; + } + else + { + mPlayer.setDrawState(MWMechanics::DrawState_Nothing); + std::cout << "Player does not have any kind of attack ready now.\n" << std::endl; + } + } + + void InputManager::screenshot() + { + mEngine.screenshot(); + + std::vector empty; + mWindows.messageBox ("Screenshot saved", empty); + } + + void InputManager::toggleInventory() + { + bool gameMode = !mWindows.isGuiMode(); + + // Toggle between game mode and inventory mode + if(gameMode) + mWindows.pushGuiMode(MWGui::GM_Inventory); + else if(mWindows.getMode() == MWGui::GM_Inventory) + mWindows.popGuiMode(); + + // .. but don't touch any other mode. + } + + void InputManager::toggleConsole() + { + bool gameMode = !mWindows.isGuiMode(); + + // Switch to console mode no matter what mode we are currently + // in, except of course if we are already in console mode + if (!gameMode) + { + if (mWindows.getMode() == MWGui::GM_Console) + mWindows.popGuiMode(); + else + mWindows.pushGuiMode(MWGui::GM_Console); + } + else + mWindows.pushGuiMode(MWGui::GM_Console); + } + + void InputManager::toggleJournal() + { + // Toggle between game mode and journal mode + bool gameMode = !mWindows.isGuiMode(); + + if(gameMode) + mWindows.pushGuiMode(MWGui::GM_Journal); + else if(mWindows.getMode() == MWGui::GM_Journal) + mWindows.popGuiMode(); + // .. but don't touch any other mode. + } + + void InputManager::activate() + { + mEngine.activate(); + } + + void InputManager::toggleAutoMove() + { + if (mWindows.isGuiMode()) return; + mPlayer.setAutoMove (!mPlayer.getAutoMove()); + } + + void InputManager::toggleWalking() + { + if (mWindows.isGuiMode()) return; + mPlayer.toggleRunning(); + } + + // Exit program now button (which is disabled in GUI mode) + void InputManager::exitNow() + { + if(!mWindows.isGuiMode()) + Ogre::Root::getSingleton().queueEndRendering (); + } + + bool InputManager::actionIsActive (int id) + { + return mInputCtrl->getChannel (id)->getValue () == 1; + } + } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 5092198da8..ba42327eeb 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -9,20 +9,20 @@ namespace OEngine { - namespace Render - { - class OgreRenderer; - } + namespace Render + { + class OgreRenderer; + } } namespace MWWorld { - class Player; + class Player; } namespace MWBase { - class WindowManager; + class WindowManager; } namespace OMW @@ -30,37 +30,154 @@ namespace OMW class Engine; } +namespace ICS +{ + class InputControlSystem; +} + +namespace OIS +{ + class Keyboard; + class Mouse; + class InputManager; +} + +#include +#include + +#include + namespace MWInput { - // Forward declaration of the real implementation. - class InputImpl; - /* Class that handles all input and key bindings for OpenMW. + /** + * @brief Class that handles all input and key bindings for OpenMW. + */ + class InputManager : public MWBase::InputManager, public OIS::KeyListener, public OIS::MouseListener, public ICS::ChannelListener + { + public: + InputManager(OEngine::Render::OgreRenderer &_ogre, + MWWorld::Player&_player, + MWBase::WindowManager &_windows, + bool debug, + OMW::Engine& engine, + const std::string& defaultFile, + const std::string& userFile, bool userFileExists); - This class is just an interface. All the messy details are in - inputmanager.cpp. - */ - struct MWInputManager : public MWBase::InputManager - { - InputImpl *impl; + virtual ~InputManager(); - public: - MWInputManager(OEngine::Render::OgreRenderer &_ogre, - MWWorld::Player&_player, - MWBase::WindowManager &_windows, - bool debug, - OMW::Engine& engine); - virtual ~MWInputManager(); + virtual void update(float dt); - virtual void update(); + virtual void changeInputMode(bool guiMode); - virtual void changeInputMode(bool guiMode); + virtual void processChangedSettings(const Settings::CategorySettingVector& changed); - virtual void processChangedSettings(const Settings::CategorySettingVector& changed); + virtual void setDragDrop(bool dragDrop); - virtual void setDragDrop(bool dragDrop); + virtual void toggleControlSwitch (const std::string& sw, bool value); - virtual void toggleControlSwitch (const std::string& sw, bool value); - }; + + public: + virtual bool keyPressed( const OIS::KeyEvent &arg ); + virtual bool keyReleased( const OIS::KeyEvent &arg ); + + virtual bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ); + virtual bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ); + virtual bool mouseMoved( const OIS::MouseEvent &arg ); + + virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); + + private: + OEngine::Render::OgreRenderer &mOgre; + MWWorld::Player &mPlayer; + MWBase::WindowManager &mWindows; + OMW::Engine& mEngine; + + ICS::InputControlSystem* mInputCtrl; + + OIS::Keyboard* mKeyboard; + OIS::Mouse* mMouse; + OIS::InputManager* mInputManager; + + std::string mUserFile; + + bool mDragDrop; + + bool mMouseLookEnabled; + bool mGuiCursorEnabled; + + int mMouseX; + int mMouseY; + + std::map mControlSwitch; + + + private: + void adjustMouseRegion(int width, int height); + + private: + void toggleMainMenu(); + void toggleSpell(); + void toggleWeapon(); + void toggleInventory(); + void toggleConsole(); + void screenshot(); + void toggleJournal(); + void activate(); + void toggleWalking(); + void toggleAutoMove(); + void exitNow(); + + bool actionIsActive (int id); + + private: + enum Actions + { + // please add new actions at the bottom, in order to preserve the channel IDs in the key configuration files + + A_GameMenu, + + A_Quit, // Exit the program + + A_Screenshot, // Take a screenshot + + A_Inventory, // Toggle inventory screen + + A_Console, // Toggle console screen + + A_MoveLeft, // Move player left / right + A_MoveRight, + A_MoveForward, // Forward / Backward + A_MoveBackward, + + A_Activate, + + A_Use, //Use weapon, spell, etc. + A_Jump, + A_AutoMove, //Toggle Auto-move forward + A_Rest, //Rest + A_Journal, //Journal + A_Weapon, //Draw/Sheath weapon + A_Spell, //Ready/Unready Casting + A_AlwaysRun, //Toggle Always Run + A_CycleSpellLeft, //cycling through spells + A_CycleSpellRight, + A_CycleWeaponLeft,//Cycling through weapons + A_CycleWeaponRight, + A_ToggleSneak, //Toggles Sneak, add Push-Sneak later + A_ToggleWalk, //Toggle Walking/Running + A_Crouch, + + A_QuickSave, + A_QuickLoad, + A_QuickMenu, + A_ToggleWeapon, + A_ToggleSpell, + + A_LAST // Marker for the last item + }; + + + }; } #endif diff --git a/apps/openmw/mwinput/mouselookevent.cpp b/apps/openmw/mwinput/mouselookevent.cpp deleted file mode 100644 index f318ce6660..0000000000 --- a/apps/openmw/mwinput/mouselookevent.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "mouselookevent.hpp" - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - -#include "../mwworld/player.hpp" - -#include -#include -#include - -using namespace OIS; -using namespace MWInput; - -void MouseLookEvent::event(Type type, int index, const void *p) -{ - if (type != EV_MouseMove || mDisabled) { - return; - } - - MouseEvent *arg = (MouseEvent*)(p); - - float x = arg->state.X.rel * sensX; - float y = arg->state.Y.rel * sensY; - - MWBase::World *world = MWBase::Environment::get().getWorld(); - world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); -} diff --git a/apps/openmw/mwinput/mouselookevent.hpp b/apps/openmw/mwinput/mouselookevent.hpp deleted file mode 100644 index af996643f3..0000000000 --- a/apps/openmw/mwinput/mouselookevent.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef _MWINPUT_MOUSELOOKEVENT_H -#define _MWINPUT_MOUSELOOKEVENT_H - -/* - A mouse-look class for Ogre. Accepts input events from Mangle::Input - and translates them. - - You can adjust the mouse sensibility and switch to a different - camera. The mouselook class also has an optional wrap protection - that keeps the camera from flipping upside down. - - You can disable the mouse looker at any time by calling - setCamera(NULL), and reenable it by setting the camera back. - - NOTE: The current implementation will ONLY work for native OIS - events. - */ - -#include - -namespace MWInput -{ - class MouseLookEvent : public Mangle::Input::Event - { - float sensX, sensY; // Mouse sensibility - bool flipProt; // Flip protection - bool mDisabled; - - public: - MouseLookEvent(float sX = 0.2, float sY = 0.2, bool prot=true) - : sensX(sX), sensY(sY), flipProt(prot) - {} - - void setSens(float sX, float sY) { - sensX = sX; - sensY = sY; - } - - void setProt(bool p) { - flipProt = p; - } - - void disable() { - mDisabled = true; - } - - void enable() { - mDisabled = false; - } - - void event(Type type, int index, const void *p); - }; - - typedef boost::shared_ptr MouseLookEventPtr; -} - -#endif diff --git a/extern/oics/CMakeLists.txt b/extern/oics/CMakeLists.txt new file mode 100644 index 0000000000..7c14387a4b --- /dev/null +++ b/extern/oics/CMakeLists.txt @@ -0,0 +1,20 @@ +set(OICS_LIBRARY "oics") + +# Sources + +set(OICS_SOURCE_FILES + ICSChannel.cpp + ICSControl.cpp + ICSInputControlSystem.cpp + ICSInputControlSystem_keyboard.cpp + ICSInputControlSystem_mouse.cpp + ICSInputControlSystem_joystick.cpp + tinyxml.cpp + tinyxmlparser.cpp + tinyxmlerror.cpp + tinystr.cpp +) + +add_library(${OICS_LIBRARY} STATIC ${OICS_SOURCE_FILES}) + +link_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/extern/oics/ICSChannel.cpp b/extern/oics/ICSChannel.cpp new file mode 100644 index 0000000000..703f2207c9 --- /dev/null +++ b/extern/oics/ICSChannel.cpp @@ -0,0 +1,258 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +#define B1(t) (t*t) +#define B2(t) (2*t*(1-t)) +#define B3(t) ((1-t)*(1-t)) + +namespace ICS +{ + Channel::Channel(int number, float initialValue + , float bezierMidPointY, float bezierMidPointX, float symmetricAt, float bezierStep) + : mNumber(number) + , mValue(initialValue) + , mSymmetricAt(symmetricAt) + , mBezierStep(bezierStep) + { + mBezierMidPoint.x = bezierMidPointX; + mBezierMidPoint.y = bezierMidPointY; + + setBezierFunction(bezierMidPointY, bezierMidPointX, symmetricAt, bezierStep); + } + + float Channel::getValue() + { + if(mValue == 0 || mValue == 1) + { + return mValue; + } + + BezierFunction::iterator it = mBezierFunction.begin(); + //size_t size_minus_1 = mBezierFunction.size() - 1; + BezierFunction::iterator last = mBezierFunction.end(); + last--; + for ( ; it != last ; ) + { + BezierPoint left = (*it); + BezierPoint right = (*(++it)); + + if( (left.x <= mValue) && (right.x > mValue) ) + { + float val = left.y - (left.x - mValue) * (left.y - right.y) / (left.x - right.x); + + return std::max(0.0,std::min(1.0, val)); + } + } + + return -1; + } + + void Channel::setValue(float value) + { + float previousValue = this->getValue(); + + mValue = value; + + if(previousValue != value) + { + notifyListeners(previousValue); + } + } + + void Channel::notifyListeners(float previousValue) + { + std::list::iterator pos = mListeners.begin(); + while (pos != mListeners.end()) + { + ((ChannelListener* )(*pos))->channelChanged((Channel*)this, this->getValue(), previousValue); + ++pos; + } + } + + void Channel::addControl(Control* control, Channel::ChannelDirection dir, float percentage) + { + ControlChannelBinderItem ccBinderItem; + ccBinderItem.control = control; + ccBinderItem.direction = dir; + ccBinderItem.percentage = percentage; + + mAttachedControls.push_back(ccBinderItem); + } + + Channel::ControlChannelBinderItem Channel::getAttachedControlBinding(Control* control) + { + for(std::vector::iterator it = mAttachedControls.begin() ; + it != mAttachedControls.end() ; it++) + { + if((*it).control == control) + { + return (*it); + } + } + + ControlChannelBinderItem nullBinderItem; + nullBinderItem.control = NULL; + nullBinderItem.direction = Channel/*::ChannelDirection*/::DIRECT; + nullBinderItem.percentage = 0; + return nullBinderItem; + } + + void Channel::update() + { + if(this->getControlsCount() == 1) + { + ControlChannelBinderItem ccBinderItem = mAttachedControls.back(); + float diff = ccBinderItem.control->getValue() - ccBinderItem.control->getInitialValue(); + + if(ccBinderItem.direction == ICS::Channel::DIRECT) + { + this->setValue(ccBinderItem.control->getInitialValue() + (ccBinderItem.percentage * diff)); + } + else + { + this->setValue(ccBinderItem.control->getInitialValue() - (ccBinderItem.percentage * diff)); + } + } + else + { + float val = 0; + std::vector::const_iterator it; + for(it=mAttachedControls.begin(); it!=mAttachedControls.end(); ++it) + { + ControlChannelBinderItem ccBinderItem = (*it); + float diff = ccBinderItem.control->getValue() - ccBinderItem.control->getInitialValue(); + + if(ccBinderItem.direction == ICS::Channel::DIRECT) + { + val += (ccBinderItem.percentage * diff); + } + else + { + val -= (ccBinderItem.percentage * diff); + } + } + + if(mAttachedControls.size() > 0) + { + this->setValue(mAttachedControls.begin()->control->getInitialValue() + val); + } + } + } + + void Channel::setBezierFunction(float bezierMidPointY, float bezierMidPointX, float symmetricAt, float bezierStep) + { + mBezierMidPoint.x = bezierMidPointX; + mBezierMidPoint.y = bezierMidPointY; + mBezierStep = bezierStep; + mSymmetricAt = symmetricAt; + + mBezierFunction.clear(); + + BezierPoint start; + start.x = 0; + start.y = 0; + + BezierPoint end; + end.x = 1; + end.y = 1; + mBezierFunction.push_front(end); + + FilterInterval interval; + interval.startX = start.x; + interval.startY = start.y; + interval.midX = mBezierMidPoint.x; + interval.midY = mBezierMidPoint.y; + interval.endX = end.x; + interval.endY = end.y; + interval.step = bezierStep; + mIntervals.push_back(interval); + + if(!(mBezierMidPoint.x == 0.5 && mBezierMidPoint.y == 0.5)) + { + float t = mBezierStep; + while(t < 1) + { + BezierPoint p; + p.x = start.x * B1(t) + mBezierMidPoint.x * B2(t) + end.x * B3(t); + p.y = start.y * B1(t) + mBezierMidPoint.y * B2(t) + end.y * B3(t); + mBezierFunction.push_front(p); + + t += mBezierStep; + } + } + + mBezierFunction.push_front(start); + } + + void Channel::addBezierInterval(float startX, float startY, float midX, float midY + , float endX, float endY, float step) + { + FilterInterval interval; + interval.startX = startX; + interval.startY = startY; + interval.midX = midX; + interval.midY = midY; + interval.endX = endX; + interval.endY = endY; + interval.step = step; + mIntervals.push_back(interval); + + float t = 0; + while(t <= 1) + { + BezierPoint p; + p.x = startX * B1(t) + midX * B2(t) + endX * B3(t); + p.y = startY * B1(t) + midY * B2(t) + endY * B3(t); + + BezierFunction::iterator it = mBezierFunction.begin(); + while( it != mBezierFunction.end() ) + { + BezierPoint left = (*it); + BezierPoint right; + ++it; + if( it != mBezierFunction.end() ) + { + right = (*it); + } + else + { + right.x = endX; + right.y = endY; + } + + if(p.x > left.x && p.x < right.x) + { + mBezierFunction.insert(it, p); + break; + } + } + + t += 1.0f / ((endX-startX)/step); + } + } +} \ No newline at end of file diff --git a/extern/oics/ICSChannel.h b/extern/oics/ICSChannel.h new file mode 100644 index 0000000000..f98f0d94d3 --- /dev/null +++ b/extern/oics/ICSChannel.h @@ -0,0 +1,122 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _Channel_H_ +#define _Channel_H_ + +#include "ICSPrerequisites.h" + +#include "ICSChannelListener.h" + +namespace ICS +{ + struct FilterInterval{ + //std::string type; //! @todo uncomment when more types implemented + float startX; + float startY; + float midX; + float midY; + float endX; + float endY; + float step; + }; + + typedef std::list IntervalList; + + class DllExport Channel + { + public: + enum ChannelDirection + { + INVERSE = -1, DIRECT = 1 + }; + + typedef struct { + ChannelDirection direction; + float percentage; + Control* control; + } ControlChannelBinderItem; + + + Channel(int number, float initialValue = 0.5 + , float bezierMidPointY = 0.5, float bezierMidPointX = 0.5 + , float symmetricAt = 0, float bezierStep = 0.2); //! @todo implement symetry + ~Channel(){}; + + void setValue(float value); + float getValue(); + + inline int getNumber(){ return mNumber; }; + + void addControl(Control* control, Channel::ChannelDirection dir, float percentage); + inline size_t getControlsCount(){ return mAttachedControls.size(); }; + std::vector getAttachedControls(){ return mAttachedControls; }; + ControlChannelBinderItem getAttachedControlBinding(Control* control); + + void addListener(ChannelListener* ob){ mListeners.push_back(ob); }; + void removeListener(ChannelListener* ob){ mListeners.remove(ob); }; + + void update(); + + void setBezierFunction(float bezierMidPointY, float bezierMidPointX = 0.5 + , float symmetricAt = 0, float bezierStep = 0.2); + + void addBezierInterval(float startX, float startY, float midX, float midY + , float endX, float endY, float step = 0.1); + + IntervalList& getIntervals(){ return mIntervals; }; + + protected: + + int mNumber; + float mValue; + + struct BezierPoint{ + float x; + float y; + bool operator < (const BezierPoint& other){ return x < other.x; } + }; + + typedef std::list BezierFunction; + + BezierPoint mBezierMidPoint; + BezierFunction mBezierFunction; + float mSymmetricAt; + float mBezierStep; + + IntervalList mIntervals; + + std::vector mAttachedControls; + + std::list mListeners; + void notifyListeners(float previousValue); + + }; + +} + + +#endif \ No newline at end of file diff --git a/extern/oics/ICSChannelListener.h b/extern/oics/ICSChannelListener.h new file mode 100644 index 0000000000..d520b3bceb --- /dev/null +++ b/extern/oics/ICSChannelListener.h @@ -0,0 +1,46 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _ChannelListener_H_ +#define _ChannelListener_H_ + +#include "ICSPrerequisites.h" + +#include "ICSChannel.h" + +namespace ICS +{ + + class DllExport ChannelListener + { + public: + virtual void channelChanged(Channel* channel, float currentValue, float previousValue) = 0; + }; + +} + + +#endif \ No newline at end of file diff --git a/extern/oics/ICSControl.cpp b/extern/oics/ICSControl.cpp new file mode 100644 index 0000000000..d43733727f --- /dev/null +++ b/extern/oics/ICSControl.cpp @@ -0,0 +1,161 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +#include "ICSControl.h" + +namespace ICS +{ + Control::Control(const std::string name, bool autoChangeDirectionOnLimitsAfterStop, bool autoReverseToInitialValue + , float initialValue, float stepSize, float stepsPerSeconds, bool axisBindable) + : mName(name) + , mValue(initialValue) + , mInitialValue(initialValue) + , mStepSize(stepSize) + , mStepsPerSeconds(stepsPerSeconds) + , mAutoReverseToInitialValue(autoReverseToInitialValue) + , mIgnoreAutoReverse(false) + , mAutoChangeDirectionOnLimitsAfterStop(autoChangeDirectionOnLimitsAfterStop) + , mAxisBindable(axisBindable) + , currentChangingDirection(STOP) + { + + } + + Control::~Control() + { + mAttachedChannels.clear(); + } + + void Control::setValue(float value) + { + float previousValue = mValue; + + mValue = std::max(0.0,std::min(1.0,value)); + + if(mValue != previousValue) + { + updateChannels(); + + notifyListeners(previousValue); + } + } + + void Control::attachChannel(Channel* channel, Channel::ChannelDirection direction, float percentage) + { + mAttachedChannels.push_back(channel); + channel->addControl(this, direction, percentage); + } + + void Control::updateChannels() + { + std::list::iterator pos = mAttachedChannels.begin(); + while (pos != mAttachedChannels.end()) + { + ((Channel* )(*pos))->update(); + ++pos; + } + } + + void Control::notifyListeners(float previousValue) + { + std::list::iterator pos = mListeners.begin(); + while (pos != mListeners.end()) + { + ((ControlListener* )(*pos))->controlChanged((Control*)this, this->getValue(), previousValue); + ++pos; + } + } + + void Control::setChangingDirection(ControlChangingDirection direction) + { + currentChangingDirection = direction; + mPendingActions.push_back(direction); + } + + void Control::update(float timeSinceLastFrame) + { + if(mPendingActions.size() > 0) + { + size_t timedActionsCount = 0; + + std::list::iterator cached_end = mPendingActions.end(); + for(std::list::iterator it = mPendingActions.begin() ; + it != cached_end ; it++) + { + if( (*it) != Control::STOP ) + { + timedActionsCount++; + } + } + + float timeSinceLastFramePart = timeSinceLastFrame / std::max(1, timedActionsCount); + for(std::list::iterator it = mPendingActions.begin() ; + it != cached_end ; it++) + { + if( (*it) != Control::STOP ) + { + this->setValue(mValue + + (((int)(*it)) * mStepSize * mStepsPerSeconds * (timeSinceLastFramePart))); + } + else if(mAutoReverseToInitialValue && !mIgnoreAutoReverse && mValue != mInitialValue ) + { + + if(mValue > mInitialValue) + { + this->setValue( std::max( mInitialValue, + mValue - (mStepSize * mStepsPerSeconds * (timeSinceLastFramePart)))); + } + else if(mValue < mInitialValue) + { + this->setValue( std::min( mInitialValue, + mValue + (mStepSize * mStepsPerSeconds * (timeSinceLastFramePart)))); + } + } + } + mPendingActions.clear(); + } + else if( currentChangingDirection != Control::STOP ) + { + this->setValue(mValue + + (((int)currentChangingDirection) * mStepSize * mStepsPerSeconds * (timeSinceLastFrame))); + } + else if(mAutoReverseToInitialValue && !mIgnoreAutoReverse && mValue != mInitialValue ) + { + if(mValue > mInitialValue) + { + this->setValue( std::max( mInitialValue, + mValue - (mStepSize * mStepsPerSeconds * (timeSinceLastFrame)))); + } + else if(mValue < mInitialValue) + { + this->setValue( std::min( mInitialValue, + mValue + (mStepSize * mStepsPerSeconds * (timeSinceLastFrame)))); + } + } + } +} diff --git a/extern/oics/ICSControl.h b/extern/oics/ICSControl.h new file mode 100644 index 0000000000..73f1d5494b --- /dev/null +++ b/extern/oics/ICSControl.h @@ -0,0 +1,107 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _Control_H_ +#define _Control_H_ + +#include "ICSPrerequisites.h" + +#include "ICSChannel.h" +#include "ICSControlListener.h" + +namespace ICS +{ + + class DllExport Control + { + public: + + enum ControlChangingDirection + { + DECREASE = -1, STOP = 0, INCREASE = 1 + }; + + Control(const std::string name, bool autoChangeDirectionOnLimitsAfterStop = false, bool autoReverseToInitialValue = false, float initialValue = 0.5, float stepSize = 0.1, float stepsPerSeconds = 2.0, bool axisBindable = true); + ~Control(); + + void setChangingDirection(ControlChangingDirection direction); + inline ControlChangingDirection getChangingDirection(){ return currentChangingDirection; }; + + void setValue(float value); + inline float getValue(){ return mValue; }; + inline float getInitialValue(){ return mInitialValue; }; + + void attachChannel(Channel* channel, Channel::ChannelDirection direction, float percentage = 1.0); + std::list getAttachedChannels(){ return mAttachedChannels; }; + + inline float getStepSize(){ return mStepSize; }; + inline float getStepsPerSeconds(){ return mStepsPerSeconds; }; + + inline void setIgnoreAutoReverse(bool value){ mIgnoreAutoReverse = value; }; // mouse disable autoreverse + inline bool isAutoReverseIgnored(){ return mIgnoreAutoReverse; }; + inline bool getAutoReverse(){ return mAutoReverseToInitialValue; }; + + inline bool getAutoChangeDirectionOnLimitsAfterStop(){ return mAutoChangeDirectionOnLimitsAfterStop; }; + + inline std::string getName(){ return mName; }; + + inline bool isAxisBindable(){ return mAxisBindable; }; + inline void setAxisBindable(bool value){ mAxisBindable = value; }; + + inline void addListener(ControlListener* ob){ mListeners.push_back(ob); }; + inline void removeListener(ControlListener* ob){ mListeners.remove(ob); }; + + void update(float timeSinceLastFrame); + + protected: + float mValue; + float mInitialValue; + std::string mName; + float mStepSize; + float mStepsPerSeconds; + bool mAutoReverseToInitialValue; + bool mIgnoreAutoReverse; + bool mAutoChangeDirectionOnLimitsAfterStop; + bool mAxisBindable; + + Control::ControlChangingDirection currentChangingDirection; + std::list mAttachedChannels; + + std::list mListeners; + + std::list mPendingActions; + + protected: + + void updateChannels(); + void notifyListeners(float previousValue); + + }; + +} + + +#endif \ No newline at end of file diff --git a/extern/oics/ICSControlListener.h b/extern/oics/ICSControlListener.h new file mode 100644 index 0000000000..067b2d6f24 --- /dev/null +++ b/extern/oics/ICSControlListener.h @@ -0,0 +1,46 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _ControlListener_H_ +#define _ControlListener_H_ + +#include "ICSPrerequisites.h" + +#include "ICSControl.h" + +namespace ICS +{ + + class DllExport ControlListener + { + public: + virtual void controlChanged(Control* control, float currentValue, float previousValue) = 0; + }; + +} + + +#endif \ No newline at end of file diff --git a/extern/oics/ICSInputControlSystem.cpp b/extern/oics/ICSInputControlSystem.cpp new file mode 100644 index 0000000000..1702c853ed --- /dev/null +++ b/extern/oics/ICSInputControlSystem.cpp @@ -0,0 +1,929 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +namespace ICS +{ + InputControlSystem::InputControlSystem(std::string file, bool active + , DetectingBindingListener* detectingBindingListener + , InputControlSystemLog* log, size_t channelCount) + : mFileName(file) + , mDetectingBindingListener(detectingBindingListener) + , mDetectingBindingControl(NULL) + , mLog(log) + , mXmouseAxisBinded(false), mYmouseAxisBinded(false) + { + ICS_LOG(" - Creating InputControlSystem - "); + + this->mActive = active; + + this->fillOISKeysMap(); + + ICS_LOG("Channel count = " + ToString(channelCount) ); + for(size_t i=0;iLoadFile(); + + if(xmlDoc->Error()) + { + std::ostringstream message; + message << "TinyXml reported an error reading \""+ file + "\". Row " << + (int)xmlDoc->ErrorRow() << ", Col " << (int)xmlDoc->ErrorCol() << ": " << + xmlDoc->ErrorDesc() ; + ICS_LOG(message.str()); + + delete xmlDoc; + return; + } + + xmlRoot = xmlDoc->RootElement(); + if(std::string(xmlRoot->Value()) != "Controller") { + ICS_LOG("Error: Invalid Controller file. Missing element."); + delete xmlDoc; + return; + } + + TiXmlElement* xmlControl = xmlRoot->FirstChildElement("Control"); + + size_t controlChannelCount = 0; + while(xmlControl) + { + TiXmlElement* xmlChannel = xmlControl->FirstChildElement("Channel"); + while(xmlChannel) + { + controlChannelCount = std::max(channelCount, FromString(xmlChannel->Attribute("number"))); + + xmlChannel = xmlChannel->NextSiblingElement("Channel"); + } + + xmlControl = xmlControl->NextSiblingElement("Control"); + } + + if(controlChannelCount > channelCount) + { + size_t dif = controlChannelCount - channelCount; + ICS_LOG("Warning: default channel count exceeded. Adding " + ToString(dif) + " channels" ); + for(size_t i = channelCount ; i < controlChannelCount ; i++) + { + mChannels.push_back(new Channel((int)i)); + } + } + + ICS_LOG("Applying filters to channels"); + // + // + // + // + + TiXmlElement* xmlChannelFilter = xmlRoot->FirstChildElement("ChannelFilter"); + while(xmlChannelFilter) + { + int ch = FromString(xmlChannelFilter->Attribute("number")); + + TiXmlElement* xmlInterval = xmlChannelFilter->FirstChildElement("Interval"); + while(xmlInterval) + { + std::string type = xmlInterval->Attribute("type"); + + if(type == "bezier") + { + float step = 0.1; + + float startX = FromString(xmlInterval->Attribute("startX")); + float startY = FromString(xmlInterval->Attribute("startY")); + float midX = FromString(xmlInterval->Attribute("midX")); + float midY = FromString(xmlInterval->Attribute("midY")); + float endX = FromString(xmlInterval->Attribute("endX")); + float endY = FromString(xmlInterval->Attribute("endY")); + + step = FromString(xmlInterval->Attribute("step")); + + ICS_LOG("Applying Bezier filter to channel [number=" + + ToString(ch) + ", startX=" + + ToString(startX) + ", startY=" + + ToString(startY) + ", midX=" + + ToString(midX) + ", midY=" + + ToString(midY) + ", endX=" + + ToString(endX) + ", endY=" + + ToString(endY) + ", step=" + + ToString(step) + "]"); + + mChannels.at(ch)->addBezierInterval(startX, startY, midX, midY, endX, endY, step); + } + + xmlInterval = xmlInterval->NextSiblingElement("Interval"); + } + + + xmlChannelFilter = xmlChannelFilter->NextSiblingElement("ChannelFilter"); + } + + xmlControl = xmlRoot->FirstChildElement("Control"); + while(xmlControl) + { + bool axisBindable = true; + if(xmlControl->Attribute("axisBindable")) + { + axisBindable = (std::string( xmlControl->Attribute("axisBindable") ) == "true"); + } + + ICS_LOG("Adding Control [name=" + + std::string( xmlControl->Attribute("name") ) + ", autoChangeDirectionOnLimitsAfterStop=" + + std::string( xmlControl->Attribute("autoChangeDirectionOnLimitsAfterStop") ) + ", autoReverseToInitialValue=" + + std::string( xmlControl->Attribute("autoReverseToInitialValue") ) + ", initialValue=" + + std::string( xmlControl->Attribute("initialValue") ) + ", stepSize=" + + std::string( xmlControl->Attribute("stepSize") ) + ", stepsPerSeconds=" + + std::string( xmlControl->Attribute("stepsPerSeconds") ) + ", axisBindable=" + + std::string( (axisBindable)? "true" : "false" ) + "]"); + + float _stepSize = 0; + if(xmlControl->Attribute("stepSize")) + { + std::string value(xmlControl->Attribute("stepSize")); + if(value == "MAX") + { + _stepSize = ICS_MAX; + } + else + { + _stepSize = FromString(value.c_str()); + } + } + else + { + ICS_LOG("Warning: no stepSize value found. Default value is 0."); + } + + float _stepsPerSeconds = 0; + if(xmlControl->Attribute("stepsPerSeconds")) + { + std::string value(xmlControl->Attribute("stepsPerSeconds")); + if(value == "MAX") + { + _stepsPerSeconds = ICS_MAX; + } + else + { + _stepsPerSeconds = FromString(value.c_str()); + } + } + else + { + ICS_LOG("Warning: no stepSize value found. Default value is 0."); + } + + addControl( new Control(xmlControl->Attribute("name") + , std::string( xmlControl->Attribute("autoChangeDirectionOnLimitsAfterStop") ) == "true" + , std::string( xmlControl->Attribute("autoReverseToInitialValue") ) == "true" + , FromString(xmlControl->Attribute("initialValue")) + , _stepSize + , _stepsPerSeconds + , axisBindable) ); + + loadKeyBinders(xmlControl); + + loadMouseAxisBinders(xmlControl); + + loadMouseButtonBinders(xmlControl); + + loadJoystickAxisBinders(xmlControl); + + loadJoystickButtonBinders(xmlControl); + + loadJoystickPOVBinders(xmlControl); + + loadJoystickSliderBinders(xmlControl); + + // Attach controls to channels + TiXmlElement* xmlChannel = xmlControl->FirstChildElement("Channel"); + while(xmlChannel) + { + ICS_LOG("\tAttaching control to channel [number=" + + std::string( xmlChannel->Attribute("number") ) + ", direction=" + + std::string( xmlChannel->Attribute("direction") ) + "]"); + + float percentage = 1; + if(xmlChannel->Attribute("percentage")) + { + if(StringIsNumber(xmlChannel->Attribute("percentage"))) + { + float val = FromString(xmlChannel->Attribute("percentage")); + if(val > 1 || val < 0) + { + ICS_LOG("ERROR: attaching percentage value range is [0,1]"); + } + else + { + percentage = val; + } + } + else + { + ICS_LOG("ERROR: attaching percentage value range is [0,1]"); + } + } + + int chNumber = FromString(xmlChannel->Attribute("number")); + if(std::string(xmlChannel->Attribute("direction")) == "DIRECT") + { + mControls.back()->attachChannel(mChannels[ chNumber ],Channel::DIRECT, percentage); + } + else if(std::string(xmlChannel->Attribute("direction")) == "INVERSE") + { + mControls.back()->attachChannel(mChannels[ chNumber ],Channel::INVERSE, percentage); + } + + xmlChannel = xmlChannel->NextSiblingElement("Channel"); + } + + xmlControl = xmlControl->NextSiblingElement("Control"); + } + + std::vector::const_iterator o; + for(o = mChannels.begin(); o != mChannels.end(); ++o) + { + (*o)->update(); + } + + delete xmlDoc; + } + + ICS_LOG(" - InputControlSystem Created - "); + } + + InputControlSystem::~InputControlSystem() + { + ICS_LOG(" - Deleting InputControlSystem (" + mFileName + ") - "); + + mJoystickIDList.clear(); + + std::vector::const_iterator o; + for(o = mChannels.begin(); o != mChannels.end(); ++o) + { + delete (*o); + } + mChannels.clear(); + + std::vector::const_iterator o2; + for(o2 = mControls.begin(); o2 != mControls.end(); ++o2) + { + delete (*o2); + } + mControls.clear(); + + mControlsKeyBinderMap.clear(); + mControlsMouseButtonBinderMap.clear(); + mControlsJoystickButtonBinderMap.clear(); + + mKeys.clear(); + mKeyCodes.clear(); + + ICS_LOG(" - InputControlSystem deleted - "); + } + + std::string InputControlSystem::getBaseFileName() + { + size_t found = mFileName.find_last_of("/\\"); + std::string file = mFileName.substr(found+1); + + return file.substr(0, file.find_last_of(".")); + } + + bool InputControlSystem::save(std::string fileName) + { + if(fileName != "") + { + mFileName = fileName; + } + + TiXmlDocument doc( mFileName.c_str() ); + + TiXmlDeclaration dec; + dec.Parse( "", 0, TIXML_ENCODING_UNKNOWN ); + doc.InsertEndChild(dec); + + TiXmlElement Controller( "Controller" ); + + for(std::vector::const_iterator o = mChannels.begin() ; o != mChannels.end(); o++) + { + ICS::IntervalList intervals = (*o)->getIntervals(); + + if(intervals.size() > 1) // all channels have a default linear filter + { + TiXmlElement ChannelFilter( "ChannelFilter" ); + + ChannelFilter.SetAttribute("number", ToString((*o)->getNumber()).c_str()); + + ICS::IntervalList::const_iterator interval = intervals.begin(); + while( interval != intervals.end() ) + { + // if not default linear filter + if(!( interval->step == 0.2f + && interval->startX == 0.0f + && interval->startY == 0.0f + && interval->midX == 0.5f + && interval->midY == 0.5f + && interval->endX == 1.0f + && interval->endY == 1.0f )) + { + TiXmlElement XMLInterval( "Interval" ); + + XMLInterval.SetAttribute("type", "bezier"); + XMLInterval.SetAttribute("step", ToString(interval->step).c_str()); + + XMLInterval.SetAttribute("startX", ToString(interval->startX).c_str()); + XMLInterval.SetAttribute("startY", ToString(interval->startY).c_str()); + XMLInterval.SetAttribute("midX", ToString(interval->midX).c_str()); + XMLInterval.SetAttribute("midY", ToString(interval->midY).c_str()); + XMLInterval.SetAttribute("endX", ToString(interval->endX).c_str()); + XMLInterval.SetAttribute("endY", ToString(interval->endY).c_str()); + + ChannelFilter.InsertEndChild(XMLInterval); + } + + interval++; + } + + Controller.InsertEndChild(ChannelFilter); + } + } + + for(std::vector::const_iterator o = mControls.begin() ; o != mControls.end(); o++) + { + TiXmlElement control( "Control" ); + + control.SetAttribute( "name", (*o)->getName().c_str() ); + if((*o)->getAutoChangeDirectionOnLimitsAfterStop()) + { + control.SetAttribute( "autoChangeDirectionOnLimitsAfterStop", "true" ); + } + else + { + control.SetAttribute( "autoChangeDirectionOnLimitsAfterStop", "false" ); + } + if((*o)->getAutoReverse()) + { + control.SetAttribute( "autoReverseToInitialValue", "true" ); + } + else + { + control.SetAttribute( "autoReverseToInitialValue", "false" ); + } + control.SetAttribute( "initialValue", ToString((*o)->getInitialValue()).c_str() ); + + if((*o)->getStepSize() == ICS_MAX) + { + control.SetAttribute( "stepSize", "MAX" ); + } + else + { + control.SetAttribute( "stepSize", ToString((*o)->getStepSize()).c_str() ); + } + + if((*o)->getStepsPerSeconds() == ICS_MAX) + { + control.SetAttribute( "stepsPerSeconds", "MAX" ); + } + else + { + control.SetAttribute( "stepsPerSeconds", ToString((*o)->getStepsPerSeconds()).c_str() ); + } + + if(!(*o)->isAxisBindable()) + { + control.SetAttribute( "axisBindable", "false" ); + } + + if(getKeyBinding(*o, Control/*::ControlChangingDirection*/::INCREASE) != OIS::KC_UNASSIGNED) + { + TiXmlElement keyBinder( "KeyBinder" ); + + keyBinder.SetAttribute( "key", keyCodeToString( + getKeyBinding(*o, Control/*::ControlChangingDirection*/::INCREASE)).c_str() ); + keyBinder.SetAttribute( "direction", "INCREASE" ); + control.InsertEndChild(keyBinder); + } + + if(getKeyBinding(*o, Control/*::ControlChangingDirection*/::DECREASE) != OIS::KC_UNASSIGNED) + { + TiXmlElement keyBinder( "KeyBinder" ); + + keyBinder.SetAttribute( "key", keyCodeToString( + getKeyBinding(*o, Control/*::ControlChangingDirection*/::DECREASE)).c_str() ); + keyBinder.SetAttribute( "direction", "DECREASE" ); + control.InsertEndChild(keyBinder); + } + + if(getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::INCREASE) + != InputControlSystem/*::NamedAxis*/::UNASSIGNED) + { + TiXmlElement binder( "MouseBinder" ); + + InputControlSystem::NamedAxis axis = + getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::INCREASE); + if(axis == InputControlSystem/*::NamedAxis*/::X) + { + binder.SetAttribute( "axis", "X" ); + } + else if(axis == InputControlSystem/*::NamedAxis*/::Y) + { + binder.SetAttribute( "axis", "Y" ); + } + else if(axis == InputControlSystem/*::NamedAxis*/::Z) + { + binder.SetAttribute( "axis", "Z" ); + } + + binder.SetAttribute( "direction", "INCREASE" ); + control.InsertEndChild(binder); + } + + if(getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::DECREASE) + != InputControlSystem/*::NamedAxis*/::UNASSIGNED) + { + TiXmlElement binder( "MouseBinder" ); + + InputControlSystem::NamedAxis axis = + getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::DECREASE); + if(axis == InputControlSystem/*::NamedAxis*/::X) + { + binder.SetAttribute( "axis", "X" ); + } + else if(axis == InputControlSystem/*::NamedAxis*/::Y) + { + binder.SetAttribute( "axis", "Y" ); + } + else if(axis == InputControlSystem/*::NamedAxis*/::Z) + { + binder.SetAttribute( "axis", "Z" ); + } + + binder.SetAttribute( "direction", "DECREASE" ); + control.InsertEndChild(binder); + } + + if(getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::INCREASE) + != ICS_MAX_DEVICE_BUTTONS) + { + TiXmlElement binder( "MouseButtonBinder" ); + + unsigned int button = getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::INCREASE); + if(button == OIS::/*MouseButtonID::*/MB_Left) + { + binder.SetAttribute( "button", "LEFT" ); + } + else if(button == OIS::/*MouseButtonID::*/MB_Middle) + { + binder.SetAttribute( "button", "MIDDLE" ); + } + else if(button == OIS::/*MouseButtonID::*/MB_Right) + { + binder.SetAttribute( "button", "RIGHT" ); + } + else + { + binder.SetAttribute( "button", ToString(button).c_str() ); + } + binder.SetAttribute( "direction", "INCREASE" ); + control.InsertEndChild(binder); + } + + if(getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::DECREASE) + != ICS_MAX_DEVICE_BUTTONS) + { + TiXmlElement binder( "MouseButtonBinder" ); + + unsigned int button = getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::DECREASE); + if(button == OIS::/*MouseButtonID::*/MB_Left) + { + binder.SetAttribute( "button", "LEFT" ); + } + else if(button == OIS::/*MouseButtonID::*/MB_Middle) + { + binder.SetAttribute( "button", "MIDDLE" ); + } + else if(button == OIS::/*MouseButtonID::*/MB_Right) + { + binder.SetAttribute( "button", "RIGHT" ); + } + else + { + binder.SetAttribute( "button", ToString(button).c_str() ); + } + binder.SetAttribute( "direction", "DECREASE" ); + control.InsertEndChild(binder); + } + + JoystickIDList::const_iterator it = mJoystickIDList.begin(); + while(it != mJoystickIDList.end()) + { + int deviceId = *it; + + if(getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE) + != /*NamedAxis::*/UNASSIGNED) + { + TiXmlElement binder( "JoystickAxisBinder" ); + + binder.SetAttribute( "axis", ToString( + getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)).c_str() ); + + binder.SetAttribute( "direction", "INCREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE) + != /*NamedAxis::*/UNASSIGNED) + { + TiXmlElement binder( "JoystickAxisBinder" ); + + binder.SetAttribute( "axis", ToString( + getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE)).c_str() ); + + binder.SetAttribute( "direction", "DECREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickButtonBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE) + != ICS_MAX_DEVICE_BUTTONS) + { + TiXmlElement binder( "JoystickButtonBinder" ); + + binder.SetAttribute( "button", ToString( + getJoystickButtonBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)).c_str() ); + + binder.SetAttribute( "direction", "INCREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickButtonBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE) + != ICS_MAX_DEVICE_BUTTONS) + { + TiXmlElement binder( "JoystickButtonBinder" ); + + binder.SetAttribute( "button", ToString( + getJoystickButtonBinding(*o, *it, Control/*::ControlChangingDirection*/::DECREASE)).c_str() ); + + binder.SetAttribute( "direction", "DECREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE).index >= 0) + { + TiXmlElement binder( "JoystickPOVBinder" ); + + POVBindingPair POVPair = getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE); + + binder.SetAttribute( "pov", ToString(POVPair.index).c_str() ); + + binder.SetAttribute( "direction", "INCREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + if(POVPair.axis == ICS::InputControlSystem::EastWest) + { + binder.SetAttribute( "axis", "EastWest" ); + } + else + { + binder.SetAttribute( "axis", "NorthSouth" ); + } + + control.InsertEndChild(binder); + } + + if(getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE).index >= 0) + { + TiXmlElement binder( "JoystickPOVBinder" ); + + POVBindingPair POVPair = getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE); + + binder.SetAttribute( "pov", ToString(POVPair.index).c_str() ); + + binder.SetAttribute( "direction", "DECREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + if(POVPair.axis == ICS::InputControlSystem::EastWest) + { + binder.SetAttribute( "axis", "EastWest" ); + } + else + { + binder.SetAttribute( "axis", "NorthSouth" ); + } + + control.InsertEndChild(binder); + } + + if(getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE) + != /*NamedAxis::*/UNASSIGNED) + { + TiXmlElement binder( "JoystickSliderBinder" ); + + binder.SetAttribute( "slider", ToString( + getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)).c_str() ); + + binder.SetAttribute( "direction", "INCREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE) + != /*NamedAxis::*/UNASSIGNED) + { + TiXmlElement binder( "JoystickSliderBinder" ); + + binder.SetAttribute( "slider", ToString( + getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE)).c_str() ); + + binder.SetAttribute( "direction", "DECREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + it++; + } + + + std::list channels = (*o)->getAttachedChannels(); + for(std::list::iterator it = channels.begin() ; + it != channels.end() ; it++) + { + TiXmlElement binder( "Channel" ); + + binder.SetAttribute( "number", ToString((*it)->getNumber()).c_str() ); + + Channel::ChannelDirection direction = (*it)->getAttachedControlBinding(*o).direction; + if(direction == Channel/*::ChannelDirection*/::DIRECT) + { + binder.SetAttribute( "direction", "DIRECT" ); + } + else + { + binder.SetAttribute( "direction", "INVERSE" ); + } + + float percentage = (*it)->getAttachedControlBinding(*o).percentage; + binder.SetAttribute( "percentage", ToString(percentage).c_str() ); + + control.InsertEndChild(binder); + } + + Controller.InsertEndChild(control); + } + + doc.InsertEndChild(Controller); + return doc.SaveFile(); + } + + void InputControlSystem::update(float lTimeSinceLastFrame) + { + if(mActive) + { + std::vector::const_iterator it; + for(it=mControls.begin(); it!=mControls.end(); ++it) + { + (*it)->update(lTimeSinceLastFrame); + } + } + + //! @todo Future versions should consider channel exponentials and mixtures, so + // after updating Controls, Channels should be updated according to their values + } + + float InputControlSystem::getChannelValue(int i) + { + return std::max(0.0,std::min(1.0,mChannels[i]->getValue())); + } + + float InputControlSystem::getControlValue(int i) + { + return mControls[i]->getValue(); + } + + void InputControlSystem::addJoystick(int deviceId) + { + ICS_LOG("Adding joystick (device id: " + ToString(deviceId) + ")"); + + for(int j = 0 ; j < ICS_MAX_JOYSTICK_AXIS ; j++) + { + if(mControlsJoystickAxisBinderMap[deviceId].find(j) == mControlsJoystickAxisBinderMap[deviceId].end()) + { + ControlAxisBinderItem controlJoystickBinderItem; + controlJoystickBinderItem.direction = Control::STOP; + controlJoystickBinderItem.control = NULL; + mControlsJoystickAxisBinderMap[deviceId][j] = controlJoystickBinderItem; + } + } + + mJoystickIDList.push_back(deviceId); + } + + Control* InputControlSystem::findControl(std::string name) + { + if(mActive) + { + std::vector::const_iterator it; + for(it = mControls.begin(); it != mControls.end(); ++it) + { + if( ((Control*)(*it))->getName() == name) + { + return (Control*)(*it); + } + } + } + + return NULL; + } + + void InputControlSystem::enableDetectingBindingState(Control* control + , Control::ControlChangingDirection direction) + { + mDetectingBindingControl = control; + mDetectingBindingDirection = direction; + + mMouseAxisBindingInitialValues[0] = ICS_MOUSE_AXIS_BINDING_NULL_VALUE; + } + + void InputControlSystem::cancelDetectingBindingState() + { + mDetectingBindingControl = NULL; + } + + void InputControlSystem::fillOISKeysMap() + { + mKeys["UNASSIGNED"]= OIS::KC_UNASSIGNED; + mKeys["ESCAPE"]= OIS::KC_ESCAPE; + mKeys["1"]= OIS::KC_1; + mKeys["2"]= OIS::KC_2; + mKeys["3"]= OIS::KC_3; + mKeys["4"]= OIS::KC_4; + mKeys["5"]= OIS::KC_5; + mKeys["6"]= OIS::KC_6; + mKeys["7"]= OIS::KC_7; + mKeys["8"]= OIS::KC_8; + mKeys["9"]= OIS::KC_9; + mKeys["0"]= OIS::KC_0; + mKeys["MINUS"]= OIS::KC_MINUS; + mKeys["EQUALS"]= OIS::KC_EQUALS; + mKeys["BACK"]= OIS::KC_BACK; + mKeys["TAB"]= OIS::KC_TAB; + mKeys["Q"]= OIS::KC_Q; + mKeys["W"]= OIS::KC_W; + mKeys["E"]= OIS::KC_E; + mKeys["R"]= OIS::KC_R; + mKeys["T"]= OIS::KC_T; + mKeys["Y"]= OIS::KC_Y; + mKeys["U"]= OIS::KC_U; + mKeys["I"]= OIS::KC_I; + mKeys["O"]= OIS::KC_O; + mKeys["P"]= OIS::KC_P; + mKeys["LBRACKET"]= OIS::KC_LBRACKET; + mKeys["RBRACKET"]= OIS::KC_RBRACKET; + mKeys["RETURN"]= OIS::KC_RETURN; + mKeys["LCONTROL"]= OIS::KC_LCONTROL; + mKeys["A"]= OIS::KC_A; + mKeys["S"]= OIS::KC_S; + mKeys["D"]= OIS::KC_D; + mKeys["F"]= OIS::KC_F; + mKeys["G"]= OIS::KC_G; + mKeys["H"]= OIS::KC_H; + mKeys["J"]= OIS::KC_J; + mKeys["K"]= OIS::KC_K; + mKeys["L"]= OIS::KC_L; + mKeys["SEMICOLON"]= OIS::KC_SEMICOLON; + mKeys["APOSTROPHE"]= OIS::KC_APOSTROPHE; + mKeys["GRAVE"]= OIS::KC_GRAVE; + mKeys["LSHIFT"]= OIS::KC_LSHIFT; + mKeys["BACKSLASH"]= OIS::KC_BACKSLASH; + mKeys["Z"]= OIS::KC_Z; + mKeys["X"]= OIS::KC_X; + mKeys["C"]= OIS::KC_C; + mKeys["V"]= OIS::KC_V; + mKeys["B"]= OIS::KC_B; + mKeys["N"]= OIS::KC_N; + mKeys["M"]= OIS::KC_M; + mKeys["COMMA"]= OIS::KC_COMMA; + mKeys["PERIOD"]= OIS::KC_PERIOD; + mKeys["SLASH"]= OIS::KC_SLASH; + mKeys["RSHIFT"]= OIS::KC_RSHIFT; + mKeys["MULTIPLY"]= OIS::KC_MULTIPLY; + mKeys["LMENU"]= OIS::KC_LMENU; + mKeys["SPACE"]= OIS::KC_SPACE; + mKeys["CAPITAL"]= OIS::KC_CAPITAL; + mKeys["F1"]= OIS::KC_F1; + mKeys["F2"]= OIS::KC_F2; + mKeys["F3"]= OIS::KC_F3; + mKeys["F4"]= OIS::KC_F4; + mKeys["F5"]= OIS::KC_F5; + mKeys["F6"]= OIS::KC_F6; + mKeys["F7"]= OIS::KC_F7; + mKeys["F8"]= OIS::KC_F8; + mKeys["F9"]= OIS::KC_F9; + mKeys["F10"]= OIS::KC_F10; + mKeys["F11"]= OIS::KC_F11; + mKeys["F12"]= OIS::KC_F12; + mKeys["NUMLOCK"]= OIS::KC_NUMLOCK; + mKeys["SCROLL"]= OIS::KC_SCROLL; + mKeys["NUMPAD7"]= OIS::KC_NUMPAD7; + mKeys["NUMPAD8"]= OIS::KC_NUMPAD8; + mKeys["NUMPAD9"]= OIS::KC_NUMPAD9; + mKeys["SUBTRACT"]= OIS::KC_SUBTRACT; + mKeys["NUMPAD4"]= OIS::KC_NUMPAD4; + mKeys["NUMPAD5"]= OIS::KC_NUMPAD5; + mKeys["NUMPAD6"]= OIS::KC_NUMPAD6; + mKeys["ADD"]= OIS::KC_ADD; + mKeys["NUMPAD1"]= OIS::KC_NUMPAD1; + mKeys["NUMPAD2"]= OIS::KC_NUMPAD2; + mKeys["NUMPAD3"]= OIS::KC_NUMPAD3; + mKeys["NUMPAD0"]= OIS::KC_NUMPAD0; + mKeys["DECIMAL"]= OIS::KC_DECIMAL; + mKeys["RCONTROL"]= OIS::KC_RCONTROL; + mKeys["DIVIDE"]= OIS::KC_DIVIDE; + mKeys["SYSRQ"]= OIS::KC_SYSRQ; + mKeys["RMENU"]= OIS::KC_RMENU; + mKeys["PAUSE"]= OIS::KC_PAUSE; + mKeys["HOME"]= OIS::KC_HOME; + mKeys["UP"]= OIS::KC_UP; + mKeys["PGUP"]= OIS::KC_PGUP; + mKeys["LEFT"]= OIS::KC_LEFT; + mKeys["RIGHT"]= OIS::KC_RIGHT; + mKeys["END"]= OIS::KC_END; + mKeys["DOWN"]= OIS::KC_DOWN; + mKeys["PGDOWN"]= OIS::KC_PGDOWN; + mKeys["INSERT"]= OIS::KC_INSERT; + mKeys["DELETE"]= OIS::KC_DELETE; + mKeys["LWIN"]= OIS::KC_LWIN; + mKeys["RWIN"]= OIS::KC_RWIN; + mKeys["APPS"]= OIS::KC_APPS; + + mKeys["NUMPADENTER"]= OIS::KC_NUMPADENTER; + + for(std::map::iterator it = mKeys.begin() + ; it != mKeys.end() ; it++) + { + mKeyCodes[ it->second ] = it->first; + } + } + + std::string InputControlSystem::keyCodeToString(OIS::KeyCode key) + { + return mKeyCodes[key]; + } + + OIS::KeyCode InputControlSystem::stringToKeyCode(std::string key) + { + return mKeys[key]; + } +} \ No newline at end of file diff --git a/extern/oics/ICSInputControlSystem.h b/extern/oics/ICSInputControlSystem.h new file mode 100644 index 0000000000..f1c12d3b59 --- /dev/null +++ b/extern/oics/ICSInputControlSystem.h @@ -0,0 +1,256 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _InputControlSystem_H_ +#define _InputControlSystem_H_ + +#include "ICSPrerequisites.h" + +#include "ICSControl.h" +#include "ICSChannel.h" + +#define ICS_LOG(text) if(mLog) mLog->logMessage( ("ICS: " + std::string(text)).c_str() ); +#define ICS_MAX_JOYSTICK_AXIS 16 +#define ICS_MOUSE_BINDING_MARGIN 30 +#define ICS_JOYSTICK_AXIS_BINDING_MARGIN 10000 +#define ICS_JOYSTICK_SLIDER_BINDING_MARGIN 10000 +#define ICS_MOUSE_AXIS_BINDING_NULL_VALUE std::numeric_limits::max() + +namespace ICS +{ + class DllExport InputControlSystemLog + { + public: + virtual void logMessage(const char* text) = 0; + }; + + class DllExport InputControlSystem : + public OIS::MouseListener, + public OIS::KeyListener, + public OIS::JoyStickListener + { + + public: + + enum NamedAxis { X = -1, Y = -2, Z = -3, UNASSIGNED = -4 }; + enum POVAxis { NorthSouth = 0, EastWest = 1 }; + + typedef NamedAxis MouseAxis; // MouseAxis is deprecated. It will be removed in future versions + + typedef std::list JoystickIDList; + + typedef struct + { + int index; + POVAxis axis; + } POVBindingPair; + + InputControlSystem(std::string file = "", bool active = true + , DetectingBindingListener* detectingBindingListener = NULL + , InputControlSystemLog* log = NULL, size_t channelCount = 16); + ~InputControlSystem(); + + std::string getFileName(){ return mFileName; }; + std::string getBaseFileName(); + + void setDetectingBindingListener(DetectingBindingListener* detectingBindingListener){ mDetectingBindingListener = detectingBindingListener; }; + DetectingBindingListener* getDetectingBindingListener(){ return mDetectingBindingListener; }; + + // in seconds + void update(float timeSinceLastFrame); + + inline Channel* getChannel(int i){ return mChannels[i]; }; + float getChannelValue(int i); + inline int getChannelCount(){ return (int)mChannels.size(); }; + + inline Control* getControl(int i){ return mControls[i]; }; + float getControlValue(int i); + inline int getControlCount(){ return (int)mControls.size(); }; + inline void addControl(Control* control){ mControls.push_back(control); }; + + Control* findControl(std::string name); + + inline void activate(){ this->mActive = true; }; + inline void deactivate(){ this->mActive = false; }; + + void addJoystick(int deviceId); + JoystickIDList& getJoystickIdList(){ return mJoystickIDList; }; + + // MouseListener + bool mouseMoved(const OIS::MouseEvent &evt); + bool mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID); + bool mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID); + + // KeyListener + bool keyPressed(const OIS::KeyEvent &evt); + bool keyReleased(const OIS::KeyEvent &evt); + + // JoyStickListener + bool buttonPressed(const OIS::JoyStickEvent &evt, int button); + bool buttonReleased(const OIS::JoyStickEvent &evt, int button); + bool axisMoved(const OIS::JoyStickEvent &evt, int axis); + bool povMoved(const OIS::JoyStickEvent &evt, int index); + bool sliderMoved(const OIS::JoyStickEvent &evt, int index); + + void addKeyBinding(Control* control, OIS::KeyCode key, Control::ControlChangingDirection direction); + void addMouseAxisBinding(Control* control, NamedAxis axis, Control::ControlChangingDirection direction); + void addMouseButtonBinding(Control* control, unsigned int button, Control::ControlChangingDirection direction); + void addJoystickAxisBinding(Control* control, int deviceId, int axis, Control::ControlChangingDirection direction); + void addJoystickButtonBinding(Control* control, int deviceId, unsigned int button, Control::ControlChangingDirection direction); + void addJoystickPOVBinding(Control* control, int deviceId, int index, POVAxis axis, Control::ControlChangingDirection direction); + void addJoystickSliderBinding(Control* control, int deviceId, int index, Control::ControlChangingDirection direction); + void removeKeyBinding(OIS::KeyCode key); + void removeMouseAxisBinding(NamedAxis axis); + void removeMouseButtonBinding(unsigned int button); + void removeJoystickAxisBinding(int deviceId, int axis); + void removeJoystickButtonBinding(int deviceId, unsigned int button); + void removeJoystickPOVBinding(int deviceId, int index, POVAxis axis); + void removeJoystickSliderBinding(int deviceId, int index); + + OIS::KeyCode getKeyBinding(Control* control, ICS::Control::ControlChangingDirection direction); + NamedAxis getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction); + unsigned int getMouseButtonBinding(Control* control, ICS::Control::ControlChangingDirection direction); + int getJoystickAxisBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); + unsigned int getJoystickButtonBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); + POVBindingPair getJoystickPOVBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); + int getJoystickSliderBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); + + std::string keyCodeToString(OIS::KeyCode key); + OIS::KeyCode stringToKeyCode(std::string key); + + void enableDetectingBindingState(Control* control, Control::ControlChangingDirection direction); + void cancelDetectingBindingState(); + + bool save(std::string fileName = ""); + + protected: + + void loadKeyBinders(TiXmlElement* xmlControlNode); + void loadMouseAxisBinders(TiXmlElement* xmlControlNode); + void loadMouseButtonBinders(TiXmlElement* xmlControlNode); + void loadJoystickAxisBinders(TiXmlElement* xmlControlNode); + void loadJoystickButtonBinders(TiXmlElement* xmlControlNode); + void loadJoystickPOVBinders(TiXmlElement* xmlControlNode); + void loadJoystickSliderBinders(TiXmlElement* xmlControlNode); + + void addMouseAxisBinding_(Control* control, int axis, Control::ControlChangingDirection direction); + void removeMouseAxisBinding_(int axis); + + protected: + + typedef struct { + Control::ControlChangingDirection direction; + Control* control; + } ControlKeyBinderItem; + + typedef ControlKeyBinderItem ControlAxisBinderItem; + typedef ControlKeyBinderItem ControlButtonBinderItem; + typedef ControlKeyBinderItem ControlPOVBinderItem; + typedef ControlKeyBinderItem ControlSliderBinderItem; + + typedef struct { + Control* control; + Control::ControlChangingDirection direction; + } PendingActionItem; + + std::list mPendingActions; + + std::string mFileName; + + typedef std::map ControlsKeyBinderMapType; // + typedef std::map ControlsAxisBinderMapType; // + typedef std::map ControlsButtonBinderMapType; // + typedef std::map ControlsPOVBinderMapType; // + typedef std::map ControlsSliderBinderMapType; // + + typedef std::map JoystickAxisBinderMapType; // > + typedef std::map JoystickButtonBinderMapType; // > + typedef std::map > JoystickPOVBinderMapType; // > > + typedef std::map JoystickSliderBinderMapType; // > + + ControlsAxisBinderMapType mControlsMouseAxisBinderMap; // + ControlsButtonBinderMapType mControlsMouseButtonBinderMap; // + JoystickAxisBinderMapType mControlsJoystickAxisBinderMap; // > + JoystickButtonBinderMapType mControlsJoystickButtonBinderMap; // > + JoystickPOVBinderMapType mControlsJoystickPOVBinderMap; // > > + JoystickSliderBinderMapType mControlsJoystickSliderBinderMap; // > + + std::vector mControls; + std::vector mChannels; + + ControlsKeyBinderMapType mControlsKeyBinderMap; + std::map mKeys; + std::map mKeyCodes; + + bool mActive; + InputControlSystemLog* mLog; + + DetectingBindingListener* mDetectingBindingListener; + Control* mDetectingBindingControl; + Control::ControlChangingDirection mDetectingBindingDirection; + + bool mXmouseAxisBinded; + bool mYmouseAxisBinded; + + JoystickIDList mJoystickIDList; + + int mMouseAxisBindingInitialValues[3]; + + private: + + void fillOISKeysMap(); + }; + + class DllExport DetectingBindingListener + { + public: + virtual void keyBindingDetected(InputControlSystem* ICS, Control* control + , OIS::KeyCode key, Control::ControlChangingDirection direction); + + virtual void mouseAxisBindingDetected(InputControlSystem* ICS, Control* control + , InputControlSystem::NamedAxis axis, Control::ControlChangingDirection direction); + + virtual void mouseButtonBindingDetected(InputControlSystem* ICS, Control* control + , unsigned int button, Control::ControlChangingDirection direction); + + virtual void joystickAxisBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int axis, Control::ControlChangingDirection direction); + + virtual void joystickButtonBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, unsigned int button, Control::ControlChangingDirection direction); + + virtual void joystickPOVBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int pov, InputControlSystem::POVAxis axis, Control::ControlChangingDirection direction); + + virtual void joystickSliderBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int slider, Control::ControlChangingDirection direction); + }; + + static const float ICS_MAX = std::numeric_limits::max(); +} + + +#endif diff --git a/extern/oics/ICSInputControlSystem_joystick.cpp b/extern/oics/ICSInputControlSystem_joystick.cpp new file mode 100644 index 0000000000..1e66599ead --- /dev/null +++ b/extern/oics/ICSInputControlSystem_joystick.cpp @@ -0,0 +1,665 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +namespace ICS +{ + // load xml + void InputControlSystem::loadJoystickAxisBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlJoystickBinder = xmlControlNode->FirstChildElement("JoystickAxisBinder"); + while(xmlJoystickBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlJoystickBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlJoystickBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + addJoystickAxisBinding(mControls.back(), FromString(xmlJoystickBinder->Attribute("deviceId")) + , FromString(xmlJoystickBinder->Attribute("axis")), dir); + + xmlJoystickBinder = xmlJoystickBinder->NextSiblingElement("JoystickAxisBinder"); + } + } + + void InputControlSystem::loadJoystickButtonBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlJoystickButtonBinder = xmlControlNode->FirstChildElement("JoystickButtonBinder"); + while(xmlJoystickButtonBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlJoystickButtonBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlJoystickButtonBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + addJoystickButtonBinding(mControls.back(), FromString(xmlJoystickButtonBinder->Attribute("deviceId")) + , FromString(xmlJoystickButtonBinder->Attribute("button")), dir); + + xmlJoystickButtonBinder = xmlJoystickButtonBinder->NextSiblingElement("JoystickButtonBinder"); + } + } + + void InputControlSystem::loadJoystickPOVBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlJoystickPOVBinder = xmlControlNode->FirstChildElement("JoystickPOVBinder"); + while(xmlJoystickPOVBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlJoystickPOVBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlJoystickPOVBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + InputControlSystem::POVAxis axis = /*POVAxis::*/NorthSouth; + if(std::string(xmlJoystickPOVBinder->Attribute("axis")) == "EastWest") + { + axis = /*POVAxis::*/EastWest; + } + + addJoystickPOVBinding(mControls.back(), FromString(xmlJoystickPOVBinder->Attribute("deviceId")) + , FromString(xmlJoystickPOVBinder->Attribute("pov")), axis, dir); + + xmlJoystickPOVBinder = xmlJoystickPOVBinder->NextSiblingElement("JoystickPOVBinder"); + } + } + + void InputControlSystem::loadJoystickSliderBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlJoystickSliderBinder = xmlControlNode->FirstChildElement("JoystickSliderBinder"); + while(xmlJoystickSliderBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlJoystickSliderBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlJoystickSliderBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + addJoystickSliderBinding(mControls.back(), FromString(xmlJoystickSliderBinder->Attribute("deviceId")) + , FromString(xmlJoystickSliderBinder->Attribute("slider")), dir); + + xmlJoystickSliderBinder = xmlJoystickSliderBinder->NextSiblingElement("JoystickSliderBinder"); + } + } + + // add bindings + void InputControlSystem::addJoystickAxisBinding(Control* control, int deviceId, int axis, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding AxisBinder [deviceid=" + + ToString(deviceId) + ", axis=" + + ToString(axis) + ", direction=" + + ToString(direction) + "]"); + + ControlAxisBinderItem controlAxisBinderItem; + controlAxisBinderItem.control = control; + controlAxisBinderItem.direction = direction; + mControlsJoystickAxisBinderMap[ deviceId ][ axis ] = controlAxisBinderItem; + } + + void InputControlSystem::addJoystickButtonBinding(Control* control, int deviceId, unsigned int button, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding JoystickButtonBinder [deviceId=" + + ToString(deviceId) + ", button=" + + ToString(button) + ", direction=" + + ToString(direction) + "]"); + + ControlButtonBinderItem controlJoystickButtonBinderItem; + controlJoystickButtonBinderItem.direction = direction; + controlJoystickButtonBinderItem.control = control; + mControlsJoystickButtonBinderMap[ deviceId ][ button ] = controlJoystickButtonBinderItem; + } + + void InputControlSystem::addJoystickPOVBinding(Control* control, int deviceId, int index, InputControlSystem::POVAxis axis, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding JoystickPOVBinder [deviceId=" + + ToString(deviceId) + ", pov=" + + ToString(index) + ", axis=" + + ToString(axis) + ", direction=" + + ToString(direction) + "]"); + + ControlPOVBinderItem ControlPOVBinderItem; + ControlPOVBinderItem.direction = direction; + ControlPOVBinderItem.control = control; + mControlsJoystickPOVBinderMap[ deviceId ][ index ][ axis ] = ControlPOVBinderItem; + } + + void InputControlSystem::addJoystickSliderBinding(Control* control, int deviceId, int index, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding JoystickSliderBinder [deviceId=" + + ToString(deviceId) + ", direction=" + + ToString(index) + ", direction=" + + ToString(direction) + "]"); + + ControlSliderBinderItem ControlSliderBinderItem; + ControlSliderBinderItem.direction = direction; + ControlSliderBinderItem.control = control; + mControlsJoystickSliderBinderMap[ deviceId ][ index ] = ControlSliderBinderItem; + } + + // get bindings + int InputControlSystem::getJoystickAxisBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction) + { + if(mControlsJoystickAxisBinderMap.find(deviceId) != mControlsJoystickAxisBinderMap.end()) + { + ControlsAxisBinderMapType::iterator it = mControlsJoystickAxisBinderMap[deviceId].begin(); + while(it != mControlsJoystickAxisBinderMap[deviceId].end()) + { + if(it->first >= 0 && it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + } + + return /*NamedAxis::*/UNASSIGNED; + } + + unsigned int InputControlSystem::getJoystickButtonBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction) + { + if(mControlsJoystickButtonBinderMap.find(deviceId) != mControlsJoystickButtonBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickButtonBinderMap[deviceId].begin(); + while(it != mControlsJoystickButtonBinderMap[deviceId].end()) + { + if(it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + } + + return ICS_MAX_DEVICE_BUTTONS; + } + + InputControlSystem::POVBindingPair InputControlSystem::getJoystickPOVBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction) + { + POVBindingPair result; + result.index = -1; + + if(mControlsJoystickPOVBinderMap.find(deviceId) != mControlsJoystickPOVBinderMap.end()) + { + //ControlsAxisBinderMapType::iterator it = mControlsJoystickPOVBinderMap[deviceId].begin(); + std::map::iterator it = mControlsJoystickPOVBinderMap[deviceId].begin(); + while(it != mControlsJoystickPOVBinderMap[deviceId].end()) + { + ControlsPOVBinderMapType::const_iterator it2 = it->second.begin(); + while(it2 != it->second.end()) + { + if(it2->second.control == control && it2->second.direction == direction) + { + result.index = it->first; + result.axis = (POVAxis)it2->first; + return result; + } + it2++; + } + + it++; + } + } + + return result; + } + + int InputControlSystem::getJoystickSliderBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction) + { + if(mControlsJoystickSliderBinderMap.find(deviceId) != mControlsJoystickSliderBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickSliderBinderMap[deviceId].begin(); + while(it != mControlsJoystickSliderBinderMap[deviceId].end()) + { + if(it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + } + + return /*NamedAxis::*/UNASSIGNED; + } + + // remove bindings + void InputControlSystem::removeJoystickAxisBinding(int deviceId, int axis) + { + if(mControlsJoystickAxisBinderMap.find(deviceId) != mControlsJoystickAxisBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickAxisBinderMap[deviceId].find(axis); + if(it != mControlsJoystickAxisBinderMap[deviceId].end()) + { + mControlsJoystickAxisBinderMap[deviceId].erase(it); + } + } + } + + void InputControlSystem::removeJoystickButtonBinding(int deviceId, unsigned int button) + { + if(mControlsJoystickButtonBinderMap.find(deviceId) != mControlsJoystickButtonBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickButtonBinderMap[deviceId].find(button); + if(it != mControlsJoystickButtonBinderMap[deviceId].end()) + { + mControlsJoystickButtonBinderMap[deviceId].erase(it); + } + } + } + + void InputControlSystem::removeJoystickPOVBinding(int deviceId, int index, POVAxis axis) + { + if(mControlsJoystickPOVBinderMap.find(deviceId) != mControlsJoystickPOVBinderMap.end()) + { + std::map::iterator it = mControlsJoystickPOVBinderMap[deviceId].find(index); + if(it != mControlsJoystickPOVBinderMap[deviceId].end()) + { + if(it->second.find(axis) != it->second.end()) + { + mControlsJoystickPOVBinderMap[deviceId].find(index)->second.erase( it->second.find(axis) ); + } + } + } + } + + void InputControlSystem::removeJoystickSliderBinding(int deviceId, int index) + { + if(mControlsJoystickSliderBinderMap.find(deviceId) != mControlsJoystickSliderBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickSliderBinderMap[deviceId].find(index); + if(it != mControlsJoystickSliderBinderMap[deviceId].end()) + { + mControlsJoystickSliderBinderMap[deviceId].erase(it); + } + } + } + + // joyStick listeners + bool InputControlSystem::buttonPressed(const OIS::JoyStickEvent &evt, int button) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mControlsJoystickButtonBinderMap.find(evt.device->getID()) != mControlsJoystickButtonBinderMap.end()) + { + ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[evt.device->getID()].find(button); + if(it != mControlsJoystickButtonBinderMap[evt.device->getID()].end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + } + else if(mDetectingBindingListener) + { + mDetectingBindingListener->joystickButtonBindingDetected(this, + mDetectingBindingControl, evt.device->getID(), button, mDetectingBindingDirection); + } + } + + return true; + } + + bool InputControlSystem::buttonReleased(const OIS::JoyStickEvent &evt, int button) + { + if(mActive) + { + if(mControlsJoystickButtonBinderMap.find(evt.device->getID()) != mControlsJoystickButtonBinderMap.end()) + { + ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[evt.device->getID()].find(button); + if(it != mControlsJoystickButtonBinderMap[evt.device->getID()].end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + } + } + return true; + } + + bool InputControlSystem::axisMoved(const OIS::JoyStickEvent &evt, int axis) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mControlsJoystickAxisBinderMap.find(evt.device->getID()) != mControlsJoystickAxisBinderMap.end()) + { + ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[ evt.device->getID() ][ axis ]; // joystic axis start at 0 index + Control* ctrl = joystickBinderItem.control; + if(ctrl) + { + ctrl->setIgnoreAutoReverse(true); + if(joystickBinderItem.direction == Control::INCREASE) + { + float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS; + float valDisplaced = (float)( evt.state.mAxes[axis].abs - OIS::JoyStick::MIN_AXIS); + + ctrl->setValue( valDisplaced / axisRange ); + } + else if(joystickBinderItem.direction == Control::DECREASE) + { + float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS; + float valDisplaced = (float)(evt.state.mAxes[axis].abs - OIS::JoyStick::MIN_AXIS); + + ctrl->setValue( 1 - ( valDisplaced / axisRange ) ); + } + } + } + } + else if(mDetectingBindingListener) + { + //ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[ evt.device->getID() ][ axis ]; // joystic axis start at 0 index + //Control* ctrl = joystickBinderItem.control; + //if(ctrl && ctrl->isAxisBindable()) + if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable()) + { + if( abs( evt.state.mAxes[axis].abs ) > ICS_JOYSTICK_AXIS_BINDING_MARGIN) + { + mDetectingBindingListener->joystickAxisBindingDetected(this, + mDetectingBindingControl, evt.device->getID(), axis, mDetectingBindingDirection); + } + } + } + } + + return true; + } + + bool InputControlSystem::povMoved(const OIS::JoyStickEvent &evt, int index) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mControlsJoystickPOVBinderMap.find(evt.device->getID()) != mControlsJoystickPOVBinderMap.end()) + { + std::map::const_iterator i = mControlsJoystickPOVBinderMap[ evt.device->getID() ].find(index); + if(i != mControlsJoystickPOVBinderMap[ evt.device->getID() ].end()) + { + if(evt.state.mPOV[index].direction != OIS::Pov::West + && evt.state.mPOV[index].direction != OIS::Pov::East + && evt.state.mPOV[index].direction != OIS::Pov::Centered) + { + ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/NorthSouth ); + if(it != i->second.end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + if(evt.state.mPOV[index].direction == OIS::Pov::North + || evt.state.mPOV[index].direction == OIS::Pov::NorthWest + || evt.state.mPOV[index].direction == OIS::Pov::NorthEast) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + it->second.control->setChangingDirection((Control::ControlChangingDirection)(-1 * it->second.direction)); + } + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + + if(evt.state.mPOV[index].direction != OIS::Pov::North + && evt.state.mPOV[index].direction != OIS::Pov::South + && evt.state.mPOV[index].direction != OIS::Pov::Centered) + { + ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/EastWest ); + if(it != i->second.end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + if(evt.state.mPOV[index].direction == OIS::Pov::East + || evt.state.mPOV[index].direction == OIS::Pov::NorthEast + || evt.state.mPOV[index].direction == OIS::Pov::SouthEast) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + it->second.control->setChangingDirection((Control::ControlChangingDirection)(-1 * it->second.direction)); + } + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + + if(evt.state.mPOV[index].direction == OIS::Pov::Centered) + { + ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/NorthSouth ); + if(it != i->second.end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + + it = i->second.find( /*POVAxis::*/EastWest ); + if(it != i->second.end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + } + } + } + } + else if(mDetectingBindingListener) + { + if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable()) + { + if(evt.state.mPOV[index].direction == OIS::Pov::West + || evt.state.mPOV[index].direction == OIS::Pov::East + || evt.state.mPOV[index].direction == OIS::Pov::North + || evt.state.mPOV[index].direction == OIS::Pov::South) + { + POVAxis povAxis = NorthSouth; + if(evt.state.mPOV[index].direction == OIS::Pov::West + || evt.state.mPOV[index].direction == OIS::Pov::East) + { + povAxis = EastWest; + } + + mDetectingBindingListener->joystickPOVBindingDetected(this, + mDetectingBindingControl, evt.device->getID(), index, povAxis, mDetectingBindingDirection); + } + } + } + } + + return true; + } + + bool InputControlSystem::sliderMoved(const OIS::JoyStickEvent &evt, int index) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mControlsJoystickSliderBinderMap.find(evt.device->getID()) != mControlsJoystickSliderBinderMap.end()) + { + ControlSliderBinderItem joystickBinderItem = mControlsJoystickSliderBinderMap[ evt.device->getID() ][ index ]; + Control* ctrl = joystickBinderItem.control; + if(ctrl) + { + ctrl->setIgnoreAutoReverse(true); + if(joystickBinderItem.direction == Control::INCREASE) + { + float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS; + float valDisplaced = (float)( evt.state.mSliders[index].abX - OIS::JoyStick::MIN_AXIS); + + ctrl->setValue( valDisplaced / axisRange ); + } + else if(joystickBinderItem.direction == Control::DECREASE) + { + float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS; + float valDisplaced = (float)(evt.state.mSliders[index].abX - OIS::JoyStick::MIN_AXIS); + + ctrl->setValue( 1 - ( valDisplaced / axisRange ) ); + } + } + } + } + else if(mDetectingBindingListener) + { + /*ControlSliderBinderItem joystickBinderItem = mControlsJoystickSliderBinderMap[ evt.device->getID() ][ index ]; + Control* ctrl = joystickBinderItem.control; + if(ctrl && ctrl->isAxisBindable()) + {*/ + if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable()) + { + if( abs( evt.state.mSliders[index].abX ) > ICS_JOYSTICK_SLIDER_BINDING_MARGIN) + { + mDetectingBindingListener->joystickSliderBindingDetected(this, + mDetectingBindingControl, evt.device->getID(), index, mDetectingBindingDirection); + } + } + } + } + + return true; + } + + // joystick auto bindings + void DetectingBindingListener::joystickAxisBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int axis, Control::ControlChangingDirection direction) + { + // if the joystick axis is used by another control, remove it + ICS->removeJoystickAxisBinding(deviceId, axis); + + // if the control has an axis assigned, remove it + int oldAxis = ICS->getJoystickAxisBinding(control, deviceId, direction); + if(oldAxis != InputControlSystem::UNASSIGNED) + { + ICS->removeJoystickAxisBinding(deviceId, oldAxis); + } + + ICS->addJoystickAxisBinding(control, deviceId, axis, direction); + ICS->cancelDetectingBindingState(); + } + void DetectingBindingListener::joystickButtonBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, unsigned int button, Control::ControlChangingDirection direction) + { + // if the joystick button is used by another control, remove it + ICS->removeJoystickButtonBinding(deviceId, button); + + // if the control has a joystick button assigned, remove it + unsigned int oldButton = ICS->getJoystickButtonBinding(control, deviceId, direction); + if(oldButton != ICS_MAX_DEVICE_BUTTONS) + { + ICS->removeJoystickButtonBinding(deviceId, oldButton); + } + + ICS->addJoystickButtonBinding(control, deviceId, button, direction); + ICS->cancelDetectingBindingState(); + } + + + void DetectingBindingListener::joystickPOVBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int pov, InputControlSystem::POVAxis axis, Control::ControlChangingDirection direction) + { + // if the joystick slider is used by another control, remove it + ICS->removeJoystickPOVBinding(deviceId, pov, axis); + + // if the control has a joystick button assigned, remove it + ICS::InputControlSystem::POVBindingPair oldPOV = ICS->getJoystickPOVBinding(control, deviceId, direction); + if(oldPOV.index >= 0 && oldPOV.axis == axis) + { + ICS->removeJoystickPOVBinding(deviceId, oldPOV.index, oldPOV.axis); + } + + ICS->addJoystickPOVBinding(control, deviceId, pov, axis, direction); + ICS->cancelDetectingBindingState(); + } + + void DetectingBindingListener::joystickSliderBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int slider, Control::ControlChangingDirection direction) + { + // if the joystick slider is used by another control, remove it + ICS->removeJoystickSliderBinding(deviceId, slider); + + // if the control has a joystick slider assigned, remove it + int oldSlider = ICS->getJoystickSliderBinding(control, deviceId, direction); + if(oldSlider != InputControlSystem::/*NamedAxis::*/UNASSIGNED) + { + ICS->removeJoystickSliderBinding(deviceId, oldSlider); + } + + ICS->addJoystickSliderBinding(control, deviceId, slider, direction); + ICS->cancelDetectingBindingState(); + } +} \ No newline at end of file diff --git a/extern/oics/ICSInputControlSystem_keyboard.cpp b/extern/oics/ICSInputControlSystem_keyboard.cpp new file mode 100644 index 0000000000..8ef81d9794 --- /dev/null +++ b/extern/oics/ICSInputControlSystem_keyboard.cpp @@ -0,0 +1,156 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +namespace ICS +{ + void InputControlSystem::loadKeyBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlKeyBinder = xmlControlNode->FirstChildElement("KeyBinder"); + while(xmlKeyBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlKeyBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlKeyBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + addKeyBinding(mControls.back(), mKeys[xmlKeyBinder->Attribute("key")], dir); + + xmlKeyBinder = xmlKeyBinder->NextSiblingElement("KeyBinder"); + } + } + + void InputControlSystem::addKeyBinding(Control* control, OIS::KeyCode key, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding KeyBinder [key=" + + keyCodeToString(key) + ", direction=" + + ToString(direction) + "]"); + + ControlKeyBinderItem controlKeyBinderItem; + controlKeyBinderItem.control = control; + controlKeyBinderItem.direction = direction; + mControlsKeyBinderMap[ key ] = controlKeyBinderItem; + } + + void InputControlSystem::removeKeyBinding(OIS::KeyCode key) + { + ControlsKeyBinderMapType::iterator it = mControlsKeyBinderMap.find(key); + if(it != mControlsKeyBinderMap.end()) + { + mControlsKeyBinderMap.erase(it); + } + } + + OIS::KeyCode InputControlSystem::getKeyBinding(Control* control + , ICS::Control::ControlChangingDirection direction) + { + ControlsKeyBinderMapType::iterator it = mControlsKeyBinderMap.begin(); + while(it != mControlsKeyBinderMap.end()) + { + if(it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + + return OIS::KC_UNASSIGNED; + } + bool InputControlSystem::keyPressed(const OIS::KeyEvent &evt) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + ControlsKeyBinderMapType::const_iterator it = mControlsKeyBinderMap.find(evt.key); + if(it != mControlsKeyBinderMap.end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + else if(mDetectingBindingListener) + { + mDetectingBindingListener->keyBindingDetected(this, + mDetectingBindingControl, evt.key, mDetectingBindingDirection); + } + } + + return true; + } + + bool InputControlSystem::keyReleased(const OIS::KeyEvent &evt) + { + if(mActive) + { + ControlsKeyBinderMapType::const_iterator it = mControlsKeyBinderMap.find(evt.key); + if(it != mControlsKeyBinderMap.end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + } + + return true; + } + + void DetectingBindingListener::keyBindingDetected(InputControlSystem* ICS, Control* control + , OIS::KeyCode key, Control::ControlChangingDirection direction) + { + // if the key is used by another control, remove it + ICS->removeKeyBinding(key); + + // if the control has a key assigned, remove it + OIS::KeyCode oldKey = ICS->getKeyBinding(control, direction); + if(oldKey != OIS::KC_UNASSIGNED) + { + ICS->removeKeyBinding(oldKey); + } + + ICS->addKeyBinding(control, key, direction); + ICS->cancelDetectingBindingState(); + } + +} \ No newline at end of file diff --git a/extern/oics/ICSInputControlSystem_mouse.cpp b/extern/oics/ICSInputControlSystem_mouse.cpp new file mode 100644 index 0000000000..c62f1765e6 --- /dev/null +++ b/extern/oics/ICSInputControlSystem_mouse.cpp @@ -0,0 +1,397 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +namespace ICS +{ + // load xml + void InputControlSystem::loadMouseAxisBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlMouseBinder = xmlControlNode->FirstChildElement("MouseBinder"); + while(xmlMouseBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlMouseBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlMouseBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + NamedAxis axis = /*NamedAxis::*/ X; + if((*xmlMouseBinder->Attribute("axis")) == 'Y') + { + axis = /*NamedAxis::*/ Y; + } + else if((*xmlMouseBinder->Attribute("axis")) == 'Z') + { + axis = /*NamedAxis::*/ Z; + } + + addMouseAxisBinding(mControls.back(), axis, dir); + + xmlMouseBinder = xmlMouseBinder->NextSiblingElement("MouseBinder"); + } + } + + void InputControlSystem::loadMouseButtonBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlMouseButtonBinder = xmlControlNode->FirstChildElement("MouseButtonBinder"); + while(xmlMouseButtonBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlMouseButtonBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlMouseButtonBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + int button = 0; + if(std::string(xmlMouseButtonBinder->Attribute("button")) == "LEFT") + { + button = OIS::/*MouseButtonID::*/MB_Left; + } + else if(std::string(xmlMouseButtonBinder->Attribute("button")) == "RIGHT") + { + button = OIS::/*MouseButtonID::*/MB_Right; + } + else if(std::string(xmlMouseButtonBinder->Attribute("button")) == "MIDDLE") + { + button = OIS::/*MouseButtonID::*/MB_Middle; + } + else + { + button = FromString(xmlMouseButtonBinder->Attribute("button")); + } + + addMouseButtonBinding(mControls.back(), button, dir); + + xmlMouseButtonBinder = xmlMouseButtonBinder->NextSiblingElement("MouseButtonBinder"); + } + } + + + // add bindings + void InputControlSystem::addMouseAxisBinding(Control* control, NamedAxis axis, Control::ControlChangingDirection direction) + { + if(axis == /*NamedAxis::*/X) + { + mXmouseAxisBinded = true; + } + else if(axis == /*NamedAxis::*/Y) + { + mYmouseAxisBinded = true; + } + + addMouseAxisBinding_(control, axis, direction); + } + + /*protected*/ void InputControlSystem::addMouseAxisBinding_(Control* control, int axis, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding AxisBinder [axis=" + + ToString(axis) + ", direction=" + + ToString(direction) + "]"); + + ControlAxisBinderItem controlAxisBinderItem; + controlAxisBinderItem.control = control; + controlAxisBinderItem.direction = direction; + mControlsMouseAxisBinderMap[ axis ] = controlAxisBinderItem; + } + + void InputControlSystem::addMouseButtonBinding(Control* control, unsigned int button, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding MouseButtonBinder [button=" + + ToString(button) + ", direction=" + + ToString(direction) + "]"); + + ControlButtonBinderItem controlMouseButtonBinderItem; + controlMouseButtonBinderItem.direction = direction; + controlMouseButtonBinderItem.control = control; + mControlsMouseButtonBinderMap[ button ] = controlMouseButtonBinderItem; + } + + // get bindings + InputControlSystem::NamedAxis InputControlSystem::getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction) + { + ControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.begin(); + while(it != mControlsMouseAxisBinderMap.end()) + { + if(it->first < 0 && it->second.control == control && it->second.direction == direction) + { + return (InputControlSystem::NamedAxis)(it->first); + } + it++; + } + + return /*NamedAxis::*/UNASSIGNED; + } + + //int InputControlSystem::getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction) + //{ + // ControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.begin(); + // while(it != mControlsMouseAxisBinderMap.end()) + // { + // if(it->first >= 0 && it->second.control == control && it->second.direction == direction) + // { + // return it->first; + // } + // it++; + // } + + // return /*NamedAxis::*/UNASSIGNED; + //} + + unsigned int InputControlSystem::getMouseButtonBinding(Control* control, ICS::Control::ControlChangingDirection direction) + { + ControlsButtonBinderMapType::iterator it = mControlsMouseButtonBinderMap.begin(); + while(it != mControlsMouseButtonBinderMap.end()) + { + if(it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + + return ICS_MAX_DEVICE_BUTTONS; + } + + // remove bindings + void InputControlSystem::removeMouseAxisBinding(NamedAxis axis) + { + if(axis == /*NamedAxis::*/X) + { + mXmouseAxisBinded = false; + } + else if(axis == /*NamedAxis::*/Y) + { + mYmouseAxisBinded = false; + } + + removeMouseAxisBinding_(axis); + } + /*protected*/ void InputControlSystem::removeMouseAxisBinding_(int axis) + { + ControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.find(axis); + if(it != mControlsMouseAxisBinderMap.end()) + { + mControlsMouseAxisBinderMap.erase(it); + } + } + + + void InputControlSystem::removeMouseButtonBinding(unsigned int button) + { + ControlsButtonBinderMapType::iterator it = mControlsMouseButtonBinderMap.find(button); + if(it != mControlsMouseButtonBinderMap.end()) + { + mControlsMouseButtonBinderMap.erase(it); + } + } + + // mouse Listeners + bool InputControlSystem::mouseMoved(const OIS::MouseEvent &evt) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mXmouseAxisBinded && evt.state.X.rel) + { + ControlAxisBinderItem mouseBinderItem = mControlsMouseAxisBinderMap[ /*NamedAxis::*/X ]; + Control* ctrl = mouseBinderItem.control; + ctrl->setIgnoreAutoReverse(true); + if(mouseBinderItem.direction == Control::INCREASE) + { + ctrl->setValue( float( (evt.state.X.abs) / float(evt.state.width) ) ); + } + else if(mouseBinderItem.direction == Control::DECREASE) + { + ctrl->setValue( 1 - float( evt.state.X.abs / float(evt.state.width) ) ); + } + } + + if(mYmouseAxisBinded && evt.state.Y.rel) + { + ControlAxisBinderItem mouseBinderItem = mControlsMouseAxisBinderMap[ /*NamedAxis::*/Y ]; + Control* ctrl = mouseBinderItem.control; + ctrl->setIgnoreAutoReverse(true); + if(mouseBinderItem.direction == Control::INCREASE) + { + ctrl->setValue( float( (evt.state.Y.abs) / float(evt.state.height) ) ); + } + else if(mouseBinderItem.direction == Control::DECREASE) + { + ctrl->setValue( 1 - float( evt.state.Y.abs / float(evt.state.height) ) ); + } + } + + //! @todo Whats the range of the Z axis? + /*if(evt.state.Z.rel) + { + ControlAxisBinderItem mouseBinderItem = mControlsAxisBinderMap[ NamedAxis::Z ]; + Control* ctrl = mouseBinderItem.control; + ctrl->setIgnoreAutoReverse(true); + if(mouseBinderItem.direction == Control::INCREASE) + { + ctrl->setValue( float( (evt.state.Z.abs) / float(evt.state.¿width?) ) ); + } + else if(mouseBinderItem.direction == Control::DECREASE) + { + ctrl->setValue( float( (1 - evt.state.Z.abs) / float(evt.state.¿width?) ) ); + } + }*/ + } + else if(mDetectingBindingListener) + { + if(mDetectingBindingControl->isAxisBindable()) + { + if(mMouseAxisBindingInitialValues[0] == ICS_MOUSE_AXIS_BINDING_NULL_VALUE) + { + mMouseAxisBindingInitialValues[0] = 0; + mMouseAxisBindingInitialValues[1] = 0; + mMouseAxisBindingInitialValues[2] = 0; + } + + mMouseAxisBindingInitialValues[0] += evt.state.X.rel; + mMouseAxisBindingInitialValues[1] += evt.state.Y.rel; + mMouseAxisBindingInitialValues[2] += evt.state.Z.rel; + + if( abs(mMouseAxisBindingInitialValues[0]) > ICS_MOUSE_BINDING_MARGIN ) + { + mDetectingBindingListener->mouseAxisBindingDetected(this, + mDetectingBindingControl, X, mDetectingBindingDirection); + } + else if( abs(mMouseAxisBindingInitialValues[1]) > ICS_MOUSE_BINDING_MARGIN ) + { + mDetectingBindingListener->mouseAxisBindingDetected(this, + mDetectingBindingControl, Y, mDetectingBindingDirection); + } + else if( abs(mMouseAxisBindingInitialValues[2]) > ICS_MOUSE_BINDING_MARGIN ) + { + mDetectingBindingListener->mouseAxisBindingDetected(this, + mDetectingBindingControl, Z, mDetectingBindingDirection); + } + } + } + } + + return true; + } + + bool InputControlSystem::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID btn) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + ControlsButtonBinderMapType::const_iterator it = mControlsMouseButtonBinderMap.find((int)btn); + if(it != mControlsMouseButtonBinderMap.end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + else if(mDetectingBindingListener) + { + mDetectingBindingListener->mouseButtonBindingDetected(this, + mDetectingBindingControl, btn, mDetectingBindingDirection); + } + } + + return true; + } + + bool InputControlSystem::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID btn) + { + if(mActive) + { + ControlsButtonBinderMapType::const_iterator it = mControlsMouseButtonBinderMap.find((int)btn); + if(it != mControlsMouseButtonBinderMap.end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + } + + return true; + } + + // mouse auto bindings + void DetectingBindingListener::mouseAxisBindingDetected(InputControlSystem* ICS, Control* control + , InputControlSystem::NamedAxis axis, Control::ControlChangingDirection direction) + { + // if the mouse axis is used by another control, remove it + ICS->removeMouseAxisBinding(axis); + + // if the control has an axis assigned, remove it + InputControlSystem::NamedAxis oldAxis = ICS->getMouseAxisBinding(control, direction); + if(oldAxis != InputControlSystem::UNASSIGNED) + { + ICS->removeMouseAxisBinding(oldAxis); + } + + ICS->addMouseAxisBinding(control, axis, direction); + ICS->cancelDetectingBindingState(); + } + + void DetectingBindingListener::mouseButtonBindingDetected(InputControlSystem* ICS, Control* control + , unsigned int button, Control::ControlChangingDirection direction) + { + // if the mouse button is used by another control, remove it + ICS->removeMouseButtonBinding(button); + + // if the control has a mouse button assigned, remove it + unsigned int oldButton = ICS->getMouseButtonBinding(control, direction); + if(oldButton != ICS_MAX_DEVICE_BUTTONS) + { + ICS->removeMouseButtonBinding(oldButton); + } + + ICS->addMouseButtonBinding(control, button, direction); + ICS->cancelDetectingBindingState(); + } + +} \ No newline at end of file diff --git a/extern/oics/ICSPrerequisites.cpp b/extern/oics/ICSPrerequisites.cpp new file mode 100644 index 0000000000..2824950ed1 --- /dev/null +++ b/extern/oics/ICSPrerequisites.cpp @@ -0,0 +1,27 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSPrerequisites.h" diff --git a/extern/oics/ICSPrerequisites.h b/extern/oics/ICSPrerequisites.h new file mode 100644 index 0000000000..864dad15fd --- /dev/null +++ b/extern/oics/ICSPrerequisites.h @@ -0,0 +1,111 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +//! @todo add mouse wheel support + +#ifndef _InputControlSystem_Prerequisites_H_ +#define _InputControlSystem_Prerequisites_H_ + +/// Include external headers +#include +#include +#include +#include +#include +#include + +#include "tinyxml.h" + +#define OIS_DYNAMIC_LIB +#include +#include +#include +#include +#include + +/// Define the dll export qualifier if compiling for Windows + +/* +#ifdef ICS_PLATFORM_WIN32 + #ifdef ICS_LIB + #define DllExport __declspec (dllexport) + #else + #define DllExport __declspec (dllimport) + #endif +#else + #define DllExport +#endif +*/ +#define DllExport + +// Define some macros +#define ICS_DEPRECATED __declspec(deprecated("Deprecated. It will be removed in future versions.")) + +/// Version defines +#define ICS_VERSION_MAJOR 0 +#define ICS_VERSION_MINOR 3 +#define ICS_VERSION_PATCH 1 + +#define ICS_MAX_DEVICE_BUTTONS 30 + +namespace ICS +{ + template + bool StringIsNumber ( const std::string &Text ) + { + std::stringstream ss(Text); + T result; + return ss >> result ? true : false; + } + + // from http://www.cplusplus.com/forum/articles/9645/ + template + std::string ToString ( T value ) + { + std::stringstream ss; + ss << value; + return ss.str(); + } + + // from http://www.cplusplus.com/forum/articles/9645/ + template + T FromString ( const std::string &Text )//Text not by const reference so that the function can be used with a + { //character array as argument + std::stringstream ss(Text); + T result; + return ss >> result ? result : 0; + } + + class InputControlSystem; + class Channel; + class ChannelListener; + class Control; + class ControlListener; + class DetectingBindingListener; + class InputControlSystemLog; +} + +#endif diff --git a/extern/oics/tinystr.cpp b/extern/oics/tinystr.cpp new file mode 100644 index 0000000000..681250714b --- /dev/null +++ b/extern/oics/tinystr.cpp @@ -0,0 +1,116 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Løvset, 7. April 2005. + */ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/extern/oics/tinystr.h b/extern/oics/tinystr.h new file mode 100644 index 0000000000..419e647e16 --- /dev/null +++ b/extern/oics/tinystr.h @@ -0,0 +1,319 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005. + * + * - completely rewritten. compact, clean, and fast implementation. + * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems) + * - fixed reserve() to work as per specification. + * - fixed buggy compares operator==(), operator<(), and operator>() + * - fixed operator+=() to take a const ref argument, following spec. + * - added "copy" constructor with length, and most compare operators. + * - added swap(), clear(), size(), capacity(), operator+(). + */ + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include +#include + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + // = operator + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + // = operator + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/extern/oics/tinyxml.cpp b/extern/oics/tinyxml.cpp new file mode 100644 index 0000000000..841a41cd39 --- /dev/null +++ b/extern/oics/tinyxml.cpp @@ -0,0 +1,1888 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include + +#ifdef TIXML_USE_STL +#include +#include +#endif + +#include "tinyxml.h" + + +bool TiXmlBase::condenseWhiteSpace = true; + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; + #else + return fopen( filename, mode ); + #endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::DOCUMENT ) + { + delete node; + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( replaceThis->parent != this ) + return 0; + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +void TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return &node->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const char* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s ); + } + else { + *i = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const std::string* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s->c_str() ); + } + else { + *i = 0; + } + } + return s; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const char* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s ); + } + else { + *d = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const std::string* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s->c_str() ); + } + else { + *d = 0; + } + } + return s; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + char buf[64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%d", val ); + #else + sprintf( buf, "%d", val ); + #endif + SetAttribute( name, buf ); +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + std::ostringstream oss; + oss << val; + SetAttribute( name, oss.str() ); +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + char buf[256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); + #else + sprintf( buf, "%f", val ); + #endif + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING _name( cname ); + TIXML_STRING _value( cvalue ); + #else + const char* _name = cname; + const char* _value = cvalue; + #endif + + TiXmlAttribute* node = attributeSet.Find( _name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( cname, cvalue ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, const std::string& _value ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; iNext() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a node + // 2) An element with only a text child is printed as text + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + // See STL_STRING_BUG below. + //StringToBuffer buf( value ); + + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + // See STL_STRING_BUG below. +// StringToBuffer buf( value ); +// +// if ( buf.buffer && SaveFile( buf.buffer ) ) +// return true; +// +// return false; + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + // There was a really terrifying little bug here. The code: + // value = filename + // in the STL case, cause the assignment method of the std::string to + // be called. What is strange, is that the std::string had the same + // address as it's c_str() method, and so bad things happen. Looks + // like a bug in the Microsoft STL implementation. + // Add an extra string to avoid the crash. + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // If we have a file, assume it is all one big XML file, and read it in. + // The document parser may decide the document ends sooner than the entire file, however. + TIXML_STRING data; + data.reserve( length ); + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // + // + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + const char* lastPos = buf; + const char* p = buf; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + if ( *p == 0xa ) { + // Newline character. No special rules for this. Append all the characters + // since the last string, and include the newline. + data.append( lastPos, (p-lastPos+1) ); // append, include the newline + ++p; // move past the newline + lastPos = p; // and point to the new buffer (may be 0) + assert( p <= (buf+length) ); + } + else if ( *p == 0xd ) { + // Carriage return. Append what we have so far, then + // handle moving forward in the buffer. + if ( (p-lastPos) > 0 ) { + data.append( lastPos, p-lastPos ); // do not add the CR + } + data += (char)0xa; // a proper newline + + if ( *(p+1) == 0xa ) { + // Carriage return - new line sequence + p += 2; + lastPos = p; + assert( p <= (buf+length) ); + } + else { + // it was followed by something else...that is presumably characters again. + ++p; + lastPos = p; + assert( p <= (buf+length) ); + } + } + else { + ++p; + } + } + // Handle any left over characters. + if ( p-lastPos ) { + data.append( lastPos, p-lastPos ); + } + delete [] buf; + buf = 0; + + Parse( data.c_str(), 0, encoding ); + + if ( Error() ) + return false; + else + return true; +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); + #else + sprintf (buf, "%lf", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} +*/ +#endif + + +const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} +*/ + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && iNextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && iNextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && iNextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && iNextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += ""; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += ""; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += ""; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/extern/oics/tinyxml.h b/extern/oics/tinyxml.h new file mode 100644 index 0000000000..e69913b59c --- /dev/null +++ b/extern/oics/tinyxml.h @@ -0,0 +1,1802 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +//#define TIXML_USE_STL + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_STL + #include + #include + #include + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SNSCANF _snscanf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SNSCANF _snscanf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SNSCANF snscanf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 5; +const int TIXML_PATCH_VERSION = 3; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simple called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its sibilings will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_OUT_OF_MEMORY, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + DOCUMENT, + ELEMENT, + COMMENT, + UNKNOWN, + TEXT, + DECLARATION, + TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: DOCUMENT, ELEMENT, COMMENT, + UNKNOWN, TEXT, and DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + const TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* Find( const char* _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + #ifdef TIXML_USE_STL + const TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* Find( const std::string& _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + + #endif + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + void operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + /* + This is - in theory - a bug fix for "QueryValueAtribute returns truncated std::string" + but template specialization is hard to get working cross-compiler. Leaving the bug for now. + + // The above will fail for std::string because the space character is used as a seperator. + // Specialize for strings. Bug [ 1695429 ] QueryValueAtribute returns truncated std::string + template<> int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + */ + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + void operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); } + void operator=( const TiXmlText& base ) { base.CopyTo( this ); } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + void operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); } + void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + void operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && LoadFile( f.buffer, encoding )); + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && SaveFile( f.buffer )); + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i +#include + +#include "tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; iappend( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p ) + p += strlen( endTag ); + return p; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + TiXmlDocument* doc = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: "; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + +// int tabsize = 4; +// if ( document ) +// tabsize = document->TabSize(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = ""; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/mangle/.gitignore b/libs/mangle/.gitignore deleted file mode 100644 index cd24d78972..0000000000 --- a/libs/mangle/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -upload_docs.sh -docs -*~ diff --git a/libs/mangle/Doxyfile b/libs/mangle/Doxyfile deleted file mode 100644 index f3e0180029..0000000000 --- a/libs/mangle/Doxyfile +++ /dev/null @@ -1,1510 +0,0 @@ -# Doxyfile 1.5.8 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = Mangle - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = 1 - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, -# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, -# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, -# Spanish, Swedish, and Ukrainian. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it parses. -# With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this tag. -# The format is ext=language, where ext is a file extension, and language is one of -# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, -# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), -# use: inc=Fortran f=C - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = YES - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will rougly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by -# doxygen. The layout file controls the global structure of the generated output files -# in an output format independent way. The create the layout file that represents -# doxygen's defaults, run doxygen with the -l option. You can optionally specify a -# file name after the option, if omitted DoxygenLayout.xml will be used as the name -# of the layout file. - -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = sound stream vfs input - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 - -FILE_PATTERNS = *.h - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = */tests/* - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = docs - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER -# are set, an additional index file will be generated that can be used as input for -# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated -# HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. -# For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's -# filter section matches. -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to FRAME, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, -# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are -# probably better off using the HTML help feature. Other possible values -# for this tag are: HIERARCHIES, which will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list; -# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which -# disables this behavior completely. For backwards compatibility with previous -# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE -# respectively. - -GENERATE_TREEVIEW = NONE - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. -# This is useful -# if you want to understand what is going on. -# On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = YES - -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. - -DOT_FONTNAME = FreeSans - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES - -#--------------------------------------------------------------------------- -# Options related to the search engine -#--------------------------------------------------------------------------- - -# The SEARCHENGINE tag specifies whether or not a search engine should be -# used. If set to NO the values of all tags below this one will be ignored. - -SEARCHENGINE = NO diff --git a/libs/mangle/LICENSE.txt b/libs/mangle/LICENSE.txt deleted file mode 100644 index ccfcc9f220..0000000000 --- a/libs/mangle/LICENSE.txt +++ /dev/null @@ -1,26 +0,0 @@ -Minimal Abstraction Game Layer (Mangle) is licensed under the -'zlib/libpng' license: - ----- - -Copyright (c) 2009 Nicolay Korslund - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. - diff --git a/libs/mangle/README.txt b/libs/mangle/README.txt deleted file mode 100644 index f4849bebda..0000000000 --- a/libs/mangle/README.txt +++ /dev/null @@ -1,129 +0,0 @@ -Welcome to Mangle v0.1 ----------------------- - -Written by: Nicolay Korslund (korslund@gmail.com) -License: zlib/png (see LICENSE.txt) -WWW: http://asm-soft.com/mangle/ -Documentation: http://asm-soft.com/mangle/docs - - - -Mangle is the project name for a small set of generic interfaces for -various game middleware libraries, such as sound, input, graphics, and -so on. You can imagine that it stands for "Minimal Abstraction Game -Layer", if you like. It will consist of several more or less -independent modules, one for each of these areas. These may be used -together to build an entire game engine, or they can be used -individually as separate libraries. - -However, Mangle does NOT actually implement a game engine, or any new -fundamental functionality. More on that below. - -Currently there's modules for sound and streams / archives (virtual -file systems.) More will come in the future (including input, 2D/3D -graphics, GUI, physics, and more.) - - -Main idea ---------- - -The idea behind Mangle is to provide a uniform, consistent interface -to other game libraries. The library does not provide ANY -functionality on its own. Instead it connects to a backend -implementation of your choice (or of your making.) - -The Sound module, for example, currently has backends for OpenAL -(output only), FFmpeg (input only) and for Audiere. Hopefully we'll -add IrrKlang, FMod, DirectSound, Miles and more in the future. It can -combine libraries to get more complete functionality (like using -OpenAL for output and FFmpeg to decode sound files), and it's also -easy to write your own backend if you're using a different (or -home-brewed) sound system. - -Regardless of what backend you use, the front-end interfaces (found -eg. in sound/output.h) is identical, and as a library user you -shouldn't notice much difference at all if you swap one backend for -another at a later point. It should Just Work. - -The interfaces themselves are also quite simple. Setting up a sound -stream from FFmpeg or other decoder into OpenAL can be quite hairy - -but with Mangle the hairy parts have already been written for you. You -just plug the parts together. - -The goal in the long run is to support a wide variety of game-related -libraries, and as many backend libraries (free and commercial) as -possible, so that you the user will have to write as little code as -possible. - - - -What is it good for -------------------- - -The main point of Mangle, as we said above, is that it connects to any -library of your choice "behind the scenes" but provides the same, -super-simple interface front-end for all of them. There can benefit -you in many ways: - -- If you want to use a new library that Mangle support. You don't have - to scour the net for tutorials and usage examples, since much of the - common usage code is already included in the implementation classes. - -- If you don't want to pollute your code with library-specific code. - The Mangle interfaces can help you keep your code clean, and its - user interface is often simpler than the exteral library one. - -- If you want to quickly connect different libraries together, it - really helps if they speak a common language. The Mangle interfaces - are exactly that - a common language between libraries. Do you need - Audiere to load sounds from a weird archive format only implemented - for PhysFS, all channeled through the OGRE resource system? No - problem! - -- If you are creating a library that depends on a specific feature - (such as sound), but you don't want to lock your users into any - specific sound library. Mangle works as an abstraction that lets - your users select their own implementation. - -- If you want to support multiple backends for your game/app, or want - to make it possible to easily switch backends later. You can select - backends at compile time or even at runtime. For example you might - want to switch to to a commercial sound library at a later stage in - development, or you may want to use a different input library on - console platforms than on PC. - -The Mangle implementations are extremely light-weight - often just one -or two cpp/h pairs per module. You can plug them directly into your -program, there's no separate library building step required. - -Since the library aims to be very modularly put together, you can -also, in many cases, just copy-and-paste the parts you need and ignore -the rest. Or modify stuff without fearing that the whole 'system' will -come crashing down, because there is no big 'system' to speak of. - - -Past and future ---------------- - -Mangle started out as (and still is) a spin-off from OpenMW, another -project I am personally working on ( http://openmw.com/ ). OpenMW is -an attempt to recreate the engine behind the commercial game -Morrowind, using only open source software. - -The projects are still tightly interlinked, and they will continue to -be until OpenMW is finished. Most near-future work on Mangle will be -focused chiefly on OpenMW at the moment. However I will gladly include -external contributions and suggestions that are not OpenMW-related if -someone sends them to me. - - -Conclusion ----------- - -As you might have guessed, Mangle is more a concept in development -than a finished library right now. - -All feedback, ideas, concepts, questions and code are very -welcome. Send them to: korslund@gmail.com - -I will put up a forum later as well if there's enough interest. diff --git a/libs/mangle/input/clients/ogre_input_capture.hpp b/libs/mangle/input/clients/ogre_input_capture.hpp deleted file mode 100644 index 2e77dc10b1..0000000000 --- a/libs/mangle/input/clients/ogre_input_capture.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef MANGLE_INPUT_OGREINPUTFRAME_H -#define MANGLE_INPUT_OGREINPUTFRAME_H - -/* - This Ogre FrameListener calls capture() on an input driver every frame. - */ - -#include -#include "../driver.hpp" - -namespace Mangle { -namespace Input { - - struct OgreInputCapture : Ogre::FrameListener - { - Mangle::Input::Driver &driver; - - OgreInputCapture(Mangle::Input::Driver &drv) - : driver(drv) {} - - bool frameStarted(const Ogre::FrameEvent &evt) - { - driver.capture(); - return true; - } - }; -}} - -#endif diff --git a/libs/mangle/input/driver.hpp b/libs/mangle/input/driver.hpp deleted file mode 100644 index f4ba159c52..0000000000 --- a/libs/mangle/input/driver.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef MANGLE_INPUT_DRIVER_H -#define MANGLE_INPUT_DRIVER_H - -#include "event.hpp" - -namespace Mangle -{ - namespace Input - { - /** Input::Driver is the main interface to any input system that - handles keyboard and/or mouse input, along with any other - input source like joysticks. - - It is really a generalized event system, and could also be - used for non-input related events. The definition of the event - codes and structures are entirely dependent on the - implementation. - - A system-independent key code list will be found in keys.hpp, - and input drivers should privide optional translations to/from - this list for full compatibility. - */ - struct Driver - { - Driver() {} - virtual ~Driver() {} - - /** Captures input and produces the relevant events from it. An - event callback must be set with setEvent(), or all events - will be ignored. - */ - virtual void capture() = 0; - - /** Check the state of a given key or button. The key/button - definitions depends on the driver. - */ - virtual bool isDown(int index) = 0; - - /** Show or hide system mouse cursor - */ - virtual void showMouse(bool show) = 0; - - /** Set the event handler for input events. The evt->event() - function is called for each event. The meaning of the index - and *p parameters will be specific to each driver and to - each input system. - */ - void setEvent(EventPtr evt) - { event = evt; } - - /** Instigate an event. Is used internally for all events, but - can also be called from the outside to "fake" events from - this driver. - */ - void makeEvent(Event::Type type, int index, const void *p=NULL) - { - if(event) - event->event(type,index,p); - } - - private: - /// Holds the event callback set byt setEvent() - EventPtr event; - }; - - typedef boost::shared_ptr DriverPtr; - } -} -#endif diff --git a/libs/mangle/input/event.hpp b/libs/mangle/input/event.hpp deleted file mode 100644 index dc7b470887..0000000000 --- a/libs/mangle/input/event.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef MANGLE_INPUT_EVENT_H -#define MANGLE_INPUT_EVENT_H - -#include "../tools/shared_ptr.hpp" - -namespace Mangle -{ - namespace Input - { - /** Generic callback for input events. The meaning of the - parameters depend on the system producing the events. - */ - struct Event - { - /// Event types - enum Type - { - EV_Unknown = 1, // Unknown event type - EV_KeyDown = 2, // Keyboard button was pressed - EV_KeyUp = 4, // Keyboard button was released - EV_Keyboard = 6, // All keyboard events - - EV_MouseMove = 8, // Mouse movement - EV_MouseDown = 16, // Mouse button pressed - EV_MouseUp = 32, // Mouse button released - EV_Mouse = 56, // All mouse events - - EV_ALL = 63 // All events - }; - - /** - Called upon all events. The first parameter give the event - type, the second gives additional data (usually the local - keysym or button index as defined by the driver), and the - pointer points to the full custom event structure provided by - the driver (the type may vary depending on the EventType, - this is defined in the Driver documentation.) - */ - virtual void event(Type type, int index, const void *p) = 0; - virtual ~Event() {} - }; - - typedef boost::shared_ptr EventPtr; - } -} -#endif diff --git a/libs/mangle/input/filters/eventlist.hpp b/libs/mangle/input/filters/eventlist.hpp deleted file mode 100644 index b3e2ff8f24..0000000000 --- a/libs/mangle/input/filters/eventlist.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef MANGLE_INPUT_EVENTLIST_H -#define MANGLE_INPUT_EVENTLIST_H - -#include "../event.hpp" -#include - -namespace Mangle -{ - namespace Input - { - /** And Event handler that distributes each event to a list of - other handlers. Supports filtering events by their Type - parameter. - */ - struct EventList : Event - { - struct Filter - { - EventPtr evt; - int flags; - }; - std::vector list; - - void add(EventPtr e, int flags = EV_ALL) - { - Filter f; - f.evt = e; - f.flags = flags; - list.push_back(f); - } - - virtual void event(Type type, int index, const void *p) - { - std::vector::iterator it; - - for(it=list.begin(); it!=list.end(); it++) - { - if(type & it->flags) - it->evt->event(type,index,p); - } - } - }; - - typedef boost::shared_ptr EventListPtr; - } -} -#endif diff --git a/libs/mangle/input/servers/ois_driver.cpp b/libs/mangle/input/servers/ois_driver.cpp deleted file mode 100644 index 07ba3e83a7..0000000000 --- a/libs/mangle/input/servers/ois_driver.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include "ois_driver.hpp" - -#include -#include -#include -#include - -#ifdef __APPLE_CC__ -#include -#endif - -using namespace Mangle::Input; -using namespace OIS; - -struct Mangle::Input::OISListener : OIS::KeyListener, OIS::MouseListener -{ - OISDriver &drv; - - OISListener(OISDriver &driver) - : drv(driver) {} - - bool keyPressed( const OIS::KeyEvent &arg ) - { - drv.makeEvent(Event::EV_KeyDown, arg.key, &arg); - return true; - } - - bool keyReleased( const OIS::KeyEvent &arg ) - { - drv.makeEvent(Event::EV_KeyUp, arg.key, &arg); - return true; - } - - bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) - { - // Mouse button events are handled as key events - // TODO: Translate mouse buttons into pseudo-keysyms - drv.makeEvent(Event::EV_MouseDown, id, &arg); - return true; - } - - bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) - { - // TODO: ditto - drv.makeEvent(Event::EV_MouseUp, id, &arg); - return true; - } - - bool mouseMoved( const OIS::MouseEvent &arg ) - { - drv.makeEvent(Event::EV_MouseMove, -1, &arg); - return true; - } -}; - -OISDriver::OISDriver(Ogre::RenderWindow *window, bool exclusive) -{ - assert(window); - - size_t windowHnd; - - window->getCustomAttribute("WINDOW", &windowHnd); - - std::ostringstream windowHndStr; - ParamList pl; - - windowHndStr << windowHnd; - pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str())); - - // Set non-exclusive mouse and keyboard input if the user requested - // it. - if(!exclusive) - { -#if defined OIS_WIN32_PLATFORM - pl.insert(std::make_pair(std::string("w32_mouse"), - std::string("DISCL_FOREGROUND" ))); - pl.insert(std::make_pair(std::string("w32_mouse"), - std::string("DISCL_NONEXCLUSIVE"))); - pl.insert(std::make_pair(std::string("w32_keyboard"), - std::string("DISCL_FOREGROUND"))); - pl.insert(std::make_pair(std::string("w32_keyboard"), - std::string("DISCL_NONEXCLUSIVE"))); -#elif defined OIS_LINUX_PLATFORM - pl.insert(std::make_pair(std::string("x11_mouse_grab"), - std::string("false"))); - pl.insert(std::make_pair(std::string("x11_mouse_hide"), - std::string("false"))); - pl.insert(std::make_pair(std::string("x11_keyboard_grab"), - std::string("false"))); - pl.insert(std::make_pair(std::string("XAutoRepeatOn"), - std::string("true"))); -#endif - } - -#ifdef __APPLE_CC__ - // Give the application window focus to receive input events - ProcessSerialNumber psn = { 0, kCurrentProcess }; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - SetFrontProcess(&psn); -#endif - - inputMgr = InputManager::createInputSystem( pl ); - - // Create all devices - keyboard = static_cast(inputMgr->createInputObject - ( OISKeyboard, true )); - mouse = static_cast(inputMgr->createInputObject - ( OISMouse, true )); - - // Set mouse region - const MouseState &ms = mouse->getMouseState(); - ms.width = window->getWidth(); - ms.height = window->getHeight(); - - // Set up the input listener - listener = new OISListener(*this); - keyboard-> setEventCallback(listener); - mouse-> setEventCallback(listener); -} - -OISDriver::~OISDriver() -{ - // Delete the listener object - delete listener; - - if(inputMgr == NULL) return; - - // Kill the input systems. This will reset input options such as key - // repeat rate. - inputMgr->destroyInputObject(keyboard); - inputMgr->destroyInputObject(mouse); - InputManager::destroyInputSystem(inputMgr); - inputMgr = NULL; -} - -void OISDriver::capture() -{ - // Capture keyboard and mouse events - keyboard->capture(); - mouse->capture(); -} - -bool OISDriver::isDown(int index) -{ - // TODO: Extend to mouse buttons as well - return keyboard->isKeyDown((OIS::KeyCode)index); -} - -void OISDriver::adjustMouseClippingSize(int width, int height) -{ - const OIS::MouseState &ms = mouse->getMouseState(); - ms.width = width; - ms.height = height; -} diff --git a/libs/mangle/input/servers/ois_driver.hpp b/libs/mangle/input/servers/ois_driver.hpp deleted file mode 100644 index 81633542fb..0000000000 --- a/libs/mangle/input/servers/ois_driver.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef MANGLE_INPUT_OIS_DRIVER_H -#define MANGLE_INPUT_OIS_DRIVER_H - -#include "../driver.hpp" - -namespace OIS -{ - class InputManager; - class Mouse; - class Keyboard; -} - -namespace Ogre -{ - class RenderWindow; -} - -namespace Mangle -{ - namespace Input - { - struct OISListener; - - /** Input driver for OIS, the input manager typically used with - Ogre. - */ - struct OISDriver : Driver - { - /// If exclusive=true, then we capture mouse and keyboard from - /// the OS. - OISDriver(Ogre::RenderWindow *window, bool exclusive=true); - ~OISDriver(); - - void adjustMouseClippingSize(int width, int height); - - void capture(); - bool isDown(int index); - /// Not currently supported. - void showMouse(bool) {} - - private: - OIS::InputManager *inputMgr; - OIS::Mouse *mouse; - OIS::Keyboard *keyboard; - - OISListener *listener; - }; - } -} -#endif diff --git a/libs/mangle/input/servers/sdl_driver.cpp b/libs/mangle/input/servers/sdl_driver.cpp deleted file mode 100644 index 93884a6e6c..0000000000 --- a/libs/mangle/input/servers/sdl_driver.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "sdl_driver.hpp" - -#include - -using namespace Mangle::Input; - -void SDLDriver::capture() -{ - // Poll for events - SDL_Event evt; - while(SDL_PollEvent(&evt)) - { - Event::Type type = Event::EV_Unknown; - int index = -1; - - switch(evt.type) - { - // For key events, send the keysym as the index. - case SDL_KEYDOWN: - type = Event::EV_KeyDown; - index = evt.key.keysym.sym; - break; - case SDL_KEYUP: - type = Event::EV_KeyUp; - index = evt.key.keysym.sym; - break; - case SDL_MOUSEMOTION: - type = Event::EV_MouseMove; - break; - // Add more event types later - } - - // Pass the event along, using -1 as index for unidentified - // event types. - makeEvent(type, index, &evt); - } -} - -bool SDLDriver::isDown(int index) -{ - int num; - Uint8 *keys = SDL_GetKeyState(&num); - assert(index >= 0 && index < num); - - // The returned array from GetKeyState is indexed by the - // SDLK_KEYNAME enums and is just a list of bools. If the indexed - // value is true, the button is down. - return keys[index]; -} - -void SDLDriver::showMouse(bool show) -{ - SDL_ShowCursor(show?SDL_ENABLE:SDL_DISABLE); -} diff --git a/libs/mangle/input/servers/sdl_driver.hpp b/libs/mangle/input/servers/sdl_driver.hpp deleted file mode 100644 index b71346cba1..0000000000 --- a/libs/mangle/input/servers/sdl_driver.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef MANGLE_INPUT_SDL_DRIVER_H -#define MANGLE_INPUT_SDL_DRIVER_H - -#include "../driver.hpp" - -namespace Mangle -{ - namespace Input - { - /** Input driver for SDL. As the input system of SDL is seldomly - used alone (most often along with the video system), it is - assumed that you do your own initialization and cleanup of SDL - before and after using this driver. - - The Event.event() calls will be given the proper EV_ type, the - key index (for key up/down events), and a pointer to the full - SDL_Event structure. - */ - struct SDLDriver : Driver - { - void capture(); - bool isDown(int index); - void showMouse(bool); - }; - } -} -#endif diff --git a/libs/mangle/input/tests/.gitignore b/libs/mangle/input/tests/.gitignore deleted file mode 100644 index 460c76f00b..0000000000 --- a/libs/mangle/input/tests/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*_test -ogre.cfg diff --git a/libs/mangle/input/tests/Makefile b/libs/mangle/input/tests/Makefile deleted file mode 100644 index 8760adfe73..0000000000 --- a/libs/mangle/input/tests/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -GCC=g++ -Wall - -all: sdl_driver_test ois_driver_test evtlist_test - -sdl_driver_test: sdl_driver_test.cpp - $(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL - -ois_driver_test: ois_driver_test.cpp - $(GCC) $< ../servers/ois_driver.cpp -o $@ -I/usr/local/include/OGRE/ -lOgreMain -lOIS -lboost_filesystem - -evtlist_test: evtlist_test.cpp ../filters/eventlist.hpp ../event.hpp - $(GCC) $< -o $@ - -clean: - rm *_test diff --git a/libs/mangle/input/tests/common.cpp b/libs/mangle/input/tests/common.cpp deleted file mode 100644 index 0c7c76466b..0000000000 --- a/libs/mangle/input/tests/common.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include "../driver.hpp" -#include -using namespace std; -using namespace Mangle::Input; - -Driver *input; - -struct MyCB : Event -{ - void event(Event::Type type, int i, const void *p) - { - cout << "got event: type=" << type << " index=" << i << endl; - } -}; - -void mainLoop(int argc, int quitKey) -{ - cout << "Hold the Q key to quit:\n"; - input->setEvent(EventPtr(new MyCB)); - while(!input->isDown(quitKey)) - { - input->capture(); - usleep(20000); - - if(argc == 1) - { - cout << "You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly\n"; - break; - } - } - - delete input; - cout << "\nBye bye!\n"; -} diff --git a/libs/mangle/input/tests/evtlist_test.cpp b/libs/mangle/input/tests/evtlist_test.cpp deleted file mode 100644 index fbd980cbd9..0000000000 --- a/libs/mangle/input/tests/evtlist_test.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include "../filters/eventlist.hpp" - -using namespace std; -using namespace Mangle::Input; - -struct MyEvent : Event -{ - int ii; - MyEvent(int i) : ii(i) {} - - void event(Event::Type type, int i, const void *p) - { - cout << " #" << ii << " got event: type=" << type << " index=" << i << endl; - } -}; - -EventList lst; - -int iii=1; -void make(int flags) -{ - lst.add(EventPtr(new MyEvent(iii++)), flags); -} - -void send(Event::Type type) -{ - cout << "Sending type " << type << endl; - lst.event(type,0,NULL); -} - -int main() -{ - make(Event::EV_ALL); - make(Event::EV_KeyDown); - make(Event::EV_KeyUp | Event::EV_MouseDown); - - send(Event::EV_Unknown); - send(Event::EV_KeyDown); - send(Event::EV_KeyUp); - send(Event::EV_MouseDown); - - cout << "Enough of that\n"; - return 0; -} diff --git a/libs/mangle/input/tests/ois_driver_test.cpp b/libs/mangle/input/tests/ois_driver_test.cpp deleted file mode 100644 index 386f24055e..0000000000 --- a/libs/mangle/input/tests/ois_driver_test.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "common.cpp" - -#include "../servers/ois_driver.hpp" -#include -#include -#include - -bool isFile(const char *name) -{ - boost::filesystem::path cfg_file_path(name); - return boost::filesystem::exists(cfg_file_path); -} - -using namespace Ogre; -using namespace OIS; - -Root *root; -RenderWindow *window; - -void setupOgre() -{ - // Disable logging - new LogManager; - Log *log = LogManager::getSingleton().createLog(""); - log->setDebugOutputEnabled(false); - - bool useConfig = isFile("ogre.cfg"); - - // Set up Root - root = new Root("plugins.cfg", "ogre.cfg", ""); - - // Configure - if(!useConfig) - root->showConfigDialog(); - else - root->restoreConfig(); - - // Initialize OGRE window - window = root->initialise(true, "test", ""); -} - -int main(int argc, char** argv) -{ - setupOgre(); - input = new OISDriver(window); - - mainLoop(argc, KC_Q); - - delete root; - return 0; -} diff --git a/libs/mangle/input/tests/output/evtlist_test.out b/libs/mangle/input/tests/output/evtlist_test.out deleted file mode 100644 index 180dcc58a8..0000000000 --- a/libs/mangle/input/tests/output/evtlist_test.out +++ /dev/null @@ -1,12 +0,0 @@ -Sending type 1 - #1 got event: type=1 index=0 -Sending type 2 - #1 got event: type=2 index=0 - #2 got event: type=2 index=0 -Sending type 4 - #1 got event: type=4 index=0 - #3 got event: type=4 index=0 -Sending type 16 - #1 got event: type=16 index=0 - #3 got event: type=16 index=0 -Enough of that diff --git a/libs/mangle/input/tests/output/ois_driver_test.out b/libs/mangle/input/tests/output/ois_driver_test.out deleted file mode 100644 index 7d273fd46d..0000000000 --- a/libs/mangle/input/tests/output/ois_driver_test.out +++ /dev/null @@ -1,5 +0,0 @@ -Hold the Q key to quit: -got event: type=8 index=-1 -You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly - -Bye bye! diff --git a/libs/mangle/input/tests/output/sdl_driver_test.out b/libs/mangle/input/tests/output/sdl_driver_test.out deleted file mode 100644 index 2df2e4014e..0000000000 --- a/libs/mangle/input/tests/output/sdl_driver_test.out +++ /dev/null @@ -1,5 +0,0 @@ -Hold the Q key to quit: -got event: type=1 index=-1 -You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly - -Bye bye! diff --git a/libs/mangle/input/tests/plugins.cfg b/libs/mangle/input/tests/plugins.cfg deleted file mode 100644 index 57ec54e1a0..0000000000 --- a/libs/mangle/input/tests/plugins.cfg +++ /dev/null @@ -1,12 +0,0 @@ -# Defines plugins to load - -# Define plugin folder -PluginFolder=/usr/local/lib/OGRE/ - -# Define plugins -Plugin=RenderSystem_GL -Plugin=Plugin_ParticleFX -Plugin=Plugin_OctreeSceneManager -# Plugin=Plugin_CgProgramManager - - diff --git a/libs/mangle/input/tests/sdl_driver_test.cpp b/libs/mangle/input/tests/sdl_driver_test.cpp deleted file mode 100644 index 5db6dbba8f..0000000000 --- a/libs/mangle/input/tests/sdl_driver_test.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "common.cpp" - -#include "../servers/sdl_driver.hpp" -#include - -int main(int argc, char** argv) -{ - SDL_Init(SDL_INIT_VIDEO); - SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE); - input = new SDLDriver(); - - mainLoop(argc, SDLK_q); - - SDL_Quit(); - return 0; -} diff --git a/libs/mangle/input/tests/test.sh b/libs/mangle/input/tests/test.sh deleted file mode 100755 index 2d07708adc..0000000000 --- a/libs/mangle/input/tests/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -make || exit - -mkdir -p output - -PROGS=*_test - -for a in $PROGS; do - if [ -f "output/$a.out" ]; then - echo "Running $a:" - ./$a | diff output/$a.out - - else - echo "Creating $a.out" - ./$a > "output/$a.out" - git add "output/$a.out" - fi -done diff --git a/libs/mangle/rend2d/driver.hpp b/libs/mangle/rend2d/driver.hpp deleted file mode 100644 index 08a15b0aeb..0000000000 --- a/libs/mangle/rend2d/driver.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef MANGLE_REND2D_DRIVER_H -#define MANGLE_REND2D_DRIVER_H - -#include -#include "sprite.hpp" - -namespace Mangle -{ - namespace Rend2D - { - /** - The driver is the connection to the backend system that powers - 2D sprite rendering. For example the backend could be SDL or - any other 2D-capable graphics library. - */ - struct Driver - { - /// Get the screen sprite - virtual Sprite *getScreen() = 0; - - /// Sets the video mode. - virtual void setVideoMode(int width, int height, int bpp=32, bool fullscreen=false) = 0; - - /** Update the screen. Until this function is called, none of - the changes written to the screen sprite will be visible. - */ - virtual void update() = 0; - - /// Set the window title, as well as the title of the window - /// when "iconified" - virtual void setWindowTitle(const std::string &title, - const std::string &icon) = 0; - - /// Set the window title - void setWindowTitle(const std::string &title) { setWindowTitle(title,title); } - - /// Load sprite from an image file. Thows an exception on - /// failure. - virtual Sprite* loadImage(const std::string &file) = 0; - - /// Load a sprite from an image file stored in memory. Throws - /// exception on failure. - virtual Sprite* loadImage(const void* data, size_t size) = 0; - - /** @brief Set gamma value for all colors. - - Note: Setting this in windowed mode will affect the ENTIRE - SCREEN! - */ - virtual void setGamma(float gamma) = 0; - - /// Set gamma individually for red, green, blue - virtual void setGamma(float red, float green, float blue) = 0; - - /// Get screen width - virtual int width() = 0; - - /// Get screen height - virtual int height() = 0; - }; - } -} -#endif diff --git a/libs/mangle/rend2d/servers/sdl_driver.cpp b/libs/mangle/rend2d/servers/sdl_driver.cpp deleted file mode 100644 index 84a17933ff..0000000000 --- a/libs/mangle/rend2d/servers/sdl_driver.cpp +++ /dev/null @@ -1,259 +0,0 @@ -#include "sdl_driver.hpp" - -#include -#include -#include -#include - -using namespace Mangle::Rend2D; - -const SpriteData *SDL_Sprite::lock() -{ - // Make sure we aren't already locked - assert(!data.pixels); - - // Lock the surface and set up the data structure - SDL_LockSurface(surface); - - data.pixels = surface->pixels; - data.w = surface->w; - data.h = surface->h; - data.pitch = surface->pitch; - data.bypp = surface->format->BytesPerPixel; - - return &data; -} - -void SDL_Sprite::unlock() -{ - if(data.pixels) - { - SDL_UnlockSurface(surface); - data.pixels = NULL; - } -} - -// This is a really crappy and slow implementation, only intended for -// testing purposes. Use lock/unlock for faster pixel drawing. -void SDL_Sprite::pixel(int x, int y, int color) -{ - SDL_LockSurface(surface); - - int bpp = surface->format->BytesPerPixel; - char *p = (char*)surface->pixels + y*surface->pitch + x*bpp; - - switch(bpp) - { - case 1: *p = color; break; - case 3: - if(SDL_BYTEORDER == SDL_BIG_ENDIAN) - { - p[0] = (color >> 16) & 0xff; - p[1] = (color >> 8) & 0xff; - p[2] = color & 0xff; - } - else - { - p[0] = color & 0xff; - p[1] = (color >> 8) & 0xff; - p[2] = (color >> 16) & 0xff; - } - break; - case 4: - *(int*)p = color; - break; - } - SDL_UnlockSurface(surface); -} - -void SDL_Sprite::draw(Sprite *s, // Must be SDL_Sprite - int x, int y, // Destination position - int sx, int sy, // Source position - int w, int h // Amount to draw. -1 means remainder. - ) -{ - // Get source surface - SDL_Sprite *other = dynamic_cast(s); - assert(other != NULL); - SDL_Surface *img = other->getSurface(); - - // Check coordinate validity - assert(sx <= img->w && sy <= img->h); - assert(x <= surface->w && y <= surface->h); - assert(sx >= 0 && sy >= 0); - - // Compute width and height if necessary - if(w == -1) w = img->w - sx; - if(h == -1) h = img->h - sy; - - // Check them if they're valid - assert(w >= 0 && w <= img->w); - assert(h >= 0 && h <= img->h); - - SDL_Rect dest; - dest.x = x; - dest.y = y; - dest.w = w; - dest.h = h; - - SDL_Rect src; - src.x = sx; - src.y = sy; - src.w = w; - src.h = h; - - // Do the Blitman - SDL_BlitSurface(img, &src, surface, &dest); -} - -SDL_Sprite::SDL_Sprite(SDL_Surface *s, bool autoDelete) - : surface(s), autoDel(autoDelete) -{ - assert(surface != NULL); - data.pixels = NULL; -} - -SDL_Sprite::~SDL_Sprite() -{ - if(autoDel) - SDL_FreeSurface(surface); -} - -void SDL_Sprite::fill(int value) -{ - SDL_FillRect(surface, NULL, value); -} - -int SDL_Sprite::width() { return surface->w; } -int SDL_Sprite::height() { return surface->h; } - -SDLDriver::SDLDriver() : display(NULL), realDisp(NULL), softDouble(false) -{ - if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1) - throw std::runtime_error("Error initializing SDL video"); -} -SDLDriver::~SDLDriver() -{ - if(display) delete display; - SDL_Quit(); -} - -void SDLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen) -{ - unsigned int flags; - - if(display) delete display; - - if (fullscreen) - // Assume fullscreen mode allows a double-bufferd hardware - // mode. We need more test code for this to be safe though. - flags = SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF; - else - flags = SDL_SWSURFACE; - - // Create the surface and check it - realDisp = SDL_SetVideoMode(width, height, bpp, flags); - if(realDisp == NULL) - throw std::runtime_error("Failed setting SDL video mode"); - - // Code for software double buffering. I haven't found this to be - // any speed advantage at all in windowed mode (it's slower, as one - // would expect.) Not properly tested in fullscreen mode with - // hardware buffers, but it will probably only be an improvement if - // we do excessive writing (ie. write each pixel on average more - // than once) or try to read from the display buffer. - if(softDouble) - { - // Make a new surface with the same attributes as the real - // display surface. - SDL_Surface *back = SDL_DisplayFormat(realDisp); - assert(back != NULL); - - // Create a sprite representing the double buffer - display = new SDL_Sprite(back); - } - else - { - // Create a sprite directly representing the display surface. - // The 'false' parameter means do not autodelete the screen - // surface upon exit (since SDL manages it) - display = new SDL_Sprite(realDisp, false); - } -} - -/// Update the screen -void SDLDriver::update() -{ - // Blit the soft double buffer onto the real display buffer - if(softDouble) - SDL_BlitSurface(display->getSurface(), NULL, realDisp, NULL ); - - if(realDisp) - SDL_Flip(realDisp); -} - -/// Set the window title, as well as the title of the window when -/// "iconified" -void SDLDriver::setWindowTitle(const std::string &title, - const std::string &icon) -{ - SDL_WM_SetCaption( title.c_str(), icon.c_str() ); -} - -// Convert the given surface to display format. -static SDL_Surface* convertImage(SDL_Surface* surf) -{ - if(surf != NULL) - { - // Convert the image to the display buffer format, for faster - // blitting - SDL_Surface *surf2 = SDL_DisplayFormat(surf); - SDL_FreeSurface(surf); - surf = surf2; - } - return surf; -} - -/// Load sprite from an image file, using SDL_image. -Sprite* SDLDriver::loadImage(const std::string &file) -{ - SDL_Surface *surf = IMG_Load(file.c_str()); - surf = convertImage(surf); - if(surf == NULL) - throw std::runtime_error("SDL failed to load image file '" + file + "'"); - return spriteFromSDL(surf); -} - -/// Load sprite from an SDL_RWops structure. autoFree determines -/// whether the RWops struct should be closed/freed after use. -Sprite* SDLDriver::loadImage(SDL_RWops *src, bool autoFree) -{ - SDL_Surface *surf = IMG_Load_RW(src, autoFree); - surf = convertImage(surf); - if(surf == NULL) - throw std::runtime_error("SDL failed to load image"); - return spriteFromSDL(surf); -} - -/// Load a sprite from an image file stored in memory. Uses -/// SDL_image. -Sprite* SDLDriver::loadImage(const void* data, size_t size) -{ - SDL_RWops *rw = SDL_RWFromConstMem(data, size); - return loadImage(rw, true); -} - -void SDLDriver::setGamma(float red, float green, float blue) -{ - SDL_SetGamma(red,green,blue); -} - -/// Convert an existing SDL surface into a sprite -Sprite* SDLDriver::spriteFromSDL(SDL_Surface *surf, bool autoFree) -{ - assert(surf); - return new SDL_Sprite(surf, autoFree); -} - -void SDLDriver::sleep(int ms) { SDL_Delay(ms); } -unsigned int SDLDriver::ticks() { return SDL_GetTicks(); } diff --git a/libs/mangle/rend2d/servers/sdl_driver.hpp b/libs/mangle/rend2d/servers/sdl_driver.hpp deleted file mode 100644 index 0f205ba34c..0000000000 --- a/libs/mangle/rend2d/servers/sdl_driver.hpp +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef MANGLE_DRAW2D_SDL_H -#define MANGLE_DRAW2D_SDL_H - -#include "../driver.hpp" - -// Predeclarations keep the streets safe at night -struct SDL_Surface; -struct SDL_RWops; - -namespace Mangle -{ - namespace Rend2D - { - /// SDL-implementation of Sprite - struct SDL_Sprite : Sprite - { - /** Draw a sprite in the given position. Can only draw other SDL - sprites. - */ - void draw(Sprite *s, // Must be SDL_Sprite - int x, int y, // Destination position - int sx=0, int sy=0, // Source position - int w=-1, int h=-1 // Amount to draw. -1 means remainder. - ); - - SDL_Sprite(SDL_Surface *s, bool autoDelete=true); - ~SDL_Sprite(); - - // Information retrieval - int width(); - int height(); - SDL_Surface *getSurface() { return surface; } - - // Fill with a given pixel value - void fill(int value); - - // Set one pixel - void pixel(int x, int y, int value); - - const SpriteData *lock(); - void unlock(); - - private: - // The SDL surface - SDL_Surface* surface; - - // Used for locking - SpriteData data; - - // If true, delete this surface when the canvas is destructed - bool autoDel; - }; - - class SDLDriver : public Driver - { - // The main display surface - SDL_Sprite *display; - - // The actual display surface. May or may not be the same - // surface pointed to by 'display' above, depending on the - // softDouble flag. - SDL_Surface *realDisp; - - // If true, we do software double buffering. - bool softDouble; - - public: - SDLDriver(); - ~SDLDriver(); - - /// Sets the video mode. Will create the window if it is not - /// already set up. Note that for SDL, bpp=0 means use current - /// bpp. - void setVideoMode(int width, int height, int bpp=0, bool fullscreen=false); - - /// Update the screen - void update(); - - /// Set the window title, as well as the title of the window - /// when "iconified" - void setWindowTitle(const std::string &title, - const std::string &icon); - - // Include overloads from our Glorious parent - using Driver::setWindowTitle; - - /// Load sprite from an image file, using SDL_image. - Sprite* loadImage(const std::string &file); - - /// Load sprite from an SDL_RWops structure. autoFree determines - /// whether the RWops struct should be closed/freed after use. - Sprite* loadImage(SDL_RWops *src, bool autoFree=false); - - /// Load a sprite from an image file stored in memory. Uses - /// SDL_image. - Sprite* loadImage(const void* data, size_t size); - - /// Set gamma value - void setGamma(float gamma) { setGamma(gamma,gamma,gamma); } - - /// Set gamma individually for red, green, blue - void setGamma(float red, float green, float blue); - - /// Convert an existing SDL surface into a sprite - Sprite* spriteFromSDL(SDL_Surface *surf, bool autoFree = true); - - // Get width and height - int width() { return display ? display->width() : 0; } - int height() { return display ? display->height() : 0; } - - /// Get the screen sprite - Sprite *getScreen() { return display; } - - /// Not really a graphic-related function, but very - /// handly. Sleeps the given number of milliseconds using - /// SDL_Delay(). - void sleep(int ms); - - /// Get the number of ticks since SDL initialization, using - /// SDL_GetTicks(). - unsigned int ticks(); - }; - } -} -#endif diff --git a/libs/mangle/rend2d/servers/sdl_gl_driver.cpp b/libs/mangle/rend2d/servers/sdl_gl_driver.cpp deleted file mode 100644 index db519e0911..0000000000 --- a/libs/mangle/rend2d/servers/sdl_gl_driver.cpp +++ /dev/null @@ -1,311 +0,0 @@ -#include "sdl_gl_driver.hpp" - -#include -#include -#include -#include -#include - -using namespace Mangle::Rend2D; - -void SDLGL_Sprite::draw(Sprite *s, // Must be SDLGL_Sprite - int x, int y, // Destination position - int sx, int sy, // Source position - int w, int h // Amount to draw. -1 means remainder. - ) -{ - // Get source surface - SDLGL_Sprite *other = dynamic_cast(s); - assert(other != NULL); - SDL_Surface *img = other->getSurface(); - - // Check coordinate validity - assert(sx <= img->w && sy <= img->h); - assert(x <= surface->w && y <= surface->h); - assert(sx >= 0 && sy >= 0); - - // Compute width and height if necessary - if(w == -1) w = img->w - sx; - if(h == -1) h = img->h - sy; - - // Check them if they're valid - assert(w >= 0 && w <= img->w); - assert(h >= 0 && h <= img->h); - - SDL_Rect dest; - dest.x = x; - dest.y = y; - dest.w = w; - dest.h = h; - - SDL_Rect src; - src.x = sx; - src.y = sy; - src.w = w; - src.h = h; - - // Do the Blitman - SDL_BlitSurface(img, &src, surface, &dest); -} - -SDLGL_Sprite::SDLGL_Sprite(SDL_Surface *s, bool autoDelete) - : surface(s), autoDel(autoDelete) -{ - assert(surface != NULL); -} - -SDLGL_Sprite::~SDLGL_Sprite() -{ - if(autoDel) - SDL_FreeSurface(surface); -} - -void SDLGL_Sprite::fill(int value) -{ - SDL_FillRect(surface, NULL, value); -} - -int SDLGL_Sprite::width() { return surface->w; } -int SDLGL_Sprite::height() { return surface->h; } - -SDLGLDriver::SDLGLDriver() : display(NULL), realDisp(NULL) -{ - if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1) - throw std::runtime_error("Error initializing SDL video"); -} -SDLGLDriver::~SDLGLDriver() -{ - if(display) delete display; - SDL_Quit(); -} - -// Surface used for the screen. Since OpenGL surfaces must have sizes -// that are powers of 2, we have to "fake" the returned display size -// to match the screen, not the surface itself. If we don't use this, -// the client program will get confused about the actual size of our -// screen, thinking it is bigger than it is. -struct FakeSizeSprite : SDLGL_Sprite -{ - int fakeW, fakeH; - - FakeSizeSprite(SDL_Surface *s, int fw, int fh) - : SDLGL_Sprite(s), fakeW(fw), fakeH(fh) - {} - - int width() { return fakeW; } - int height() { return fakeH; } -}; - -static int makePow2(int num) -{ - assert(num); - if((num & (num-1)) != 0) - { - int cnt = 0; - while(num) - { - num >>= 1; - cnt++; - } - num = 1 << cnt; - } - return num; -} - -void SDLGLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen) -{ - unsigned int flags; - - if(display) delete display; - - flags = SDL_OPENGL; - - if (fullscreen) - flags |= SDL_FULLSCREEN; - - SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); - SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1 ); - - // Create the surface and check it - screen = SDL_SetVideoMode(width, height, bpp, flags); - if(screen == NULL) - throw std::runtime_error("Failed setting SDL video mode"); - - // Expand width and height to be powers of 2 - int width2 = makePow2(width); - int height2 = makePow2(height); - - // Create a new SDL surface of this size - const SDL_PixelFormat& fmt = *(screen->format); - realDisp = SDL_CreateRGBSurface(SDL_SWSURFACE,width2,height2, - fmt.BitsPerPixel, - fmt.Rmask,fmt.Gmask,fmt.Bmask,fmt.Amask); - - // Create a sprite directly representing the display surface. This - // allows the user to blit to it directly. - display = new FakeSizeSprite(realDisp, width, height); - - // Set up the OpenGL format - nOfColors = fmt.BytesPerPixel; - - if(nOfColors == 4) - { - if (fmt.Rmask == 0x000000ff) - texture_format = GL_RGBA; - else - texture_format = GL_BGRA; - } - else if(nOfColors == 3) - { - if (fmt.Rmask == 0x000000ff) - texture_format = GL_RGB; - else - texture_format = GL_BGR; - } - else - assert(0 && "unsupported screen format"); - - glEnable(GL_TEXTURE_2D); - - // Have OpenGL generate a texture object handle for us - glGenTextures( 1, &texture ); - - // Bind the texture object - glBindTexture( GL_TEXTURE_2D, texture ); - - // Set the texture's stretching properties - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); -} - -void SDLGLDriver::updateNoSwap() -{ - if(!realDisp) return; - - // Fist, set up the screen texture: - - // Bind the texture object - glBindTexture( GL_TEXTURE_2D, texture ); - - // Edit the texture object's image data - glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, realDisp->w, realDisp->h, 0, - texture_format, GL_UNSIGNED_BYTE, realDisp->pixels ); - - glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT); - glLoadIdentity(); - - // OpenGL barf. Set up the projection to match our screen - int vPort[4]; - glGetIntegerv(GL_VIEWPORT, vPort); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0, vPort[2], 0, vPort[3], -1, 1); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glBegin( GL_QUADS ); - - // Needed to move the screen into the right place - int diff = screen->h - realDisp->h; - - // Bottom-left vertex (corner) - glTexCoord2i( 0, 1 ); - glVertex3f(0,diff,0); - - // Bottom-right vertex (corner) - glTexCoord2i( 1, 1 ); - glVertex3f( realDisp->w, diff, 0.f ); - - // Top-right vertex (corner) - glTexCoord2i( 1, 0 ); - glVertex3f( realDisp->w, screen->h, 0.f ); - - // Top-left vertex (corner) - glTexCoord2i( 0, 0 ); - glVertex3f( 0, screen->h, 0.f ); - glEnd(); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); -} - -void SDLGLDriver::swap() -{ - SDL_GL_SwapBuffers(); -} - -void SDLGLDriver::update() -{ - updateNoSwap(); - swap(); -} - -/// Set the window title, as well as the title of the window when -/// "iconified" -void SDLGLDriver::setWindowTitle(const std::string &title, - const std::string &icon) -{ - SDL_WM_SetCaption( title.c_str(), icon.c_str() ); -} - -// Convert the given surface to display format. -static SDL_Surface* convertImage(SDL_Surface* surf) -{ - if(surf != NULL) - { - // Convert the image to the display buffer format, for faster - // blitting - SDL_Surface *surf2 = SDL_DisplayFormat(surf); - SDL_FreeSurface(surf); - surf = surf2; - } - return surf; -} - -/// Load sprite from an image file, using SDL_image. -Sprite* SDLGLDriver::loadImage(const std::string &file) -{ - SDL_Surface *surf = IMG_Load(file.c_str()); - surf = convertImage(surf); - if(surf == NULL) - throw std::runtime_error("SDL failed to load image file '" + file + "'"); - return spriteFromSDL(surf); -} - -/// Load sprite from an SDL_RWops structure. autoFree determines -/// whether the RWops struct should be closed/freed after use. -Sprite* SDLGLDriver::loadImage(SDL_RWops *src, bool autoFree) -{ - SDL_Surface *surf = IMG_Load_RW(src, autoFree); - surf = convertImage(surf); - if(surf == NULL) - throw std::runtime_error("SDL failed to load image"); - return spriteFromSDL(surf); -} - -/// Load a sprite from an image file stored in memory. Uses -/// SDL_image. -Sprite* SDLGLDriver::loadImage(const void* data, size_t size) -{ - SDL_RWops *rw = SDL_RWFromConstMem(data, size); - return loadImage(rw, true); -} - -void SDLGLDriver::setGamma(float red, float green, float blue) -{ - SDL_SetGamma(red,green,blue); -} - -/// Convert an existing SDL surface into a sprite -Sprite* SDLGLDriver::spriteFromSDL(SDL_Surface *surf, bool autoFree) -{ - assert(surf); - return new SDLGL_Sprite(surf, autoFree); -} - -void SDLGLDriver::sleep(int ms) { SDL_Delay(ms); } -unsigned int SDLGLDriver::ticks() { return SDL_GetTicks(); } diff --git a/libs/mangle/rend2d/servers/sdl_gl_driver.hpp b/libs/mangle/rend2d/servers/sdl_gl_driver.hpp deleted file mode 100644 index d116e3659b..0000000000 --- a/libs/mangle/rend2d/servers/sdl_gl_driver.hpp +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef MANGLE_DRAW2D_SDLGL_H -#define MANGLE_DRAW2D_SDLGL_H - -/** This driver is similar to SDLDriver, except that it uses SDL on - top of OpenGL. - - I've decided to make it a separate file instead of just adding - optional OpenGL support to the original, so that pure SDL users - don't have to add OpenGL as a dependency. - */ - -#include "../driver.hpp" - -// Predeclarations keep the streets safe at night -struct SDL_Surface; -struct SDL_RWops; - -namespace Mangle -{ - namespace Rend2D - { - /// SDL-implementation of Sprite - struct SDLGL_Sprite : Sprite - { - /** Draw a sprite in the given position. Can only draw other SDL - sprites. - */ - void draw(Sprite *s, // Must be SDLGL_Sprite - int x, int y, // Destination position - int sx=0, int sy=0, // Source position - int w=-1, int h=-1 // Amount to draw. -1 means remainder. - ); - - SDLGL_Sprite(SDL_Surface *s, bool autoDelete=true); - ~SDLGL_Sprite(); - - // Information retrieval - virtual int width(); - virtual int height(); - SDL_Surface *getSurface() { return surface; } - - // Fill with a given pixel value - void fill(int value); - - private: - // The SDL surface - SDL_Surface* surface; - - // If true, delete this surface when the canvas is destructed - bool autoDel; - }; - - class SDLGLDriver : public Driver - { - // The main display surface - SDLGL_Sprite *display; - - // The screen surface. This is completely unused. - SDL_Surface *screen; - - // The display surface and main GL texture. These are used when - // drawing the entire screen as one surface, as a drop-in - // replacement for SDLDriver. - SDL_Surface *realDisp; - unsigned int texture; - int nOfColors, texture_format; - - public: - SDLGLDriver(); - ~SDLGLDriver(); - - /// Sets the video mode. Will create the window if it is not - /// already set up. Note that for SDL, bpp=0 means use current - /// bpp. - void setVideoMode(int width, int height, int bpp=0, bool fullscreen=false); - - /// Update the screen - void update(); - - /// Calls SDL_GL_SwapBuffers - void swap(); - - /// Draw surface to screen but do not call SDL_GL_SwapBuffers() - void updateNoSwap(); - - /// Set the window title, as well as the title of the window - /// when "iconified" - void setWindowTitle(const std::string &title, - const std::string &icon); - - // Include overloads from our Glorious parent - using Driver::setWindowTitle; - - /// Load sprite from an image file, using SDL_image. - Sprite* loadImage(const std::string &file); - - /// Load sprite from an SDL_RWops structure. autoFree determines - /// whether the RWops struct should be closed/freed after use. - Sprite* loadImage(SDL_RWops *src, bool autoFree=false); - - /// Load a sprite from an image file stored in memory. Uses - /// SDL_image. - Sprite* loadImage(const void* data, size_t size); - - /// Set gamma value - void setGamma(float gamma) { setGamma(gamma,gamma,gamma); } - - /// Set gamma individually for red, green, blue - void setGamma(float red, float green, float blue); - - /// Convert an existing SDL surface into a sprite - Sprite* spriteFromSDL(SDL_Surface *surf, bool autoFree = true); - - // Get width and height - int width() { return display ? display->width() : 0; } - int height() { return display ? display->height() : 0; } - - /// Get the screen sprite - Sprite *getScreen() { return display; } - - /// Not really a graphic-related function, but very - /// handly. Sleeps the given number of milliseconds using - /// SDL_Delay(). - void sleep(int ms); - - /// Get the number of ticks since SDL initialization, using - /// SDL_GetTicks(). - unsigned int ticks(); - }; - } -} -#endif diff --git a/libs/mangle/rend2d/sprite.hpp b/libs/mangle/rend2d/sprite.hpp deleted file mode 100644 index f49da6cb6d..0000000000 --- a/libs/mangle/rend2d/sprite.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef MANGLE_REND2D_SPRITE_H -#define MANGLE_REND2D_SPRITE_H - -namespace Mangle -{ - namespace Rend2D - { - /** - A pointer to sprite data for direct drawing. Only to be used - while the corresponding sprite is locked. - */ - struct SpriteData - { - void *pixels; // Pixel data - int w, h; // Width and height - int pitch, bypp; // Pitch (bytes) and bytes per pixel - }; - - /** - A Sprite is either a bitmap to be drawn or an output of area - for blitting other bitmaps, or both. They are created by the - Driver. - */ - struct Sprite - { - /// Draw a sprite in the given position - virtual void draw(Sprite *s, // The sprite to draw - int x, int y, // Destination position - int sx=0, int sy=0, // Source position - int w=-1, int h=-1 // Amount to draw. -1 means remainder. - ) = 0; - - virtual ~Sprite() {} - - // Information retrieval - virtual int width() = 0; - virtual int height() = 0; - - /// Fill the sprite with the given pixel value. The pixel format - /// depends on the format of the sprite. - virtual void fill(int value) = 0; - - /// Set one pixel value. The pixel format depends on the sprite - /// format. This is not expected to be fast, and in some - /// implementations may not work at all. - virtual void pixel(int x, int y, int value) {} - - /// Lock sprite for direct drawing, and return a struct - /// containing the necessary pointer. When finished, unlock the - /// sprite with unlock(). May return NULL, if so then direct - /// drawing is not possible. - virtual const SpriteData *lock() { return NULL; } - virtual void unlock() {} - }; - } -} -#endif diff --git a/libs/mangle/rend2d/tests/.gitignore b/libs/mangle/rend2d/tests/.gitignore deleted file mode 100644 index 8144904045..0000000000 --- a/libs/mangle/rend2d/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*_test diff --git a/libs/mangle/rend2d/tests/Makefile b/libs/mangle/rend2d/tests/Makefile deleted file mode 100644 index d430f60a93..0000000000 --- a/libs/mangle/rend2d/tests/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -GCC=g++ -Wall -Werror - -all: sdl_test sdl_move_test sdlgl_move_test - -sdl_test: sdl_test.cpp - $(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image - -sdl_move_test: sdl_move_test.cpp ../servers/sdl_driver.cpp - $(GCC) $^ -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image - -sdlgl_move_test: sdlgl_move_test.cpp ../servers/sdl_gl_driver.cpp - $(GCC) $^ -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image -lGL - -clean: - rm *_test diff --git a/libs/mangle/rend2d/tests/output/sdl_move_test.out b/libs/mangle/rend2d/tests/output/sdl_move_test.out deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/libs/mangle/rend2d/tests/output/sdl_test.out b/libs/mangle/rend2d/tests/output/sdl_test.out deleted file mode 100644 index 4528e1a98a..0000000000 --- a/libs/mangle/rend2d/tests/output/sdl_test.out +++ /dev/null @@ -1,11 +0,0 @@ -Loading SDL driver. -Creating window. -Current mode: 640x480 -Setting fancy title, cause we like fancy titles. -Loading tile1-blue.png from file. -Loading tile1-yellow.png from memory. -Going bananas. -Taking a breather. -WOW DID YOU SEE THAT!? -Mucking about with the gamma settings -Done. diff --git a/libs/mangle/rend2d/tests/output/sdlgl_move_test.out b/libs/mangle/rend2d/tests/output/sdlgl_move_test.out deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/libs/mangle/rend2d/tests/sdl_move_test.cpp b/libs/mangle/rend2d/tests/sdl_move_test.cpp deleted file mode 100644 index bfbca98fa7..0000000000 --- a/libs/mangle/rend2d/tests/sdl_move_test.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include - -using namespace std; - -#include "../servers/sdl_driver.hpp" - -using namespace Mangle::Rend2D; - -int main() -{ - SDLDriver sdl; - - sdl.setVideoMode(640,480,0,false); - sdl.setWindowTitle("Testing 123"); - Sprite *screen = sdl.getScreen(); - const char* imgName = "tile1-blue.png"; - Sprite *image = sdl.loadImage(imgName); - - for(int frames=0; frames<170; frames++) - { - screen->fill(0); - for(int j=0; j<10; j++) - for(int i=0; i<25; i++) - screen->draw(image, 2*frames+30*j, 20*i); - sdl.update(); - sdl.sleep(10); - } - return 0; -} diff --git a/libs/mangle/rend2d/tests/sdl_test.cpp b/libs/mangle/rend2d/tests/sdl_test.cpp deleted file mode 100644 index 0355112e61..0000000000 --- a/libs/mangle/rend2d/tests/sdl_test.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include - -using namespace std; - -#include "../servers/sdl_driver.hpp" - -using namespace Mangle::Rend2D; - -int main() -{ - cout << "Loading SDL driver.\n"; - SDLDriver sdl; - - cout << "Creating window.\n"; - sdl.setVideoMode(640,480); - cout << "Current mode: " << sdl.width() << "x" << sdl.height() << endl; - - cout << "Setting fancy title, cause we like fancy titles.\n"; - sdl.setWindowTitle("Chief executive window"); - - // Display surface - Sprite *screen = sdl.getScreen(); - - const char* imgName = "tile1-blue.png"; - cout << "Loading " << imgName << " from file.\n"; - Sprite *image = sdl.loadImage(imgName); - - const char* imgName2 = "tile1-yellow.png"; - cout << "Loading " << imgName2 << " from memory.\n"; - Sprite *image2; - { - // This is hard-coded for file sizes below 500 bytes, so obviously - // you shouldn't mess with the image files. - ifstream file(imgName2, ios::binary); - char buf[500]; - file.read(buf, 500); - int size = file.gcount(); - image2 = sdl.loadImage(buf, size); - } - - cout << "Going bananas.\n"; - for(int i=1; i<20; i++) - screen->draw(image, 30*i, 20*i); - - cout << "Taking a breather.\n"; - sdl.update(); - for(int i=1; i<20; i++) - screen->draw(image2, 30*(20-i), 20*i); - sdl.sleep(800); - sdl.update(); - cout << "WOW DID YOU SEE THAT!?\n"; - sdl.sleep(800); - - cout << "Mucking about with the gamma settings\n"; - sdl.setGamma(2.0, 0.1, 0.8); - sdl.sleep(100); - sdl.setGamma(0.6, 2.1, 2.1); - sdl.sleep(100); - sdl.setGamma(1.6); - sdl.sleep(100); - - cout << "Done.\n"; - return 0; -} diff --git a/libs/mangle/rend2d/tests/sdlgl_move_test.cpp b/libs/mangle/rend2d/tests/sdlgl_move_test.cpp deleted file mode 100644 index b769ee837d..0000000000 --- a/libs/mangle/rend2d/tests/sdlgl_move_test.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include - -using namespace std; - -#include "../servers/sdl_gl_driver.hpp" - -using namespace Mangle::Rend2D; - -int main() -{ - SDLGLDriver sdl; - - sdl.setVideoMode(640,480,0,false); - sdl.setWindowTitle("Testing 123"); - Sprite *screen = sdl.getScreen(); - const char* imgName = "tile1-blue.png"; - Sprite *image = sdl.loadImage(imgName); - - for(int frames=0; frames<170; frames++) - { - screen->fill(0); - for(int j=0; j<10; j++) - for(int i=0; i<25; i++) - screen->draw(image, 2*frames+30*j, 20*i); - sdl.update(); - sdl.sleep(5); - } - - return 0; -} diff --git a/libs/mangle/rend2d/tests/test.sh b/libs/mangle/rend2d/tests/test.sh deleted file mode 100755 index 2d07708adc..0000000000 --- a/libs/mangle/rend2d/tests/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -make || exit - -mkdir -p output - -PROGS=*_test - -for a in $PROGS; do - if [ -f "output/$a.out" ]; then - echo "Running $a:" - ./$a | diff output/$a.out - - else - echo "Creating $a.out" - ./$a > "output/$a.out" - git add "output/$a.out" - fi -done diff --git a/libs/mangle/rend2d/tests/tile1-blue.png b/libs/mangle/rend2d/tests/tile1-blue.png deleted file mode 100644 index 066e6f8eb932e81d0ab391bc16a3b9e0fb063d7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgg4HgO>q^~cs6azG);64!_l=ltB< z)VvY~=c3falGGH1^30M91$R&1fbd2>aiF3cPZ!4!jq}MON!e)%tQ!)zB&BycNVusb zWdv<3H_ml_#P;5c#V!`t_IO|Ks0DIaVrPLG@TdWn2J z|KvBBkM=tLPH;V9awjzAOkRtr!tdY5?;n;qvu%fo@uXRM82JyY98GH4whCx3gQu&X J%Q~loCIFm~TL1t6 diff --git a/libs/mangle/rend2d/tests/tile1-yellow.png b/libs/mangle/rend2d/tests/tile1-yellow.png deleted file mode 100644 index 2aaf9015d313d5cd00915ffbb1df2f0262ea2116..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z8pdfpRr>`sfEjDo>Htxq)+3JBpk|nMYCC>S|xv6<249-QVi6yBi3gww4 z84B*6z5(HleBwYwd7dtgAsXkCb@CIF6<9YUa9LVOJ4;MTQp*TBJ?;FFMT!6KZ8mro zxg%{yo!B3~hU~W2DM<=~6P7+aaBe@#)q{84nt0F0&sDtcsm}jj_2%Ux)y>}yc6DU# wSS@pBCTF(8x9=wp*PqRuS(uroVp_?}Ab(0lNY${m9cVLyr>mdKI;Vst0EqflTmS$7 diff --git a/libs/mangle/testall.sh b/libs/mangle/testall.sh deleted file mode 100755 index b93fee2159..0000000000 --- a/libs/mangle/testall.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -function run() -{ - echo "TESTING $1" - cd "$1/tests/" - ./test.sh - cd ../../ -} - -run stream -run vfs -run sound -run input -run rend2d -run . diff --git a/libs/mangle/tests/.gitignore b/libs/mangle/tests/.gitignore deleted file mode 100644 index 8144904045..0000000000 --- a/libs/mangle/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*_test diff --git a/libs/mangle/tests/Makefile b/libs/mangle/tests/Makefile deleted file mode 100644 index d912c0784b..0000000000 --- a/libs/mangle/tests/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -GCC=g++ -I../ - -all: ogrevfs_audiere_openal_test - -I_OGRE=$(shell pkg-config --cflags OGRE) -L_OGRE=$(shell pkg-config --libs OGRE) -L_OPENAL=$(shell pkg-config --libs openal) -L_AUDIERE=-laudiere - -ogrevfs_audiere_openal_test: ogrevfs_audiere_openal_test.cpp ../vfs/servers/ogre_vfs.cpp ../sound/sources/audiere_source.cpp ../sound/outputs/openal_out.cpp ../stream/clients/audiere_file.cpp - $(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE) $(L_OPENAL) $(L_AUDIERE) - -clean: - rm *_test diff --git a/libs/mangle/tests/ogrevfs_audiere_openal_test.cpp b/libs/mangle/tests/ogrevfs_audiere_openal_test.cpp deleted file mode 100644 index 4936538c55..0000000000 --- a/libs/mangle/tests/ogrevfs_audiere_openal_test.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - This example combines: - - - the OGRE VFS system (to read from zip) - - Audiere (for decoding sound data) - - OpenAL (for sound playback) - - */ - -#include "sound/filters/openal_audiere.hpp" -#include "vfs/servers/ogre_vfs.hpp" -#include -#include - -using namespace Ogre; -using namespace Mangle; -using namespace std; - -int main() -{ - // Disable Ogre logging - new LogManager; - Log *log = LogManager::getSingleton().createLog(""); - log->setDebugOutputEnabled(false); - - // Set up Root - Root *root = new Root("","",""); - - // Add zip file with a sound in it - root->addResourceLocation("sound.zip", "Zip", "General"); - - // Ogre file system - VFS::OgreVFS vfs; - - // The main sound system - Sound::OpenAL_Audiere_Factory mg; - Sound::SoundPtr snd = mg.load(vfs.open("owl.ogg")); - - cout << "Playing 'owl.ogg' from 'sound.zip'\n"; - snd->play(); - - while(snd->isPlaying()) - { - usleep(10000); - if(mg.needsUpdate) mg.update(); - } - - return 0; -} diff --git a/libs/mangle/tests/output/ogrevfs_audiere_openal_test.out b/libs/mangle/tests/output/ogrevfs_audiere_openal_test.out deleted file mode 100644 index 28ea8a71b7..0000000000 --- a/libs/mangle/tests/output/ogrevfs_audiere_openal_test.out +++ /dev/null @@ -1 +0,0 @@ -Playing 'owl.ogg' from 'sound.zip' diff --git a/libs/mangle/tests/sound.zip b/libs/mangle/tests/sound.zip deleted file mode 100644 index fd32b35299859ba2d92e702db719367110d8b994..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18159 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 diff --git a/libs/mangle/tests/test.sh b/libs/mangle/tests/test.sh deleted file mode 100755 index 2d07708adc..0000000000 --- a/libs/mangle/tests/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -make || exit - -mkdir -p output - -PROGS=*_test - -for a in $PROGS; do - if [ -f "output/$a.out" ]; then - echo "Running $a:" - ./$a | diff output/$a.out - - else - echo "Creating $a.out" - ./$a > "output/$a.out" - git add "output/$a.out" - fi -done diff --git a/libs/mangle/tools/shared_ptr.hpp b/libs/mangle/tools/shared_ptr.hpp deleted file mode 100644 index 3d073fc24f..0000000000 --- a/libs/mangle/tools/shared_ptr.hpp +++ /dev/null @@ -1,3 +0,0 @@ -// This file should include whatever it needs to define the boost/tr1 -// shared_ptr<> and weak_ptr<> templates. -#include diff --git a/libs/openengine/gui/events.cpp b/libs/openengine/gui/events.cpp deleted file mode 100644 index 35b01158bc..0000000000 --- a/libs/openengine/gui/events.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include -#include - -#include "events.hpp" - -using namespace OIS; -using namespace OEngine::GUI; - -EventInjector::EventInjector(MyGUI::Gui *g) - : gui(g), enabled(true) - , mMouseX(0) - , mMouseY(0) -{ - assert(gui); - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - mMouseX = viewSize.width/2; - mMouseY = viewSize.height/2; -} - -void EventInjector::event(Type type, int index, const void *p) -{ - if(!enabled) return; - - if(type & EV_Keyboard) - { - KeyEvent *key = (KeyEvent*)p; - MyGUI::KeyCode code = MyGUI::KeyCode::Enum(key->key); - if(type == EV_KeyDown) - { - /* - This is just a first approximation. Apparently, OIS is - unable to provide reliable unicode characters on all - platforms. At least that's what I surmise from the amount - of workaround that the MyGUI folks have put in place for - this. See Common/Input/OIS/InputManager.cpp in the MyGUI - sources for details. - - If the work they have done there is indeed necessary (I - haven't tested that it is, although I have had dubious - experinces with OIS events in the past), then we should - probably adapt all that code here. Or even better, - directly into the OIS input manager in Mangle. - - Note that all this only affects the 'text' field, and - should thus only affect typed text in input boxes (which - is still pretty significant.) - */ - MyGUI::Char text = (MyGUI::Char)key->text; - MyGUI::InputManager::getInstance().injectKeyPress(code,text); - } - else - { - MyGUI::InputManager::getInstance().injectKeyRelease(code); - } - } - else if(type & EV_Mouse) - { - MouseEvent *mouse = (MouseEvent*)p; - MyGUI::MouseButton id = MyGUI::MouseButton::Enum(index); - - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - - // Update mouse position - mMouseX += mouse->state.X.rel; - mMouseY += mouse->state.Y.rel; - mMouseX = std::max(0, std::min(mMouseX, viewSize.width)); - mMouseY = std::max(0, std::min(mMouseY, viewSize.height)); - - if(type == EV_MouseDown) - MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, id); - else if(type == EV_MouseUp) - MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, id); - else - MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, mouse->state.Z.abs); - } -} diff --git a/libs/openengine/gui/events.hpp b/libs/openengine/gui/events.hpp deleted file mode 100644 index 10c5309bc3..0000000000 --- a/libs/openengine/gui/events.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef OENGINE_MYGUI_EVENTS_H -#define OENGINE_MYGUI_EVENTS_H - -#include - -namespace MyGUI -{ - class Gui; -} - -namespace OEngine { -namespace GUI -{ - /** Event handler that injects OIS events into MyGUI - */ - class EventInjector : public Mangle::Input::Event - { - MyGUI::Gui *gui; - - int mMouseX; - int mMouseY; - - public: - bool enabled; - - EventInjector(MyGUI::Gui *g); - void event(Type type, int index, const void *p); - }; - - typedef boost::shared_ptr EventInjectorPtr; -}} -#endif diff --git a/libs/openengine/ogre/mouselook.cpp b/libs/openengine/ogre/mouselook.cpp deleted file mode 100644 index 841bab603a..0000000000 --- a/libs/openengine/ogre/mouselook.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "mouselook.hpp" - -#include -#include -#include - -using namespace OIS; -using namespace Ogre; -using namespace OEngine::Render; - -void MouseLookEvent::event(Type type, int index, const void *p) -{ - if(type != EV_MouseMove || camera == NULL) return; - - MouseEvent *arg = (MouseEvent*)(p); - - float x = arg->state.X.rel * sensX; - float y = arg->state.Y.rel * sensY; - - camera->getParentSceneNode()->getParentSceneNode()->yaw(Degree(-x)); - camera->getParentSceneNode()->pitch(Degree(-y)); - if(flipProt) - { - // The camera before pitching - /*Quaternion nopitch = camera->getParentSceneNode()->getOrientation(); - - camera->getParentSceneNode()->pitch(Degree(-y)); - - // Apply some failsafe measures against the camera flipping - // upside down. Is the camera close to pointing straight up or - // down? - if(Ogre::Vector3(camera->getParentSceneNode()->getOrientation()*Ogre::Vector3::UNIT_Y)[1] <= 0.1) - // If so, undo the last pitch - camera->getParentSceneNode()->setOrientation(nopitch);*/ - //camera->getU - - // Angle of rotation around the X-axis. - float pitchAngle = (2 * Ogre::Degree(Ogre::Math::ACos(camera->getParentSceneNode()->getOrientation().w)).valueDegrees()); - - // Just to determine the sign of the angle we pick up above, the - // value itself does not interest us. - float pitchAngleSign = camera->getParentSceneNode()->getOrientation().x; - - // Limit the pitch between -90 degress and +90 degrees, Quake3-style. - if (pitchAngle > 90.0f) - { - if (pitchAngleSign > 0) - // Set orientation to 90 degrees on X-axis. - camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f), - Ogre::Math::Sqrt(0.5f), 0, 0)); - else if (pitchAngleSign < 0) - // Sets orientation to -90 degrees on X-axis. - camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f), - -Ogre::Math::Sqrt(0.5f), 0, 0)); - } - } -} diff --git a/libs/openengine/ogre/mouselook.hpp b/libs/openengine/ogre/mouselook.hpp deleted file mode 100644 index 6e09ff4a10..0000000000 --- a/libs/openengine/ogre/mouselook.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef OENGINE_OGRE_MOUSELOOK_H -#define OENGINE_OGRE_MOUSELOOK_H - -/* - A mouse-look class for Ogre. Accepts input events from Mangle::Input - and translates them. - - You can adjust the mouse sensibility and switch to a different - camera. The mouselook class also has an optional wrap protection - that keeps the camera from flipping upside down. - - You can disable the mouse looker at any time by calling - setCamera(NULL), and reenable it by setting the camera back. - - NOTE: The current implementation will ONLY work for native OIS - events. - */ - -#include - -namespace Ogre -{ - class Camera; -} - -namespace OEngine { -namespace Render -{ - class MouseLookEvent : public Mangle::Input::Event - { - Ogre::Camera* camera; - float sensX, sensY; // Mouse sensibility - bool flipProt; // Flip protection - - public: - MouseLookEvent(Ogre::Camera *cam=NULL, - float sX=0.2, float sY=0.2, - bool prot=true) - : camera(cam) - , sensX(sX) - , sensY(sY) - , flipProt(prot) - {} - - void setCamera(Ogre::Camera *cam) - { camera = cam; } - void setSens(float sX, float sY) - { sensX = sX; sensY = sY; } - void setProt(bool p) { flipProt = p; } - - void event(Type type, int index, const void *p); - }; - - typedef boost::shared_ptr MouseLookEventPtr; -}} -#endif From 97c45455fd762196a583b6da361caf313b314124 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 21:21:23 +0200 Subject: [PATCH 16/27] fix WindowManager destruction --- apps/openmw/mwbase/environment.cpp | 3 +-- apps/openmw/mwgui/windowmanagerimp.cpp | 3 ++- libs/openengine/gui/manager.cpp | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 8d786db910..9aaa5af85a 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -146,8 +146,7 @@ void MWBase::Environment::cleanup() delete mScriptManager; mScriptManager = 0; -/// \todo Re-enable (currently throwing an exception) -// delete mWindowManager; + delete mWindowManager; mWindowManager = 0; delete mWorld; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 2a6f7ec9b3..183c435fdf 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -159,7 +159,6 @@ WindowManager::WindowManager( WindowManager::~WindowManager() { - delete mGuiManager; delete mConsole; delete mMessageBoxManager; delete mHud; @@ -182,6 +181,8 @@ WindowManager::~WindowManager() delete mSpellWindow; cleanupGarbage(); + + delete mGuiManager; } void WindowManager::cleanupGarbage() diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index 9c6ca37eb1..58929ba8b5 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -36,6 +36,7 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool void MyGUIManager::shutdown() { + mGui->shutdown (); delete mGui; if(mPlatform) { From 90f1d9c2f2989011878c83852a0779e85f0fdcca Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 21:25:13 +0200 Subject: [PATCH 17/27] OSX suggestion by corristo --- apps/openmw/mwinput/inputmanagerimp.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index d1c400ceab..e1eacae055 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1,5 +1,9 @@ #include "inputmanagerimp.hpp" +#if defined(__APPLE__) && !defined(__LP64__) +#include +#endif + #include #include @@ -76,12 +80,12 @@ namespace MWInput #endif } - #ifdef __APPLE_CC__ +#if defined(__APPLE__) && !defined(__LP64__) // Give the application window focus to receive input events ProcessSerialNumber psn = { 0, kCurrentProcess }; TransformProcessType(&psn, kProcessTransformToForegroundApplication); SetFrontProcess(&psn); - #endif +#endif mInputManager = OIS::InputManager::createInputSystem( pl ); From 976ad7a301bff286a65a119cc7d7532e61daea25 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 22:59:58 +0200 Subject: [PATCH 18/27] key defaults specified in the code now, required in order to keep the configuration files valid across multiple versions of openmw --- CMakeLists.txt | 3 - apps/openmw/engine.cpp | 20 +---- apps/openmw/mwinput/inputmanagerimp.cpp | 67 ++++++++++++---- apps/openmw/mwinput/inputmanagerimp.hpp | 5 +- files/input-default.xml | 100 ------------------------ 5 files changed, 58 insertions(+), 137 deletions(-) delete mode 100644 files/input-default.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index 79e33f1815..68fdee1e8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -285,9 +285,6 @@ endif (APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg "${OpenMW_BINARY_DIR}/settings-default.cfg") -configure_file(${OpenMW_SOURCE_DIR}/files/input-default.xml - "${OpenMW_BINARY_DIR}/input-default.xml") - configure_file(${OpenMW_SOURCE_DIR}/files/transparency-overrides.cfg "${OpenMW_BINARY_DIR}/transparency-overrides.cfg") diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 128d975534..edd0dead11 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -271,22 +271,10 @@ void OMW::Engine::go() settings.loadUser(globaldefault); // Get the path for the keybinder xml file - std::string keybinderDefault; - - // load user settings if they exist, otherwise just load the default settings as user settings - const std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); - const std::string keybinderDefaultLocal = (mCfgMgr.getLocalPath() / "input-default.xml").string(); - const std::string keybinderDefaultGlobal = (mCfgMgr.getGlobalPath() / "input-default.xml").string(); - - bool keybinderUserExists = boost::filesystem::exists(keybinderUser); - - if (boost::filesystem::exists(keybinderDefaultLocal)) - keybinderDefault = keybinderDefaultLocal; - else if (boost::filesystem::exists(keybinderDefaultGlobal)) - keybinderDefault = keybinderDefaultGlobal; - else - throw std::runtime_error ("No default input settings found! Make sure the file \"input-default.xml\" was properly installed."); + std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); + if (!boost::filesystem::exists(keybinderUser)) + keybinderUser = ""; mFpsLevel = settings.getInt("fps", "HUD"); @@ -386,7 +374,7 @@ void OMW::Engine::go() mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, MWBase::Environment::get().getWorld()->getPlayer(), - *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderDefault, keybinderUser, keybinderUserExists)); + *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser)); std::cout << "\nPress Q/ESC or close window to exit.\n"; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index e1eacae055..0318995389 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -7,8 +7,7 @@ #include #include -#include -#include +#include #include @@ -32,8 +31,7 @@ namespace MWInput MWBase::WindowManager &windows, bool debug, OMW::Engine& engine, - const std::string& defaultFile, - const std::string& userFile, bool userFileExists) + const std::string& userFile) : mOgre(ogre) , mPlayer(player) , mWindows(windows) @@ -43,6 +41,7 @@ namespace MWInput , mMouseY(ogre.getWindow()->getHeight ()/2.f) , mUserFile(userFile) , mDragDrop(false) + , mGuiCursorEnabled(false) { Ogre::RenderWindow* window = ogre.getWindow (); size_t windowHnd; @@ -102,15 +101,9 @@ namespace MWInput MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, mMouse->getMouseState ().Z.abs); - std::string configFile; - if (userFileExists) - configFile = userFile; - else - configFile = defaultFile; + mInputCtrl = new ICS::InputControlSystem(userFile, true, NULL, NULL, A_LAST); - std::cout << "Loading input configuration: " << configFile << std::endl; - - mInputCtrl = new ICS::InputControlSystem(configFile, true, NULL, NULL, A_LAST); + loadKeyDefaults(); for (int i = 0; i < A_LAST; ++i) { @@ -339,8 +332,7 @@ namespace MWInput { mInputCtrl->keyReleased (arg); - if (mGuiCursorEnabled) - MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(arg.key)); + MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(arg.key)); return true; } @@ -359,8 +351,7 @@ namespace MWInput { mInputCtrl->mouseReleased (arg, id); - if (mGuiCursorEnabled) - MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); return true; } @@ -449,11 +440,15 @@ namespace MWInput { bool gameMode = !mWindows.isGuiMode(); + std::cout << "gameMode: " << gameMode << std::endl; + // Toggle between game mode and inventory mode if(gameMode) mWindows.pushGuiMode(MWGui::GM_Inventory); else if(mWindows.getMode() == MWGui::GM_Inventory) mWindows.popGuiMode(); + else + std::cout << "toggleInv didnt do anything!!!" << std::endl; // .. but don't touch any other mode. } @@ -516,4 +511,44 @@ namespace MWInput return mInputCtrl->getChannel (id)->getValue () == 1; } + void InputManager::loadKeyDefaults () + { + // using hardcoded key defaults is inevitable, if we want the configuration files to stay valid + // across different versions of OpenMW (in the case where another input action is added) + std::map defaultKeyBindings; + + defaultKeyBindings[A_Activate] = OIS::KC_SPACE; + defaultKeyBindings[A_MoveBackward] = OIS::KC_S; + defaultKeyBindings[A_MoveForward] = OIS::KC_W; + defaultKeyBindings[A_MoveLeft] = OIS::KC_A; + defaultKeyBindings[A_MoveRight] = OIS::KC_D; + defaultKeyBindings[A_ToggleWeapon] = OIS::KC_F; + defaultKeyBindings[A_ToggleSpell] = OIS::KC_R; + defaultKeyBindings[A_Console] = OIS::KC_F1; + defaultKeyBindings[A_Crouch] = OIS::KC_LCONTROL; + defaultKeyBindings[A_AutoMove] = OIS::KC_Q; + defaultKeyBindings[A_Jump] = OIS::KC_E; + defaultKeyBindings[A_Journal] = OIS::KC_J; + defaultKeyBindings[A_Rest] = OIS::KC_T; + defaultKeyBindings[A_GameMenu] = OIS::KC_ESCAPE; + + std::map defaultMouseButtonBindings; + defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; + + for (int i = 0; i < A_LAST; ++i) + { + if (mInputCtrl->getChannel(i)->getControlsCount () == 0) + { + ICS::Control* control1 = new ICS::Control(boost::lexical_cast(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX); + mInputCtrl->addControl(control1); + control1->attachChannel(mInputCtrl->getChannel(i), ICS::Channel::DIRECT); + + if (defaultKeyBindings.find(i) != defaultKeyBindings.end()) + mInputCtrl->addKeyBinding(control1, static_cast(defaultKeyBindings[i]), ICS::Control::INCREASE); + else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end()) + mInputCtrl->addMouseButtonBinding (control1, defaultMouseButtonBindings[i], ICS::Control::INCREASE); + } + } + } + } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index ba42327eeb..9fc77aeeb4 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -61,8 +61,7 @@ namespace MWInput MWBase::WindowManager &_windows, bool debug, OMW::Engine& engine, - const std::string& defaultFile, - const std::string& userFile, bool userFileExists); + const std::string& userFile); virtual ~InputManager(); @@ -130,6 +129,8 @@ namespace MWInput bool actionIsActive (int id); + void loadKeyDefaults(); + private: enum Actions { diff --git a/files/input-default.xml b/files/input-default.xml deleted file mode 100644 index e44b3455ac..0000000000 --- a/files/input-default.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From bc6e4feedc6d68958ea72a7689ca73f86b913db1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Aug 2012 01:26:15 +0200 Subject: [PATCH 19/27] hotkey window first version --- apps/openmw/engine.cpp | 6 +- apps/openmw/mwbase/inputmanager.hpp | 6 + apps/openmw/mwbase/windowmanager.hpp | 4 + apps/openmw/mwgui/hud.cpp | 3 + apps/openmw/mwgui/hud.hpp | 2 + apps/openmw/mwgui/settingswindow.cpp | 51 +++++++ apps/openmw/mwgui/settingswindow.hpp | 9 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 24 ++++ apps/openmw/mwgui/windowmanagerimp.hpp | 6 + apps/openmw/mwinput/inputmanagerimp.cpp | 158 ++++++++++++++++++++-- apps/openmw/mwinput/inputmanagerimp.hpp | 36 ++++- files/mygui/openmw_settings_window.layout | 7 + 12 files changed, 291 insertions(+), 21 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index edd0dead11..0bfd97c5dc 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -272,9 +272,7 @@ void OMW::Engine::go() // Get the path for the keybinder xml file std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); - - if (!boost::filesystem::exists(keybinderUser)) - keybinderUser = ""; + bool keybinderUserExists = boost::filesystem::exists(keybinderUser); mFpsLevel = settings.getInt("fps", "HUD"); @@ -374,7 +372,7 @@ void OMW::Engine::go() mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, MWBase::Environment::get().getWorld()->getPlayer(), - *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser)); + *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser, keybinderUserExists)); std::cout << "\nPress Q/ESC or close window to exit.\n"; diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 5d73025a7d..bf1d8e45f6 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -31,6 +31,12 @@ namespace MWBase virtual void setDragDrop(bool dragDrop) = 0; virtual void toggleControlSwitch (const std::string& sw, bool value) = 0; + + virtual std::string getActionDescription (int action) = 0; + virtual std::string getActionBindingName (int action) = 0; + virtual std::vector getActionSorting () = 0; + virtual int getNumActions() = 0; + virtual void enableDetectingBindingMode (int action) = 0; }; } diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 931353a900..fde965256c 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -177,6 +177,10 @@ namespace MWBase virtual void unsetSelectedSpell() = 0; virtual void unsetSelectedWeapon() = 0; + virtual void disallowMouse() = 0; + virtual void allowMouse() = 0; + virtual void notifyInputActionBound() = 0; + virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0; ///< Hides dialog and schedules dialog to be deleted. diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index bdbb316b05..92dc4e495f 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -237,6 +237,9 @@ void HUD::setBottomRightVisibility(bool effectBoxVisible, bool minimapBoxVisible void HUD::onWorldClicked(MyGUI::Widget* _sender) { + if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) + return; + if (mDragAndDrop->mIsOnDragAndDrop) { // drop item into the gameworld diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index 485c788fc3..baa350a10c 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -48,6 +48,8 @@ namespace MWGui MyGUI::TextBox* mCellNameBox; MyGUI::TextBox* mWeaponSpellBox; + MyGUI::Widget* mDummy; + MyGUI::WidgetPtr fpsbox; MyGUI::TextBox* fpscounter; MyGUI::TextBox* trianglecounter; diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index f6597a64ef..477ffe0e24 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -115,6 +115,7 @@ namespace MWGui getWidget(mMiscShadows, "MiscShadows"); getWidget(mShadowsDebug, "ShadowsDebug"); getWidget(mUnderwaterButton, "UnderwaterButton"); + getWidget(mControlsBox, "ControlsBox"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mUnderwaterButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); @@ -511,4 +512,54 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->processChangedSettings(changed); MWBase::Environment::get().getInputManager()->processChangedSettings(changed); } + + void SettingsWindow::updateControlsBox() + { + while (mControlsBox->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mControlsBox->getChildAt(0)); + + std::vector actions = MWBase::Environment::get().getInputManager()->getActionSorting (); + + const int h = 18; + const int w = mControlsBox->getWidth() - 34; + int curH = 6; + for (std::vector::const_iterator it = actions.begin(); it != actions.end(); ++it) + { + std::string desc = MWBase::Environment::get().getInputManager()->getActionDescription (*it); + if (desc == "") + continue; + + std::string binding = MWBase::Environment::get().getInputManager()->getActionBindingName (*it); + + MyGUI::TextBox* leftText = mControlsBox->createWidget("SandText", MyGUI::IntCoord(0,curH,w,h), MyGUI::Align::Default); + leftText->setCaptionWithReplacing(desc); + + MyGUI::Button* rightText = mControlsBox->createWidget("SandTextButton", MyGUI::IntCoord(0,curH,w,h), MyGUI::Align::Default); + rightText->setCaptionWithReplacing(binding); + rightText->setTextAlign (MyGUI::Align::Right); + rightText->setUserData(*it); // save the action id for callbacks + rightText->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onRebindAction); + curH += h; + } + + mControlsBox->setCanvasSize (mControlsBox->getWidth(), std::max(curH, mControlsBox->getHeight())); + } + + void SettingsWindow::onRebindAction(MyGUI::Widget* _sender) + { + int actionId = *_sender->getUserData(); + + static_cast(_sender)->setCaptionWithReplacing("#{sNone}"); + + MWBase::Environment::get().getWindowManager ()->messageBox ("#{sControlsMenu3}", std::vector()); + MWBase::Environment::get().getWindowManager ()->disallowMouse(); + + MWBase::Environment::get().getInputManager ()->enableDetectingBindingMode (actionId); + + } + + void SettingsWindow::open() + { + updateControlsBox (); + } } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index ca11b6f9c2..61614da014 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -15,6 +15,10 @@ namespace MWGui public: SettingsWindow(MWBase::WindowManager& parWindowManager); + virtual void open(); + + void updateControlsBox(); + private: static int const sFovMin = 30; static int const sFovMax = 140; @@ -60,6 +64,9 @@ namespace MWGui MyGUI::ScrollBar* mFootstepsVolumeSlider; MyGUI::ScrollBar* mMusicVolumeSlider; + // controls + MyGUI::ScrollView* mControlsBox; + void onOkButtonClicked(MyGUI::Widget* _sender); void onFpsToggled(MyGUI::Widget* _sender); void onTextureFilteringToggled(MyGUI::Widget* _sender); @@ -72,6 +79,8 @@ namespace MWGui void onShadersToggled(MyGUI::Widget* _sender); void onShadowTextureSize(MyGUI::Widget* _sender); + void onRebindAction(MyGUI::Widget* _sender); + void apply(); }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 183c435fdf..676eb2046e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -134,6 +134,8 @@ WindowManager::WindowManager( mAlchemyWindow = new AlchemyWindow(*this); mSpellWindow = new SpellWindow(*this); + mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); + // The HUD is always on mHud->setVisible(true); @@ -230,11 +232,16 @@ void WindowManager::updateVisible() bool gameMode = !isGuiMode(); + mInputBlocker->setVisible (gameMode); + if (gameMode) mToolTips->enterGameMode(); else mToolTips->enterGuiMode(); + if (gameMode) + MyGUI::InputManager::getInstance ().setKeyFocusWidget (NULL); + setMinimapVisibility((mAllowed & GW_Map) && !mMap->pinned()); setWeaponVisibility((mAllowed & GW_Inventory) && !mInventoryWindow->pinned()); setSpellVisibility((mAllowed & GW_Magic) && !mSpellWindow->pinned()); @@ -646,6 +653,7 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector mScrollWindow->center(); mBookWindow->center(); mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); + mInputBlocker->setSize(MyGUI::IntSize(x,y)); } } @@ -823,3 +831,19 @@ WindowManager::SkillList WindowManager::getPlayerMajorSkills() { return mPlayerMajorSkills; } + +void WindowManager::disallowMouse() +{ + mInputBlocker->setVisible (true); +} + +void WindowManager::allowMouse() +{ + mInputBlocker->setVisible (!isGuiMode ()); +} + +void WindowManager::notifyInputActionBound () +{ + mSettingsWindow->updateControlsBox (); + allowMouse(); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index eaa6a16832..3913055942 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -157,6 +157,10 @@ namespace MWGui virtual void unsetSelectedSpell(); virtual void unsetSelectedWeapon(); + virtual void disallowMouse(); + virtual void allowMouse(); + virtual void notifyInputActionBound(); + virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. virtual void messageBox (const std::string& message, const std::vector& buttons); @@ -208,6 +212,8 @@ namespace MWGui CharacterCreation* mCharGen; + MyGUI::Widget* mInputBlocker; + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. // Various stats about player as needed by window manager ESM::Class mPlayerClass; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 0318995389..09ed50944c 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -14,8 +14,6 @@ #include #include -#include - #include #include "../engine.hpp" @@ -31,7 +29,7 @@ namespace MWInput MWBase::WindowManager &windows, bool debug, OMW::Engine& engine, - const std::string& userFile) + const std::string& userFile, bool userFileExists) : mOgre(ogre) , mPlayer(player) , mWindows(windows) @@ -101,11 +99,12 @@ namespace MWInput MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, mMouse->getMouseState ().Z.abs); - mInputCtrl = new ICS::InputControlSystem(userFile, true, NULL, NULL, A_LAST); + std::string file = userFileExists ? userFile : ""; + mInputCtrl = new ICS::InputControlSystem(file, true, this, NULL, A_Last); loadKeyDefaults(); - for (int i = 0; i < A_LAST; ++i) + for (int i = 0; i < A_Last; ++i) { mInputCtrl->getChannel (i)->addListener (this); } @@ -322,8 +321,7 @@ namespace MWInput { mInputCtrl->keyPressed (arg); - if (mGuiCursorEnabled) - MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), arg.text); + MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), arg.text); return true; } @@ -341,8 +339,7 @@ namespace MWInput { mInputCtrl->mousePressed (arg, id); - if (mGuiCursorEnabled) - MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); return true; } @@ -440,15 +437,11 @@ namespace MWInput { bool gameMode = !mWindows.isGuiMode(); - std::cout << "gameMode: " << gameMode << std::endl; - // Toggle between game mode and inventory mode if(gameMode) mWindows.pushGuiMode(MWGui::GM_Inventory); else if(mWindows.getMode() == MWGui::GM_Inventory) mWindows.popGuiMode(); - else - std::cout << "toggleInv didnt do anything!!!" << std::endl; // .. but don't touch any other mode. } @@ -535,7 +528,7 @@ namespace MWInput std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; - for (int i = 0; i < A_LAST; ++i) + for (int i = 0; i < A_Last; ++i) { if (mInputCtrl->getChannel(i)->getControlsCount () == 0) { @@ -551,4 +544,141 @@ namespace MWInput } } + std::string InputManager::getActionDescription (int action) + { + std::map descriptions; + + descriptions[A_Activate] = "sActivate"; + descriptions[A_MoveBackward] = "sBack"; + descriptions[A_MoveForward] = "sForward"; + descriptions[A_MoveLeft] = "sLeft"; + descriptions[A_MoveRight] = "sRight"; + descriptions[A_ToggleWeapon] = "sReady_Weapon"; + descriptions[A_ToggleSpell] = "sReady_Magic"; + descriptions[A_Console] = "sConsoleTitle"; + descriptions[A_Crouch] = "sCrouch_Sneak"; + descriptions[A_AutoMove] = "sAuto_Run"; + descriptions[A_Jump] = "sJump"; + descriptions[A_Journal] = "sJournal"; + descriptions[A_Rest] = "sRestKey"; + descriptions[A_Inventory] = "sInventory"; + + if (action == A_GameMenu) + return "Menu"; // not configurable in morrowind so no GMST + + if (descriptions[action] == "") + return ""; // not configurable + + return "#{" + descriptions[action] + "}"; + } + + std::string InputManager::getActionBindingName (int action) + { + if (mInputCtrl->getChannel (action)->getControlsCount () == 0) + return "#{sNone}"; + + ICS::Control* c = mInputCtrl->getChannel (action)->getAttachedControls ().front().control; + + if (mInputCtrl->getKeyBinding (c, ICS::Control::INCREASE) != OIS::KC_UNASSIGNED) + return mInputCtrl->keyCodeToString (mInputCtrl->getKeyBinding (c, ICS::Control::INCREASE)); + else if (mInputCtrl->getMouseButtonBinding (c, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) + return "#{sMouse} " + boost::lexical_cast(mInputCtrl->getMouseButtonBinding (c, ICS::Control::INCREASE)); + else + return "#{sNone}"; + } + + std::vector InputManager::getActionSorting() + { + std::vector ret; + ret.push_back(A_MoveForward); + ret.push_back(A_MoveBackward); + ret.push_back(A_MoveLeft); + ret.push_back(A_MoveRight); + ret.push_back(A_Crouch); + ret.push_back(A_Activate); + ret.push_back(A_ToggleWeapon); + ret.push_back(A_AutoMove); + ret.push_back(A_Jump); + ret.push_back(A_Inventory); + ret.push_back(A_Journal); + ret.push_back(A_Rest); + ret.push_back(A_Console); + ret.push_back(A_GameMenu); + + return ret; + } + + void InputManager::enableDetectingBindingMode (int action) + { + ICS::Control* c = mInputCtrl->getChannel (action)->getAttachedControls ().front().control; + + mInputCtrl->enableDetectingBindingState (c, ICS::Control::INCREASE); + } + + void InputManager::mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction) + { + // we don't want mouse movement bindings + return; + } + + void InputManager::keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , OIS::KeyCode key, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::keyBindingDetected (ICS, control, key, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , unsigned int button, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::mouseButtonBindingDetected (ICS, control, button, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int axis, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickAxisBindingDetected (ICS, control, deviceId, axis, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, unsigned int button, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickButtonBindingDetected (ICS, control, deviceId, button, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickPOVBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int pov,ICS:: InputControlSystem::POVAxis axis, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickPOVBindingDetected (ICS, control, deviceId, pov, axis, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickSliderBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int slider, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickSliderBindingDetected (ICS, control, deviceId, slider, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::clearAllBindings (ICS::Control* control) + { + // right now we don't really need multiple bindings for the same action, so remove all others first + if (mInputCtrl->getKeyBinding (control, ICS::Control::INCREASE) != OIS::KC_UNASSIGNED) + mInputCtrl->removeKeyBinding (mInputCtrl->getKeyBinding (control, ICS::Control::INCREASE)); + if (mInputCtrl->getMouseButtonBinding (control, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) + mInputCtrl->removeMouseButtonBinding (mInputCtrl->getMouseButtonBinding (control, ICS::Control::INCREASE)); + + /// \todo add joysticks here once they are added + } + } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 9fc77aeeb4..d3d4d13853 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -46,6 +46,7 @@ namespace OIS #include #include +#include namespace MWInput { @@ -53,7 +54,7 @@ namespace MWInput /** * @brief Class that handles all input and key bindings for OpenMW. */ - class InputManager : public MWBase::InputManager, public OIS::KeyListener, public OIS::MouseListener, public ICS::ChannelListener + class InputManager : public MWBase::InputManager, public OIS::KeyListener, public OIS::MouseListener, public ICS::ChannelListener, public ICS::DetectingBindingListener { public: InputManager(OEngine::Render::OgreRenderer &_ogre, @@ -61,7 +62,7 @@ namespace MWInput MWBase::WindowManager &_windows, bool debug, OMW::Engine& engine, - const std::string& userFile); + const std::string& userFile, bool userFileExists); virtual ~InputManager(); @@ -75,6 +76,12 @@ namespace MWInput virtual void toggleControlSwitch (const std::string& sw, bool value); + virtual std::string getActionDescription (int action); + virtual std::string getActionBindingName (int action); + virtual int getNumActions() { return A_Last; } + virtual std::vector getActionSorting (); + virtual void enableDetectingBindingMode (int action); + public: virtual bool keyPressed( const OIS::KeyEvent &arg ); @@ -86,6 +93,29 @@ namespace MWInput virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); + virtual void mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction); + + virtual void keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , OIS::KeyCode key, ICS::Control::ControlChangingDirection direction); + + virtual void mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , unsigned int button, ICS::Control::ControlChangingDirection direction); + + virtual void joystickAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int axis, ICS::Control::ControlChangingDirection direction); + + virtual void joystickButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, unsigned int button, ICS::Control::ControlChangingDirection direction); + + virtual void joystickPOVBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int pov,ICS:: InputControlSystem::POVAxis axis, ICS::Control::ControlChangingDirection direction); + + virtual void joystickSliderBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int slider, ICS::Control::ControlChangingDirection direction); + + void clearAllBindings (ICS::Control* control); + private: OEngine::Render::OgreRenderer &mOgre; MWWorld::Player &mPlayer; @@ -175,7 +205,7 @@ namespace MWInput A_ToggleWeapon, A_ToggleSpell, - A_LAST // Marker for the last item + A_Last // Marker for the last item }; diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index a14606ade9..ee4855f381 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -82,6 +82,13 @@ + + + + + + + From c7b8787c323701253175d86819a0a12f08f6ab42 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Aug 2012 02:55:22 +0200 Subject: [PATCH 20/27] "reset to defaults" button, invert y axis button --- apps/openmw/mwbase/inputmanager.hpp | 1 + apps/openmw/mwgui/settingswindow.cpp | 38 +++++++++++++++++++++-- apps/openmw/mwgui/settingswindow.hpp | 5 +++ apps/openmw/mwinput/inputmanagerimp.cpp | 37 +++++++++++++++++----- apps/openmw/mwinput/inputmanagerimp.hpp | 5 ++- files/mygui/openmw_settings_window.layout | 9 +++++- files/settings-default.cfg | 5 +++ 7 files changed, 88 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index bf1d8e45f6..4c73c72b98 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -37,6 +37,7 @@ namespace MWBase virtual std::vector getActionSorting () = 0; virtual int getNumActions() = 0; virtual void enableDetectingBindingMode (int action) = 0; + virtual void resetToDefaultBindings() = 0; }; } diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 477ffe0e24..5bcec9f70f 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -116,7 +116,10 @@ namespace MWGui getWidget(mShadowsDebug, "ShadowsDebug"); getWidget(mUnderwaterButton, "UnderwaterButton"); getWidget(mControlsBox, "ControlsBox"); + getWidget(mResetControlsButton, "ResetControlsButton"); + getWidget(mInvertYButton, "InvertYButton"); + mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mUnderwaterButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled); @@ -155,6 +158,9 @@ namespace MWGui mOkButton->setCoord(mMainWidget->getWidth()-16-okSize, mOkButton->getTop(), okSize, mOkButton->getHeight()); + mResetControlsButton->setSize (mResetControlsButton->getTextSize ().width + 24, mResetControlsButton->getHeight()); + mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings); + // fill resolution list Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem(); Ogre::StringVector videoModes = rs->getConfigOptions()["Video Mode"].possibleValues; @@ -220,6 +226,8 @@ namespace MWGui mMiscShadows->setCaptionWithReplacing(Settings::Manager::getBool("misc shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); mShadowsDebug->setCaptionWithReplacing(Settings::Manager::getBool("debug", "Shadows") ? "#{sOn}" : "#{sOff}"); + mInvertYButton->setCaptionWithReplacing(Settings::Manager::getBool("invert y axis", "Input") ? "#{sOn}" : "#{sOff}"); + std::string shaders; if (!Settings::Manager::getBool("shaders", "Objects")) shaders = "off"; @@ -383,6 +391,8 @@ namespace MWGui Settings::Manager::setBool("misc shadows", "Shadows", newState); else if (_sender == mShadowsDebug) Settings::Manager::setBool("debug", "Shadows", newState); + else if (_sender == mInvertYButton) + Settings::Manager::setBool("invert y axis", "Input", newState); apply(); } @@ -521,8 +531,8 @@ namespace MWGui std::vector actions = MWBase::Environment::get().getInputManager()->getActionSorting (); const int h = 18; - const int w = mControlsBox->getWidth() - 34; - int curH = 6; + const int w = mControlsBox->getWidth() - 28; + int curH = 0; for (std::vector::const_iterator it = actions.begin(); it != actions.end(); ++it) { std::string desc = MWBase::Environment::get().getInputManager()->getActionDescription (*it); @@ -539,6 +549,7 @@ namespace MWGui rightText->setTextAlign (MyGUI::Align::Right); rightText->setUserData(*it); // save the action id for callbacks rightText->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onRebindAction); + rightText->eventMouseWheel += MyGUI::newDelegate(this, &SettingsWindow::onInputTabMouseWheel); curH += h; } @@ -558,6 +569,29 @@ namespace MWGui } + void SettingsWindow::onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mControlsBox->getViewOffset().top + _rel*0.3 > 0) + mControlsBox->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mControlsBox->setViewOffset(MyGUI::IntPoint(0, mControlsBox->getViewOffset().top + _rel*0.3)); + } + + void SettingsWindow::onResetDefaultBindings(MyGUI::Widget* _sender) + { + ConfirmationDialog* dialog = mWindowManager.getConfirmationDialog(); + dialog->open("#{sNotifyMessage66}"); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindingsAccept); + dialog->eventCancelClicked.clear(); + } + + void SettingsWindow::onResetDefaultBindingsAccept() + { + MWBase::Environment::get().getInputManager ()->resetToDefaultBindings (); + updateControlsBox (); + } + void SettingsWindow::open() { updateControlsBox (); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 61614da014..aad37826e9 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -66,6 +66,8 @@ namespace MWGui // controls MyGUI::ScrollView* mControlsBox; + MyGUI::Button* mResetControlsButton; + MyGUI::Button* mInvertYButton; void onOkButtonClicked(MyGUI::Widget* _sender); void onFpsToggled(MyGUI::Widget* _sender); @@ -80,6 +82,9 @@ namespace MWGui void onShadowTextureSize(MyGUI::Widget* _sender); void onRebindAction(MyGUI::Widget* _sender); + void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel); + void onResetDefaultBindings(MyGUI::Widget* _sender); + void onResetDefaultBindingsAccept (); void apply(); }; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 09ed50944c..9f757c6867 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -40,6 +40,7 @@ namespace MWInput , mUserFile(userFile) , mDragDrop(false) , mGuiCursorEnabled(false) + , mInvertY (Settings::Manager::getBool("invert y axis", "Input")) { Ogre::RenderWindow* window = ogre.getWindow (); size_t windowHnd; @@ -280,6 +281,9 @@ namespace MWInput { if (it->first == "Video" && (it->second == "resolution x" || it->second == "resolution y")) changeRes = true; + + if (it->first == "Input" && it->second == "invert y axis") + mInvertY = Settings::Manager::getBool("invert y axis", "Input"); } if (changeRes) @@ -374,7 +378,7 @@ namespace MWInput if (mMouseLookEnabled) { float x = arg.state.X.rel * 0.2; - float y = arg.state.Y.rel * 0.2; + float y = arg.state.Y.rel * 0.2 * (mInvertY ? -1 : 1); MWBase::World *world = MWBase::Environment::get().getWorld(); world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); @@ -504,7 +508,7 @@ namespace MWInput return mInputCtrl->getChannel (id)->getValue () == 1; } - void InputManager::loadKeyDefaults () + void InputManager::loadKeyDefaults (bool force) { // using hardcoded key defaults is inevitable, if we want the configuration files to stay valid // across different versions of OpenMW (in the case where another input action is added) @@ -530,16 +534,27 @@ namespace MWInput for (int i = 0; i < A_Last; ++i) { - if (mInputCtrl->getChannel(i)->getControlsCount () == 0) + ICS::Control* control; + bool controlExists = mInputCtrl->getChannel(i)->getControlsCount () != 0; + if (!controlExists) { - ICS::Control* control1 = new ICS::Control(boost::lexical_cast(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX); - mInputCtrl->addControl(control1); - control1->attachChannel(mInputCtrl->getChannel(i), ICS::Channel::DIRECT); + control = new ICS::Control(boost::lexical_cast(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX); + mInputCtrl->addControl(control); + control->attachChannel(mInputCtrl->getChannel(i), ICS::Channel::DIRECT); + } + else + { + control = mInputCtrl->getChannel(i)->getAttachedControls ().front().control; + } + + if (!controlExists || force) + { + clearAllBindings (control); if (defaultKeyBindings.find(i) != defaultKeyBindings.end()) - mInputCtrl->addKeyBinding(control1, static_cast(defaultKeyBindings[i]), ICS::Control::INCREASE); + mInputCtrl->addKeyBinding(control, static_cast(defaultKeyBindings[i]), ICS::Control::INCREASE); else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end()) - mInputCtrl->addMouseButtonBinding (control1, defaultMouseButtonBindings[i], ICS::Control::INCREASE); + mInputCtrl->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE); } } } @@ -597,6 +612,7 @@ namespace MWInput ret.push_back(A_Crouch); ret.push_back(A_Activate); ret.push_back(A_ToggleWeapon); + ret.push_back(A_ToggleSpell); ret.push_back(A_AutoMove); ret.push_back(A_Jump); ret.push_back(A_Inventory); @@ -681,4 +697,9 @@ namespace MWInput /// \todo add joysticks here once they are added } + void InputManager::resetToDefaultBindings() + { + loadKeyDefaults(true); + } + } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index d3d4d13853..42c90a73a5 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -81,6 +81,7 @@ namespace MWInput virtual int getNumActions() { return A_Last; } virtual std::vector getActionSorting (); virtual void enableDetectingBindingMode (int action); + virtual void resetToDefaultBindings(); public: @@ -132,6 +133,8 @@ namespace MWInput bool mDragDrop; + bool mInvertY; + bool mMouseLookEnabled; bool mGuiCursorEnabled; @@ -159,7 +162,7 @@ namespace MWInput bool actionIsActive (int id); - void loadKeyDefaults(); + void loadKeyDefaults(bool force = false); private: enum Actions diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index ee4855f381..5908c16f91 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -84,9 +84,16 @@ - + + + + + + + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index a723e307c8..fd0c6e7af1 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -144,3 +144,8 @@ sfx volume = 1.0 music volume = 0.4 footsteps volume = 0.6 voice volume = 1.0 + + +[Input] + +invert y axis = false From 67577c61924860e1c67ccb303ef17149e42f9f39 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Aug 2012 18:48:50 +0200 Subject: [PATCH 21/27] UI cursor & camera sensitivity sliders --- apps/openmw/mwgui/settingswindow.cpp | 14 +++++++ apps/openmw/mwgui/settingswindow.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 23 +++++++---- apps/openmw/mwinput/inputmanagerimp.hpp | 5 +++ files/mygui/openmw_settings_window.layout | 48 +++++++++++++++++++---- files/settings-default.cfg | 8 ++++ 6 files changed, 85 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 5bcec9f70f..4039136a17 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -118,6 +118,8 @@ namespace MWGui getWidget(mControlsBox, "ControlsBox"); getWidget(mResetControlsButton, "ResetControlsButton"); getWidget(mInvertYButton, "InvertYButton"); + getWidget(mUISensitivitySlider, "UISensitivitySlider"); + getWidget(mCameraSensitivitySlider, "CameraSensitivitySlider"); mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); @@ -226,6 +228,14 @@ namespace MWGui mMiscShadows->setCaptionWithReplacing(Settings::Manager::getBool("misc shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); mShadowsDebug->setCaptionWithReplacing(Settings::Manager::getBool("debug", "Shadows") ? "#{sOn}" : "#{sOff}"); + float cameraSens = (Settings::Manager::getFloat("camera sensitivity", "Input")-0.2)/(5.0-0.2); + mCameraSensitivitySlider->setScrollPosition (cameraSens * (mCameraSensitivitySlider->getScrollRange()-1)); + float uiSens = (Settings::Manager::getFloat("ui sensitivity", "Input")-0.2)/(5.0-0.2); + mUISensitivitySlider->setScrollPosition (uiSens * (mUISensitivitySlider->getScrollRange()-1)); + mCameraSensitivitySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); + mUISensitivitySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); + + mInvertYButton->setCaptionWithReplacing(Settings::Manager::getBool("invert y axis", "Input") ? "#{sOn}" : "#{sOff}"); std::string shaders; @@ -510,6 +520,10 @@ namespace MWGui Settings::Manager::setFloat("footsteps volume", "Sound", val); else if (scroller == mMusicVolumeSlider) Settings::Manager::setFloat("music volume", "Sound", val); + else if (scroller == mUISensitivitySlider) + Settings::Manager::setFloat("ui sensitivity", "Input", (1-val) * 0.2 + val * 5.f); + else if (scroller == mCameraSensitivitySlider) + Settings::Manager::setFloat("camera sensitivity", "Input", (1-val) * 0.2 + val * 5.f); apply(); } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index aad37826e9..159d52bdcc 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -68,6 +68,8 @@ namespace MWGui MyGUI::ScrollView* mControlsBox; MyGUI::Button* mResetControlsButton; MyGUI::Button* mInvertYButton; + MyGUI::ScrollBar* mUISensitivitySlider; + MyGUI::ScrollBar* mCameraSensitivitySlider; void onOkButtonClicked(MyGUI::Widget* _sender); void onFpsToggled(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 9f757c6867..febe528c0d 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -41,6 +41,10 @@ namespace MWInput , mDragDrop(false) , mGuiCursorEnabled(false) , mInvertY (Settings::Manager::getBool("invert y axis", "Input")) + , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) + , mUISensitivity (Settings::Manager::getFloat("ui sensitivity", "Input")) + , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) + , mUIYMultiplier (Settings::Manager::getFloat("ui y multiplier", "Input")) { Ogre::RenderWindow* window = ogre.getWindow (); size_t windowHnd; @@ -284,6 +288,13 @@ namespace MWInput if (it->first == "Input" && it->second == "invert y axis") mInvertY = Settings::Manager::getBool("invert y axis", "Input"); + + if (it->first == "Input" && it->second == "camera sensitivity") + mCameraSensitivity = Settings::Manager::getFloat("camera sensitivity", "Input"); + + if (it->first == "Input" && it->second == "ui sensitivity") + mUISensitivity = Settings::Manager::getFloat("ui sensitivity", "Input"); + } if (changeRes) @@ -367,8 +378,8 @@ namespace MWInput // We keep track of our own mouse position, so that moving the mouse while in // game mode does not move the position of the GUI cursor - mMouseX += arg.state.X.rel; - mMouseY += arg.state.Y.rel; + mMouseX += arg.state.X.rel * mUISensitivity; + mMouseY += arg.state.Y.rel * mUISensitivity * mUIYMultiplier; mMouseX = std::max(0, std::min(mMouseX, viewSize.width)); mMouseY = std::max(0, std::min(mMouseY, viewSize.height)); @@ -377,8 +388,8 @@ namespace MWInput if (mMouseLookEnabled) { - float x = arg.state.X.rel * 0.2; - float y = arg.state.Y.rel * 0.2 * (mInvertY ? -1 : 1); + float x = arg.state.X.rel * mCameraSensitivity * 0.2; + float y = arg.state.Y.rel * mCameraSensitivity * 0.2 * (mInvertY ? -1 : 1) * mUIYMultiplier; MWBase::World *world = MWBase::Environment::get().getWorld(); world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); @@ -578,9 +589,6 @@ namespace MWInput descriptions[A_Rest] = "sRestKey"; descriptions[A_Inventory] = "sInventory"; - if (action == A_GameMenu) - return "Menu"; // not configurable in morrowind so no GMST - if (descriptions[action] == "") return ""; // not configurable @@ -619,7 +627,6 @@ namespace MWInput ret.push_back(A_Journal); ret.push_back(A_Rest); ret.push_back(A_Console); - ret.push_back(A_GameMenu); return ret; } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 42c90a73a5..79e5a70fc9 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -135,6 +135,11 @@ namespace MWInput bool mInvertY; + float mCameraSensitivity; + float mUISensitivity; + float mCameraYMultiplier; + float mUIYMultiplier; + bool mMouseLookEnabled; bool mGuiCursorEnabled; diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 5908c16f91..ad906d2a13 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -83,19 +83,53 @@ - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -135,11 +169,11 @@ - + - + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index fd0c6e7af1..effd9f3da5 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -149,3 +149,11 @@ voice volume = 1.0 [Input] invert y axis = false + +camera sensitivity = 1.0 + +ui sensitivity = 1.0 + +camera y multiplier = 1.0 + +ui y multiplier = 1.0 From b373d0ec7bafb013032a05d88e8f183d90348242 Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Mon, 13 Aug 2012 13:53:54 -0400 Subject: [PATCH 22/27] Correct struct to class in forward declarations Fixes http://bugs.openmw.org/issues/362 --- apps/openmw/mwworld/class.hpp | 2 +- apps/openmw/mwworld/inventorystore.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 6ab92319da..3c3b0e34be 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -21,7 +21,7 @@ namespace MWRender namespace MWMechanics { - struct CreatureStats; + class CreatureStats; class NpcStats; struct Movement; } diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 45b3cab268..e55eca0ae7 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -7,7 +7,7 @@ namespace MWMechanics { - struct NpcStats; + class NpcStats; } namespace MWWorld From f9efd543e438cc832ec5b695cc901d3a1f43ab96 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Aug 2012 21:33:53 +0200 Subject: [PATCH 23/27] use float for the mouse position tracking, should be more accurate for sensitivity multipliers != 1 --- apps/openmw/mwinput/inputmanagerimp.cpp | 10 +++++----- apps/openmw/mwinput/inputmanagerimp.hpp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index febe528c0d..0c0bb74e6f 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -378,12 +378,12 @@ namespace MWInput // We keep track of our own mouse position, so that moving the mouse while in // game mode does not move the position of the GUI cursor - mMouseX += arg.state.X.rel * mUISensitivity; - mMouseY += arg.state.Y.rel * mUISensitivity * mUIYMultiplier; - mMouseX = std::max(0, std::min(mMouseX, viewSize.width)); - mMouseY = std::max(0, std::min(mMouseY, viewSize.height)); + mMouseX += float(arg.state.X.rel) * mUISensitivity; + mMouseY += float(arg.state.Y.rel) * mUISensitivity * mUIYMultiplier; + mMouseX = std::max(0.f, std::min(mMouseX, float(viewSize.width))); + mMouseY = std::max(0.f, std::min(mMouseY, float(viewSize.height))); - MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, arg.state.Z.abs); + MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), arg.state.Z.abs); } if (mMouseLookEnabled) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 79e5a70fc9..c8b48e727f 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -143,8 +143,8 @@ namespace MWInput bool mMouseLookEnabled; bool mGuiCursorEnabled; - int mMouseX; - int mMouseY; + float mMouseX; + float mMouseY; std::map mControlSwitch; From b1bd2aab7423c6a7d46867bd1c7bb70655bd6c6c Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Tue, 14 Aug 2012 02:24:46 +0200 Subject: [PATCH 24/27] Added font creators and a formulae researcher to credits.txt --- credits.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/credits.txt b/credits.txt index a0be853926..ca0ff7323b 100644 --- a/credits.txt +++ b/credits.txt @@ -61,6 +61,7 @@ spyboot - German News Writer Formula Research: +Epsilon fragonard Greendogo HiPhish @@ -101,3 +102,12 @@ used as the application icon and project logo. Thanks to Kevin Ryan, for creating the icon used for the Data Files tab of the OpenMW Launcher. + +Thanks to Georg Duffner, +for the open-source EB Garamond fontface. + +Thanks to Dongle, +for his Daedric fontface, see Daedric Font License.txt for his license terms. + +Thanks to Bitstream Inc. +for their Bitstream Vera fontface, see Bitstream Vera License.txt for their license terms. From 9501e133f5c840836c4a79881ddba6efc140d7ce Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Tue, 14 Aug 2012 03:23:57 +0200 Subject: [PATCH 25/27] Updated the EB Garamond font to a higher-resolution version --- files/launcher.qss | 9 ++++++--- files/mygui/EBGaramond-Regular.ttf | Bin 231904 -> 405476 bytes 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/files/launcher.qss b/files/launcher.qss index 8be235f71b..1eb056d4d3 100644 --- a/files/launcher.qss +++ b/files/launcher.qss @@ -22,7 +22,8 @@ stop:0.9 rgba(0, 0, 0, 55), stop:1 rgba(0, 0, 0, 100)); - font: 26pt "EB Garamond"; + font-size: 26pt; + font-family: "EB Garamond", "EB Garamond 08"; color: black; border-right: 1px solid rgba(0, 0, 0, 155); @@ -54,7 +55,8 @@ } #ProfileLabel { - font: 18pt "EB Garamond"; + font-size: 18pt; + font-family: "EB Garamond", "EB Garamond 08"; } #ProfilesComboBox { @@ -82,7 +84,8 @@ padding-top: 3px; padding-left: 4px; - font: 12pt "EB Garamond"; + font-size: 12pt; + font-family: "EB Garamond", "EB Garamond 08"; } #ProfilesComboBox::drop-down { diff --git a/files/mygui/EBGaramond-Regular.ttf b/files/mygui/EBGaramond-Regular.ttf index dde4869030022bf4d25ac5ea4403795ebd864fd1..3f6f6c191d9d47870201c2979caf8acbb41a7e63 100644 GIT binary patch literal 405476 zcmeFa2bfhw(l_3H^1XA@+&noaCg))?Fu*VjX&7LD0Z9TPIfD`vR20R;BBHCvqN^;6 zEFvNzqGC>%b6^lrL6=ojgggIV*SRRW>%L*%_y2s)_dMTRx=weWJ{_v7tE#)J8^#%9 zZUBWP_UhHzZ^DunG8p4Ih>h*t(6iSgd}l7gjd0t04;(PqKV|jHj7dK-=FRLqcu4nR ze_$D7fkei7of$AVzvz-%@7&DT=vaI|X8g3VGj|--lM%ig1)Lr~e{Q1O-FJp*X$SB= zZ{o~J(|XqD-Hq_Kj9D(7GY|BHkNd8QvGH@6a=dBs#bYNJCF@r) zwOM>$HW?A#R#!U0s1Hk>JZF7+#BF>#{D0<;GMVq=kHNp6?}z_!{y6*x_+j{8mzFRtEtQrsSz4w%$hfjo*~uj3 zFUnuw-=#bZ{~qO0`1dIX;eS&3n#sxue#u{S$DJ(f1RC7rmv)5!ocI-`7@c;ZA8yRmf53QtAH&6HuM|<*i134Eq&qt zyS(ISV`pE=ywfh7b}5UVbIIb1St6AJfVv4BGd2)3Ai|@m> zDdkBeQl(Tc^^%&U7O71dElrT7NHeAR(&f@h>1yeE=@x0DbeFVM+9vIk_DK7sr=;hl zSEaY452VkeFQk*wchVV|$(n4F-EvTlms8~~a)DeXSIgbyM!CP-DvyxI$P?wM@@#pb zyi~pt6tzQb2h0#qt|I@_1m_TJruY`}9~F?d6C6!3l#-7Se!1?~7|G0`oML_?m@goI zpZu!`?jyKBK)E~l?;_ZXAmzy~Q=UfeZO|I=?Q_B}ZMwjpLjH{+o+k*uG)8^%ykBmj z^xf(0x8%2y|0uyL2(}WeA-Ip=y8=pPp7a%eiQMxntxS$ng{^8urUNA9# zVV2IR9A%dj@DTZH=lD|n`t)jOH1aG96 z%Tx_mDx{c!1RVrz1kE}ND6trD(i)7>2G*BFv;J%pOJ+;)D`M;LD+WJZ4?Zj8Ln+&V zUm4qtUpadYzY2C7zpf-DDtS4tVpaSeeh;fx?pGdQ-IVRhcGg3A5YnIla^NA>6B6JN z)~K9Teq>F`8RZ=7r*tZvY!Gq%7M<&y4b~N1VXe9a5_yO*5WIf|YH1_s!$|JfA#t8$ z4w5x)$dQ+sM|od)pCu?CDW9-JjGB*R|W^!%e%t~9iu;7E8GaUF>n*%rozpJTL`xl?n<~d zaO>dK!)=0t&cW}4+YYx2ZZF&cxI=I+!X1Wt2ks-d4!AGjpmXrkaAzT|B{%~P5?u1a zMZ+b+rNiaG6~R@&)xtHvHDQd0#7JQ?Xf(wTG{@#N@^2#8E+E@Z{&MousAL2=jU%2w zZx<09O={KM1!P~7zlvZ6#q3i*Mf|lC(?rnxDvHKF zLH4$?2WfsJ_^om?%E~4|=sQ2vVB=1P}iT14pR6X4}nHUw|3*g1Ql-Y_D9z zB>52JT6b>cF7D^C=&xj+$@6##uY|vz_u|dGg}3q1d;-E#_)I<@&&%Of@~ipvc-{iH z5$-Oyt$Z8biRT`;{cumgJrDOPe~aOde*pIx|AL>y^E-Y9JuRY{1Ui;%lAHWNDPBsI zx=5g3sSK_ft~(s)SL!c;dZiJRZj3ZhnkvnfK)2A{m~^GIMgq-B>)|%RZIrQy3G^v}KBd!eXJtt?WQXjPqvb?k()@yudG=p_E?>$aYd4s4RX#4rV@P&83e;8g`0O-av4ifYJ(r)KY0J#S@0P`IeyR z=NEhxe%m6_%O?KKwwt(JLit>n{zAWr+b5~qu9Sm={8TPzP1GueU@JjtHR}$Ulg83n z2DHsA)`ew5m(OGQtbi3l?i54PltIE&u&%_ft0A8nSTEL_H9|%;vlcd#4P$MPQR^U^ zo`aNm5c>2FkSAwV4!L2~BXpZ?*B!c3cj=M3NB8PJJ)j5mC_P$_(PQ;EJqgkU+ADv7 zzo@*Tyvh`W#JCCK8INZw`a6XgfD-keA!^T?1jIZ;Xu69r-oc9@Et$}nQU4M^h8i85n(A84b8fa4Y^k@hnrT@G9Vd@{J-!F$9z-26^VAr32U?wDCF25q?045`Rkh z6lHSMMa(=oWax*0X@Wb+&&qj#9jMO)_B?x)y~RFYpRq3><-TKQprmVW_&UCxZ{nNzeSACL#rHzi9^xnv@iO7(_YpMxuaN&5f-wS0R8MaDt0+E6K)I3L=2FaQf++;E2~HsR8o?a`N@o5~ zkiTmuG&#OS_!YA)RG%<@y!6%k1gR{^Y$=r@QM3U=u_%mf=t>x`#moXuQ^TC#E`2ca1#gLm2Hb)a6!p(<+{=ink zT@42vf`4ea9iQF!9kB;&?6Z12nIa@9D;#IFmMS5KEc2#ID1#%hixLbT|iD_ zhaI69qCHMyA>6+vJ(#eG($D(!TXta%yXb8xL34Cuk^e&ar#>~mk__m-3iMYSS}kfW z{`R2m=W8y)zj8Pc659L)d=45>(2N#Zy!hj(<`X_}Kfp3GEHrxY_xHRIVd92({=yH# z>7WgXKa)4&35|%t#3RKEp08p>2oURRJWaVl;E`ad_7eQ1n$UTdXyD)cB*Bn|^(mgF ziIgeYD}YyM^N?n@hP5>Inzj=$F?+PD;IGlv!vBPJ7Vt8WC*P!93;%wC zXH~K0^_*G=cv2Pe%B?Iwnis+l`S3VJ)TdZT9bQ9l7QrP{+j#_sij_Iuq=*v75OgR< zk=qf<^N5&7vTs!{;Bg_z*l7X(6$Y4}@8l0-uJ)AjjPk7VobrP5lJYXeVVXSRN8TY{t&6d^*_ag29{D)5OWo0nU<70uGhYQ|yQW8e`uo4#ZF$*v-l^#7t8`C#a)U>QJ_^3)cxJh;>3P ziFHEW3>^o5SSL)xL#z$Th*7aVr$gtkK(mOzsIxN%b3((2gfesg+kqsT((jD-BDOXXqr2k^wJpV!M{`cB>e#LY;t(fkn71QaoVj2=F zro0!em`?vc(0=cK(Ek4p9)15y`S|~r@(KJ0`J|06nlqk#GJfpTx$N`tW9MAVz8*h* z+Dvw8{QQDK&H#(J23X8(fMwi0etuyg500N-RLJ82i+Jkz`4t7c3t%BHm@xH{NxW>r z^cmB5^~Bj@$Mf!!MNAXGSUzy-^m)_xQ0&c2EU}GMg`+AyO`%F?+&v zzIvwcUpMEH=@a=)bEb`-$v4cIgSy=bSj4vgmh$`O%qc41JLb$AH;3<@GjHY`zE5Pt z4-(~<66{7$15`0Otr(XM=Ak$-)8Lpf37AbVk6=DQNH@e65-cKEOt6BWA5fwZEm8?7 z$T7p122m_j&N!_n?V<73oTS>(FNdfi(>M>Iph?SEu*arZ% zv0kzUdjK0Dh407g3qX=vM-7zC+kw#!XGE-WJ zxyaShI;`|>#yrI2nH+0o)MBY2{KRUvsHfBtehxL;FHH!CSJIA#gqcdE86$^N%0uZ5 zmq&!-XNSY{!{O^GtjM>>Zz<7YRw&9*)`Xweho85EpBv4umAk@WA)!S217WF0&x~EHP`GSsM=T4?hovpNGQlUp2#7dEsZc2eKMW3e4&s zjt}E8Yjik%TsVHZ`OJZ=6XoaM6n^e7p9M>)9Hrp;;tl2d%0Dqvm!AngL*}zW3O_YU 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%0AXjnz^wYyL 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 literal 231904 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 T4s-SWr@ZM*DnIDIeBu8Aczgq4 From 4ae0a5c02e1b4721da6041b8c00e2633942320c8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Aug 2012 19:29:00 +0200 Subject: [PATCH 26/27] fix silhouettes around objects above the water --- files/materials/water.shader | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/files/materials/water.shader b/files/materials/water.shader index 08a19ace93..8f46aed344 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -118,7 +118,6 @@ #define WAVE_CHOPPYNESS 0.15 // wave choppyness #define WAVE_SCALE 75 // overall wave scale - #define ABBERATION 0.001 // chromatic abberation amount #define BUMP 1.5 // overall water surface bumpiness #define REFL_BUMP 0.08 // reflection distortion amount #define REFR_BUMP 0.06 // refraction distortion amount @@ -256,11 +255,15 @@ // refraction float3 R = reflect(vVec, normal); + + // check the depth at the refracted coords, and don't do any normal distortion for the refraction if the object to refract + // is actually above the water (objectDepth < waterDepth) + // this solves silhouettes around objects above the water + float refractDepth = shSample(depthMap, screenCoords-(shoreFade * normal.xz*REFR_BUMP)).x * far - depthPassthrough; + float doRefraction = (refractDepth < 0) ? 0.f : 1.f; float3 refraction = float3(0,0,0); - refraction.r = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0).r; - refraction.g = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0-(R.xy*ABBERATION)).g; - refraction.b = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0-(R.xy*ABBERATION*2.0)).b; + refraction.rgb = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP * doRefraction))*1.0).rgb; // brighten up the refraction underwater refraction = (cameraPos.y < 0) ? shSaturate(refraction * 1.5) : refraction; From e97d23e626ca688ed503c31d410cf5543410b762 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 16 Aug 2012 15:42:37 +0200 Subject: [PATCH 27/27] Issue #361: reset skill level in race selection stage --- .../mwmechanics/mechanicsmanagerimp.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7fd0b29c38..2ac146c2f7 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -66,15 +66,18 @@ namespace MWMechanics static_cast (male ? attribute->male : attribute->female)); } - for (int i=0; i<7; ++i) + for (int i=0; i<27; ++i) { - int index = race->data.bonus[i].skill; - - if (index>=0 && index<27) - { - npcStats.getSkill (index).setBase ( - npcStats.getSkill (index).getBase() + race->data.bonus[i].bonus); - } + int bonus = 0; + + for (int i2=0; i2<7; ++i2) + if (race->data.bonus[i2].skill==i) + { + bonus = race->data.bonus[i2].bonus; + break; + } + + npcStats.getSkill (i).setBase (5 + bonus); } for (std::vector::const_iterator iter (race->powers.list.begin());