1 #include "windowmanagerimp.hpp"
2 
3 #include <algorithm>
4 #include <cassert>
5 #include <chrono>
6 #include <thread>
7 
8 #include <osgViewer/Viewer>
9 
10 #include <MyGUI_UString.h>
11 #include <MyGUI_IPointer.h>
12 #include <MyGUI_FactoryManager.h>
13 #include <MyGUI_LanguageManager.h>
14 #include <MyGUI_PointerManager.h>
15 #include <MyGUI_InputManager.h>
16 #include <MyGUI_Gui.h>
17 #include <MyGUI_ClipboardManager.h>
18 #include <MyGUI_WidgetManager.h>
19 
20 // For BT_NO_PROFILE
21 #include <LinearMath/btQuickprof.h>
22 
23 #include <SDL_keyboard.h>
24 #include <SDL_clipboard.h>
25 
26 #include <components/debug/debuglog.hpp>
27 
28 #include <components/sdlutil/sdlcursormanager.hpp>
29 #include <components/sdlutil/sdlvideowrapper.hpp>
30 
31 #include <components/esm/esmreader.hpp>
32 #include <components/esm/esmwriter.hpp>
33 
34 #include <components/fontloader/fontloader.hpp>
35 
36 #include <components/resource/resourcesystem.hpp>
37 #include <components/resource/imagemanager.hpp>
38 
39 #include <components/sceneutil/workqueue.hpp>
40 
41 #include <components/translation/translation.hpp>
42 
43 #include <components/myguiplatform/myguiplatform.hpp>
44 #include <components/myguiplatform/myguirendermanager.hpp>
45 #include <components/myguiplatform/additivelayer.hpp>
46 #include <components/myguiplatform/scalinglayer.hpp>
47 
48 #include <components/vfs/manager.hpp>
49 
50 #include <components/widgets/tags.hpp>
51 
52 #include <components/misc/resourcehelpers.hpp>
53 #include <components/misc/frameratelimiter.hpp>
54 
55 #include "../mwbase/inputmanager.hpp"
56 #include "../mwbase/statemanager.hpp"
57 #include "../mwbase/soundmanager.hpp"
58 #include "../mwbase/world.hpp"
59 
60 #include "../mwrender/vismask.hpp"
61 
62 #include "../mwworld/class.hpp"
63 #include "../mwworld/player.hpp"
64 #include "../mwworld/cellstore.hpp"
65 #include "../mwworld/esmstore.hpp"
66 
67 #include "../mwmechanics/npcstats.hpp"
68 #include "../mwmechanics/actorutil.hpp"
69 
70 #include "../mwrender/localmap.hpp"
71 
72 #include "console.hpp"
73 #include "journalwindow.hpp"
74 #include "journalviewmodel.hpp"
75 #include "charactercreation.hpp"
76 #include "dialogue.hpp"
77 #include "statswindow.hpp"
78 #include "messagebox.hpp"
79 #include "tooltips.hpp"
80 #include "scrollwindow.hpp"
81 #include "bookwindow.hpp"
82 #include "hud.hpp"
83 #include "mainmenu.hpp"
84 #include "countdialog.hpp"
85 #include "tradewindow.hpp"
86 #include "spellbuyingwindow.hpp"
87 #include "travelwindow.hpp"
88 #include "settingswindow.hpp"
89 #include "confirmationdialog.hpp"
90 #include "alchemywindow.hpp"
91 #include "spellwindow.hpp"
92 #include "quickkeysmenu.hpp"
93 #include "loadingscreen.hpp"
94 #include "levelupdialog.hpp"
95 #include "waitdialog.hpp"
96 #include "enchantingdialog.hpp"
97 #include "trainingwindow.hpp"
98 #include "recharge.hpp"
99 #include "exposedwindow.hpp"
100 #include "cursor.hpp"
101 #include "merchantrepair.hpp"
102 #include "repair.hpp"
103 #include "soulgemdialog.hpp"
104 #include "companionwindow.hpp"
105 #include "inventorywindow.hpp"
106 #include "bookpage.hpp"
107 #include "itemview.hpp"
108 #include "videowidget.hpp"
109 #include "backgroundimage.hpp"
110 #include "itemwidget.hpp"
111 #include "screenfader.hpp"
112 #include "debugwindow.hpp"
113 #include "spellview.hpp"
114 #include "draganddrop.hpp"
115 #include "container.hpp"
116 #include "controllers.hpp"
117 #include "jailscreen.hpp"
118 #include "itemchargeview.hpp"
119 #include "keyboardnavigation.hpp"
120 #include "resourceskin.hpp"
121 
122 namespace MWGui
123 {
WindowManager(SDL_Window * window,osgViewer::Viewer * viewer,osg::Group * guiRoot,Resource::ResourceSystem * resourceSystem,SceneUtil::WorkQueue * workQueue,const std::string & logpath,const std::string & resourcePath,bool consoleOnlyScripts,Translation::Storage & translationDataStorage,ToUTF8::FromType encoding,bool exportFonts,const std::string & versionDescription,const std::string & userDataPath)124     WindowManager::WindowManager(
125             SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
126             const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage,
127             ToUTF8::FromType encoding, bool exportFonts, const std::string& versionDescription, const std::string& userDataPath)
128       : mOldUpdateMask(0)
129       , mOldCullMask(0)
130       , mStore(nullptr)
131       , mResourceSystem(resourceSystem)
132       , mWorkQueue(workQueue)
133       , mViewer(viewer)
134       , mConsoleOnlyScripts(consoleOnlyScripts)
135       , mCurrentModals()
136       , mHud(nullptr)
137       , mMap(nullptr)
138       , mLocalMapRender(nullptr)
139       , mToolTips(nullptr)
140       , mStatsWindow(nullptr)
141       , mMessageBoxManager(nullptr)
142       , mConsole(nullptr)
143       , mDialogueWindow(nullptr)
144       , mDragAndDrop(nullptr)
145       , mInventoryWindow(nullptr)
146       , mScrollWindow(nullptr)
147       , mBookWindow(nullptr)
148       , mCountDialog(nullptr)
149       , mTradeWindow(nullptr)
150       , mSettingsWindow(nullptr)
151       , mConfirmationDialog(nullptr)
152       , mSpellWindow(nullptr)
153       , mQuickKeysMenu(nullptr)
154       , mLoadingScreen(nullptr)
155       , mWaitDialog(nullptr)
156       , mSoulgemDialog(nullptr)
157       , mVideoBackground(nullptr)
158       , mVideoWidget(nullptr)
159       , mWerewolfFader(nullptr)
160       , mBlindnessFader(nullptr)
161       , mHitFader(nullptr)
162       , mScreenFader(nullptr)
163       , mDebugWindow(nullptr)
164       , mJailScreen(nullptr)
165       , mTranslationDataStorage (translationDataStorage)
166       , mCharGen(nullptr)
167       , mInputBlocker(nullptr)
168       , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD"))
169       , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI"))
170       , mHitFaderEnabled(Settings::Manager::getBool ("hit fader", "GUI"))
171       , mWerewolfOverlayEnabled(Settings::Manager::getBool ("werewolf overlay", "GUI"))
172       , mHudEnabled(true)
173       , mCursorVisible(true)
174       , mCursorActive(true)
175       , mPlayerBounty(-1)
176       , mGui(nullptr)
177       , mGuiModes()
178       , mCursorManager(nullptr)
179       , mGarbageDialogs()
180       , mShown(GW_ALL)
181       , mForceHidden(GW_None)
182       , mAllowed(GW_ALL)
183       , mRestAllowed(true)
184       , mShowOwned(0)
185       , mEncoding(encoding)
186       , mVersionDescription(versionDescription)
187       , mWindowVisible(true)
188     {
189         mScalingFactor = std::clamp(Settings::Manager::getFloat("scaling factor", "GUI"), 0.5f, 8.f);
190         mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), mScalingFactor);
191         mGuiPlatform->initialise(resourcePath, (boost::filesystem::path(logpath) / "MyGUI.log").generic_string());
192 
193         mGui = new MyGUI::Gui;
194         mGui->initialise("");
195 
196         createTextures();
197 
198         MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag);
199 
200         // Load fonts
201         mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS(), userDataPath, mScalingFactor));
202         mFontLoader->loadBitmapFonts(exportFonts);
203 
204         //Register own widgets with MyGUI
205         MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSkill>("Widget");
206         MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWAttribute>("Widget");
207         MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSpell>("Widget");
208         MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWEffectList>("Widget");
209         MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSpellEffect>("Widget");
210         MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWDynamicStat>("Widget");
211         MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Window>("Widget");
212         MyGUI::FactoryManager::getInstance().registerFactory<VideoWidget>("Widget");
213         MyGUI::FactoryManager::getInstance().registerFactory<BackgroundImage>("Widget");
214         MyGUI::FactoryManager::getInstance().registerFactory<osgMyGUI::AdditiveLayer>("Layer");
215         MyGUI::FactoryManager::getInstance().registerFactory<osgMyGUI::ScalingLayer>("Layer");
216         BookPage::registerMyGUIComponents ();
217         ItemView::registerComponents();
218         ItemChargeView::registerComponents();
219         ItemWidget::registerComponents();
220         SpellView::registerComponents();
221         Gui::registerAllWidgets();
222 
223         MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Controllers::ControllerFollowMouse>("Controller");
224 
225         MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer");
226         MyGUI::FactoryManager::getInstance().registerFactory<AutoSizedResourceSkin>("Resource", "AutoSizedResourceSkin");
227         MyGUI::ResourceManager::getInstance().load("core.xml");
228         WindowManager::loadUserFonts();
229 
230         bool keyboardNav = Settings::Manager::getBool("keyboard navigation", "GUI");
231         mKeyboardNavigation.reset(new KeyboardNavigation());
232         mKeyboardNavigation->setEnabled(keyboardNav);
233         Gui::ImageButton::setDefaultNeedKeyFocus(keyboardNav);
234 
235         mLoadingScreen = new LoadingScreen(mResourceSystem, mViewer);
236         mWindows.push_back(mLoadingScreen);
237 
238         //set up the hardware cursor manager
239         mCursorManager = new SDLUtil::SDLCursorManager();
240 
241         MyGUI::PointerManager::getInstance().eventChangeMousePointer += MyGUI::newDelegate(this, &WindowManager::onCursorChange);
242 
243         MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged);
244 
245         // Create all cursors in advance
246         createCursors();
247         onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer());
248         mCursorManager->setEnabled(true);
249 
250         // hide mygui's pointer
251         MyGUI::PointerManager::getInstance().setVisible(false);
252 
253         mVideoBackground = MyGUI::Gui::getInstance().createWidgetReal<MyGUI::ImageBox>("ImageBox", 0,0,1,1,
254             MyGUI::Align::Default, "InputBlocker");
255         mVideoBackground->setImageTexture("black");
256         mVideoBackground->setVisible(false);
257         mVideoBackground->setNeedMouseFocus(true);
258         mVideoBackground->setNeedKeyFocus(true);
259 
260         mVideoWidget = mVideoBackground->createWidgetReal<VideoWidget>("ImageBox", 0,0,1,1, MyGUI::Align::Default);
261         mVideoWidget->setNeedMouseFocus(true);
262         mVideoWidget->setNeedKeyFocus(true);
263         mVideoWidget->setVFS(resourceSystem->getVFS());
264 
265         // Removes default MyGUI system clipboard implementation, which supports windows only
266         MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear();
267         MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear();
268 
269         MyGUI::ClipboardManager::getInstance().eventClipboardChanged += MyGUI::newDelegate(this, &WindowManager::onClipboardChanged);
270         MyGUI::ClipboardManager::getInstance().eventClipboardRequested += MyGUI::newDelegate(this, &WindowManager::onClipboardRequested);
271 
272         mShowOwned = Settings::Manager::getInt("show owned", "Game");
273 
274         mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer);
275         mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"),
276                                         Settings::Manager::getFloat("contrast", "Video"));
277 
278         mStatsWatcher.reset(new StatsWatcher());
279     }
280 
loadUserFonts()281     void WindowManager::loadUserFonts()
282     {
283         mFontLoader->loadTrueTypeFonts();
284     }
285 
initUI()286     void WindowManager::initUI()
287     {
288         // Get size info from the Gui object
289         int w = MyGUI::RenderManager::getInstance().getViewSize().width;
290         int h = MyGUI::RenderManager::getInstance().getViewSize().height;
291 
292         mTextColours.loadColours();
293 
294         mDragAndDrop = new DragAndDrop();
295 
296         Recharge* recharge = new Recharge();
297         mGuiModeStates[GM_Recharge] = GuiModeState(recharge);
298         mWindows.push_back(recharge);
299 
300         MainMenu* menu = new MainMenu(w, h, mResourceSystem->getVFS(), mVersionDescription);
301         mGuiModeStates[GM_MainMenu] = GuiModeState(menu);
302         mWindows.push_back(menu);
303 
304         mLocalMapRender = new MWRender::LocalMap(mViewer->getSceneData()->asGroup());
305         mMap = new MapWindow(mCustomMarkers, mDragAndDrop, mLocalMapRender, mWorkQueue);
306         mWindows.push_back(mMap);
307         mMap->renderGlobalMap();
308         trackWindow(mMap, "map");
309 
310         mStatsWindow = new StatsWindow(mDragAndDrop);
311         mWindows.push_back(mStatsWindow);
312         trackWindow(mStatsWindow, "stats");
313 
314         mInventoryWindow = new InventoryWindow(mDragAndDrop, mViewer->getSceneData()->asGroup(), mResourceSystem);
315         mWindows.push_back(mInventoryWindow);
316 
317         mSpellWindow = new SpellWindow(mDragAndDrop);
318         mWindows.push_back(mSpellWindow);
319         trackWindow(mSpellWindow, "spells");
320 
321         mGuiModeStates[GM_Inventory] = GuiModeState({mMap, mInventoryWindow, mSpellWindow, mStatsWindow});
322         mGuiModeStates[GM_None] = GuiModeState({mMap, mInventoryWindow, mSpellWindow, mStatsWindow});
323 
324         mTradeWindow = new TradeWindow();
325         mWindows.push_back(mTradeWindow);
326         trackWindow(mTradeWindow, "barter");
327         mGuiModeStates[GM_Barter] = GuiModeState({mInventoryWindow, mTradeWindow});
328 
329         mConsole = new Console(w,h, mConsoleOnlyScripts);
330         mWindows.push_back(mConsole);
331         trackWindow(mConsole, "console");
332 
333         bool questList = mResourceSystem->getVFS()->exists("textures/tx_menubook_options_over.dds");
334         JournalWindow* journal = JournalWindow::create(JournalViewModel::create (), questList, mEncoding);
335         mWindows.push_back(journal);
336         mGuiModeStates[GM_Journal] = GuiModeState(journal);
337         mGuiModeStates[GM_Journal].mCloseSound = "book close";
338         mGuiModeStates[GM_Journal].mOpenSound = "book open";
339 
340         mMessageBoxManager = new MessageBoxManager(mStore->get<ESM::GameSetting>().find("fMessageTimePerChar")->mValue.getFloat());
341 
342         SpellBuyingWindow* spellBuyingWindow = new SpellBuyingWindow();
343         mWindows.push_back(spellBuyingWindow);
344         mGuiModeStates[GM_SpellBuying] = GuiModeState(spellBuyingWindow);
345 
346         TravelWindow* travelWindow = new TravelWindow();
347         mWindows.push_back(travelWindow);
348         mGuiModeStates[GM_Travel] = GuiModeState(travelWindow);
349 
350         mDialogueWindow = new DialogueWindow();
351         mWindows.push_back(mDialogueWindow);
352         trackWindow(mDialogueWindow, "dialogue");
353         mGuiModeStates[GM_Dialogue] = GuiModeState(mDialogueWindow);
354         mTradeWindow->eventTradeDone += MyGUI::newDelegate(mDialogueWindow, &DialogueWindow::onTradeComplete);
355 
356         ContainerWindow* containerWindow = new ContainerWindow(mDragAndDrop);
357         mWindows.push_back(containerWindow);
358         trackWindow(containerWindow, "container");
359         mGuiModeStates[GM_Container] = GuiModeState({containerWindow, mInventoryWindow});
360 
361         mHud = new HUD(mCustomMarkers, mDragAndDrop, mLocalMapRender);
362         mWindows.push_back(mHud);
363 
364         mToolTips = new ToolTips();
365 
366         mScrollWindow = new ScrollWindow();
367         mWindows.push_back(mScrollWindow);
368         mGuiModeStates[GM_Scroll] = GuiModeState(mScrollWindow);
369         mGuiModeStates[GM_Scroll].mOpenSound = "scroll";
370         mGuiModeStates[GM_Scroll].mCloseSound = "scroll";
371 
372         mBookWindow = new BookWindow();
373         mWindows.push_back(mBookWindow);
374         mGuiModeStates[GM_Book] = GuiModeState(mBookWindow);
375         mGuiModeStates[GM_Book].mOpenSound = "book open";
376         mGuiModeStates[GM_Book].mCloseSound = "book close";
377 
378         mCountDialog = new CountDialog();
379         mWindows.push_back(mCountDialog);
380 
381         mSettingsWindow = new SettingsWindow();
382         mWindows.push_back(mSettingsWindow);
383         mGuiModeStates[GM_Settings] = GuiModeState(mSettingsWindow);
384 
385         mConfirmationDialog = new ConfirmationDialog();
386         mWindows.push_back(mConfirmationDialog);
387 
388         AlchemyWindow* alchemyWindow = new AlchemyWindow();
389         mWindows.push_back(alchemyWindow);
390         trackWindow(alchemyWindow, "alchemy");
391         mGuiModeStates[GM_Alchemy] = GuiModeState(alchemyWindow);
392 
393         mQuickKeysMenu = new QuickKeysMenu();
394         mWindows.push_back(mQuickKeysMenu);
395         mGuiModeStates[GM_QuickKeysMenu] = GuiModeState(mQuickKeysMenu);
396 
397         LevelupDialog* levelupDialog = new LevelupDialog();
398         mWindows.push_back(levelupDialog);
399         mGuiModeStates[GM_Levelup] = GuiModeState(levelupDialog);
400 
401         mWaitDialog = new WaitDialog();
402         mWindows.push_back(mWaitDialog);
403         mGuiModeStates[GM_Rest] = GuiModeState({mWaitDialog->getProgressBar(), mWaitDialog});
404 
405         SpellCreationDialog* spellCreationDialog = new SpellCreationDialog();
406         mWindows.push_back(spellCreationDialog);
407         mGuiModeStates[GM_SpellCreation] = GuiModeState(spellCreationDialog);
408 
409         EnchantingDialog* enchantingDialog = new EnchantingDialog();
410         mWindows.push_back(enchantingDialog);
411         mGuiModeStates[GM_Enchanting] = GuiModeState(enchantingDialog);
412 
413         TrainingWindow* trainingWindow = new TrainingWindow();
414         mWindows.push_back(trainingWindow);
415         mGuiModeStates[GM_Training] = GuiModeState({trainingWindow->getProgressBar(), trainingWindow});
416 
417         MerchantRepair* merchantRepair = new MerchantRepair();
418         mWindows.push_back(merchantRepair);
419         mGuiModeStates[GM_MerchantRepair] = GuiModeState(merchantRepair);
420 
421         Repair* repair = new Repair();
422         mWindows.push_back(repair);
423         mGuiModeStates[GM_Repair] = GuiModeState(repair);
424 
425         mSoulgemDialog = new SoulgemDialog(mMessageBoxManager);
426 
427         CompanionWindow* companionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager);
428         mWindows.push_back(companionWindow);
429         trackWindow(companionWindow, "companion");
430         mGuiModeStates[GM_Companion] = GuiModeState({mInventoryWindow, companionWindow});
431 
432         mJailScreen = new JailScreen();
433         mWindows.push_back(mJailScreen);
434         mGuiModeStates[GM_Jail] = GuiModeState(mJailScreen);
435 
436         std::string werewolfFaderTex = "textures\\werewolfoverlay.dds";
437         if (mResourceSystem->getVFS()->exists(werewolfFaderTex))
438         {
439             mWerewolfFader = new ScreenFader(werewolfFaderTex);
440             mWindows.push_back(mWerewolfFader);
441         }
442         mBlindnessFader = new ScreenFader("black");
443         mWindows.push_back(mBlindnessFader);
444 
445         // fall back to player_hit_01.dds if bm_player_hit_01.dds is not available
446         std::string hitFaderTexture = "textures\\bm_player_hit_01.dds";
447         const std::string hitFaderLayout = "openmw_screen_fader_hit.layout";
448         MyGUI::FloatCoord hitFaderCoord (0,0,1,1);
449         if(!mResourceSystem->getVFS()->exists(hitFaderTexture))
450         {
451             hitFaderTexture = "textures\\player_hit_01.dds";
452             hitFaderCoord = MyGUI::FloatCoord(0.2, 0.25, 0.6, 0.5);
453         }
454         mHitFader = new ScreenFader(hitFaderTexture, hitFaderLayout, hitFaderCoord);
455         mWindows.push_back(mHitFader);
456 
457         mScreenFader = new ScreenFader("black");
458         mWindows.push_back(mScreenFader);
459 
460         mDebugWindow = new DebugWindow();
461         mWindows.push_back(mDebugWindow);
462 
463         mInputBlocker = MyGUI::Gui::getInstance().createWidget<MyGUI::Widget>("",0,0,w,h,MyGUI::Align::Stretch,"InputBlocker");
464 
465         mHud->setVisible(true);
466 
467         mCharGen = new CharacterCreation(mViewer->getSceneData()->asGroup(), mResourceSystem);
468 
469         updatePinnedWindows();
470 
471         // Set up visibility
472         updateVisible();
473 
474         mStatsWatcher->addListener(mHud);
475         mStatsWatcher->addListener(mStatsWindow);
476         mStatsWatcher->addListener(mCharGen);
477     }
478 
getFontHeight() const479     int WindowManager::getFontHeight() const
480     {
481         return mFontLoader->getFontHeight();
482     }
483 
setNewGame(bool newgame)484     void WindowManager::setNewGame(bool newgame)
485     {
486         if (newgame)
487         {
488             disallowAll();
489 
490             mStatsWatcher->removeListener(mCharGen);
491             delete mCharGen;
492             mCharGen = new CharacterCreation(mViewer->getSceneData()->asGroup(), mResourceSystem);
493             mStatsWatcher->addListener(mCharGen);
494         }
495         else
496             allow(GW_ALL);
497 
498         mStatsWatcher->forceUpdate();
499     }
500 
~WindowManager()501     WindowManager::~WindowManager()
502     {
503         try
504         {
505             mStatsWatcher.reset();
506 
507             MyGUI::LanguageManager::getInstance().eventRequestTag.clear();
508             MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear();
509             MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear();
510             MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear();
511             MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear();
512 
513             for (WindowBase* window : mWindows)
514                 delete window;
515             mWindows.clear();
516 
517             delete mMessageBoxManager;
518             delete mLocalMapRender;
519             delete mCharGen;
520             delete mDragAndDrop;
521             delete mSoulgemDialog;
522             delete mCursorManager;
523             delete mToolTips;
524 
525             mKeyboardNavigation.reset();
526 
527             cleanupGarbage();
528 
529             mFontLoader.reset();
530 
531             mGui->shutdown();
532             delete mGui;
533 
534             mGuiPlatform->shutdown();
535             delete mGuiPlatform;
536             delete mVideoWrapper;
537         }
538         catch(const MyGUI::Exception& e)
539         {
540             Log(Debug::Error) << "Error in the destructor: " << e.what();
541         }
542     }
543 
setStore(const MWWorld::ESMStore & store)544     void WindowManager::setStore(const MWWorld::ESMStore &store)
545     {
546         mStore = &store;
547     }
548 
cleanupGarbage()549     void WindowManager::cleanupGarbage()
550     {
551         // Delete any dialogs which are no longer in use
552         if (!mGarbageDialogs.empty())
553         {
554             for (Layout* widget : mGarbageDialogs)
555             {
556                 delete widget;
557             }
558             mGarbageDialogs.clear();
559         }
560     }
561 
enableScene(bool enable)562     void WindowManager::enableScene(bool enable)
563     {
564         unsigned int disablemask = MWRender::Mask_GUI|MWRender::Mask_PreCompile;
565         if (!enable && mViewer->getCamera()->getCullMask() != disablemask)
566         {
567             mOldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask();
568             mOldCullMask = mViewer->getCamera()->getCullMask();
569             mViewer->getUpdateVisitor()->setTraversalMask(disablemask);
570             mViewer->getCamera()->setCullMask(disablemask);
571         }
572         else if (enable && mViewer->getCamera()->getCullMask() == disablemask)
573         {
574             mViewer->getUpdateVisitor()->setTraversalMask(mOldUpdateMask);
575             mViewer->getCamera()->setCullMask(mOldCullMask);
576         }
577     }
578 
updateConsoleObjectPtr(const MWWorld::Ptr & currentPtr,const MWWorld::Ptr & newPtr)579     void WindowManager::updateConsoleObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr)
580     {
581         mConsole->updateSelectedObjectPtr(currentPtr, newPtr);
582     }
583 
updateVisible()584     void WindowManager::updateVisible()
585     {
586         bool loading = (getMode() == GM_Loading || getMode() == GM_LoadingWallpaper);
587 
588         bool mainmenucover = containsMode(GM_MainMenu) && MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame;
589 
590         enableScene(!loading && !mainmenucover);
591 
592         if (!mMap)
593             return; // UI not created yet
594 
595         mHud->setVisible(mHudEnabled && !loading);
596         mToolTips->setVisible(mHudEnabled && !loading);
597 
598         bool gameMode = !isGuiMode();
599 
600         MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode);
601 
602         mInputBlocker->setVisible (gameMode);
603 
604         if (loading)
605             setCursorVisible(mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox());
606         else
607             setCursorVisible(!gameMode);
608 
609         if (gameMode)
610             setKeyFocusWidget (nullptr);
611 
612         // Icons of forced hidden windows are displayed
613         setMinimapVisibility((mAllowed & GW_Map) && (!mMap->pinned() || (mForceHidden & GW_Map)));
614         setWeaponVisibility((mAllowed & GW_Inventory) && (!mInventoryWindow->pinned() || (mForceHidden & GW_Inventory)));
615         setSpellVisibility((mAllowed & GW_Magic) && (!mSpellWindow->pinned() || (mForceHidden & GW_Magic)));
616         setHMSVisibility((mAllowed & GW_Stats) && (!mStatsWindow->pinned() || (mForceHidden & GW_Stats)));
617 
618         mInventoryWindow->setGuiMode(getMode());
619 
620         // If in game mode (or interactive messagebox), show the pinned windows
621         if (mGuiModes.empty())
622         {
623             mMap->setVisible(mMap->pinned() && !isConsoleMode() && !(mForceHidden & GW_Map) && (mAllowed & GW_Map));
624             mStatsWindow->setVisible(mStatsWindow->pinned() && !isConsoleMode() && !(mForceHidden & GW_Stats) && (mAllowed & GW_Stats));
625             mInventoryWindow->setVisible(mInventoryWindow->pinned() && !isConsoleMode() && !(mForceHidden & GW_Inventory) && (mAllowed & GW_Inventory));
626             mSpellWindow->setVisible(mSpellWindow->pinned() && !isConsoleMode() && !(mForceHidden & GW_Magic) && (mAllowed & GW_Magic));
627             return;
628         }
629         else if (getMode() != GM_Inventory)
630         {
631             mMap->setVisible(false);
632             mStatsWindow->setVisible(false);
633             mSpellWindow->setVisible(false);
634             mInventoryWindow->setVisible(getMode() == GM_Container || getMode() == GM_Barter || getMode() == GM_Companion);
635         }
636 
637         GuiMode mode = mGuiModes.back();
638 
639         mInventoryWindow->setTrading(mode == GM_Barter);
640 
641         if (getMode() == GM_Inventory)
642         {
643             // For the inventory mode, compute the effective set of windows to show.
644             // This is controlled both by what windows the
645             // user has opened/closed (the 'shown' variable) and by what
646             // windows we are allowed to show (the 'allowed' var.)
647             int eff = mShown & mAllowed & ~mForceHidden;
648             mMap->setVisible(eff & GW_Map);
649             mInventoryWindow->setVisible(eff & GW_Inventory);
650             mSpellWindow->setVisible(eff & GW_Magic);
651             mStatsWindow->setVisible(eff & GW_Stats);
652         }
653 
654         switch (mode)
655         {
656         // FIXME: refactor chargen windows to use modes properly (or not use them at all)
657         case GM_Name:
658         case GM_Race:
659         case GM_Class:
660         case GM_ClassPick:
661         case GM_ClassCreate:
662         case GM_Birth:
663         case GM_ClassGenerate:
664         case GM_Review:
665             mCharGen->spawnDialog(mode);
666             break;
667         default:
668             break;
669         }
670     }
671 
setDrowningTimeLeft(float time,float maxTime)672     void WindowManager::setDrowningTimeLeft (float time, float maxTime)
673     {
674         mHud->setDrowningTimeLeft(time, maxTime);
675     }
676 
removeDialog(Layout * dialog)677     void WindowManager::removeDialog(Layout*dialog)
678     {
679         if (!dialog)
680             return;
681         dialog->setVisible(false);
682         mGarbageDialogs.push_back(dialog);
683     }
684 
exitCurrentGuiMode()685     void WindowManager::exitCurrentGuiMode()
686     {
687         if (mDragAndDrop && mDragAndDrop->mIsOnDragAndDrop)
688         {
689             mDragAndDrop->finish();
690             return;
691         }
692 
693         GuiModeState& state = mGuiModeStates[mGuiModes.back()];
694         for (WindowBase* window : state.mWindows)
695         {
696             if (!window->exit())
697             {
698                 // unable to exit window, but give access to main menu
699                 if (!MyGUI::InputManager::getInstance().isModalAny() && getMode() != GM_MainMenu)
700                     pushGuiMode (GM_MainMenu);
701                 return;
702             }
703         }
704 
705         popGuiMode();
706     }
707 
interactiveMessageBox(const std::string & message,const std::vector<std::string> & buttons,bool block)708     void WindowManager::interactiveMessageBox(const std::string &message, const std::vector<std::string> &buttons, bool block)
709     {
710         mMessageBoxManager->createInteractiveMessageBox(message, buttons);
711         updateVisible();
712 
713         if (block)
714         {
715             Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(MWBase::Environment::get().getFrameRateLimit());
716             while (mMessageBoxManager->readPressedButton(false) == -1
717                    && !MWBase::Environment::get().getStateManager()->hasQuitRequest())
718             {
719                 const double dt = std::chrono::duration_cast<std::chrono::duration<double>>(frameRateLimiter.getLastFrameDuration()).count();
720 
721                 mKeyboardNavigation->onFrame();
722                 mMessageBoxManager->onFrame(dt);
723                 MWBase::Environment::get().getInputManager()->update(dt, true, false);
724 
725                 if (!mWindowVisible)
726                     std::this_thread::sleep_for(std::chrono::milliseconds(5));
727                 else
728                 {
729                     mViewer->eventTraversal();
730                     mViewer->updateTraversal();
731                     mViewer->renderingTraversals();
732                 }
733                 // at the time this function is called we are in the middle of a frame,
734                 // so out of order calls are necessary to get a correct frameNumber for the next frame.
735                 // refer to the advance() and frame() order in Engine::go()
736                 mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
737 
738                 frameRateLimiter.limit();
739             }
740         }
741     }
742 
messageBox(const std::string & message,enum MWGui::ShowInDialogueMode showInDialogueMode)743     void WindowManager::messageBox (const std::string& message, enum MWGui::ShowInDialogueMode showInDialogueMode)
744     {
745         if (getMode() == GM_Dialogue && showInDialogueMode != MWGui::ShowInDialogueMode_Never) {
746             mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message));
747         } else if (showInDialogueMode != MWGui::ShowInDialogueMode_Only) {
748             mMessageBoxManager->createMessageBox(message);
749         }
750     }
751 
staticMessageBox(const std::string & message)752     void WindowManager::staticMessageBox(const std::string& message)
753     {
754         mMessageBoxManager->createMessageBox(message, true);
755     }
756 
removeStaticMessageBox()757     void WindowManager::removeStaticMessageBox()
758     {
759         mMessageBoxManager->removeStaticMessageBox();
760     }
761 
readPressedButton()762     int WindowManager::readPressedButton ()
763     {
764         return mMessageBoxManager->readPressedButton();
765     }
766 
getGameSettingString(const std::string & id,const std::string & default_)767     std::string WindowManager::getGameSettingString(const std::string &id, const std::string &default_)
768     {
769         const ESM::GameSetting *setting = mStore->get<ESM::GameSetting>().search(id);
770 
771         if (setting && setting->mValue.getType()==ESM::VT_String)
772             return setting->mValue.getString();
773 
774         return default_;
775     }
776 
updateMap()777     void WindowManager::updateMap()
778     {
779         if (!mLocalMapRender)
780             return;
781 
782         MWWorld::ConstPtr player = MWMechanics::getPlayer();
783 
784         osg::Vec3f playerPosition = player.getRefData().getPosition().asVec3();
785         osg::Quat playerOrientation (-player.getRefData().getPosition().rot[2], osg::Vec3(0,0,1));
786 
787         osg::Vec3f playerdirection;
788         int x,y;
789         float u,v;
790         mLocalMapRender->updatePlayer(playerPosition, playerOrientation, u, v, x, y, playerdirection);
791 
792         if (!player.getCell()->isExterior())
793         {
794             setActiveMap(x, y, true);
795         }
796         // else: need to know the current grid center, call setActiveMap from changeCell
797 
798         mMap->setPlayerDir(playerdirection.x(), playerdirection.y());
799         mMap->setPlayerPos(x, y, u, v);
800         mHud->setPlayerDir(playerdirection.x(), playerdirection.y());
801         mHud->setPlayerPos(x, y, u, v);
802     }
803 
update(float frameDuration)804     void WindowManager::update (float frameDuration)
805     {
806         bool gameRunning = MWBase::Environment::get().getStateManager()->getState()!=
807             MWBase::StateManager::State_NoGame;
808 
809         if (gameRunning)
810             updateMap();
811 
812         if (!mGuiModes.empty())
813         {
814             GuiModeState& state = mGuiModeStates[mGuiModes.back()];
815             for (WindowBase* window : state.mWindows)
816                 window->onFrame(frameDuration);
817         }
818         else
819         {
820             // update pinned windows if visible
821             for (WindowBase* window : mGuiModeStates[GM_Inventory].mWindows)
822                 if (window->isVisible())
823                     window->onFrame(frameDuration);
824         }
825 
826         // Make sure message boxes are always in front
827         // This is an awful workaround for a series of awfully interwoven issues that couldn't be worked around
828         // in a better way because of an impressive number of even more awfully interwoven issues.
829         if (mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox() && mCurrentModals.back() != mMessageBoxManager->getInteractiveMessageBox())
830         {
831             std::vector<WindowModal*>::iterator found = std::find(mCurrentModals.begin(), mCurrentModals.end(), mMessageBoxManager->getInteractiveMessageBox());
832             if (found != mCurrentModals.end())
833             {
834                 WindowModal* msgbox = *found;
835                 std::swap(*found, mCurrentModals.back());
836                 MyGUI::InputManager::getInstance().addWidgetModal(msgbox->mMainWidget);
837                 mKeyboardNavigation->setModalWindow(msgbox->mMainWidget);
838                 mKeyboardNavigation->setDefaultFocus(msgbox->mMainWidget, msgbox->getDefaultKeyFocus());
839             }
840         }
841 
842         if (!mCurrentModals.empty())
843             mCurrentModals.back()->onFrame(frameDuration);
844 
845         mKeyboardNavigation->onFrame();
846 
847         if (mMessageBoxManager)
848             mMessageBoxManager->onFrame(frameDuration);
849 
850         mToolTips->onFrame(frameDuration);
851 
852         if (mLocalMapRender)
853             mLocalMapRender->cleanupCameras();
854 
855         if (!gameRunning)
856             return;
857 
858         // We should display message about crime only once per frame, even if there are several crimes.
859         // Otherwise we will get message spam when stealing several items via Take All button.
860         const MWWorld::Ptr player = MWMechanics::getPlayer();
861         int currentBounty = player.getClass().getNpcStats(player).getBounty();
862         if (currentBounty != mPlayerBounty)
863         {
864             if (mPlayerBounty >= 0 && currentBounty > mPlayerBounty)
865                 messageBox("#{sCrimeMessage}");
866 
867             mPlayerBounty = currentBounty;
868         }
869 
870         mDragAndDrop->onFrame();
871 
872         mHud->onFrame(frameDuration);
873 
874         mDebugWindow->onFrame(frameDuration);
875 
876         if (mCharGen)
877             mCharGen->onFrame(frameDuration);
878 
879         updateActivatedQuickKey();
880 
881         mStatsWatcher->update();
882 
883         cleanupGarbage();
884     }
885 
changeCell(const MWWorld::CellStore * cell)886     void WindowManager::changeCell(const MWWorld::CellStore* cell)
887     {
888         mMap->requestMapRender(cell);
889 
890         std::string name = MWBase::Environment::get().getWorld()->getCellName (cell);
891 
892         mMap->setCellName( name );
893         mHud->setCellName( name );
894 
895         if (cell->getCell()->isExterior())
896         {
897             if (!cell->getCell()->mName.empty())
898                 mMap->addVisitedLocation (name, cell->getCell()->getGridX (), cell->getCell()->getGridY ());
899 
900             mMap->cellExplored (cell->getCell()->getGridX(), cell->getCell()->getGridY());
901 
902             setActiveMap(cell->getCell()->getGridX(), cell->getCell()->getGridY(), false);
903         }
904         else
905         {
906             mMap->setCellPrefix (cell->getCell()->mName );
907             mHud->setCellPrefix (cell->getCell()->mName );
908 
909             osg::Vec3f worldPos;
910             if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos))
911                 worldPos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition();
912             else
913                 MWBase::Environment::get().getWorld()->getPlayer().setLastKnownExteriorPosition(worldPos);
914             mMap->setGlobalMapPlayerPosition(worldPos.x(), worldPos.y());
915 
916             setActiveMap(0, 0, true);
917         }
918     }
919 
setActiveMap(int x,int y,bool interior)920     void WindowManager::setActiveMap(int x, int y, bool interior)
921     {
922         mMap->setActiveCell(x,y, interior);
923         mHud->setActiveCell(x,y, interior);
924     }
925 
setDrowningBarVisibility(bool visible)926     void WindowManager::setDrowningBarVisibility(bool visible)
927     {
928         mHud->setDrowningBarVisible(visible);
929     }
930 
setHMSVisibility(bool visible)931     void WindowManager::setHMSVisibility(bool visible)
932     {
933         mHud->setHmsVisible (visible);
934     }
935 
setMinimapVisibility(bool visible)936     void WindowManager::setMinimapVisibility(bool visible)
937     {
938         mHud->setMinimapVisible (visible);
939     }
940 
toggleFogOfWar()941     bool WindowManager::toggleFogOfWar()
942     {
943         mMap->toggleFogOfWar();
944         return mHud->toggleFogOfWar();
945     }
946 
setFocusObject(const MWWorld::Ptr & focus)947     void WindowManager::setFocusObject(const MWWorld::Ptr& focus)
948     {
949         mToolTips->setFocusObject(focus);
950 
951         if(mHud && (mShowOwned == 2 || mShowOwned == 3))
952         {
953             bool owned = mToolTips->checkOwned();
954             mHud->setCrosshairOwned(owned);
955         }
956     }
957 
setFocusObjectScreenCoords(float min_x,float min_y,float max_x,float max_y)958     void WindowManager::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y)
959     {
960         mToolTips->setFocusObjectScreenCoords(min_x, min_y, max_x, max_y);
961     }
962 
toggleFullHelp()963     bool WindowManager::toggleFullHelp()
964     {
965         return mToolTips->toggleFullHelp();
966     }
967 
getFullHelp() const968     bool WindowManager::getFullHelp() const
969     {
970         return mToolTips->getFullHelp();
971     }
972 
setWeaponVisibility(bool visible)973     void WindowManager::setWeaponVisibility(bool visible)
974     {
975         mHud->setWeapVisible (visible);
976     }
977 
setSpellVisibility(bool visible)978     void WindowManager::setSpellVisibility(bool visible)
979     {
980         mHud->setSpellVisible (visible);
981         mHud->setEffectVisible (visible);
982     }
983 
setSneakVisibility(bool visible)984     void WindowManager::setSneakVisibility(bool visible)
985     {
986         mHud->setSneakVisible(visible);
987     }
988 
setDragDrop(bool dragDrop)989     void WindowManager::setDragDrop(bool dragDrop)
990     {
991         mToolTips->setEnabled(!dragDrop);
992         MWBase::Environment::get().getInputManager()->setDragDrop(dragDrop);
993     }
994 
setCursorVisible(bool visible)995     void WindowManager::setCursorVisible(bool visible)
996     {
997         mCursorVisible = visible;
998     }
999 
setCursorActive(bool active)1000     void WindowManager::setCursorActive(bool active)
1001     {
1002         mCursorActive = active;
1003     }
1004 
onRetrieveTag(const MyGUI::UString & _tag,MyGUI::UString & _result)1005     void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result)
1006     {
1007         std::string tag(_tag);
1008 
1009         std::string MyGuiPrefix = "setting=";
1010         size_t MyGuiPrefixLength = MyGuiPrefix.length();
1011 
1012         std::string tokenToFind = "sCell=";
1013         size_t tokenLength = tokenToFind.length();
1014 
1015         if(tag.compare(0, MyGuiPrefixLength, MyGuiPrefix) == 0)
1016         {
1017             tag = tag.substr(MyGuiPrefixLength, tag.length());
1018             size_t comma_pos = tag.find(',');
1019             std::string settingSection = tag.substr(0, comma_pos);
1020             std::string settingTag = tag.substr(comma_pos+1, tag.length());
1021 
1022             _result = Settings::Manager::getString(settingTag, settingSection);
1023         }
1024         else if (tag.compare(0, tokenLength, tokenToFind) == 0)
1025         {
1026             _result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength));
1027             _result = MyGUI::TextIterator::toTagsString(_result);
1028         }
1029         else if (Gui::replaceTag(tag, _result))
1030         {
1031             return;
1032         }
1033         else
1034         {
1035             if (!mStore)
1036             {
1037                 Log(Debug::Error) << "Error: WindowManager::onRetrieveTag: no Store set up yet, can not replace '" << tag << "'";
1038                 return;
1039             }
1040             const ESM::GameSetting *setting = mStore->get<ESM::GameSetting>().find(tag);
1041 
1042             if (setting && setting->mValue.getType()==ESM::VT_String)
1043                 _result = setting->mValue.getString();
1044             else
1045                 _result = tag;
1046         }
1047     }
1048 
processChangedSettings(const Settings::CategorySettingVector & changed)1049     void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed)
1050     {
1051         mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI"));
1052 
1053         bool changeRes = false;
1054         for (const auto& setting : changed)
1055         {
1056             if (setting.first == "HUD" && setting.second == "crosshair")
1057                 mCrosshairEnabled = Settings::Manager::getBool ("crosshair", "HUD");
1058             else if (setting.first == "GUI" && setting.second == "subtitles")
1059                 mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI");
1060             else if (setting.first == "GUI" && setting.second == "menu transparency")
1061                 setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI"));
1062             else if (setting.first == "Video" && (
1063                     setting.second == "resolution x"
1064                     || setting.second == "resolution y"
1065                     || setting.second == "fullscreen"
1066                     || setting.second == "window border"))
1067                 changeRes = true;
1068 
1069             else if (setting.first == "Video" && setting.second == "vsync")
1070                 mVideoWrapper->setSyncToVBlank(Settings::Manager::getBool("vsync", "Video"));
1071             else if (setting.first == "Video" && (setting.second == "gamma" || setting.second == "contrast"))
1072                 mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"),
1073                                                 Settings::Manager::getFloat("contrast", "Video"));
1074         }
1075 
1076         if (changeRes)
1077         {
1078             mVideoWrapper->setVideoMode(Settings::Manager::getInt("resolution x", "Video"),
1079                                         Settings::Manager::getInt("resolution y", "Video"),
1080                                         Settings::Manager::getBool("fullscreen", "Video"),
1081                                         Settings::Manager::getBool("window border", "Video"));
1082         }
1083     }
1084 
windowResized(int x,int y)1085     void WindowManager::windowResized(int x, int y)
1086     {
1087         // Note: this is a side effect of resolution change or window resize.
1088         // There is no need to track these changes.
1089         Settings::Manager::setInt("resolution x", "Video", x);
1090         Settings::Manager::setInt("resolution y", "Video", y);
1091         Settings::Manager::resetPendingChange("resolution x", "Video");
1092         Settings::Manager::resetPendingChange("resolution y", "Video");
1093 
1094         mGuiPlatform->getRenderManagerPtr()->setViewSize(x, y);
1095 
1096         // scaled size
1097         const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize();
1098         x = viewSize.width;
1099         y = viewSize.height;
1100 
1101         sizeVideo(x, y);
1102 
1103         if (!mHud)
1104             return; // UI not initialized yet
1105 
1106         for (std::map<MyGUI::Window*, std::string>::iterator it = mTrackedWindows.begin(); it != mTrackedWindows.end(); ++it)
1107         {
1108             std::string settingName = it->second;
1109             if (Settings::Manager::getBool(settingName + " maximized", "Windows"))
1110                 settingName += " maximized";
1111 
1112             MyGUI::IntPoint pos(static_cast<int>(Settings::Manager::getFloat(settingName + " x", "Windows") * x),
1113                                 static_cast<int>(Settings::Manager::getFloat(settingName + " y", "Windows") * y));
1114             MyGUI::IntSize size(static_cast<int>(Settings::Manager::getFloat(settingName + " w", "Windows") * x),
1115                                  static_cast<int>(Settings::Manager::getFloat(settingName + " h", "Windows") * y));
1116             it->first->setPosition(pos);
1117             it->first->setSize(size);
1118         }
1119 
1120         for (WindowBase* window : mWindows)
1121             window->onResChange(x, y);
1122 
1123         // We should reload TrueType fonts to fit new resolution
1124         loadUserFonts();
1125 
1126         // TODO: check if any windows are now off-screen and move them back if so
1127     }
1128 
isWindowVisible()1129     bool WindowManager::isWindowVisible()
1130     {
1131         return mWindowVisible;
1132     }
1133 
windowVisibilityChange(bool visible)1134     void WindowManager::windowVisibilityChange(bool visible)
1135     {
1136         mWindowVisible = visible;
1137     }
1138 
windowClosed()1139     void WindowManager::windowClosed()
1140     {
1141         MWBase::Environment::get().getStateManager()->requestQuit();
1142     }
1143 
onCursorChange(const std::string & name)1144     void WindowManager::onCursorChange(const std::string &name)
1145     {
1146         mCursorManager->cursorChanged(name);
1147     }
1148 
pushGuiMode(GuiMode mode)1149     void WindowManager::pushGuiMode(GuiMode mode)
1150     {
1151         pushGuiMode(mode, MWWorld::Ptr());
1152     }
1153 
pushGuiMode(GuiMode mode,const MWWorld::Ptr & arg)1154     void WindowManager::pushGuiMode(GuiMode mode, const MWWorld::Ptr& arg)
1155     {
1156         if (mode==GM_Inventory && mAllowed==GW_None)
1157             return;
1158 
1159         if (mGuiModes.empty() || mGuiModes.back() != mode)
1160         {
1161             // If this mode already exists somewhere in the stack, just bring it to the front.
1162             if (std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end())
1163             {
1164                 mGuiModes.erase(std::find(mGuiModes.begin(), mGuiModes.end(), mode));
1165             }
1166 
1167             if (!mGuiModes.empty())
1168             {
1169                 mKeyboardNavigation->saveFocus(mGuiModes.back());
1170                 mGuiModeStates[mGuiModes.back()].update(false);
1171             }
1172             mGuiModes.push_back(mode);
1173 
1174             mGuiModeStates[mode].update(true);
1175             playSound(mGuiModeStates[mode].mOpenSound);
1176         }
1177         for (WindowBase* window : mGuiModeStates[mode].mWindows)
1178             window->setPtr(arg);
1179 
1180         mKeyboardNavigation->restoreFocus(mode);
1181 
1182         updateVisible();
1183     }
1184 
popGuiMode(bool noSound)1185     void WindowManager::popGuiMode(bool noSound)
1186     {
1187         if (mDragAndDrop && mDragAndDrop->mIsOnDragAndDrop)
1188         {
1189             mDragAndDrop->finish();
1190         }
1191 
1192         if (!mGuiModes.empty())
1193         {
1194             const GuiMode mode = mGuiModes.back();
1195             mKeyboardNavigation->saveFocus(mode);
1196             mGuiModes.pop_back();
1197             mGuiModeStates[mode].update(false);
1198             if (!noSound)
1199                 playSound(mGuiModeStates[mode].mCloseSound);
1200         }
1201 
1202         if (!mGuiModes.empty())
1203         {
1204             const GuiMode mode = mGuiModes.back();
1205             mGuiModeStates[mode].update(true);
1206             mKeyboardNavigation->restoreFocus(mode);
1207         }
1208 
1209         updateVisible();
1210 
1211         // To make sure that console window get focus again
1212         if (mConsole && mConsole->isVisible())
1213             mConsole->onOpen();
1214     }
1215 
removeGuiMode(GuiMode mode,bool noSound)1216     void WindowManager::removeGuiMode(GuiMode mode, bool noSound)
1217     {
1218         if (!mGuiModes.empty() && mGuiModes.back() == mode)
1219         {
1220             popGuiMode(noSound);
1221             return;
1222         }
1223 
1224         std::vector<GuiMode>::iterator it = mGuiModes.begin();
1225         while (it != mGuiModes.end())
1226         {
1227             if (*it == mode)
1228                 it = mGuiModes.erase(it);
1229             else
1230                 ++it;
1231         }
1232 
1233         updateVisible();
1234     }
1235 
goToJail(int days)1236     void WindowManager::goToJail(int days)
1237     {
1238         pushGuiMode(MWGui::GM_Jail);
1239         mJailScreen->goToJail(days);
1240     }
1241 
setSelectedSpell(const std::string & spellId,int successChancePercent)1242     void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent)
1243     {
1244         mSelectedSpell = spellId;
1245         mSelectedEnchantItem = MWWorld::Ptr();
1246         mHud->setSelectedSpell(spellId, successChancePercent);
1247 
1248         const ESM::Spell* spell = mStore->get<ESM::Spell>().find(spellId);
1249 
1250         mSpellWindow->setTitle(spell->mName);
1251     }
1252 
setSelectedEnchantItem(const MWWorld::Ptr & item)1253     void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item)
1254     {
1255         mSelectedEnchantItem = item;
1256         mSelectedSpell = "";
1257         const ESM::Enchantment* ench = mStore->get<ESM::Enchantment>()
1258                 .find(item.getClass().getEnchantment(item));
1259 
1260         int chargePercent = static_cast<int>(item.getCellRef().getNormalizedEnchantmentCharge(ench->mData.mCharge) * 100);
1261         mHud->setSelectedEnchantItem(item, chargePercent);
1262         mSpellWindow->setTitle(item.getClass().getName(item));
1263     }
1264 
getSelectedEnchantItem() const1265     const MWWorld::Ptr &WindowManager::getSelectedEnchantItem() const
1266     {
1267         return mSelectedEnchantItem;
1268     }
1269 
setSelectedWeapon(const MWWorld::Ptr & item)1270     void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item)
1271     {
1272         mSelectedWeapon = item;
1273         int durabilityPercent = 100;
1274         if (item.getClass().hasItemHealth(item))
1275         {
1276             durabilityPercent = static_cast<int>(item.getClass().getItemNormalizedHealth(item) * 100);
1277         }
1278         mHud->setSelectedWeapon(item, durabilityPercent);
1279         mInventoryWindow->setTitle(item.getClass().getName(item));
1280     }
1281 
getSelectedWeapon() const1282     const MWWorld::Ptr &WindowManager::getSelectedWeapon() const
1283     {
1284         return mSelectedWeapon;
1285     }
1286 
unsetSelectedSpell()1287     void WindowManager::unsetSelectedSpell()
1288     {
1289         mSelectedSpell = "";
1290         mSelectedEnchantItem = MWWorld::Ptr();
1291         mHud->unsetSelectedSpell();
1292 
1293         MWWorld::Player* player = &MWBase::Environment::get().getWorld()->getPlayer();
1294         if (player->getDrawState() == MWMechanics::DrawState_Spell)
1295             player->setDrawState(MWMechanics::DrawState_Nothing);
1296 
1297         mSpellWindow->setTitle("#{sNone}");
1298     }
1299 
unsetSelectedWeapon()1300     void WindowManager::unsetSelectedWeapon()
1301     {
1302         mSelectedWeapon = MWWorld::Ptr();
1303         mHud->unsetSelectedWeapon();
1304         mInventoryWindow->setTitle("#{sSkillHandtohand}");
1305     }
1306 
getMousePosition(int & x,int & y)1307     void WindowManager::getMousePosition(int &x, int &y)
1308     {
1309         const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition();
1310         x = pos.left;
1311         y = pos.top;
1312     }
1313 
getMousePosition(float & x,float & y)1314     void WindowManager::getMousePosition(float &x, float &y)
1315     {
1316         const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition();
1317         x = static_cast<float>(pos.left);
1318         y = static_cast<float>(pos.top);
1319         const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize();
1320         x /= viewSize.width;
1321         y /= viewSize.height;
1322     }
1323 
getWorldMouseOver()1324     bool WindowManager::getWorldMouseOver()
1325     {
1326         return mHud->getWorldMouseOver();
1327     }
1328 
getScalingFactor()1329     float WindowManager::getScalingFactor()
1330     {
1331         return mScalingFactor;
1332     }
1333 
executeInConsole(const std::string & path)1334     void WindowManager::executeInConsole (const std::string& path)
1335     {
1336         mConsole->executeFile (path);
1337     }
1338 
getInventoryWindow()1339     MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; }
getCountDialog()1340     MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; }
getConfirmationDialog()1341     MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; }
getTradeWindow()1342     MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; }
1343 
useItem(const MWWorld::Ptr & item,bool bypassBeastRestrictions)1344     void WindowManager::useItem(const MWWorld::Ptr &item, bool bypassBeastRestrictions)
1345     {
1346         if (mInventoryWindow)
1347             mInventoryWindow->useItem(item, bypassBeastRestrictions);
1348     }
1349 
isAllowed(GuiWindow wnd) const1350     bool WindowManager::isAllowed (GuiWindow wnd) const
1351     {
1352         return (mAllowed & wnd) != 0;
1353     }
1354 
allow(GuiWindow wnd)1355     void WindowManager::allow (GuiWindow wnd)
1356     {
1357         mAllowed = (GuiWindow)(mAllowed | wnd);
1358 
1359         if (wnd & GW_Inventory)
1360         {
1361             mBookWindow->setInventoryAllowed (true);
1362             mScrollWindow->setInventoryAllowed (true);
1363         }
1364 
1365         updateVisible();
1366     }
1367 
disallowAll()1368     void WindowManager::disallowAll()
1369     {
1370         mAllowed = GW_None;
1371         mRestAllowed = false;
1372 
1373         mBookWindow->setInventoryAllowed (false);
1374         mScrollWindow->setInventoryAllowed (false);
1375 
1376         updateVisible();
1377     }
1378 
toggleVisible(GuiWindow wnd)1379     void WindowManager::toggleVisible (GuiWindow wnd)
1380     {
1381         if (getMode() != GM_Inventory)
1382             return;
1383 
1384         std::string settingName;
1385         switch (wnd)
1386         {
1387             case GW_Inventory:
1388                 settingName = "inventory";
1389                 break;
1390             case GW_Map:
1391                 settingName = "map";
1392                 break;
1393             case GW_Magic:
1394                 settingName = "spells";
1395                 break;
1396             case GW_Stats:
1397                 settingName = "stats";
1398                 break;
1399             default:
1400                 break;
1401         }
1402 
1403         if (!settingName.empty())
1404         {
1405             settingName += " hidden";
1406             bool hidden = Settings::Manager::getBool(settingName, "Windows");
1407             Settings::Manager::setBool(settingName, "Windows", !hidden);
1408         }
1409 
1410         mShown = (GuiWindow)(mShown ^ wnd);
1411         updateVisible();
1412     }
1413 
forceHide(GuiWindow wnd)1414     void WindowManager::forceHide(GuiWindow wnd)
1415     {
1416         mForceHidden = (GuiWindow)(mForceHidden | wnd);
1417         updateVisible();
1418     }
1419 
unsetForceHide(GuiWindow wnd)1420     void WindowManager::unsetForceHide(GuiWindow wnd)
1421     {
1422         mForceHidden = (GuiWindow)(mForceHidden & ~wnd);
1423         updateVisible();
1424     }
1425 
isGuiMode() const1426     bool WindowManager::isGuiMode() const
1427     {
1428         return
1429             !mGuiModes.empty() ||
1430             isConsoleMode() ||
1431             (mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox());
1432     }
1433 
isConsoleMode() const1434     bool WindowManager::isConsoleMode() const
1435     {
1436         return mConsole && mConsole->isVisible();
1437     }
1438 
getMode() const1439     MWGui::GuiMode WindowManager::getMode() const
1440     {
1441         if (mGuiModes.empty())
1442             return GM_None;
1443         return mGuiModes.back();
1444     }
1445 
disallowMouse()1446     void WindowManager::disallowMouse()
1447     {
1448         mInputBlocker->setVisible (true);
1449     }
1450 
allowMouse()1451     void WindowManager::allowMouse()
1452     {
1453         mInputBlocker->setVisible (!isGuiMode ());
1454     }
1455 
notifyInputActionBound()1456     void WindowManager::notifyInputActionBound ()
1457     {
1458         mSettingsWindow->updateControlsBox ();
1459         allowMouse();
1460     }
1461 
containsMode(GuiMode mode) const1462     bool WindowManager::containsMode(GuiMode mode) const
1463     {
1464         if(mGuiModes.empty())
1465             return false;
1466 
1467         return std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end();
1468     }
1469 
showCrosshair(bool show)1470     void WindowManager::showCrosshair (bool show)
1471     {
1472         if (mHud)
1473             mHud->setCrosshairVisible (show && mCrosshairEnabled);
1474     }
1475 
updateActivatedQuickKey()1476     void WindowManager::updateActivatedQuickKey ()
1477     {
1478         mQuickKeysMenu->updateActivatedQuickKey();
1479     }
1480 
activateQuickKey(int index)1481     void WindowManager::activateQuickKey (int index)
1482     {
1483         mQuickKeysMenu->activateQuickKey(index);
1484     }
1485 
getSubtitlesEnabled()1486     bool WindowManager::getSubtitlesEnabled ()
1487     {
1488         return mSubtitlesEnabled;
1489     }
1490 
toggleHud()1491     bool WindowManager::toggleHud()
1492     {
1493         mHudEnabled = !mHudEnabled;
1494         updateVisible();
1495         return mHudEnabled;
1496     }
1497 
getRestEnabled()1498     bool WindowManager::getRestEnabled()
1499     {
1500         //Enable rest dialogue if character creation finished
1501         if(mRestAllowed==false && MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1)
1502             mRestAllowed=true;
1503         return mRestAllowed;
1504     }
1505 
getPlayerSleeping()1506     bool WindowManager::getPlayerSleeping ()
1507     {
1508         return mWaitDialog->getSleeping();
1509     }
1510 
wakeUpPlayer()1511     void WindowManager::wakeUpPlayer()
1512     {
1513         mWaitDialog->wakeUp();
1514     }
1515 
addVisitedLocation(const std::string & name,int x,int y)1516     void WindowManager::addVisitedLocation(const std::string& name, int x, int y)
1517     {
1518         mMap->addVisitedLocation (name, x, y);
1519     }
1520 
getTranslationDataStorage() const1521     const Translation::Storage& WindowManager::getTranslationDataStorage() const
1522     {
1523         return mTranslationDataStorage;
1524     }
1525 
changePointer(const std::string & name)1526     void WindowManager::changePointer(const std::string &name)
1527     {
1528         MyGUI::PointerManager::getInstance().setPointer(name);
1529         onCursorChange(name);
1530     }
1531 
showSoulgemDialog(MWWorld::Ptr item)1532     void WindowManager::showSoulgemDialog(MWWorld::Ptr item)
1533     {
1534         mSoulgemDialog->show(item);
1535         updateVisible();
1536     }
1537 
updatePlayer()1538     void WindowManager::updatePlayer()
1539     {
1540         mInventoryWindow->updatePlayer();
1541 
1542         const MWWorld::Ptr player = MWMechanics::getPlayer();
1543         if (player.getClass().getNpcStats(player).isWerewolf())
1544         {
1545             setWerewolfOverlay(true);
1546             forceHide((GuiWindow)(MWGui::GW_Inventory | MWGui::GW_Magic));
1547         }
1548     }
1549 
1550     // Remove this wrapper once onKeyFocusChanged call is rendered unnecessary
setKeyFocusWidget(MyGUI::Widget * widget)1551     void WindowManager::setKeyFocusWidget(MyGUI::Widget *widget)
1552     {
1553         MyGUI::InputManager::getInstance().setKeyFocusWidget(widget);
1554         onKeyFocusChanged(widget);
1555     }
1556 
onKeyFocusChanged(MyGUI::Widget * widget)1557     void WindowManager::onKeyFocusChanged(MyGUI::Widget *widget)
1558     {
1559         if (widget && widget->castType<MyGUI::EditBox>(false))
1560             SDL_StartTextInput();
1561         else
1562             SDL_StopTextInput();
1563     }
1564 
setEnemy(const MWWorld::Ptr & enemy)1565     void WindowManager::setEnemy(const MWWorld::Ptr &enemy)
1566     {
1567         mHud->setEnemy(enemy);
1568     }
1569 
getMessagesCount() const1570     int WindowManager::getMessagesCount() const
1571     {
1572         int count = 0;
1573         if (mMessageBoxManager)
1574             count = mMessageBoxManager->getMessagesCount();
1575 
1576         return count;
1577     }
1578 
getLoadingScreen()1579     Loading::Listener* WindowManager::getLoadingScreen()
1580     {
1581         return mLoadingScreen;
1582     }
1583 
getCursorVisible()1584     bool WindowManager::getCursorVisible()
1585     {
1586         return mCursorVisible && mCursorActive;
1587     }
1588 
trackWindow(Layout * layout,const std::string & name)1589     void WindowManager::trackWindow(Layout *layout, const std::string &name)
1590     {
1591         std::string settingName = name;
1592         MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
1593         bool isMaximized = Settings::Manager::getBool(name + " maximized", "Windows");
1594         if (isMaximized)
1595             settingName += " maximized";
1596 
1597         MyGUI::IntPoint pos(static_cast<int>(Settings::Manager::getFloat(settingName + " x", "Windows") * viewSize.width),
1598                             static_cast<int>(Settings::Manager::getFloat(settingName + " y", "Windows") * viewSize.height));
1599         MyGUI::IntSize size (static_cast<int>(Settings::Manager::getFloat(settingName + " w", "Windows") * viewSize.width),
1600                              static_cast<int>(Settings::Manager::getFloat(settingName + " h", "Windows") * viewSize.height));
1601         layout->mMainWidget->setPosition(pos);
1602         layout->mMainWidget->setSize(size);
1603 
1604         MyGUI::Window* window = layout->mMainWidget->castType<MyGUI::Window>();
1605         window->eventWindowChangeCoord += MyGUI::newDelegate(this, &WindowManager::onWindowChangeCoord);
1606         mTrackedWindows[window] = name;
1607     }
1608 
toggleMaximized(Layout * layout)1609     void WindowManager::toggleMaximized(Layout *layout)
1610     {
1611         MyGUI::Window* window = layout->mMainWidget->castType<MyGUI::Window>();
1612         std::string setting = mTrackedWindows[window];
1613         if (setting.empty())
1614             return;
1615 
1616         bool maximized = !Settings::Manager::getBool(setting + " maximized", "Windows");
1617         if (maximized)
1618             setting += " maximized";
1619 
1620         MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
1621         float x = Settings::Manager::getFloat(setting + " x", "Windows") * float(viewSize.width);
1622         float y = Settings::Manager::getFloat(setting + " y", "Windows") * float(viewSize.height);
1623         float w = Settings::Manager::getFloat(setting + " w", "Windows") * float(viewSize.width);
1624         float h = Settings::Manager::getFloat(setting + " h", "Windows") * float(viewSize.height);
1625         window->setCoord(x, y, w, h);
1626         Settings::Manager::setBool(mTrackedWindows[window] + " maximized", "Windows", maximized);
1627     }
1628 
onWindowChangeCoord(MyGUI::Window * _sender)1629     void WindowManager::onWindowChangeCoord(MyGUI::Window *_sender)
1630     {
1631         std::string setting = mTrackedWindows[_sender];
1632         MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
1633         float x = _sender->getPosition().left / float(viewSize.width);
1634         float y = _sender->getPosition().top / float(viewSize.height);
1635         float w = _sender->getSize().width / float(viewSize.width);
1636         float h = _sender->getSize().height / float(viewSize.height);
1637         Settings::Manager::setFloat(setting + " x", "Windows", x);
1638         Settings::Manager::setFloat(setting + " y", "Windows", y);
1639         Settings::Manager::setFloat(setting + " w", "Windows", w);
1640         Settings::Manager::setFloat(setting + " h", "Windows", h);
1641         bool maximized = Settings::Manager::getBool(setting + " maximized", "Windows");
1642         if (maximized)
1643             Settings::Manager::setBool(setting + " maximized", "Windows", false);
1644     }
1645 
clear()1646     void WindowManager::clear()
1647     {
1648         mPlayerBounty = -1;
1649 
1650         for (WindowBase* window : mWindows)
1651             window->clear();
1652 
1653         if (mLocalMapRender)
1654             mLocalMapRender->clear();
1655 
1656         mMessageBoxManager->clear();
1657 
1658         mToolTips->clear();
1659 
1660         mSelectedSpell.clear();
1661         mCustomMarkers.clear();
1662 
1663         mForceHidden = GW_None;
1664         mRestAllowed = true;
1665 
1666         while (!mGuiModes.empty())
1667             popGuiMode();
1668 
1669         updateVisible();
1670     }
1671 
write(ESM::ESMWriter & writer,Loading::Listener & progress)1672     void WindowManager::write(ESM::ESMWriter &writer, Loading::Listener& progress)
1673     {
1674         mMap->write(writer, progress);
1675 
1676         mQuickKeysMenu->write(writer);
1677 
1678         if (!mSelectedSpell.empty())
1679         {
1680             writer.startRecord(ESM::REC_ASPL);
1681             writer.writeHNString("ID__", mSelectedSpell);
1682             writer.endRecord(ESM::REC_ASPL);
1683         }
1684 
1685         for (CustomMarkerCollection::ContainerType::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it)
1686         {
1687             writer.startRecord(ESM::REC_MARK);
1688             it->second.save(writer);
1689             writer.endRecord(ESM::REC_MARK);
1690         }
1691     }
1692 
readRecord(ESM::ESMReader & reader,uint32_t type)1693     void WindowManager::readRecord(ESM::ESMReader &reader, uint32_t type)
1694     {
1695         if (type == ESM::REC_GMAP)
1696             mMap->readRecord(reader, type);
1697         else if (type == ESM::REC_KEYS)
1698             mQuickKeysMenu->readRecord(reader, type);
1699         else if (type == ESM::REC_ASPL)
1700         {
1701             reader.getSubNameIs("ID__");
1702             std::string spell = reader.getHString();
1703             if (mStore->get<ESM::Spell>().search(spell))
1704                 mSelectedSpell = spell;
1705         }
1706         else if (type == ESM::REC_MARK)
1707         {
1708             ESM::CustomMarker marker;
1709             marker.load(reader);
1710             mCustomMarkers.addMarker(marker, false);
1711         }
1712     }
1713 
countSavedGameRecords() const1714     int WindowManager::countSavedGameRecords() const
1715     {
1716         return 1 // Global map
1717                 + 1 // QuickKeysMenu
1718                 + mCustomMarkers.size()
1719                 + (!mSelectedSpell.empty() ? 1 : 0);
1720     }
1721 
isSavingAllowed() const1722     bool WindowManager::isSavingAllowed() const
1723     {
1724         return !MyGUI::InputManager::getInstance().isModalAny()
1725                 && !isConsoleMode()
1726                 // TODO: remove this, once we have properly serialized the state of open windows
1727                 && (!isGuiMode() || (mGuiModes.size() == 1 && (getMode() == GM_MainMenu || getMode() == GM_Rest)));
1728     }
1729 
playVideo(const std::string & name,bool allowSkipping)1730     void WindowManager::playVideo(const std::string &name, bool allowSkipping)
1731     {
1732         mVideoWidget->playVideo("video\\" + name);
1733 
1734         mVideoWidget->eventKeyButtonPressed.clear();
1735         mVideoBackground->eventKeyButtonPressed.clear();
1736         if (allowSkipping)
1737         {
1738             mVideoWidget->eventKeyButtonPressed += MyGUI::newDelegate(this, &WindowManager::onVideoKeyPressed);
1739             mVideoBackground->eventKeyButtonPressed += MyGUI::newDelegate(this, &WindowManager::onVideoKeyPressed);
1740         }
1741 
1742         enableScene(false);
1743 
1744         MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize();
1745         sizeVideo(screenSize.width, screenSize.height);
1746 
1747         MyGUI::Widget* oldKeyFocus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
1748         setKeyFocusWidget(mVideoWidget);
1749 
1750         mVideoBackground->setVisible(true);
1751 
1752         bool cursorWasVisible = mCursorVisible;
1753         setCursorVisible(false);
1754 
1755         if (mVideoWidget->hasAudioStream())
1756             MWBase::Environment::get().getSoundManager()->pauseSounds(MWSound::VideoPlayback,
1757                 ~MWSound::Type::Movie & MWSound::Type::Mask
1758             );
1759 
1760         Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(MWBase::Environment::get().getFrameRateLimit());
1761         while (mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest())
1762         {
1763             const double dt = std::chrono::duration_cast<std::chrono::duration<double>>(frameRateLimiter.getLastFrameDuration()).count();
1764 
1765             MWBase::Environment::get().getInputManager()->update(dt, true, false);
1766 
1767             if (!mWindowVisible)
1768             {
1769                 mVideoWidget->pause();
1770                 std::this_thread::sleep_for(std::chrono::milliseconds(5));
1771             }
1772             else
1773             {
1774                 if (mVideoWidget->isPaused())
1775                     mVideoWidget->resume();
1776 
1777                 mViewer->eventTraversal();
1778                 mViewer->updateTraversal();
1779                 mViewer->renderingTraversals();
1780             }
1781             // at the time this function is called we are in the middle of a frame,
1782             // so out of order calls are necessary to get a correct frameNumber for the next frame.
1783             // refer to the advance() and frame() order in Engine::go()
1784             mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
1785 
1786             frameRateLimiter.limit();
1787         }
1788         mVideoWidget->stop();
1789 
1790         MWBase::Environment::get().getSoundManager()->resumeSounds(MWSound::VideoPlayback);
1791 
1792         setKeyFocusWidget(oldKeyFocus);
1793 
1794         setCursorVisible(cursorWasVisible);
1795 
1796         // Restore normal rendering
1797         updateVisible();
1798 
1799         mVideoBackground->setVisible(false);
1800     }
1801 
sizeVideo(int screenWidth,int screenHeight)1802     void WindowManager::sizeVideo(int screenWidth, int screenHeight)
1803     {
1804         // Use black bars to correct aspect ratio
1805         bool stretch = Settings::Manager::getBool("stretch menu background", "GUI");
1806         mVideoBackground->setSize(screenWidth, screenHeight);
1807         mVideoWidget->autoResize(stretch);
1808     }
1809 
exitCurrentModal()1810     void WindowManager::exitCurrentModal()
1811     {
1812         if (!mCurrentModals.empty())
1813         {
1814             WindowModal* window = mCurrentModals.back();
1815             if (!window->exit())
1816                 return;
1817             window->setVisible(false);
1818         }
1819     }
1820 
addCurrentModal(WindowModal * input)1821     void WindowManager::addCurrentModal(WindowModal *input)
1822     {
1823         if (mCurrentModals.empty())
1824             mKeyboardNavigation->saveFocus(getMode());
1825 
1826         mCurrentModals.push_back(input);
1827         mKeyboardNavigation->restoreFocus(-1);
1828 
1829         mKeyboardNavigation->setModalWindow(input->mMainWidget);
1830         mKeyboardNavigation->setDefaultFocus(input->mMainWidget, input->getDefaultKeyFocus());
1831     }
1832 
removeCurrentModal(WindowModal * input)1833     void WindowManager::removeCurrentModal(WindowModal* input)
1834     {
1835         if(!mCurrentModals.empty())
1836         {
1837             if(input == mCurrentModals.back())
1838             {
1839                 mCurrentModals.pop_back();
1840                 mKeyboardNavigation->saveFocus(-1);
1841             }
1842             else
1843             {
1844                 auto found = std::find(mCurrentModals.begin(), mCurrentModals.end(), input);
1845                 if (found != mCurrentModals.end())
1846                     mCurrentModals.erase(found);
1847                 else
1848                     Log(Debug::Warning) << "Warning: can't find modal window " << input;
1849             }
1850         }
1851         if (mCurrentModals.empty())
1852         {
1853             mKeyboardNavigation->setModalWindow(nullptr);
1854             mKeyboardNavigation->restoreFocus(getMode());
1855         }
1856         else
1857             mKeyboardNavigation->setModalWindow(mCurrentModals.back()->mMainWidget);
1858     }
1859 
onVideoKeyPressed(MyGUI::Widget * _sender,MyGUI::KeyCode _key,MyGUI::Char _char)1860     void WindowManager::onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char)
1861     {
1862         if (_key == MyGUI::KeyCode::Escape)
1863             mVideoWidget->stop();
1864     }
1865 
updatePinnedWindows()1866     void WindowManager::updatePinnedWindows()
1867     {
1868         mInventoryWindow->setPinned(Settings::Manager::getBool("inventory pin", "Windows"));
1869         if (Settings::Manager::getBool("inventory hidden", "Windows"))
1870             mShown = (GuiWindow)(mShown ^ GW_Inventory);
1871 
1872         mMap->setPinned(Settings::Manager::getBool("map pin", "Windows"));
1873         if (Settings::Manager::getBool("map hidden", "Windows"))
1874             mShown = (GuiWindow)(mShown ^ GW_Map);
1875 
1876         mSpellWindow->setPinned(Settings::Manager::getBool("spells pin", "Windows"));
1877         if (Settings::Manager::getBool("spells hidden", "Windows"))
1878             mShown = (GuiWindow)(mShown ^ GW_Magic);
1879 
1880         mStatsWindow->setPinned(Settings::Manager::getBool("stats pin", "Windows"));
1881         if (Settings::Manager::getBool("stats hidden", "Windows"))
1882             mShown = (GuiWindow)(mShown ^ GW_Stats);
1883     }
1884 
pinWindow(GuiWindow window)1885     void WindowManager::pinWindow(GuiWindow window)
1886     {
1887         switch (window)
1888         {
1889         case GW_Inventory:
1890             mInventoryWindow->setPinned(true);
1891             break;
1892         case GW_Map:
1893             mMap->setPinned(true);
1894             break;
1895         case GW_Magic:
1896             mSpellWindow->setPinned(true);
1897             break;
1898         case GW_Stats:
1899             mStatsWindow->setPinned(true);
1900             break;
1901         default:
1902             break;
1903         }
1904 
1905         updateVisible();
1906     }
1907 
fadeScreenIn(const float time,bool clearQueue,float delay)1908     void WindowManager::fadeScreenIn(const float time, bool clearQueue, float delay)
1909     {
1910         if (clearQueue)
1911             mScreenFader->clearQueue();
1912         mScreenFader->fadeOut(time, delay);
1913     }
1914 
fadeScreenOut(const float time,bool clearQueue,float delay)1915     void WindowManager::fadeScreenOut(const float time, bool clearQueue, float delay)
1916     {
1917         if (clearQueue)
1918             mScreenFader->clearQueue();
1919         mScreenFader->fadeIn(time, delay);
1920     }
1921 
fadeScreenTo(const int percent,const float time,bool clearQueue,float delay)1922     void WindowManager::fadeScreenTo(const int percent, const float time, bool clearQueue, float delay)
1923     {
1924         if (clearQueue)
1925             mScreenFader->clearQueue();
1926         mScreenFader->fadeTo(percent, time, delay);
1927     }
1928 
setBlindness(const int percent)1929     void WindowManager::setBlindness(const int percent)
1930     {
1931         mBlindnessFader->notifyAlphaChanged(percent / 100.f);
1932     }
1933 
activateHitOverlay(bool interrupt)1934     void WindowManager::activateHitOverlay(bool interrupt)
1935     {
1936         if (!mHitFaderEnabled)
1937             return;
1938 
1939         if (!interrupt && !mHitFader->isEmpty())
1940             return;
1941 
1942         mHitFader->clearQueue();
1943         mHitFader->fadeTo(100, 0.0f);
1944         mHitFader->fadeTo(0, 0.5f);
1945     }
1946 
setWerewolfOverlay(bool set)1947     void WindowManager::setWerewolfOverlay(bool set)
1948     {
1949         if (!mWerewolfOverlayEnabled)
1950             return;
1951 
1952         if (mWerewolfFader)
1953             mWerewolfFader->notifyAlphaChanged(set ? 1.0f : 0.0f);
1954     }
1955 
onClipboardChanged(const std::string & _type,const std::string & _data)1956     void WindowManager::onClipboardChanged(const std::string &_type, const std::string &_data)
1957     {
1958         if (_type == "Text")
1959             SDL_SetClipboardText(MyGUI::TextIterator::getOnlyText(MyGUI::UString(_data)).asUTF8().c_str());
1960     }
1961 
onClipboardRequested(const std::string & _type,std::string & _data)1962     void WindowManager::onClipboardRequested(const std::string &_type, std::string &_data)
1963     {
1964         if (_type != "Text")
1965             return;
1966         char* text=nullptr;
1967         text = SDL_GetClipboardText();
1968         if (text)
1969             _data = MyGUI::TextIterator::toTagsString(text);
1970 
1971         SDL_free(text);
1972     }
1973 
toggleConsole()1974     void WindowManager::toggleConsole()
1975     {
1976         bool visible = mConsole->isVisible();
1977 
1978         if (!visible && !mGuiModes.empty())
1979             mKeyboardNavigation->saveFocus(mGuiModes.back());
1980 
1981         mConsole->setVisible(!visible);
1982 
1983         if (visible && !mGuiModes.empty())
1984             mKeyboardNavigation->restoreFocus(mGuiModes.back());
1985 
1986         updateVisible();
1987     }
1988 
toggleDebugWindow()1989     void WindowManager::toggleDebugWindow()
1990     {
1991 #ifndef BT_NO_PROFILE
1992         mDebugWindow->setVisible(!mDebugWindow->isVisible());
1993 #endif
1994     }
1995 
cycleSpell(bool next)1996     void WindowManager::cycleSpell(bool next)
1997     {
1998         if (!isGuiMode())
1999             mSpellWindow->cycle(next);
2000     }
2001 
cycleWeapon(bool next)2002     void WindowManager::cycleWeapon(bool next)
2003     {
2004         if (!isGuiMode())
2005             mInventoryWindow->cycle(next);
2006     }
2007 
playSound(const std::string & soundId,float volume,float pitch)2008     void WindowManager::playSound(const std::string& soundId, float volume, float pitch)
2009     {
2010         if (soundId.empty())
2011             return;
2012 
2013         MWBase::Environment::get().getSoundManager()->playSound(soundId, volume, pitch, MWSound::Type::Sfx, MWSound::PlayMode::NoEnv);
2014     }
2015 
updateSpellWindow()2016     void WindowManager::updateSpellWindow()
2017     {
2018         if (mSpellWindow)
2019             mSpellWindow->updateSpells();
2020     }
2021 
setConsoleSelectedObject(const MWWorld::Ptr & object)2022     void WindowManager::setConsoleSelectedObject(const MWWorld::Ptr &object)
2023     {
2024         mConsole->setSelectedObject(object);
2025     }
2026 
correctIconPath(const std::string & path)2027     std::string WindowManager::correctIconPath(const std::string& path)
2028     {
2029         return Misc::ResourceHelpers::correctIconPath(path, mResourceSystem->getVFS());
2030     }
2031 
correctBookartPath(const std::string & path,int width,int height,bool * exists)2032     std::string WindowManager::correctBookartPath(const std::string& path, int width, int height, bool* exists)
2033     {
2034         std::string corrected = Misc::ResourceHelpers::correctBookartPath(path, width, height, mResourceSystem->getVFS());
2035         if (exists)
2036             *exists = mResourceSystem->getVFS()->exists(corrected);
2037         return corrected;
2038     }
2039 
correctTexturePath(const std::string & path)2040     std::string WindowManager::correctTexturePath(const std::string& path)
2041     {
2042         return Misc::ResourceHelpers::correctTexturePath(path, mResourceSystem->getVFS());
2043     }
2044 
textureExists(const std::string & path)2045     bool WindowManager::textureExists(const std::string &path)
2046     {
2047         std::string corrected = Misc::ResourceHelpers::correctTexturePath(path, mResourceSystem->getVFS());
2048         return mResourceSystem->getVFS()->exists(corrected);
2049     }
2050 
createCursors()2051     void WindowManager::createCursors()
2052     {
2053         // FIXME: currently we do not scale cursor since it is not a MyGUI widget.
2054         // In theory, we can do it manually (rescale the cursor image via osg::Imag::scaleImage() and scale the hotspot position).
2055         // Unfortunately, this apploach can lead to driver crashes on some setups (e.g. on laptops with nvidia-prime on Linux).
2056         MyGUI::ResourceManager::EnumeratorPtr enumerator = MyGUI::ResourceManager::getInstance().getEnumerator();
2057         while (enumerator.next())
2058         {
2059             MyGUI::IResource* resource = enumerator.current().second;
2060             ResourceImageSetPointerFix* imgSetPointer = resource->castType<ResourceImageSetPointerFix>(false);
2061             if (!imgSetPointer)
2062                 continue;
2063             std::string tex_name = imgSetPointer->getImageSet()->getIndexInfo(0,0).texture;
2064 
2065             osg::ref_ptr<osg::Image> image = mResourceSystem->getImageManager()->getImage(tex_name);
2066 
2067             if(image.valid())
2068             {
2069                 //everything looks good, send it to the cursor manager
2070                 Uint8 hotspot_x = imgSetPointer->getHotSpot().left;
2071                 Uint8 hotspot_y = imgSetPointer->getHotSpot().top;
2072                 int rotation = imgSetPointer->getRotation();
2073 
2074                 mCursorManager->createCursor(imgSetPointer->getResourceName(), rotation, image, hotspot_x, hotspot_y);
2075             }
2076         }
2077     }
2078 
createTextures()2079     void WindowManager::createTextures()
2080     {
2081         {
2082             MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture("white");
2083             tex->createManual(8, 8, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8);
2084             unsigned char* data = reinterpret_cast<unsigned char*>(tex->lock(MyGUI::TextureUsage::Write));
2085             for (int x=0; x<8; ++x)
2086                 for (int y=0; y<8; ++y)
2087                 {
2088                     *(data++) = 255;
2089                     *(data++) = 255;
2090                     *(data++) = 255;
2091                 }
2092             tex->unlock();
2093         }
2094 
2095         {
2096             MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture("black");
2097             tex->createManual(8, 8, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8);
2098             unsigned char* data = reinterpret_cast<unsigned char*>(tex->lock(MyGUI::TextureUsage::Write));
2099             for (int x=0; x<8; ++x)
2100                 for (int y=0; y<8; ++y)
2101                 {
2102                     *(data++) = 0;
2103                     *(data++) = 0;
2104                     *(data++) = 0;
2105                 }
2106             tex->unlock();
2107         }
2108 
2109         {
2110             MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture("transparent");
2111             tex->createManual(8, 8, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8A8);
2112             setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI"));
2113         }
2114     }
2115 
setMenuTransparency(float value)2116     void WindowManager::setMenuTransparency(float value)
2117     {
2118         MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().getTexture("transparent");
2119         unsigned char* data = reinterpret_cast<unsigned char*>(tex->lock(MyGUI::TextureUsage::Write));
2120         for (int x=0; x<8; ++x)
2121             for (int y=0; y<8; ++y)
2122             {
2123                 *(data++) = 255;
2124                 *(data++) = 255;
2125                 *(data++) = 255;
2126                 *(data++) = static_cast<unsigned char>(value*255);
2127             }
2128         tex->unlock();
2129     }
2130 
addCell(MWWorld::CellStore * cell)2131     void WindowManager::addCell(MWWorld::CellStore* cell)
2132     {
2133         mLocalMapRender->addCell(cell);
2134     }
2135 
removeCell(MWWorld::CellStore * cell)2136     void WindowManager::removeCell(MWWorld::CellStore *cell)
2137     {
2138         mLocalMapRender->removeCell(cell);
2139     }
2140 
writeFog(MWWorld::CellStore * cell)2141     void WindowManager::writeFog(MWWorld::CellStore *cell)
2142     {
2143         mLocalMapRender->saveFogOfWar(cell);
2144     }
2145 
getTextColours()2146     const MWGui::TextColours& WindowManager::getTextColours()
2147     {
2148         return mTextColours;
2149     }
2150 
injectKeyPress(MyGUI::KeyCode key,unsigned int text,bool repeat)2151     bool WindowManager::injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat)
2152     {
2153         if (!mKeyboardNavigation->injectKeyPress(key, text, repeat))
2154         {
2155             MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
2156             bool widgetActive = MyGUI::InputManager::getInstance().injectKeyPress(key, text);
2157             if (!widgetActive || !focus)
2158                 return false;
2159             // FIXME: MyGUI doesn't allow widgets to state if a given key was actually used, so make a guess
2160             if (focus->getTypeName().find("Button") != std::string::npos)
2161             {
2162                 switch (key.getValue())
2163                 {
2164                 case MyGUI::KeyCode::ArrowDown:
2165                 case MyGUI::KeyCode::ArrowUp:
2166                 case MyGUI::KeyCode::ArrowLeft:
2167                 case MyGUI::KeyCode::ArrowRight:
2168                 case MyGUI::KeyCode::Return:
2169                 case MyGUI::KeyCode::NumpadEnter:
2170                 case MyGUI::KeyCode::Space:
2171                     return true;
2172                 default:
2173                     return false;
2174                 }
2175             }
2176             return false;
2177         }
2178         else
2179             return true;
2180     }
2181 
injectKeyRelease(MyGUI::KeyCode key)2182     bool WindowManager::injectKeyRelease(MyGUI::KeyCode key)
2183     {
2184         return MyGUI::InputManager::getInstance().injectKeyRelease(key);
2185     }
2186 
update(bool visible)2187     void WindowManager::GuiModeState::update(bool visible)
2188     {
2189         for (unsigned int i=0; i<mWindows.size(); ++i)
2190             mWindows[i]->setVisible(visible);
2191     }
2192 
watchActor(const MWWorld::Ptr & ptr)2193     void WindowManager::watchActor(const MWWorld::Ptr& ptr)
2194     {
2195         mStatsWatcher->watchActor(ptr);
2196     }
2197 
getWatchedActor() const2198     MWWorld::Ptr WindowManager::getWatchedActor() const
2199     {
2200         return mStatsWatcher->getWatchedActor();
2201     }
2202 }
2203